@repobuddy/storybook 2.2.1 → 2.3.0
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 +84 -4
- package/esm/index.js +18 -20
- package/esm/storybook-addon-tag-badges/index.d.ts +43 -21
- package/package.json +8 -7
- package/src/decorators/with_story_card.tsx +22 -25
- package/src/index.ts +2 -0
- package/src/storybook-addon-tag-badges/types.ts +3 -2
- package/src/types/_extract_string_literals.ts +1 -0
- package/src/types/extends_meta.ts +37 -0
- package/src/types/extends_story_obj.ts +42 -0
- package/src/types.ts +4 -0
package/esm/index.d.ts
CHANGED
|
@@ -4,8 +4,8 @@ import { ReactNode } from "react";
|
|
|
4
4
|
import * as react_jsx_runtime0 from "react/jsx-runtime";
|
|
5
5
|
import { ClassNameProps, StyleProps } from "@just-web/css";
|
|
6
6
|
import { Args, DecoratorFunction, Renderer } from "storybook/internal/csf";
|
|
7
|
-
import { ClassValue } from "class-variance-authority/types";
|
|
8
7
|
import { Decorator, Meta, StoryContext, StoryObj, StrictArgs } from "@storybook/react-vite";
|
|
8
|
+
import { IsStringLiteral } from "type-plus";
|
|
9
9
|
export * from "@repobuddy/test";
|
|
10
10
|
|
|
11
11
|
//#region src/components/show_html.d.ts
|
|
@@ -56,9 +56,9 @@ type StoryCardProps = {
|
|
|
56
56
|
* If a function is provided, it receives the card state and default className,
|
|
57
57
|
* and should return the final className string.
|
|
58
58
|
*/
|
|
59
|
-
className?: ((state: Pick<StoryCardProps, 'status'
|
|
59
|
+
className?: ((state: Required<Pick<StoryCardProps, 'status'>> & {
|
|
60
60
|
defaultClassName: string;
|
|
61
|
-
}) => string) |
|
|
61
|
+
}) => string) | string | undefined;
|
|
62
62
|
/**
|
|
63
63
|
* Content to display in the card body.
|
|
64
64
|
* Can be any React node (string, JSX, etc.).
|
|
@@ -137,6 +137,7 @@ type StoryCardProps = {
|
|
|
137
137
|
*/
|
|
138
138
|
declare function withStoryCard<TRenderer extends Renderer = Renderer>({
|
|
139
139
|
title,
|
|
140
|
+
status,
|
|
140
141
|
content: contentProp,
|
|
141
142
|
...rest
|
|
142
143
|
}?: StoryCardProps): DecoratorFunction<TRenderer>;
|
|
@@ -645,6 +646,8 @@ declare function whenRunningInTest<TArgs = StrictArgs>(decoratorOrHandler: ((...
|
|
|
645
646
|
* @template M - The base Meta type
|
|
646
647
|
* @template E - The extension type containing tagType
|
|
647
648
|
*
|
|
649
|
+
* @deprecated use `import { ExtendsMeta } from '@repobuddy/storybook'` instead.
|
|
650
|
+
*
|
|
648
651
|
* @example
|
|
649
652
|
* ```ts
|
|
650
653
|
* // Create a generic Meta type for a project
|
|
@@ -665,6 +668,8 @@ type ExtendMeta<TCmpOrArgs, M extends Meta<TCmpOrArgs>, E extends {
|
|
|
665
668
|
* @template S - The base StoryObj type
|
|
666
669
|
* @template E - The extension type containing tagType
|
|
667
670
|
*
|
|
671
|
+
* @deprecated use `import { ExtendsStoryObj } from '@repobuddy/storybook'` instead.
|
|
672
|
+
*
|
|
668
673
|
* @example
|
|
669
674
|
* ```ts
|
|
670
675
|
* // Create a generic StoryObj type for a project
|
|
@@ -680,4 +685,79 @@ type ExtendStoryObj<TMetaOrCmpOrArgs, S extends StoryObj<TMetaOrCmpOrArgs>, E ex
|
|
|
680
685
|
tags?: Array<E['tag'] | (string & {})> | undefined;
|
|
681
686
|
};
|
|
682
687
|
//#endregion
|
|
683
|
-
|
|
688
|
+
//#region src/types/_extract_string_literals.d.ts
|
|
689
|
+
type ExtractStringLiterals<T> = T extends any ? (string extends T ? never : T) : never;
|
|
690
|
+
//#endregion
|
|
691
|
+
//#region src/types/extends_meta.d.ts
|
|
692
|
+
/**
|
|
693
|
+
* Extends the Storybook Meta type with custom tag types.
|
|
694
|
+
*
|
|
695
|
+
* This utility type allows you to extend the `tags` property of a Storybook Meta type
|
|
696
|
+
* with custom string literal types while preserving existing tag types from the base Meta.
|
|
697
|
+
*
|
|
698
|
+
* @template M - The base Meta type to extend
|
|
699
|
+
* @template E - The extension type containing a `tag` property with the custom tag types
|
|
700
|
+
*
|
|
701
|
+
* @example
|
|
702
|
+
* ```ts
|
|
703
|
+
* import type { ExtendsMeta } from '@repobuddy/storybook'
|
|
704
|
+
* import type { Args, Meta as M } from '@storybook/your-framework'
|
|
705
|
+
*
|
|
706
|
+
* // Create a generic Meta type for your project
|
|
707
|
+
* type Meta<TCmpOrArgs = Args> = ExtendsMeta<
|
|
708
|
+
* M<TCmpOrArgs>,
|
|
709
|
+
* { tag: 'new' | 'beta' | 'deprecated' }
|
|
710
|
+
* >
|
|
711
|
+
*
|
|
712
|
+
* // Use in component stories
|
|
713
|
+
* const meta: Meta<typeof Component> = {
|
|
714
|
+
* tags: ['new'], // <--- gets auto-completion for 'new' | 'beta' | 'deprecated'
|
|
715
|
+
* // ...
|
|
716
|
+
* }
|
|
717
|
+
* ```
|
|
718
|
+
*/
|
|
719
|
+
type ExtendsMeta<M extends {
|
|
720
|
+
tags?: string[] | undefined;
|
|
721
|
+
}, E extends {
|
|
722
|
+
tag: string;
|
|
723
|
+
}> = Omit<M, 'tags'> & {
|
|
724
|
+
tags?: ExtractStringLiterals<NonNullable<M['tags']>[number]> extends infer MT ? IsStringLiteral<MT> extends true ? Array<(string & {}) | MT | E['tag']> | undefined : Array<(string & {}) | E['tag']> | undefined : never;
|
|
725
|
+
};
|
|
726
|
+
//#endregion
|
|
727
|
+
//#region src/types/extends_story_obj.d.ts
|
|
728
|
+
/**
|
|
729
|
+
* Extends the Storybook StoryObj type with custom tag types.
|
|
730
|
+
*
|
|
731
|
+
* This utility type allows you to extend the `tags` property of a Storybook StoryObj type
|
|
732
|
+
* with custom string literal types while preserving existing tag types from the base StoryObj.
|
|
733
|
+
*
|
|
734
|
+
* @template S - The base StoryObj type to extend (must have an optional `tags` property)
|
|
735
|
+
* @template E - The extension type containing a `tag` property with the custom tag types
|
|
736
|
+
*
|
|
737
|
+
* @example
|
|
738
|
+
* ```ts
|
|
739
|
+
* import type { ExtendsStoryObj } from '@repobuddy/storybook'
|
|
740
|
+
* import type { Args, StoryObj as S } from '@storybook/your-framework'
|
|
741
|
+
*
|
|
742
|
+
* // Create a generic StoryObj type for your project
|
|
743
|
+
* type StoryObj<TCmpOrArgs = Args> = ExtendsStoryObj<
|
|
744
|
+
* S<TCmpOrArgs>,
|
|
745
|
+
* { tag: 'new' | 'beta' | 'deprecated' }
|
|
746
|
+
* >
|
|
747
|
+
*
|
|
748
|
+
* // Use in component stories
|
|
749
|
+
* const story: StoryObj<typeof Component> = {
|
|
750
|
+
* tags: ['new'], // <--- gets auto-completion for 'new' | 'beta' | 'deprecated'
|
|
751
|
+
* // ...
|
|
752
|
+
* }
|
|
753
|
+
* ```
|
|
754
|
+
*/
|
|
755
|
+
type ExtendsStoryObj<S extends {
|
|
756
|
+
tags?: string[] | undefined;
|
|
757
|
+
}, E extends {
|
|
758
|
+
tag: string;
|
|
759
|
+
}> = Omit<S, 'tags'> & {
|
|
760
|
+
tags?: ExtractStringLiterals<NonNullable<S['tags']>[number]> extends infer MT ? IsStringLiteral<MT> extends true ? Array<(string & {}) | MT | E['tag']> | undefined : Array<(string & {}) | E['tag']> | undefined : never;
|
|
761
|
+
};
|
|
762
|
+
//#endregion
|
|
763
|
+
export { ActionsParam, BackgroundsParam, DocsParam, ExtendMeta, ExtendStoryObj, ExtendsMeta, ExtendsStoryObj, GlobalApiBackgroundsParam, GlobalApiViewportParam, LayoutParam, ShowHtml, ShowHtmlProps, SourceProps, StoryCardProps, StorySortParam, StorybookBuiltInParams, TestParam, Viewport, ViewportParam, defineActionsParam, defineBackgroundsParam, defineDocsParam, defineLayoutParam, defineParameters, defineTestParam, defineViewportParam, showDocSource, whenRunningInTest, withStoryCard };
|
package/esm/index.js
CHANGED
|
@@ -150,7 +150,7 @@ function generateKey(prefix) {
|
|
|
150
150
|
* or fall back to the component description.
|
|
151
151
|
* - Cards are collected and displayed in the order they are defined in the decorators array.
|
|
152
152
|
*/
|
|
153
|
-
function withStoryCard({ title, content: contentProp, ...rest } = {}) {
|
|
153
|
+
function withStoryCard({ title, status = "info", content: contentProp, ...rest } = {}) {
|
|
154
154
|
return (Story, { parameters, viewMode }) => {
|
|
155
155
|
if (viewMode === "docs") return /* @__PURE__ */ jsx(Story, {});
|
|
156
156
|
const content = contentProp ?? parameters.docs?.description?.story ?? parameters.docs?.description?.component;
|
|
@@ -159,6 +159,7 @@ function withStoryCard({ title, content: contentProp, ...rest } = {}) {
|
|
|
159
159
|
Story,
|
|
160
160
|
content,
|
|
161
161
|
title,
|
|
162
|
+
status,
|
|
162
163
|
...rest
|
|
163
164
|
});
|
|
164
165
|
};
|
|
@@ -176,47 +177,44 @@ function StoryCardContainer({ children }) {
|
|
|
176
177
|
const [cards, setCards] = useState([]);
|
|
177
178
|
const contextValue = useMemo(() => ({
|
|
178
179
|
addCard(card) {
|
|
179
|
-
const
|
|
180
|
+
const key = generateKey("story-card");
|
|
180
181
|
setCards((cards$1) => [...cards$1, {
|
|
181
182
|
...card,
|
|
182
|
-
|
|
183
|
+
key
|
|
183
184
|
}]);
|
|
184
|
-
return
|
|
185
|
+
return key;
|
|
185
186
|
},
|
|
186
|
-
removeCard(
|
|
187
|
-
setCards((cards$1) => cards$1.filter((card) => card.
|
|
187
|
+
removeCard(key) {
|
|
188
|
+
setCards((cards$1) => cards$1.filter((card) => card.key !== key));
|
|
188
189
|
}
|
|
189
190
|
}), []);
|
|
190
191
|
return /* @__PURE__ */ jsx(StoryCardContext.Provider, {
|
|
191
192
|
value: contextValue,
|
|
192
193
|
children: /* @__PURE__ */ jsxs("div", {
|
|
193
194
|
className: "flex flex-col gap-2",
|
|
194
|
-
children: [cards.map(({
|
|
195
|
+
children: [cards.map(({ key, status, className, content, title }) => /* @__PURE__ */ jsxs("section", {
|
|
195
196
|
className: storyCardTheme({ status }, className),
|
|
196
197
|
children: [title && /* @__PURE__ */ jsx("h2", {
|
|
197
198
|
className: "text-lg font-bold",
|
|
198
199
|
children: title
|
|
199
200
|
}), content]
|
|
200
|
-
},
|
|
201
|
+
}, key)), children]
|
|
201
202
|
})
|
|
202
203
|
});
|
|
203
204
|
}
|
|
204
|
-
|
|
205
|
+
function storyCardTheme(state, className) {
|
|
205
206
|
const defaultClassName = storyCardVariants(state);
|
|
206
207
|
if (!className) return defaultClassName;
|
|
207
|
-
return
|
|
208
|
+
return typeof className === "function" ? className({
|
|
208
209
|
...state,
|
|
209
210
|
defaultClassName
|
|
210
|
-
}) : className);
|
|
211
|
-
}
|
|
212
|
-
const storyCardVariants = cva("flex flex-col gap-1 py-3 px-4 rounded text-black dark:text-gray-100", {
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
} },
|
|
218
|
-
defaultVariants: { status: "info" }
|
|
219
|
-
});
|
|
211
|
+
}) : twMerge(defaultClassName, className);
|
|
212
|
+
}
|
|
213
|
+
const storyCardVariants = cva("flex flex-col gap-1 py-3 px-4 rounded text-black dark:text-gray-100", { variants: { status: {
|
|
214
|
+
error: "bg-red-100 dark:bg-red-900",
|
|
215
|
+
warn: "bg-yellow-100 dark:bg-yellow-900",
|
|
216
|
+
info: "bg-sky-100 dark:bg-sky-900"
|
|
217
|
+
} } });
|
|
220
218
|
function StoryCardCollector({ Story, title, status, className, content }) {
|
|
221
219
|
const context = useContext(StoryCardContext);
|
|
222
220
|
const cardIdRef = useRef(null);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
|
|
2
2
|
import { TagBadgeParameters } from "storybook-addon-tag-badges/manager-helpers";
|
|
3
3
|
import { Args, Meta as Meta$1, StoryObj as StoryObj$1 } from "@storybook/react-vite";
|
|
4
|
+
import { IsStringLiteral } from "type-plus";
|
|
4
5
|
|
|
5
6
|
//#region src/storybook-addon-tag-badges/tag_badges.d.ts
|
|
6
7
|
type TagBadgeParameter = TagBadgeParameters[0];
|
|
@@ -44,32 +45,14 @@ declare const internalBadge: TagBadgeParameter;
|
|
|
44
45
|
declare const tagBadges: TagBadgeParameters;
|
|
45
46
|
//#endregion
|
|
46
47
|
//#region src/types.d.ts
|
|
47
|
-
/**
|
|
48
|
-
* Extends the Storybook Meta type with custom tag types
|
|
49
|
-
* @template TCmpOrArgs - The component or args type
|
|
50
|
-
* @template M - The base Meta type
|
|
51
|
-
* @template E - The extension type containing tagType
|
|
52
|
-
*
|
|
53
|
-
* @example
|
|
54
|
-
* ```ts
|
|
55
|
-
* // Create a generic Meta type for a project
|
|
56
|
-
* type Meta<TCmpOrArgs = Args> = ExtendMeta<TCmpOrArgs, Meta<TCmpOrArgs>, { tagType: 'tag1' | 'tag2' }>
|
|
57
|
-
*
|
|
58
|
-
* // Create a specific Meta type for a component
|
|
59
|
-
* type Meta = ExtendMeta<typeof Component, Meta<typeof Component>, { tagType: 'tag1' | 'tag2' }>
|
|
60
|
-
* ```
|
|
61
|
-
*/
|
|
62
|
-
type ExtendMeta<TCmpOrArgs, M extends Meta$1<TCmpOrArgs>, E extends {
|
|
63
|
-
tag: string;
|
|
64
|
-
}> = Omit<M, 'tags'> & {
|
|
65
|
-
tags?: Array<E['tag'] | (string & {})> | undefined;
|
|
66
|
-
};
|
|
67
48
|
/**
|
|
68
49
|
* Extends the Storybook StoryObj type with custom tag types
|
|
69
50
|
* @template TMetaOrCmpOrArgs - The meta, component or args type
|
|
70
51
|
* @template S - The base StoryObj type
|
|
71
52
|
* @template E - The extension type containing tagType
|
|
72
53
|
*
|
|
54
|
+
* @deprecated use `import { ExtendsStoryObj } from '@repobuddy/storybook'` instead.
|
|
55
|
+
*
|
|
73
56
|
* @example
|
|
74
57
|
* ```ts
|
|
75
58
|
* // Create a generic StoryObj type for a project
|
|
@@ -85,8 +68,47 @@ type ExtendStoryObj<TMetaOrCmpOrArgs, S extends StoryObj$1<TMetaOrCmpOrArgs>, E
|
|
|
85
68
|
tags?: Array<E['tag'] | (string & {})> | undefined;
|
|
86
69
|
};
|
|
87
70
|
//#endregion
|
|
71
|
+
//#region src/types/_extract_string_literals.d.ts
|
|
72
|
+
type ExtractStringLiterals<T> = T extends any ? (string extends T ? never : T) : never;
|
|
73
|
+
//#endregion
|
|
74
|
+
//#region src/types/extends_meta.d.ts
|
|
75
|
+
/**
|
|
76
|
+
* Extends the Storybook Meta type with custom tag types.
|
|
77
|
+
*
|
|
78
|
+
* This utility type allows you to extend the `tags` property of a Storybook Meta type
|
|
79
|
+
* with custom string literal types while preserving existing tag types from the base Meta.
|
|
80
|
+
*
|
|
81
|
+
* @template M - The base Meta type to extend
|
|
82
|
+
* @template E - The extension type containing a `tag` property with the custom tag types
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* ```ts
|
|
86
|
+
* import type { ExtendsMeta } from '@repobuddy/storybook'
|
|
87
|
+
* import type { Args, Meta as M } from '@storybook/your-framework'
|
|
88
|
+
*
|
|
89
|
+
* // Create a generic Meta type for your project
|
|
90
|
+
* type Meta<TCmpOrArgs = Args> = ExtendsMeta<
|
|
91
|
+
* M<TCmpOrArgs>,
|
|
92
|
+
* { tag: 'new' | 'beta' | 'deprecated' }
|
|
93
|
+
* >
|
|
94
|
+
*
|
|
95
|
+
* // Use in component stories
|
|
96
|
+
* const meta: Meta<typeof Component> = {
|
|
97
|
+
* tags: ['new'], // <--- gets auto-completion for 'new' | 'beta' | 'deprecated'
|
|
98
|
+
* // ...
|
|
99
|
+
* }
|
|
100
|
+
* ```
|
|
101
|
+
*/
|
|
102
|
+
type ExtendsMeta<M extends {
|
|
103
|
+
tags?: string[] | undefined;
|
|
104
|
+
}, E extends {
|
|
105
|
+
tag: string;
|
|
106
|
+
}> = Omit<M, 'tags'> & {
|
|
107
|
+
tags?: ExtractStringLiterals<NonNullable<M['tags']>[number]> extends infer MT ? IsStringLiteral<MT> extends true ? Array<(string & {}) | MT | E['tag']> | undefined : Array<(string & {}) | E['tag']> | undefined : never;
|
|
108
|
+
};
|
|
109
|
+
//#endregion
|
|
88
110
|
//#region src/storybook-addon-tag-badges/types.d.ts
|
|
89
|
-
type Meta<TCmpOrArgs = Args> =
|
|
111
|
+
type Meta<TCmpOrArgs = Args> = ExtendsMeta<Meta$1<TCmpOrArgs>, {
|
|
90
112
|
tag: TagNames;
|
|
91
113
|
}>;
|
|
92
114
|
type StoryObj<TMetaOrCmpOrArgs = Args> = ExtendStoryObj<TMetaOrCmpOrArgs, StoryObj$1<TMetaOrCmpOrArgs>, {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@repobuddy/storybook",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"description": "Storybook repo buddy",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"storybook",
|
|
@@ -54,14 +54,15 @@
|
|
|
54
54
|
"@repobuddy/test": "^1.0.0",
|
|
55
55
|
"class-variance-authority": "^0.7.1",
|
|
56
56
|
"htmlfy": "^1.0.0",
|
|
57
|
-
"tailwind-merge": "^3.4.0"
|
|
57
|
+
"tailwind-merge": "^3.4.0",
|
|
58
|
+
"type-plus": "8.0.0-beta.7"
|
|
58
59
|
},
|
|
59
60
|
"devDependencies": {
|
|
60
61
|
"@repobuddy/vitest": "^2.0.0",
|
|
61
62
|
"@storybook-community/storybook-dark-mode": "^7.0.2",
|
|
62
|
-
"@storybook/addon-docs": "^10.
|
|
63
|
-
"@storybook/addon-vitest": "^10.
|
|
64
|
-
"@storybook/react-vite": "^10.
|
|
63
|
+
"@storybook/addon-docs": "^10.1.10",
|
|
64
|
+
"@storybook/addon-vitest": "^10.1.10",
|
|
65
|
+
"@storybook/react-vite": "^10.1.10",
|
|
65
66
|
"@tailwindcss/cli": "^4.1.17",
|
|
66
67
|
"@tailwindcss/vite": "^4.1.17",
|
|
67
68
|
"@vitest/browser": "^4.0.16",
|
|
@@ -72,7 +73,7 @@
|
|
|
72
73
|
"react": "^19.2.0",
|
|
73
74
|
"react-dom": "^19.2.0",
|
|
74
75
|
"rimraf": "^6.1.0",
|
|
75
|
-
"storybook": "^10.
|
|
76
|
+
"storybook": "^10.1.10",
|
|
76
77
|
"storybook-addon-tag-badges": "^3.0.2",
|
|
77
78
|
"tailwindcss": "^4.1.17",
|
|
78
79
|
"tsdown": "^0.18.0",
|
|
@@ -81,7 +82,7 @@
|
|
|
81
82
|
},
|
|
82
83
|
"peerDependencies": {
|
|
83
84
|
"@storybook-community/storybook-dark-mode": "^7.0.0",
|
|
84
|
-
"@storybook/addon-docs": "^10.
|
|
85
|
+
"@storybook/addon-docs": "^10.1.10",
|
|
85
86
|
"storybook-addon-tag-badges": "^3.0.2"
|
|
86
87
|
},
|
|
87
88
|
"peerDependenciesMeta": {
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { cva } from 'class-variance-authority'
|
|
2
|
-
import type { ClassValue } from 'class-variance-authority/types'
|
|
3
2
|
import {
|
|
4
3
|
createContext,
|
|
5
4
|
useContext,
|
|
@@ -12,7 +11,8 @@ import {
|
|
|
12
11
|
} from 'react'
|
|
13
12
|
import type { DecoratorFunction, Renderer } from 'storybook/internal/csf'
|
|
14
13
|
import { twMerge } from 'tailwind-merge'
|
|
15
|
-
import {
|
|
14
|
+
import type { RequiredPick } from 'type-plus'
|
|
15
|
+
import { generateKey } from '../utils/generate_key.js'
|
|
16
16
|
|
|
17
17
|
export type StoryCardProps = {
|
|
18
18
|
/**
|
|
@@ -35,8 +35,8 @@ export type StoryCardProps = {
|
|
|
35
35
|
* and should return the final className string.
|
|
36
36
|
*/
|
|
37
37
|
className?:
|
|
38
|
-
| ((state: Pick<StoryCardProps, 'status'
|
|
39
|
-
|
|
|
38
|
+
| ((state: Required<Pick<StoryCardProps, 'status'>> & { defaultClassName: string }) => string)
|
|
39
|
+
| string
|
|
40
40
|
| undefined
|
|
41
41
|
/**
|
|
42
42
|
* Content to display in the card body.
|
|
@@ -117,6 +117,7 @@ export type StoryCardProps = {
|
|
|
117
117
|
*/
|
|
118
118
|
export function withStoryCard<TRenderer extends Renderer = Renderer>({
|
|
119
119
|
title,
|
|
120
|
+
status = 'info',
|
|
120
121
|
content: contentProp,
|
|
121
122
|
...rest
|
|
122
123
|
}: StoryCardProps = {}): DecoratorFunction<TRenderer> {
|
|
@@ -126,11 +127,11 @@ export function withStoryCard<TRenderer extends Renderer = Renderer>({
|
|
|
126
127
|
const content = contentProp ?? parameters.docs?.description?.story ?? parameters.docs?.description?.component
|
|
127
128
|
if (!content && !title) return <Story />
|
|
128
129
|
|
|
129
|
-
return <StoryCardContainerWrapper Story={Story} content={content} title={title} {...rest} />
|
|
130
|
+
return <StoryCardContainerWrapper Story={Story} content={content} title={title} status={status} {...rest} />
|
|
130
131
|
}
|
|
131
132
|
}
|
|
132
133
|
|
|
133
|
-
interface StoryCardContainerWrapperProps extends StoryCardProps {
|
|
134
|
+
interface StoryCardContainerWrapperProps extends RequiredPick<StoryCardProps, 'status'> {
|
|
134
135
|
Story: ComponentType
|
|
135
136
|
}
|
|
136
137
|
|
|
@@ -146,17 +147,17 @@ function StoryCardContainerWrapper({ Story, ...props }: StoryCardContainerWrappe
|
|
|
146
147
|
}
|
|
147
148
|
|
|
148
149
|
function StoryCardContainer({ children }: { children: ReactNode }) {
|
|
149
|
-
const [cards, setCards] = useState<
|
|
150
|
+
const [cards, setCards] = useState<StoryCardWithKey[]>([])
|
|
150
151
|
|
|
151
152
|
const contextValue: StoryCardContextValue = useMemo(
|
|
152
153
|
() => ({
|
|
153
154
|
addCard(card) {
|
|
154
|
-
const
|
|
155
|
-
setCards((cards) => [...cards, { ...card,
|
|
156
|
-
return
|
|
155
|
+
const key = generateKey('story-card')
|
|
156
|
+
setCards((cards) => [...cards, { ...card, key }])
|
|
157
|
+
return key
|
|
157
158
|
},
|
|
158
|
-
removeCard(
|
|
159
|
-
setCards((cards) => cards.filter((card) => card.
|
|
159
|
+
removeCard(key) {
|
|
160
|
+
setCards((cards) => cards.filter((card) => card.key !== key))
|
|
160
161
|
}
|
|
161
162
|
}),
|
|
162
163
|
[]
|
|
@@ -165,8 +166,8 @@ function StoryCardContainer({ children }: { children: ReactNode }) {
|
|
|
165
166
|
return (
|
|
166
167
|
<StoryCardContext.Provider value={contextValue}>
|
|
167
168
|
<div className="flex flex-col gap-2">
|
|
168
|
-
{cards.map(({
|
|
169
|
-
<section key={
|
|
169
|
+
{cards.map(({ key, status, className, content, title }) => (
|
|
170
|
+
<section key={key} className={storyCardTheme({ status }, className)}>
|
|
170
171
|
{title && <h2 className="text-lg font-bold">{title}</h2>}
|
|
171
172
|
{content}
|
|
172
173
|
</section>
|
|
@@ -177,15 +178,14 @@ function StoryCardContainer({ children }: { children: ReactNode }) {
|
|
|
177
178
|
)
|
|
178
179
|
}
|
|
179
180
|
|
|
180
|
-
type
|
|
181
|
+
type StoryCardWithKey = RequiredPick<StoryCardProps, 'status'> & { key: string }
|
|
181
182
|
|
|
182
|
-
|
|
183
|
+
function storyCardTheme(state: Required<Pick<StoryCardProps, 'status'>>, className: StoryCardProps['className']) {
|
|
183
184
|
const defaultClassName = storyCardVariants(state)
|
|
184
185
|
if (!className) return defaultClassName
|
|
185
|
-
return
|
|
186
|
-
defaultClassName
|
|
187
|
-
|
|
188
|
-
)
|
|
186
|
+
return typeof className === 'function'
|
|
187
|
+
? className({ ...state, defaultClassName })
|
|
188
|
+
: twMerge(defaultClassName, className)
|
|
189
189
|
}
|
|
190
190
|
|
|
191
191
|
const storyCardVariants = cva('flex flex-col gap-1 py-3 px-4 rounded text-black dark:text-gray-100', {
|
|
@@ -195,13 +195,10 @@ const storyCardVariants = cva('flex flex-col gap-1 py-3 px-4 rounded text-black
|
|
|
195
195
|
warn: 'bg-yellow-100 dark:bg-yellow-900',
|
|
196
196
|
info: 'bg-sky-100 dark:bg-sky-900'
|
|
197
197
|
}
|
|
198
|
-
},
|
|
199
|
-
defaultVariants: {
|
|
200
|
-
status: 'info'
|
|
201
198
|
}
|
|
202
199
|
})
|
|
203
200
|
|
|
204
|
-
interface StoryCardCollectorProps extends StoryCardProps {
|
|
201
|
+
interface StoryCardCollectorProps extends RequiredPick<StoryCardProps, 'status'> {
|
|
205
202
|
Story: ComponentType
|
|
206
203
|
}
|
|
207
204
|
|
|
@@ -229,7 +226,7 @@ function StoryCardCollector({ Story, title, status, className, content }: StoryC
|
|
|
229
226
|
}
|
|
230
227
|
|
|
231
228
|
interface StoryCardContextValue {
|
|
232
|
-
addCard: (card: StoryCardProps) => string
|
|
229
|
+
addCard: (card: RequiredPick<StoryCardProps, 'status'>) => string
|
|
233
230
|
removeCard: (id: string) => void
|
|
234
231
|
}
|
|
235
232
|
|
package/src/index.ts
CHANGED
|
@@ -12,3 +12,5 @@ export * from './parameters/define_viewport_param.ts'
|
|
|
12
12
|
export * from './parameters/story_sort.ts'
|
|
13
13
|
export * from './testing/decorators/when_running_in_test.tsx'
|
|
14
14
|
export type * from './types.ts'
|
|
15
|
+
export * from './types/extends_meta.ts'
|
|
16
|
+
export * from './types/extends_story_obj.ts'
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { Args, Meta as M, StoryObj as SBO } from '@storybook/react-vite'
|
|
2
|
-
import type {
|
|
2
|
+
import type { ExtendStoryObj } from '../types.js'
|
|
3
|
+
import type { ExtendsMeta } from '../types/extends_meta.js'
|
|
3
4
|
import type { TagNames } from './tag_badges.js'
|
|
4
5
|
|
|
5
|
-
export type Meta<TCmpOrArgs = Args> =
|
|
6
|
+
export type Meta<TCmpOrArgs = Args> = ExtendsMeta<M<TCmpOrArgs>, { tag: TagNames }>
|
|
6
7
|
|
|
7
8
|
export type StoryObj<TMetaOrCmpOrArgs = Args> = ExtendStoryObj<
|
|
8
9
|
TMetaOrCmpOrArgs,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type ExtractStringLiterals<T> = T extends any ? (string extends T ? never : T) : never
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { IsStringLiteral } from 'type-plus'
|
|
2
|
+
import type { ExtractStringLiterals } from './_extract_string_literals.js'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Extends the Storybook Meta type with custom tag types.
|
|
6
|
+
*
|
|
7
|
+
* This utility type allows you to extend the `tags` property of a Storybook Meta type
|
|
8
|
+
* with custom string literal types while preserving existing tag types from the base Meta.
|
|
9
|
+
*
|
|
10
|
+
* @template M - The base Meta type to extend
|
|
11
|
+
* @template E - The extension type containing a `tag` property with the custom tag types
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* import type { ExtendsMeta } from '@repobuddy/storybook'
|
|
16
|
+
* import type { Args, Meta as M } from '@storybook/your-framework'
|
|
17
|
+
*
|
|
18
|
+
* // Create a generic Meta type for your project
|
|
19
|
+
* type Meta<TCmpOrArgs = Args> = ExtendsMeta<
|
|
20
|
+
* M<TCmpOrArgs>,
|
|
21
|
+
* { tag: 'new' | 'beta' | 'deprecated' }
|
|
22
|
+
* >
|
|
23
|
+
*
|
|
24
|
+
* // Use in component stories
|
|
25
|
+
* const meta: Meta<typeof Component> = {
|
|
26
|
+
* tags: ['new'], // <--- gets auto-completion for 'new' | 'beta' | 'deprecated'
|
|
27
|
+
* // ...
|
|
28
|
+
* }
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export type ExtendsMeta<M extends { tags?: string[] | undefined }, E extends { tag: string }> = Omit<M, 'tags'> & {
|
|
32
|
+
tags?: ExtractStringLiterals<NonNullable<M['tags']>[number]> extends infer MT
|
|
33
|
+
? IsStringLiteral<MT> extends true
|
|
34
|
+
? Array<(string & {}) | MT | E['tag']> | undefined
|
|
35
|
+
: Array<(string & {}) | E['tag']> | undefined
|
|
36
|
+
: never
|
|
37
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { IsStringLiteral } from 'type-plus'
|
|
2
|
+
import type { ExtractStringLiterals } from './_extract_string_literals.js'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Extends the Storybook StoryObj type with custom tag types.
|
|
6
|
+
*
|
|
7
|
+
* This utility type allows you to extend the `tags` property of a Storybook StoryObj type
|
|
8
|
+
* with custom string literal types while preserving existing tag types from the base StoryObj.
|
|
9
|
+
*
|
|
10
|
+
* @template S - The base StoryObj type to extend (must have an optional `tags` property)
|
|
11
|
+
* @template E - The extension type containing a `tag` property with the custom tag types
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* import type { ExtendsStoryObj } from '@repobuddy/storybook'
|
|
16
|
+
* import type { Args, StoryObj as S } from '@storybook/your-framework'
|
|
17
|
+
*
|
|
18
|
+
* // Create a generic StoryObj type for your project
|
|
19
|
+
* type StoryObj<TCmpOrArgs = Args> = ExtendsStoryObj<
|
|
20
|
+
* S<TCmpOrArgs>,
|
|
21
|
+
* { tag: 'new' | 'beta' | 'deprecated' }
|
|
22
|
+
* >
|
|
23
|
+
*
|
|
24
|
+
* // Use in component stories
|
|
25
|
+
* const story: StoryObj<typeof Component> = {
|
|
26
|
+
* tags: ['new'], // <--- gets auto-completion for 'new' | 'beta' | 'deprecated'
|
|
27
|
+
* // ...
|
|
28
|
+
* }
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export type ExtendsStoryObj<
|
|
32
|
+
S extends { tags?: string[] | undefined },
|
|
33
|
+
E extends {
|
|
34
|
+
tag: string
|
|
35
|
+
}
|
|
36
|
+
> = Omit<S, 'tags'> & {
|
|
37
|
+
tags?: ExtractStringLiterals<NonNullable<S['tags']>[number]> extends infer MT
|
|
38
|
+
? IsStringLiteral<MT> extends true
|
|
39
|
+
? Array<(string & {}) | MT | E['tag']> | undefined
|
|
40
|
+
: Array<(string & {}) | E['tag']> | undefined
|
|
41
|
+
: never
|
|
42
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -6,6 +6,8 @@ import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
|
6
6
|
* @template M - The base Meta type
|
|
7
7
|
* @template E - The extension type containing tagType
|
|
8
8
|
*
|
|
9
|
+
* @deprecated use `import { ExtendsMeta } from '@repobuddy/storybook'` instead.
|
|
10
|
+
*
|
|
9
11
|
* @example
|
|
10
12
|
* ```ts
|
|
11
13
|
* // Create a generic Meta type for a project
|
|
@@ -31,6 +33,8 @@ export type ExtendMeta<
|
|
|
31
33
|
* @template S - The base StoryObj type
|
|
32
34
|
* @template E - The extension type containing tagType
|
|
33
35
|
*
|
|
36
|
+
* @deprecated use `import { ExtendsStoryObj } from '@repobuddy/storybook'` instead.
|
|
37
|
+
*
|
|
34
38
|
* @example
|
|
35
39
|
* ```ts
|
|
36
40
|
* // Create a generic StoryObj type for a project
|