@repobuddy/storybook 2.20.1 → 2.21.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 +2 -7
- package/esm/index.js +70 -35
- package/package.json +2 -1
- package/src/components/story_card.tsx +4 -4
- package/src/contexts/_story_card_registry_context.tsx +1 -0
- package/src/contexts/_story_card_scope.tsx +34 -21
- package/src/decorators/show_doc_source.tsx +53 -20
- package/src/decorators/with_story_card.tsx +21 -14
- package/styles.css +0 -3
package/esm/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
import { UserConfig } from "htmlfy";
|
|
3
|
+
import * as react from "react";
|
|
3
4
|
import { ReactNode } from "react";
|
|
4
5
|
import * as react_jsx_runtime0 from "react/jsx-runtime";
|
|
5
6
|
import { AnyFunction, CreateTuple, IsStringLiteral, Properties, Tail } from "type-plus";
|
|
@@ -94,13 +95,7 @@ type StoryCardThemeState = Pick<StoryCardProps, 'status' | 'appearance'> & {
|
|
|
94
95
|
* @param props - StoryCard component props
|
|
95
96
|
* @returns A section element containing the card content
|
|
96
97
|
*/
|
|
97
|
-
declare
|
|
98
|
-
status,
|
|
99
|
-
appearance,
|
|
100
|
-
className,
|
|
101
|
-
children,
|
|
102
|
-
title
|
|
103
|
-
}: StoryCardProps): react_jsx_runtime0.JSX.Element;
|
|
98
|
+
declare const StoryCard: react.NamedExoticComponent<StoryCardProps>;
|
|
104
99
|
//#endregion
|
|
105
100
|
//#region src/decorators/show_doc_source.d.ts
|
|
106
101
|
/**
|
package/esm/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
|
|
2
2
|
import { isRunningInTest } from "@repobuddy/test";
|
|
3
3
|
import { prettify } from "htmlfy";
|
|
4
|
-
import { createContext, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
4
|
+
import { createContext, memo, useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
5
5
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
6
|
import { cva } from "class-variance-authority";
|
|
7
7
|
import { twJoin, twMerge } from "tailwind-merge";
|
|
@@ -48,7 +48,7 @@ function storyCardTheme(state, className) {
|
|
|
48
48
|
if (!className) return state.defaultClassName;
|
|
49
49
|
return twMerge(typeof className === "function" ? className(state) : twJoin(state.defaultClassName, className));
|
|
50
50
|
}
|
|
51
|
-
const storyCardVariants = cva("rbsb:
|
|
51
|
+
const storyCardVariants = cva("rbsb:py-3 rbsb:px-4 rbsb:rounded rbsb:border rbsb:border-solid rbsb:text-black rbsb:dark:text-gray-100", {
|
|
52
52
|
variants: { appearance: {
|
|
53
53
|
error: "rbsb:bg-red-100 rbsb:dark:bg-red-900 rbsb:border-red-300 rbsb:dark:border-red-700",
|
|
54
54
|
warn: "rbsb:bg-yellow-100 rbsb:dark:bg-yellow-900 rbsb:border-yellow-300 rbsb:dark:border-yellow-700",
|
|
@@ -64,7 +64,7 @@ const storyCardVariants = cva("rbsb:flex rbsb:flex-col rbsb:gap-1 rbsb:py-3 rbsb
|
|
|
64
64
|
* @param props - StoryCard component props
|
|
65
65
|
* @returns A section element containing the card content
|
|
66
66
|
*/
|
|
67
|
-
function StoryCard({ status, appearance, className, children, title }) {
|
|
67
|
+
const StoryCard = memo(function StoryCard({ status, appearance, className, children, title }) {
|
|
68
68
|
const resolvedAppearance = resolveAppearance(appearance, status);
|
|
69
69
|
return /* @__PURE__ */ jsxs("section", {
|
|
70
70
|
className: storyCardTheme({
|
|
@@ -77,7 +77,7 @@ function StoryCard({ status, appearance, className, children, title }) {
|
|
|
77
77
|
children: title
|
|
78
78
|
}), children]
|
|
79
79
|
});
|
|
80
|
-
}
|
|
80
|
+
});
|
|
81
81
|
|
|
82
82
|
//#endregion
|
|
83
83
|
//#region src/utils/generate_key.ts
|
|
@@ -102,15 +102,12 @@ const StoryCardRegistryContext = createContext(null);
|
|
|
102
102
|
* Ensures a story-card collection scope: creates the root container when no context exists,
|
|
103
103
|
* otherwise renders the collector so this card participates in the existing scope.
|
|
104
104
|
*/
|
|
105
|
-
function StoryCardScope(
|
|
105
|
+
const StoryCardScope = memo(function StoryCardScope(props) {
|
|
106
106
|
const context = useContext(StoryCardRegistryContext);
|
|
107
|
-
const collector = /* @__PURE__ */ jsx(StoryCardCollector, {
|
|
108
|
-
Story,
|
|
109
|
-
...props
|
|
110
|
-
});
|
|
107
|
+
const collector = /* @__PURE__ */ jsx(StoryCardCollector, { ...props });
|
|
111
108
|
if (context === null) return /* @__PURE__ */ jsx(StoryCardContainer, { children: collector });
|
|
112
109
|
return collector;
|
|
113
|
-
}
|
|
110
|
+
});
|
|
114
111
|
function StoryCardContainer({ children }) {
|
|
115
112
|
const [cards, setCards] = useState([]);
|
|
116
113
|
const contextValue = useMemo(() => ({
|
|
@@ -124,6 +121,12 @@ function StoryCardContainer({ children }) {
|
|
|
124
121
|
},
|
|
125
122
|
remove(key) {
|
|
126
123
|
setCards((cards) => cards.filter((card) => card.key !== key));
|
|
124
|
+
},
|
|
125
|
+
update(key, card) {
|
|
126
|
+
setCards((cards) => cards.map((c) => c.key === key ? {
|
|
127
|
+
...card,
|
|
128
|
+
key
|
|
129
|
+
} : c));
|
|
127
130
|
}
|
|
128
131
|
}), []);
|
|
129
132
|
return /* @__PURE__ */ jsx(StoryCardRegistryContext.Provider, {
|
|
@@ -137,26 +140,36 @@ function StoryCardContainer({ children }) {
|
|
|
137
140
|
})
|
|
138
141
|
});
|
|
139
142
|
}
|
|
140
|
-
function StoryCardCollector({ Story, title, status, appearance, className, content }) {
|
|
143
|
+
const StoryCardCollector = memo(function StoryCardCollector({ Story, title, status, appearance, className, content }) {
|
|
141
144
|
const context = useContext(StoryCardRegistryContext);
|
|
142
145
|
const cardIdRef = useRef(null);
|
|
146
|
+
const entry = useMemo(() => ({
|
|
147
|
+
title,
|
|
148
|
+
status,
|
|
149
|
+
appearance,
|
|
150
|
+
className,
|
|
151
|
+
content
|
|
152
|
+
}), [
|
|
153
|
+
title,
|
|
154
|
+
status,
|
|
155
|
+
appearance,
|
|
156
|
+
className,
|
|
157
|
+
content
|
|
158
|
+
]);
|
|
143
159
|
useLayoutEffect(() => {
|
|
144
|
-
|
|
145
|
-
title,
|
|
146
|
-
status,
|
|
147
|
-
appearance,
|
|
148
|
-
className,
|
|
149
|
-
content
|
|
150
|
-
});
|
|
160
|
+
cardIdRef.current = context.add(entry);
|
|
151
161
|
return () => {
|
|
152
162
|
if (cardIdRef.current !== null) {
|
|
153
163
|
context.remove(cardIdRef.current);
|
|
154
164
|
cardIdRef.current = null;
|
|
155
165
|
}
|
|
156
166
|
};
|
|
157
|
-
}, []);
|
|
167
|
+
}, [context]);
|
|
168
|
+
useLayoutEffect(() => {
|
|
169
|
+
if (cardIdRef.current !== null) context.update(cardIdRef.current, entry);
|
|
170
|
+
}, [context, entry]);
|
|
158
171
|
return /* @__PURE__ */ jsx(Story, {});
|
|
159
|
-
}
|
|
172
|
+
}, (prev, next) => prev.Story === next.Story);
|
|
160
173
|
|
|
161
174
|
//#endregion
|
|
162
175
|
//#region src/decorators/show_doc_source.tsx
|
|
@@ -173,6 +186,7 @@ const channel = addons.getChannel();
|
|
|
173
186
|
* @returns A decorator function that shows the source code of a story above or below the rendered story
|
|
174
187
|
*/
|
|
175
188
|
function showDocSource(options) {
|
|
189
|
+
if (isRunningInTest()) return (Story) => /* @__PURE__ */ jsx(Story, {});
|
|
176
190
|
return (Story, { parameters: { docs, darkMode } }) => {
|
|
177
191
|
const storedItem = window.localStorage.getItem("sb-addon-themes-3");
|
|
178
192
|
const current = typeof storedItem === "string" ? JSON.parse(storedItem).current : darkMode?.current;
|
|
@@ -185,28 +199,39 @@ function showDocSource(options) {
|
|
|
185
199
|
const code = typeof options?.source === "function" ? options?.source(originalSource) : options?.source ?? originalSource;
|
|
186
200
|
const language = code === docs?.source?.originalSource ? void 0 : docs?.source?.language;
|
|
187
201
|
const isOriginalSource = code === docs?.source?.originalSource;
|
|
188
|
-
const sourceContent = /* @__PURE__ */ jsx(SyntaxHighlighter, {
|
|
202
|
+
const sourceContent = useMemo(() => /* @__PURE__ */ jsx(SyntaxHighlighter, {
|
|
203
|
+
"data-testid": "source-content",
|
|
189
204
|
language,
|
|
190
205
|
children: code
|
|
191
|
-
});
|
|
206
|
+
}), [code, language]);
|
|
192
207
|
const showBefore = options?.placement === "before";
|
|
193
|
-
const sourceCardClassName = (state) => {
|
|
208
|
+
const sourceCardClassName = useCallback((state) => {
|
|
194
209
|
const modifiedState = {
|
|
195
210
|
...state,
|
|
196
211
|
defaultClassName: twJoin(state.defaultClassName, isOriginalSource && "rbsb:bg-transparent rbsb:dark:bg-transparent")
|
|
197
212
|
};
|
|
198
213
|
const className = options?.className;
|
|
199
214
|
return typeof className === "function" ? className(modifiedState) : twJoin(modifiedState.defaultClassName, className);
|
|
200
|
-
};
|
|
215
|
+
}, [options?.className, isOriginalSource]);
|
|
201
216
|
const theme = convert(docs?.theme ?? (isDark ? themes.dark : themes.light));
|
|
217
|
+
function DocSourceCard({ children }) {
|
|
218
|
+
return /* @__PURE__ */ jsx("div", { children });
|
|
219
|
+
}
|
|
220
|
+
const scopeContent = useMemo(() => /* @__PURE__ */ jsx(DocSourceCard, { children: sourceContent }), [sourceContent]);
|
|
221
|
+
const scopeProps = useMemo(() => ({
|
|
222
|
+
Story,
|
|
223
|
+
content: scopeContent,
|
|
224
|
+
className: sourceCardClassName,
|
|
225
|
+
appearance: "source"
|
|
226
|
+
}), [
|
|
227
|
+
Story,
|
|
228
|
+
scopeContent,
|
|
229
|
+
sourceCardClassName
|
|
230
|
+
]);
|
|
231
|
+
if (isRunningInTest()) return /* @__PURE__ */ jsx(Story, {});
|
|
202
232
|
if (showBefore) return /* @__PURE__ */ jsx(ThemeProvider, {
|
|
203
233
|
theme,
|
|
204
|
-
children: /* @__PURE__ */ jsx(StoryCardScope, {
|
|
205
|
-
Story,
|
|
206
|
-
content: sourceContent,
|
|
207
|
-
className: sourceCardClassName,
|
|
208
|
-
appearance: "source"
|
|
209
|
-
})
|
|
234
|
+
children: /* @__PURE__ */ jsx(StoryCardScope, { ...scopeProps })
|
|
210
235
|
});
|
|
211
236
|
return /* @__PURE__ */ jsx(ThemeProvider, {
|
|
212
237
|
theme,
|
|
@@ -219,7 +244,7 @@ function showDocSource(options) {
|
|
|
219
244
|
children: [/* @__PURE__ */ jsx(Story, {}), /* @__PURE__ */ jsx(StoryCard, {
|
|
220
245
|
className: sourceCardClassName,
|
|
221
246
|
appearance: "source",
|
|
222
|
-
children: sourceContent
|
|
247
|
+
children: /* @__PURE__ */ jsx(DocSourceCard, { children: sourceContent })
|
|
223
248
|
})]
|
|
224
249
|
})
|
|
225
250
|
});
|
|
@@ -308,8 +333,8 @@ function showDocSource(options) {
|
|
|
308
333
|
* - The `status` option is deprecated; use `appearance` instead for the same behavior and additional variants (`source`, `output`).
|
|
309
334
|
*/
|
|
310
335
|
function withStoryCard({ title, status, appearance, content: contentProp, className, ...rest } = {}) {
|
|
336
|
+
if (isRunningInTest()) return (Story) => /* @__PURE__ */ jsx(Story, {});
|
|
311
337
|
return (Story, { parameters, viewMode }) => {
|
|
312
|
-
if (viewMode === "docs") return /* @__PURE__ */ jsx(Story, {});
|
|
313
338
|
const storyCardParam = parameters.storyCard;
|
|
314
339
|
const finalTitle = title ?? storyCardParam?.title;
|
|
315
340
|
const finalAppearance = appearance ?? storyCardParam?.appearance ?? status ?? storyCardParam?.status ?? "info";
|
|
@@ -317,8 +342,7 @@ function withStoryCard({ title, status, appearance, content: contentProp, classN
|
|
|
317
342
|
const finalContent = contentProp ?? storyCardParam?.content;
|
|
318
343
|
const finalClassName = className ?? storyCardParam?.className;
|
|
319
344
|
const content = finalContent ?? parameters.docs?.description?.story ?? parameters.docs?.description?.component;
|
|
320
|
-
|
|
321
|
-
return /* @__PURE__ */ jsx(StoryCardScope, {
|
|
345
|
+
const scopeProps = useMemo(() => ({
|
|
322
346
|
Story,
|
|
323
347
|
content,
|
|
324
348
|
title: finalTitle,
|
|
@@ -326,7 +350,18 @@ function withStoryCard({ title, status, appearance, content: contentProp, classN
|
|
|
326
350
|
appearance: finalAppearance,
|
|
327
351
|
className: finalClassName,
|
|
328
352
|
...rest
|
|
329
|
-
})
|
|
353
|
+
}), [
|
|
354
|
+
Story,
|
|
355
|
+
content,
|
|
356
|
+
finalTitle,
|
|
357
|
+
finalStatus,
|
|
358
|
+
finalAppearance,
|
|
359
|
+
finalClassName,
|
|
360
|
+
rest
|
|
361
|
+
]);
|
|
362
|
+
if (viewMode === "docs") return /* @__PURE__ */ jsx(Story, {});
|
|
363
|
+
if (!content && !finalTitle) return /* @__PURE__ */ jsx(Story, {});
|
|
364
|
+
return /* @__PURE__ */ jsx(StoryCardScope, { ...scopeProps });
|
|
330
365
|
};
|
|
331
366
|
}
|
|
332
367
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@repobuddy/storybook",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.21.0",
|
|
4
4
|
"description": "Storybook repo buddy",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"storybook",
|
|
@@ -92,6 +92,7 @@
|
|
|
92
92
|
"storybook": "^10.2.8",
|
|
93
93
|
"storybook-addon-code-editor": "^6.1.3",
|
|
94
94
|
"storybook-addon-tag-badges": "^3.0.6",
|
|
95
|
+
"storybook-addon-vis": "^3.1.2",
|
|
95
96
|
"tailwindcss": "^4.1.17",
|
|
96
97
|
"tsdown": "^0.20.0",
|
|
97
98
|
"vite": "^7.3.0",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { cva } from 'class-variance-authority'
|
|
2
|
-
import type
|
|
2
|
+
import { memo, type ReactNode } from 'react'
|
|
3
3
|
import { twJoin, twMerge } from 'tailwind-merge'
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -67,7 +67,7 @@ function storyCardTheme(state: StoryCardThemeState, className: StoryCardProps['c
|
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
const storyCardVariants = cva(
|
|
70
|
-
'rbsb:
|
|
70
|
+
'rbsb:py-3 rbsb:px-4 rbsb:rounded rbsb:border rbsb:border-solid rbsb:text-black rbsb:dark:text-gray-100',
|
|
71
71
|
{
|
|
72
72
|
variants: {
|
|
73
73
|
appearance: {
|
|
@@ -90,7 +90,7 @@ const storyCardVariants = cva(
|
|
|
90
90
|
* @param props - StoryCard component props
|
|
91
91
|
* @returns A section element containing the card content
|
|
92
92
|
*/
|
|
93
|
-
export function StoryCard({ status, appearance, className, children, title }: StoryCardProps) {
|
|
93
|
+
export const StoryCard = memo(function StoryCard({ status, appearance, className, children, title }: StoryCardProps) {
|
|
94
94
|
const resolvedAppearance = resolveAppearance(appearance, status)
|
|
95
95
|
const state: StoryCardThemeState = {
|
|
96
96
|
status,
|
|
@@ -103,4 +103,4 @@ export function StoryCard({ status, appearance, className, children, title }: St
|
|
|
103
103
|
{children}
|
|
104
104
|
</section>
|
|
105
105
|
)
|
|
106
|
-
}
|
|
106
|
+
})
|
|
@@ -11,6 +11,7 @@ export type StoryCardEntry = Omit<StoryCardProps, 'children'> & { content?: Reac
|
|
|
11
11
|
export interface StoryCardRegistryContextValue {
|
|
12
12
|
add: (card: StoryCardEntry) => string
|
|
13
13
|
remove: (id: string) => void
|
|
14
|
+
update: (id: string, card: StoryCardEntry) => void
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
export const StoryCardRegistryContext = createContext<StoryCardRegistryContextValue | null>(null)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type ComponentType, type ReactNode, useContext, useLayoutEffect, useMemo, useRef, useState } from 'react'
|
|
1
|
+
import { type ComponentType, memo, type ReactNode, useContext, useLayoutEffect, useMemo, useRef, useState } from 'react'
|
|
2
2
|
import { StoryCard } from '../components/story_card.js'
|
|
3
3
|
import { generateKey } from '../utils/generate_key.js'
|
|
4
4
|
import {
|
|
@@ -13,16 +13,16 @@ export type StoryCardScopeProps = { Story: ComponentType } & StoryCardEntry
|
|
|
13
13
|
* Ensures a story-card collection scope: creates the root container when no context exists,
|
|
14
14
|
* otherwise renders the collector so this card participates in the existing scope.
|
|
15
15
|
*/
|
|
16
|
-
export function StoryCardScope(
|
|
16
|
+
export const StoryCardScope = memo(function StoryCardScope(props: StoryCardScopeProps) {
|
|
17
17
|
const context = useContext(StoryCardRegistryContext)
|
|
18
|
-
const collector = <StoryCardCollector
|
|
18
|
+
const collector = <StoryCardCollector {...props} />
|
|
19
19
|
|
|
20
20
|
if (context === null) {
|
|
21
21
|
return <StoryCardContainer>{collector}</StoryCardContainer>
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
return collector
|
|
25
|
-
}
|
|
25
|
+
})
|
|
26
26
|
|
|
27
27
|
function StoryCardContainer({ children }: { children: ReactNode }) {
|
|
28
28
|
const [cards, setCards] = useState<StoryCardEntryWithKey[]>([])
|
|
@@ -36,6 +36,9 @@ function StoryCardContainer({ children }: { children: ReactNode }) {
|
|
|
36
36
|
},
|
|
37
37
|
remove(key) {
|
|
38
38
|
setCards((cards) => cards.filter((card) => card.key !== key))
|
|
39
|
+
},
|
|
40
|
+
update(key, card) {
|
|
41
|
+
setCards((cards) => cards.map((c) => (c.key === key ? { ...card, key } : c)))
|
|
39
42
|
}
|
|
40
43
|
}),
|
|
41
44
|
[]
|
|
@@ -59,25 +62,35 @@ type StoryCardEntryWithKey = StoryCardEntry & { key: string }
|
|
|
59
62
|
|
|
60
63
|
interface StoryCardCollectorProps extends StoryCardScopeProps {}
|
|
61
64
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
65
|
+
const StoryCardCollector = memo(
|
|
66
|
+
function StoryCardCollector({ Story, title, status, appearance, className, content }: StoryCardCollectorProps) {
|
|
67
|
+
const context = useContext(StoryCardRegistryContext)!
|
|
68
|
+
const cardIdRef = useRef<string | null>(null)
|
|
66
69
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
cardIdRef.current = context.add({ title, status, appearance, className, content })
|
|
72
|
-
}
|
|
70
|
+
const entry = useMemo(
|
|
71
|
+
() => ({ title, status, appearance, className, content }),
|
|
72
|
+
[title, status, appearance, className, content]
|
|
73
|
+
)
|
|
73
74
|
|
|
74
|
-
|
|
75
|
+
// Register on mount, unregister on unmount only
|
|
76
|
+
useLayoutEffect(() => {
|
|
77
|
+
cardIdRef.current = context.add(entry)
|
|
78
|
+
return () => {
|
|
79
|
+
if (cardIdRef.current !== null) {
|
|
80
|
+
context.remove(cardIdRef.current)
|
|
81
|
+
cardIdRef.current = null
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}, [context])
|
|
85
|
+
|
|
86
|
+
// Update registry when entry changes (avoids remove+add churn)
|
|
87
|
+
useLayoutEffect(() => {
|
|
75
88
|
if (cardIdRef.current !== null) {
|
|
76
|
-
context.
|
|
77
|
-
cardIdRef.current = null
|
|
89
|
+
context.update(cardIdRef.current, entry)
|
|
78
90
|
}
|
|
79
|
-
}
|
|
80
|
-
}, [])
|
|
91
|
+
}, [context, entry])
|
|
81
92
|
|
|
82
|
-
|
|
83
|
-
}
|
|
93
|
+
return <Story />
|
|
94
|
+
},
|
|
95
|
+
(prev, next) => prev.Story === next.Story
|
|
96
|
+
)
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isRunningInTest } from '@repobuddy/test'
|
|
2
|
+
import { type ReactNode, useCallback, useEffect, useMemo, useState } from 'react'
|
|
2
3
|
import { SyntaxHighlighter } from 'storybook/internal/components'
|
|
3
4
|
import type { Args, DecoratorFunction, Renderer } from 'storybook/internal/csf'
|
|
4
5
|
import { addons } from 'storybook/preview-api'
|
|
@@ -32,6 +33,10 @@ export function showDocSource<TRenderer extends Renderer = Renderer, TArgs = Arg
|
|
|
32
33
|
placement?: 'before' | 'after' | undefined
|
|
33
34
|
}
|
|
34
35
|
): DecoratorFunction<TRenderer, TArgs> {
|
|
36
|
+
if (isRunningInTest()) {
|
|
37
|
+
return (Story) => <Story />
|
|
38
|
+
}
|
|
39
|
+
|
|
35
40
|
return (Story, { parameters: { docs, darkMode } }) => {
|
|
36
41
|
// This is a workaround to get the current dark mode from `@storybook-community/storybook-dark-mode` without referencing it.
|
|
37
42
|
const storedItem = window.localStorage.getItem('sb-addon-themes-3')
|
|
@@ -55,40 +60,68 @@ export function showDocSource<TRenderer extends Renderer = Renderer, TArgs = Arg
|
|
|
55
60
|
|
|
56
61
|
const isOriginalSource = code === docs?.source?.originalSource
|
|
57
62
|
|
|
58
|
-
const sourceContent =
|
|
63
|
+
const sourceContent = useMemo(
|
|
64
|
+
() => (
|
|
65
|
+
<SyntaxHighlighter data-testid="source-content" language={language}>
|
|
66
|
+
{code}
|
|
67
|
+
</SyntaxHighlighter>
|
|
68
|
+
),
|
|
69
|
+
[code, language]
|
|
70
|
+
)
|
|
59
71
|
|
|
60
72
|
const showBefore = options?.placement === 'before'
|
|
61
73
|
|
|
62
|
-
const sourceCardClassName = (
|
|
63
|
-
state: Pick<StoryCardProps, 'status' | 'appearance'> & { defaultClassName: string }
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
74
|
+
const sourceCardClassName = useCallback(
|
|
75
|
+
(state: Pick<StoryCardProps, 'status' | 'appearance'> & { defaultClassName: string }) => {
|
|
76
|
+
const modifiedState = {
|
|
77
|
+
...state,
|
|
78
|
+
defaultClassName: twJoin(
|
|
79
|
+
state.defaultClassName,
|
|
80
|
+
isOriginalSource && 'rbsb:bg-transparent rbsb:dark:bg-transparent'
|
|
81
|
+
)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const className = options?.className
|
|
85
|
+
return typeof className === 'function'
|
|
86
|
+
? className(modifiedState)
|
|
87
|
+
: twJoin(modifiedState.defaultClassName, className)
|
|
88
|
+
},
|
|
89
|
+
[options?.className, isOriginalSource]
|
|
90
|
+
)
|
|
78
91
|
|
|
79
92
|
const theme = convert(docs?.theme ?? (isDark ? themes.dark : themes.light))
|
|
80
93
|
|
|
94
|
+
function DocSourceCard({ children }: { children: ReactNode }) {
|
|
95
|
+
return <div>{children}</div>
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const scopeContent = useMemo(() => <DocSourceCard>{sourceContent}</DocSourceCard>, [sourceContent])
|
|
99
|
+
|
|
100
|
+
const scopeProps = useMemo(
|
|
101
|
+
() => ({
|
|
102
|
+
Story,
|
|
103
|
+
content: scopeContent,
|
|
104
|
+
className: sourceCardClassName,
|
|
105
|
+
appearance: 'source' as const
|
|
106
|
+
}),
|
|
107
|
+
[Story, scopeContent, sourceCardClassName]
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
if (isRunningInTest()) {
|
|
111
|
+
return <Story />
|
|
112
|
+
}
|
|
113
|
+
|
|
81
114
|
if (showBefore) {
|
|
82
115
|
return (
|
|
83
116
|
<ThemeProvider theme={theme}>
|
|
84
|
-
<StoryCardScope
|
|
117
|
+
<StoryCardScope {...scopeProps} />
|
|
85
118
|
</ThemeProvider>
|
|
86
119
|
)
|
|
87
120
|
}
|
|
88
121
|
|
|
89
122
|
const storyCard = (
|
|
90
123
|
<StoryCard className={sourceCardClassName} appearance="source">
|
|
91
|
-
{sourceContent}
|
|
124
|
+
<DocSourceCard>{sourceContent}</DocSourceCard>
|
|
92
125
|
</StoryCard>
|
|
93
126
|
)
|
|
94
127
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { isRunningInTest } from '@repobuddy/test'
|
|
2
|
+
import { type ReactNode, useMemo } from 'react'
|
|
2
3
|
import type { DecoratorFunction, Renderer } from 'storybook/internal/csf'
|
|
3
4
|
import type { StoryCardProps, StoryCardStatus } from '../components/story_card.js'
|
|
4
5
|
import { StoryCardScope } from '../contexts/_story_card_scope.js'
|
|
@@ -119,9 +120,10 @@ export function withStoryCard<TRenderer extends Renderer = Renderer>({
|
|
|
119
120
|
className,
|
|
120
121
|
...rest
|
|
121
122
|
}: WithStoryCardProps = {}): DecoratorFunction<TRenderer> {
|
|
123
|
+
if (isRunningInTest()) {
|
|
124
|
+
return (Story) => <Story />
|
|
125
|
+
}
|
|
122
126
|
return (Story, { parameters, viewMode }) => {
|
|
123
|
-
if (viewMode === 'docs') return <Story />
|
|
124
|
-
|
|
125
127
|
// Get story card config from parameters if available
|
|
126
128
|
const storyCardParam = (parameters as Partial<StoryCardParam>).storyCard
|
|
127
129
|
// Decorator props override parameter values
|
|
@@ -134,18 +136,23 @@ export function withStoryCard<TRenderer extends Renderer = Renderer>({
|
|
|
134
136
|
|
|
135
137
|
// Fallback to docs description if no content provided
|
|
136
138
|
const content = finalContent ?? parameters.docs?.description?.story ?? parameters.docs?.description?.component
|
|
137
|
-
if (!content && !finalTitle) return <Story />
|
|
138
139
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
Story
|
|
142
|
-
content
|
|
143
|
-
title
|
|
144
|
-
status
|
|
145
|
-
appearance
|
|
146
|
-
className
|
|
147
|
-
|
|
148
|
-
|
|
140
|
+
const scopeProps = useMemo(
|
|
141
|
+
() => ({
|
|
142
|
+
Story,
|
|
143
|
+
content,
|
|
144
|
+
title: finalTitle,
|
|
145
|
+
status: finalStatus,
|
|
146
|
+
appearance: finalAppearance,
|
|
147
|
+
className: finalClassName,
|
|
148
|
+
...rest
|
|
149
|
+
}),
|
|
150
|
+
[Story, content, finalTitle, finalStatus, finalAppearance, finalClassName, rest]
|
|
149
151
|
)
|
|
152
|
+
|
|
153
|
+
if (viewMode === 'docs') return <Story />
|
|
154
|
+
if (!content && !finalTitle) return <Story />
|
|
155
|
+
|
|
156
|
+
return <StoryCardScope {...scopeProps} />
|
|
150
157
|
}
|
|
151
158
|
}
|