@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 +1 -68
- package/esm/index.js +5 -138
- package/package.json +17 -19
- package/src/contexts/_story_card_scope.tsx +5 -1
- package/src/decorators/with_story_card.tsx +2 -1
- package/src/index.ts +0 -1
- package/styles.css +1 -1
- package/src/parameters/create_story_sort.ts +0 -216
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,
|
|
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,
|
|
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.
|
|
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.
|
|
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.
|
|
88
|
-
"@storybook/addon-docs": "^10.3.
|
|
89
|
-
"@storybook/addon-vitest": "^10.3.
|
|
90
|
-
"@storybook/react-vite": "^10.3.
|
|
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.
|
|
93
|
-
"@vitest/browser": "^4.
|
|
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.
|
|
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.
|
|
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.
|
|
104
|
+
"storybook-addon-vis": "^4.2.2",
|
|
105
105
|
"tsdown": "^0.22.0",
|
|
106
106
|
"vite": "^8.0.8",
|
|
107
|
-
"vitest": "^4.
|
|
107
|
+
"vitest": "^4.1.4"
|
|
108
108
|
},
|
|
109
109
|
"peerDependencies": {
|
|
110
|
-
"@storybook-community/storybook-dark-mode": "^7.
|
|
111
|
-
"@storybook/addon-docs": "^10.
|
|
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
|
|
115
|
-
"storybook": "^
|
|
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:
|
|
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,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
|
-
}
|