@repobuddy/storybook 2.30.0 → 2.30.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/esm/index.d.ts CHANGED
@@ -269,73 +269,6 @@ declare function withStoryCard<TRenderer extends Renderer = Renderer>({
269
269
  ...rest
270
270
  }?: WithStoryCardProps): DecoratorFunction<TRenderer>;
271
271
  //#endregion
272
- //#region src/parameters/create_story_sort.d.ts
273
- type Story$1 = {
274
- id: string;
275
- importPath: string;
276
- name: string;
277
- title: string;
278
- tags?: string[] | undefined;
279
- };
280
- type StorySortFn$1 = (a: Story$1, b: Story$1) => number;
281
- /**
282
- * A nested ordering list. Each entry is either:
283
- * - A string (matches a title segment or tag name; `'*'` is the wildcard)
284
- * - A tuple `[name, children]` for nested sub-ordering (order only)
285
- */
286
- type OrderList = Array<string | [string, OrderList]>;
287
- interface StorySortOptions {
288
- /**
289
- * Ordered list of title path segments (root-level categories).
290
- * Supports nesting via tuples and `'*'` wildcard.
291
- *
292
- * @example
293
- * ```ts
294
- * order: ['Overview', 'components', ['Pages', ['Home', 'Login']], '*', 'WIP']
295
- * ```
296
- */
297
- order?: OrderList | undefined;
298
- /**
299
- * Ordered list of story category tags.
300
- * Stories sharing the same title are sorted by their first matching tag.
301
- * `'*'` marks where stories with unlisted tags sort.
302
- *
303
- * @example
304
- * ```ts
305
- * tag: ['playground', 'use-case', 'spec', 'props', '*', 'unit', 'integration']
306
- * ```
307
- */
308
- tag?: OrderList | undefined;
309
- }
310
- /**
311
- * Creates a story sort comparator for Storybook that sorts by kind (title hierarchy)
312
- * and then by tag category within the same component.
313
- *
314
- * In Storybook 10+, `storySort` must be defined as an inline function.
315
- * Assign the result to a variable and delegate from the inline function:
316
- *
317
- * @example
318
- * ```ts
319
- * import { createStorySort } from '@repobuddy/storybook'
320
- *
321
- * const compare = createStorySort({
322
- * order: ['Overview', 'components', 'hooks', '*', 'widgets'],
323
- * tag: ['playground', 'use-case', 'example', 'spec', 'props', '*', 'edge-case', 'unit']
324
- * })
325
- *
326
- * export default {
327
- * parameters: {
328
- * options: {
329
- * storySort(a, b) {
330
- * return compare(a, b)
331
- * }
332
- * }
333
- * }
334
- * }
335
- * ```
336
- */
337
- declare function createStorySort(options?: StorySortOptions): StorySortFn$1;
338
- //#endregion
339
272
  //#region src/parameters/define_actions_param.d.ts
340
273
  interface ActionsParam {
341
274
  actions: {
@@ -1037,4 +970,4 @@ type ExtendStoryObj<TMetaOrCmpOrArgs, S extends StoryObj<TMetaOrCmpOrArgs>, E ex
1037
970
  tags?: Array<E['tag'] | (string & {})> | undefined;
1038
971
  };
1039
972
  //#endregion
1040
- export { ActionsParam, BackgroundsParam, DocsParam, ExtendMeta, ExtendStoryObj, ExtendsMeta, ExtendsStoryObj, FnToArgTypes, GlobalApiBackgroundsParam, GlobalApiViewportParam, LayoutParam, OrderList, ShowHtml, ShowHtmlProps, ShowSourceOptions, SourceProps, StoryCard, StoryCardAppearance, StoryCardParam, StoryCardProps, StoryCardStatus, StoryCardThemeState, StorySortOptions, StorySortParam, StorybookBuiltInParams, TestParam, Viewport, ViewportParam, WithStoryCardProps, createStorySort, defineActionsParam, defineBackgroundsParam, defineDocsParam, defineLayoutParam, defineParameters, defineStoryCardParam, defineTestParam, defineViewportParam, showDocSource, showSource, whenRunningInTest, withStoryCard };
973
+ export { ActionsParam, BackgroundsParam, DocsParam, ExtendMeta, ExtendStoryObj, ExtendsMeta, ExtendsStoryObj, FnToArgTypes, GlobalApiBackgroundsParam, GlobalApiViewportParam, LayoutParam, ShowHtml, ShowHtmlProps, ShowSourceOptions, SourceProps, StoryCard, StoryCardAppearance, StoryCardParam, StoryCardProps, StoryCardStatus, StoryCardThemeState, StorySortParam, StorybookBuiltInParams, TestParam, Viewport, ViewportParam, WithStoryCardProps, defineActionsParam, defineBackgroundsParam, defineDocsParam, defineLayoutParam, defineParameters, defineStoryCardParam, defineTestParam, defineViewportParam, showDocSource, showSource, whenRunningInTest, withStoryCard };
package/esm/index.js CHANGED
@@ -144,9 +144,9 @@ function StoryCardContainer({ children }) {
144
144
  });
145
145
  }
146
146
  function entryPropsEqual(a, b) {
147
- return a.Story === b.Story && a.title === b.title && a.status === b.status && a.appearance === b.appearance && a.className === b.className && a.content === b.content;
147
+ return a.Story === b.Story && a.args === b.args && a.title === b.title && a.status === b.status && a.appearance === b.appearance && a.className === b.className && a.content === b.content;
148
148
  }
149
- const StoryCardCollector = memo(function StoryCardCollector({ Story, title, status, appearance, className, content }) {
149
+ const StoryCardCollector = memo(function StoryCardCollector({ Story, args: _args, title, status, appearance, className, content }) {
150
150
  const context = useContext(StoryCardRegistryContext);
151
151
  const cardIdRef = useRef(null);
152
152
  const entry = useMemo(() => ({
@@ -349,7 +349,7 @@ function showDocSource(options) {
349
349
  */
350
350
  function withStoryCard({ title, status, appearance, content: contentProp, className, ...rest } = {}) {
351
351
  if (isRunningInTest()) return (Story) => /* @__PURE__ */ jsx(Story, {});
352
- return (Story, { parameters, viewMode }) => {
352
+ return (Story, { parameters, viewMode, args }) => {
353
353
  if (viewMode === "docs") return /* @__PURE__ */ jsx(Story, {});
354
354
  const storyCardParam = parameters.storyCard;
355
355
  const finalTitle = title ?? storyCardParam?.title;
@@ -361,6 +361,7 @@ function withStoryCard({ title, status, appearance, content: contentProp, classN
361
361
  if (!content && !finalTitle) return /* @__PURE__ */ jsx(Story, {});
362
362
  return /* @__PURE__ */ jsx(StoryCardScope, {
363
363
  Story,
364
+ args,
364
365
  content,
365
366
  title: finalTitle,
366
367
  status: finalStatus,
@@ -371,140 +372,6 @@ function withStoryCard({ title, status, appearance, content: contentProp, classN
371
372
  };
372
373
  }
373
374
  //#endregion
374
- //#region src/parameters/create_story_sort.ts
375
- function getPositionInOrder(name, order) {
376
- const wildcardIdx = order.indexOf("*");
377
- for (let i = 0; i < order.length; i++) {
378
- const entry = order[i];
379
- if ((Array.isArray(entry) ? entry[0] : entry) === name) return i;
380
- }
381
- if (wildcardIdx !== -1) return wildcardIdx;
382
- return order.length;
383
- }
384
- function getChildOrder(name, order) {
385
- for (const entry of order) if (Array.isArray(entry) && entry[0] === name) return entry[1];
386
- }
387
- function isWildcardPosition(name, order) {
388
- for (const entry of order) if ((Array.isArray(entry) ? entry[0] : entry) === name) return false;
389
- return true;
390
- }
391
- function compareByKindOrder(aTitle, bTitle, kindOrder) {
392
- const aParts = aTitle.split("/");
393
- const bParts = bTitle.split("/");
394
- let currentOrder = kindOrder;
395
- const maxDepth = Math.max(aParts.length, bParts.length);
396
- for (let depth = 0; depth < maxDepth; depth++) {
397
- const aPart = aParts[depth];
398
- const bPart = bParts[depth];
399
- if (aPart === void 0 && bPart !== void 0) return -1;
400
- if (aPart !== void 0 && bPart === void 0) return 1;
401
- if (aPart === bPart) {
402
- const childOrder = getChildOrder(aPart, currentOrder);
403
- if (childOrder) currentOrder = childOrder;
404
- else continue;
405
- continue;
406
- }
407
- const aPos = getPositionInOrder(aPart, currentOrder);
408
- const bPos = getPositionInOrder(bPart, currentOrder);
409
- if (aPos !== bPos) return aPos - bPos;
410
- const aIsWildcard = isWildcardPosition(aPart, currentOrder);
411
- const bIsWildcard = isWildcardPosition(bPart, currentOrder);
412
- if (aIsWildcard && bIsWildcard) return aPart.localeCompare(bPart, void 0, { numeric: true });
413
- return aPart.localeCompare(bPart, void 0, { numeric: true });
414
- }
415
- return 0;
416
- }
417
- function getTagPriority(tags, tagOrder) {
418
- if (!tags) return getWildcardOrEnd(tagOrder);
419
- let bestPriority = -1;
420
- for (const tag of tags) {
421
- const pos = getExplicitPosition(tag, tagOrder);
422
- if (pos !== -1 && (bestPriority === -1 || pos < bestPriority)) bestPriority = pos;
423
- }
424
- if (bestPriority === -1) return getWildcardOrEnd(tagOrder);
425
- return bestPriority;
426
- }
427
- function getExplicitPosition(name, order) {
428
- for (let i = 0; i < order.length; i++) {
429
- const entry = order[i];
430
- if ((Array.isArray(entry) ? entry[0] : entry) === name) return i;
431
- }
432
- return -1;
433
- }
434
- function getWildcardOrEnd(order) {
435
- const wildcardIdx = order.indexOf("*");
436
- return wildcardIdx !== -1 ? wildcardIdx : order.length;
437
- }
438
- /**
439
- * Creates a story sort comparator for Storybook that sorts by kind (title hierarchy)
440
- * and then by tag category within the same component.
441
- *
442
- * In Storybook 10+, `storySort` must be defined as an inline function.
443
- * Assign the result to a variable and delegate from the inline function:
444
- *
445
- * @example
446
- * ```ts
447
- * import { createStorySort } from '@repobuddy/storybook'
448
- *
449
- * const compare = createStorySort({
450
- * order: ['Overview', 'components', 'hooks', '*', 'widgets'],
451
- * tag: ['playground', 'use-case', 'example', 'spec', 'props', '*', 'edge-case', 'unit']
452
- * })
453
- *
454
- * export default {
455
- * parameters: {
456
- * options: {
457
- * storySort(a, b) {
458
- * return compare(a, b)
459
- * }
460
- * }
461
- * }
462
- * }
463
- * ```
464
- */
465
- function createStorySort(options = {}) {
466
- return (a, b) => compareStories(a, b, options);
467
- }
468
- /**
469
- * Compares two stories for sorting by kind (title hierarchy)
470
- * and then by tag category within the same component.
471
- *
472
- * Use this directly inside an inline `storySort` function in Storybook 10+,
473
- * which requires `storySort` to be defined as an inline function.
474
- *
475
- * @example
476
- * ```ts
477
- * import { compareStories, type OrderList } from '@repobuddy/storybook'
478
- *
479
- * const order: OrderList = ['Overview', 'components', '*']
480
- * const tag: OrderList = ['playground', 'use-case', '*', 'unit']
481
- *
482
- * export default {
483
- * parameters: {
484
- * options: {
485
- * storySort(a, b) {
486
- * return compareStories(a, b, { order, tag })
487
- * }
488
- * }
489
- * }
490
- * }
491
- * ```
492
- */
493
- function compareStories(a, b, options = {}) {
494
- const { order: kindOrder, tag: tagOrder } = options;
495
- if (kindOrder && a.title !== b.title) {
496
- const kindResult = compareByKindOrder(a.title, b.title, kindOrder);
497
- if (kindResult !== 0) return kindResult;
498
- }
499
- if (a.title !== b.title) return a.title.localeCompare(b.title, void 0, { numeric: true });
500
- if (tagOrder) {
501
- const aPriority = getTagPriority(a.tags, tagOrder);
502
- const bPriority = getTagPriority(b.tags, tagOrder);
503
- if (aPriority !== bPriority) return aPriority - bPriority;
504
- }
505
- return a.name.localeCompare(b.name, void 0, { numeric: true });
506
- }
507
- //#endregion
508
375
  //#region src/parameters/define_actions_param.ts
509
376
  /**
510
377
  * Defines actions parameters for Storybook stories.
@@ -723,4 +590,4 @@ function whenRunningInTest(decoratorOrHandler) {
723
590
  };
724
591
  }
725
592
  //#endregion
726
- export { ShowHtml, StoryCard, createStorySort, defineActionsParam, defineBackgroundsParam, defineDocsParam, defineLayoutParam, defineParameters, defineStoryCardParam, defineTestParam, defineViewportParam, showDocSource, showSource, whenRunningInTest, withStoryCard };
593
+ export { ShowHtml, StoryCard, defineActionsParam, defineBackgroundsParam, defineDocsParam, defineLayoutParam, defineParameters, defineStoryCardParam, defineTestParam, defineViewportParam, showDocSource, showSource, whenRunningInTest, withStoryCard };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@repobuddy/storybook",
3
- "version": "2.30.0",
3
+ "version": "2.30.2",
4
4
  "description": "Storybook repo buddy",
5
5
  "keywords": [
6
6
  "storybook",
@@ -79,40 +79,40 @@
79
79
  "class-variance-authority": "^0.7.1",
80
80
  "htmlfy": "^1.0.0",
81
81
  "tailwind-merge": "^3.4.0",
82
- "tailwindcss": "^4.2.1",
82
+ "tailwindcss": "^4.3.0",
83
83
  "type-plus": "8.0.0-beta.8"
84
84
  },
85
85
  "devDependencies": {
86
86
  "@repobuddy/vitest": "^2.1.1",
87
- "@storybook-community/storybook-dark-mode": "^7.1.0",
88
- "@storybook/addon-docs": "^10.3.5",
89
- "@storybook/addon-vitest": "^10.3.5",
90
- "@storybook/react-vite": "^10.3.5",
87
+ "@storybook-community/storybook-dark-mode": "^7.1.2",
88
+ "@storybook/addon-docs": "^10.3.6",
89
+ "@storybook/addon-vitest": "^10.3.6",
90
+ "@storybook/react-vite": "^10.3.6",
91
91
  "@tailwindcss/cli": "^4.1.17",
92
- "@tailwindcss/vite": "^4.1.17",
93
- "@vitest/browser": "^4.0.16",
92
+ "@tailwindcss/vite": "^4.3.0",
93
+ "@vitest/browser": "^4.1.6",
94
94
  "@vitest/browser-playwright": "^4.1.4",
95
- "@vitest/coverage-v8": "^4.0.16",
95
+ "@vitest/coverage-v8": "^4.1.6",
96
96
  "dedent": "^1.7.0",
97
97
  "npm-run-all2": "^8.0.4",
98
98
  "react": "^19.2.0",
99
99
  "react-dom": "^19.2.0",
100
100
  "rimraf": "^6.1.0",
101
- "storybook": "^10.3.5",
101
+ "storybook": "^10.3.6",
102
102
  "storybook-addon-code-editor": "^6.1.3",
103
103
  "storybook-addon-tag-badges": "^3.1.0",
104
- "storybook-addon-vis": "^4.0.0",
104
+ "storybook-addon-vis": "^4.2.2",
105
105
  "tsdown": "^0.22.0",
106
106
  "vite": "^8.0.8",
107
- "vitest": "^4.0.16"
107
+ "vitest": "^4.1.4"
108
108
  },
109
109
  "peerDependencies": {
110
- "@storybook-community/storybook-dark-mode": "^7.0.0",
111
- "@storybook/addon-docs": "^10.2.4",
110
+ "@storybook-community/storybook-dark-mode": "^7.1.2",
111
+ "@storybook/addon-docs": "^10.3.6",
112
112
  "@types/react": ">= 16",
113
113
  "react": ">= 16",
114
- "storybook-addon-tag-badges": "^3.0.2",
115
- "storybook": "^10.2.4"
114
+ "storybook": "^10.3.6",
115
+ "storybook-addon-tag-badges": "^3.0.2"
116
116
  },
117
117
  "peerDependenciesMeta": {
118
118
  "@storybook-community/storybook-dark-mode": {
@@ -143,9 +143,7 @@
143
143
  "sb": "storybook dev -p 6006",
144
144
  "sb:build": "storybook build",
145
145
  "test": "vitest run",
146
- "test:dark": "vitest run --config=vitest.config.dark.ts",
147
- "test:light": "vitest run --config=vitest.config.light.ts",
148
- "test:theme": "vitest run --config=vitest.config.theme.ts",
146
+ "test:browser": "vitest run --config=vitest.config.browser.ts",
149
147
  "test:types": "tsc --noEmit",
150
148
  "w": "vitest"
151
149
  }
@@ -7,7 +7,7 @@ import {
7
7
  type StoryCardRegistryContextValue
8
8
  } from './_story_card_registry_context.js'
9
9
 
10
- type StoryCardScopeProps = { Story: ComponentType } & StoryCardEntry
10
+ type StoryCardScopeProps = { Story: ComponentType; args?: Record<string, unknown> | undefined } & StoryCardEntry
11
11
 
12
12
  /**
13
13
  * Ensures a story-card collection scope: creates the root container when no context exists,
@@ -79,6 +79,7 @@ interface StoryCardCollectorProps extends StoryCardScopeProps {}
79
79
  function entryPropsEqual(a: StoryCardCollectorProps, b: StoryCardCollectorProps): boolean {
80
80
  return (
81
81
  a.Story === b.Story &&
82
+ a.args === b.args &&
82
83
  a.title === b.title &&
83
84
  a.status === b.status &&
84
85
  a.appearance === b.appearance &&
@@ -89,6 +90,9 @@ function entryPropsEqual(a: StoryCardCollectorProps, b: StoryCardCollectorProps)
89
90
 
90
91
  const StoryCardCollector = memo(function StoryCardCollector({
91
92
  Story,
93
+ // args is intentionally unused here; it is only present so that entryPropsEqual
94
+ // can compare it and invalidate the memo when Storybook controls change.
95
+ args: _args,
92
96
  title,
93
97
  status,
94
98
  appearance,
@@ -123,7 +123,7 @@ export function withStoryCard<TRenderer extends Renderer = Renderer>({
123
123
  if (isRunningInTest()) {
124
124
  return (Story) => <Story />
125
125
  }
126
- return (Story, { parameters, viewMode }) => {
126
+ return (Story, { parameters, viewMode, args }) => {
127
127
  if (viewMode === 'docs') return <Story />
128
128
 
129
129
  // Get story card config from parameters if available
@@ -144,6 +144,7 @@ export function withStoryCard<TRenderer extends Renderer = Renderer>({
144
144
  return (
145
145
  <StoryCardScope
146
146
  Story={Story}
147
+ args={args}
147
148
  content={content}
148
149
  title={finalTitle}
149
150
  status={finalStatus}
package/src/index.ts CHANGED
@@ -5,7 +5,6 @@ export * from './components/story_card.tsx'
5
5
  export * from './decorators/show_doc_source.tsx'
6
6
  export * from './decorators/show_source.tsx'
7
7
  export * from './decorators/with_story_card.tsx'
8
- export * from './parameters/create_story_sort.ts'
9
8
  export * from './parameters/define_actions_param.ts'
10
9
  export * from './parameters/define_backgrounds_param.ts'
11
10
  export * from './parameters/define_docs_param.ts'
package/styles.css CHANGED
@@ -1,4 +1,4 @@
1
- /*! tailwindcss v4.3.0 | MIT License | https://tailwindcss.com */
1
+ /*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */
2
2
  @layer properties;
3
3
  @layer theme {
4
4
  :root, :host {
@@ -1,216 +0,0 @@
1
- type Story = {
2
- id: string
3
- importPath: string
4
- name: string
5
- title: string
6
- tags?: string[] | undefined
7
- }
8
-
9
- type StorySortFn = (a: Story, b: Story) => number
10
-
11
- /**
12
- * A nested ordering list. Each entry is either:
13
- * - A string (matches a title segment or tag name; `'*'` is the wildcard)
14
- * - A tuple `[name, children]` for nested sub-ordering (order only)
15
- */
16
- export type OrderList = Array<string | [string, OrderList]>
17
-
18
- export interface StorySortOptions {
19
- /**
20
- * Ordered list of title path segments (root-level categories).
21
- * Supports nesting via tuples and `'*'` wildcard.
22
- *
23
- * @example
24
- * ```ts
25
- * order: ['Overview', 'components', ['Pages', ['Home', 'Login']], '*', 'WIP']
26
- * ```
27
- */
28
- order?: OrderList | undefined
29
-
30
- /**
31
- * Ordered list of story category tags.
32
- * Stories sharing the same title are sorted by their first matching tag.
33
- * `'*'` marks where stories with unlisted tags sort.
34
- *
35
- * @example
36
- * ```ts
37
- * tag: ['playground', 'use-case', 'spec', 'props', '*', 'unit', 'integration']
38
- * ```
39
- */
40
- tag?: OrderList | undefined
41
- }
42
-
43
- function getPositionInOrder(name: string, order: OrderList): number {
44
- const wildcardIdx = order.indexOf('*')
45
- for (let i = 0; i < order.length; i++) {
46
- const entry = order[i]
47
- const entryName = Array.isArray(entry) ? entry[0] : entry
48
- if (entryName === name) return i
49
- }
50
- if (wildcardIdx !== -1) return wildcardIdx
51
- return order.length
52
- }
53
-
54
- function getChildOrder(name: string, order: OrderList): OrderList | undefined {
55
- for (const entry of order) {
56
- if (Array.isArray(entry) && entry[0] === name) {
57
- return entry[1]
58
- }
59
- }
60
- return undefined
61
- }
62
-
63
- function isWildcardPosition(name: string, order: OrderList): boolean {
64
- for (const entry of order) {
65
- const entryName = Array.isArray(entry) ? entry[0] : entry
66
- if (entryName === name) return false
67
- }
68
- return true
69
- }
70
-
71
- function compareByKindOrder(aTitle: string, bTitle: string, kindOrder: OrderList): number {
72
- const aParts = aTitle.split('/')
73
- const bParts = bTitle.split('/')
74
- let currentOrder = kindOrder
75
-
76
- const maxDepth = Math.max(aParts.length, bParts.length)
77
- for (let depth = 0; depth < maxDepth; depth++) {
78
- const aPart = aParts[depth]
79
- const bPart = bParts[depth]
80
-
81
- if (aPart === undefined && bPart !== undefined) return -1
82
- if (aPart !== undefined && bPart === undefined) return 1
83
- if (aPart === bPart) {
84
- const childOrder = getChildOrder(aPart!, currentOrder)
85
- if (childOrder) {
86
- currentOrder = childOrder
87
- } else {
88
- continue
89
- }
90
- continue
91
- }
92
-
93
- const aPos = getPositionInOrder(aPart!, currentOrder)
94
- const bPos = getPositionInOrder(bPart!, currentOrder)
95
-
96
- if (aPos !== bPos) return aPos - bPos
97
-
98
- const aIsWildcard = isWildcardPosition(aPart!, currentOrder)
99
- const bIsWildcard = isWildcardPosition(bPart!, currentOrder)
100
- if (aIsWildcard && bIsWildcard) {
101
- return aPart!.localeCompare(bPart!, undefined, { numeric: true })
102
- }
103
-
104
- return aPart!.localeCompare(bPart!, undefined, { numeric: true })
105
- }
106
-
107
- return 0
108
- }
109
-
110
- function getTagPriority(tags: string[] | undefined, tagOrder: OrderList): number {
111
- if (!tags) return getWildcardOrEnd(tagOrder)
112
- let bestPriority = -1
113
- for (const tag of tags) {
114
- const pos = getExplicitPosition(tag, tagOrder)
115
- if (pos !== -1 && (bestPriority === -1 || pos < bestPriority)) {
116
- bestPriority = pos
117
- }
118
- }
119
- if (bestPriority === -1) return getWildcardOrEnd(tagOrder)
120
- return bestPriority
121
- }
122
-
123
- function getExplicitPosition(name: string, order: OrderList): number {
124
- for (let i = 0; i < order.length; i++) {
125
- const entry = order[i]
126
- const entryName = Array.isArray(entry) ? entry[0] : entry
127
- if (entryName === name) return i
128
- }
129
- return -1
130
- }
131
-
132
- function getWildcardOrEnd(order: OrderList): number {
133
- const wildcardIdx = order.indexOf('*')
134
- return wildcardIdx !== -1 ? wildcardIdx : order.length
135
- }
136
-
137
- /**
138
- * Creates a story sort comparator for Storybook that sorts by kind (title hierarchy)
139
- * and then by tag category within the same component.
140
- *
141
- * In Storybook 10+, `storySort` must be defined as an inline function.
142
- * Assign the result to a variable and delegate from the inline function:
143
- *
144
- * @example
145
- * ```ts
146
- * import { createStorySort } from '@repobuddy/storybook'
147
- *
148
- * const compare = createStorySort({
149
- * order: ['Overview', 'components', 'hooks', '*', 'widgets'],
150
- * tag: ['playground', 'use-case', 'example', 'spec', 'props', '*', 'edge-case', 'unit']
151
- * })
152
- *
153
- * export default {
154
- * parameters: {
155
- * options: {
156
- * storySort(a, b) {
157
- * return compare(a, b)
158
- * }
159
- * }
160
- * }
161
- * }
162
- * ```
163
- */
164
- export function createStorySort(options: StorySortOptions = {}): StorySortFn {
165
- return (a, b) => compareStories(a, b, options)
166
- }
167
-
168
- /**
169
- * Compares two stories for sorting by kind (title hierarchy)
170
- * and then by tag category within the same component.
171
- *
172
- * Use this directly inside an inline `storySort` function in Storybook 10+,
173
- * which requires `storySort` to be defined as an inline function.
174
- *
175
- * @example
176
- * ```ts
177
- * import { compareStories, type OrderList } from '@repobuddy/storybook'
178
- *
179
- * const order: OrderList = ['Overview', 'components', '*']
180
- * const tag: OrderList = ['playground', 'use-case', '*', 'unit']
181
- *
182
- * export default {
183
- * parameters: {
184
- * options: {
185
- * storySort(a, b) {
186
- * return compareStories(a, b, { order, tag })
187
- * }
188
- * }
189
- * }
190
- * }
191
- * ```
192
- */
193
- function compareStories(
194
- a: { title: string; name: string; tags?: string[] },
195
- b: { title: string; name: string; tags?: string[] },
196
- options: StorySortOptions = {}
197
- ): number {
198
- const { order: kindOrder, tag: tagOrder } = options
199
-
200
- if (kindOrder && a.title !== b.title) {
201
- const kindResult = compareByKindOrder(a.title, b.title, kindOrder)
202
- if (kindResult !== 0) return kindResult
203
- }
204
-
205
- if (a.title !== b.title) {
206
- return a.title.localeCompare(b.title, undefined, { numeric: true })
207
- }
208
-
209
- if (tagOrder) {
210
- const aPriority = getTagPriority(a.tags, tagOrder)
211
- const bPriority = getTagPriority(b.tags, tagOrder)
212
- if (aPriority !== bPriority) return aPriority - bPriority
213
- }
214
-
215
- return a.name.localeCompare(b.name, undefined, { numeric: true })
216
- }