@repobuddy/storybook 2.21.0 → 2.21.1
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 -1
- package/esm/index.js +21 -19
- package/package.json +2 -2
- package/src/contexts/_story_card_scope.tsx +63 -34
- package/src/decorators/with_story_card.tsx +14 -16
- package/src/index.ts +1 -1
- package/styles.css +1 -1
- /package/src/arg-types/{fn-to-arg-types.ts → fn-to-arg.types.ts} +0 -0
package/esm/index.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ import { Args, DecoratorFunction, Renderer } from "storybook/internal/csf";
|
|
|
9
9
|
import { Decorator, Meta, StoryContext, StoryObj, StrictArgs } from "@storybook/react-vite";
|
|
10
10
|
export * from "@repobuddy/test";
|
|
11
11
|
|
|
12
|
-
//#region src/arg-types/fn-to-arg
|
|
12
|
+
//#region src/arg-types/fn-to-arg.types.d.ts
|
|
13
13
|
/**
|
|
14
14
|
* Converts a function's parameter types to `Args` type for Storybook.
|
|
15
15
|
* Each name maps to the parameter type at the same index in F.
|
package/esm/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { isRunningInTest } from "@repobuddy/test";
|
|
3
3
|
import { prettify } from "htmlfy";
|
|
4
4
|
import { createContext, memo, useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
5
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
6
6
|
import { cva } from "class-variance-authority";
|
|
7
7
|
import { twJoin, twMerge } from "tailwind-merge";
|
|
8
8
|
import { SyntaxHighlighter } from "storybook/internal/components";
|
|
@@ -108,6 +108,17 @@ const StoryCardScope = memo(function StoryCardScope(props) {
|
|
|
108
108
|
if (context === null) return /* @__PURE__ */ jsx(StoryCardContainer, { children: collector });
|
|
109
109
|
return collector;
|
|
110
110
|
});
|
|
111
|
+
/** Renders cards from registry state without re-rendering when only children change (avoids cascade). */
|
|
112
|
+
const StoryCardList = memo(function StoryCardList({ cards }) {
|
|
113
|
+
return /* @__PURE__ */ jsx(Fragment, { children: cards.map(({ content, key, ...rest }) => /* @__PURE__ */ jsx(StoryCard, {
|
|
114
|
+
...rest,
|
|
115
|
+
children: content
|
|
116
|
+
}, key)) });
|
|
117
|
+
});
|
|
118
|
+
/** Keeps container children from re-rendering when container state (cards) updates. */
|
|
119
|
+
const StableScopeChildren = memo(function StableScopeChildren({ children }) {
|
|
120
|
+
return /* @__PURE__ */ jsx(Fragment, { children });
|
|
121
|
+
});
|
|
111
122
|
function StoryCardContainer({ children }) {
|
|
112
123
|
const [cards, setCards] = useState([]);
|
|
113
124
|
const contextValue = useMemo(() => ({
|
|
@@ -133,13 +144,13 @@ function StoryCardContainer({ children }) {
|
|
|
133
144
|
value: contextValue,
|
|
134
145
|
children: /* @__PURE__ */ jsxs("div", {
|
|
135
146
|
className: "rbsb:flex rbsb:flex-col rbsb:gap-2",
|
|
136
|
-
children: [
|
|
137
|
-
...rest,
|
|
138
|
-
children: content
|
|
139
|
-
}, key)), children]
|
|
147
|
+
children: [/* @__PURE__ */ jsx(StoryCardList, { cards }), /* @__PURE__ */ jsx(StableScopeChildren, { children })]
|
|
140
148
|
})
|
|
141
149
|
});
|
|
142
150
|
}
|
|
151
|
+
function entryPropsEqual(a, b) {
|
|
152
|
+
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;
|
|
153
|
+
}
|
|
143
154
|
const StoryCardCollector = memo(function StoryCardCollector({ Story, title, status, appearance, className, content }) {
|
|
144
155
|
const context = useContext(StoryCardRegistryContext);
|
|
145
156
|
const cardIdRef = useRef(null);
|
|
@@ -169,7 +180,7 @@ const StoryCardCollector = memo(function StoryCardCollector({ Story, title, stat
|
|
|
169
180
|
if (cardIdRef.current !== null) context.update(cardIdRef.current, entry);
|
|
170
181
|
}, [context, entry]);
|
|
171
182
|
return /* @__PURE__ */ jsx(Story, {});
|
|
172
|
-
},
|
|
183
|
+
}, entryPropsEqual);
|
|
173
184
|
|
|
174
185
|
//#endregion
|
|
175
186
|
//#region src/decorators/show_doc_source.tsx
|
|
@@ -335,6 +346,7 @@ function showDocSource(options) {
|
|
|
335
346
|
function withStoryCard({ title, status, appearance, content: contentProp, className, ...rest } = {}) {
|
|
336
347
|
if (isRunningInTest()) return (Story) => /* @__PURE__ */ jsx(Story, {});
|
|
337
348
|
return (Story, { parameters, viewMode }) => {
|
|
349
|
+
if (viewMode === "docs") return /* @__PURE__ */ jsx(Story, {});
|
|
338
350
|
const storyCardParam = parameters.storyCard;
|
|
339
351
|
const finalTitle = title ?? storyCardParam?.title;
|
|
340
352
|
const finalAppearance = appearance ?? storyCardParam?.appearance ?? status ?? storyCardParam?.status ?? "info";
|
|
@@ -342,7 +354,8 @@ function withStoryCard({ title, status, appearance, content: contentProp, classN
|
|
|
342
354
|
const finalContent = contentProp ?? storyCardParam?.content;
|
|
343
355
|
const finalClassName = className ?? storyCardParam?.className;
|
|
344
356
|
const content = finalContent ?? parameters.docs?.description?.story ?? parameters.docs?.description?.component;
|
|
345
|
-
|
|
357
|
+
if (!content && !finalTitle) return /* @__PURE__ */ jsx(Story, {});
|
|
358
|
+
return /* @__PURE__ */ jsx(StoryCardScope, {
|
|
346
359
|
Story,
|
|
347
360
|
content,
|
|
348
361
|
title: finalTitle,
|
|
@@ -350,18 +363,7 @@ function withStoryCard({ title, status, appearance, content: contentProp, classN
|
|
|
350
363
|
appearance: finalAppearance,
|
|
351
364
|
className: finalClassName,
|
|
352
365
|
...rest
|
|
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 });
|
|
366
|
+
});
|
|
365
367
|
};
|
|
366
368
|
}
|
|
367
369
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@repobuddy/storybook",
|
|
3
|
-
"version": "2.21.
|
|
3
|
+
"version": "2.21.1",
|
|
4
4
|
"description": "Storybook repo buddy",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"storybook",
|
|
@@ -120,7 +120,7 @@
|
|
|
120
120
|
"build:code": "tsdown",
|
|
121
121
|
"build:css": "tailwindcss -i ./tailwind.css -o ./styles.css",
|
|
122
122
|
"clean": "rimraf .turbo coverage cjs esm storybook-static *.tsbuildinfo",
|
|
123
|
-
"
|
|
123
|
+
"cov": "vitest run --coverage",
|
|
124
124
|
"sb": "storybook dev -p 6006",
|
|
125
125
|
"sb:build": "storybook build",
|
|
126
126
|
"test": "vitest run",
|
|
@@ -24,6 +24,24 @@ export const StoryCardScope = memo(function StoryCardScope(props: StoryCardScope
|
|
|
24
24
|
return collector
|
|
25
25
|
})
|
|
26
26
|
|
|
27
|
+
/** Renders cards from registry state without re-rendering when only children change (avoids cascade). */
|
|
28
|
+
const StoryCardList = memo(function StoryCardList({ cards }: { cards: StoryCardEntryWithKey[] }) {
|
|
29
|
+
return (
|
|
30
|
+
<>
|
|
31
|
+
{cards.map(({ content, key, ...rest }) => (
|
|
32
|
+
<StoryCard key={key} {...rest}>
|
|
33
|
+
{content}
|
|
34
|
+
</StoryCard>
|
|
35
|
+
))}
|
|
36
|
+
</>
|
|
37
|
+
)
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
/** Keeps container children from re-rendering when container state (cards) updates. */
|
|
41
|
+
const StableScopeChildren = memo(function StableScopeChildren({ children }: { children: ReactNode }) {
|
|
42
|
+
return <>{children}</>
|
|
43
|
+
})
|
|
44
|
+
|
|
27
45
|
function StoryCardContainer({ children }: { children: ReactNode }) {
|
|
28
46
|
const [cards, setCards] = useState<StoryCardEntryWithKey[]>([])
|
|
29
47
|
|
|
@@ -47,12 +65,8 @@ function StoryCardContainer({ children }: { children: ReactNode }) {
|
|
|
47
65
|
return (
|
|
48
66
|
<StoryCardRegistryContext.Provider value={contextValue}>
|
|
49
67
|
<div className="rbsb:flex rbsb:flex-col rbsb:gap-2">
|
|
50
|
-
{cards
|
|
51
|
-
|
|
52
|
-
{content}
|
|
53
|
-
</StoryCard>
|
|
54
|
-
))}
|
|
55
|
-
{children}
|
|
68
|
+
<StoryCardList cards={cards} />
|
|
69
|
+
<StableScopeChildren>{children}</StableScopeChildren>
|
|
56
70
|
</div>
|
|
57
71
|
</StoryCardRegistryContext.Provider>
|
|
58
72
|
)
|
|
@@ -62,35 +76,50 @@ type StoryCardEntryWithKey = StoryCardEntry & { key: string }
|
|
|
62
76
|
|
|
63
77
|
interface StoryCardCollectorProps extends StoryCardScopeProps {}
|
|
64
78
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
79
|
+
function entryPropsEqual(a: StoryCardCollectorProps, b: StoryCardCollectorProps): boolean {
|
|
80
|
+
return (
|
|
81
|
+
a.Story === b.Story &&
|
|
82
|
+
a.title === b.title &&
|
|
83
|
+
a.status === b.status &&
|
|
84
|
+
a.appearance === b.appearance &&
|
|
85
|
+
a.className === b.className &&
|
|
86
|
+
a.content === b.content
|
|
87
|
+
)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const StoryCardCollector = memo(function StoryCardCollector({
|
|
91
|
+
Story,
|
|
92
|
+
title,
|
|
93
|
+
status,
|
|
94
|
+
appearance,
|
|
95
|
+
className,
|
|
96
|
+
content
|
|
97
|
+
}: StoryCardCollectorProps) {
|
|
98
|
+
const context = useContext(StoryCardRegistryContext)!
|
|
99
|
+
const cardIdRef = useRef<string | null>(null)
|
|
85
100
|
|
|
86
|
-
|
|
87
|
-
|
|
101
|
+
const entry = useMemo(
|
|
102
|
+
() => ({ title, status, appearance, className, content }),
|
|
103
|
+
[title, status, appearance, className, content]
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
// Register on mount, unregister on unmount only
|
|
107
|
+
useLayoutEffect(() => {
|
|
108
|
+
cardIdRef.current = context.add(entry)
|
|
109
|
+
return () => {
|
|
88
110
|
if (cardIdRef.current !== null) {
|
|
89
|
-
context.
|
|
111
|
+
context.remove(cardIdRef.current)
|
|
112
|
+
cardIdRef.current = null
|
|
90
113
|
}
|
|
91
|
-
}
|
|
114
|
+
}
|
|
115
|
+
}, [context])
|
|
116
|
+
|
|
117
|
+
// Update registry when entry changes (avoids remove+add churn)
|
|
118
|
+
useLayoutEffect(() => {
|
|
119
|
+
if (cardIdRef.current !== null) {
|
|
120
|
+
context.update(cardIdRef.current, entry)
|
|
121
|
+
}
|
|
122
|
+
}, [context, entry])
|
|
92
123
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
(prev, next) => prev.Story === next.Story
|
|
96
|
-
)
|
|
124
|
+
return <Story />
|
|
125
|
+
}, entryPropsEqual)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { isRunningInTest } from '@repobuddy/test'
|
|
2
|
-
import {
|
|
2
|
+
import type { ReactNode } from 'react'
|
|
3
3
|
import type { DecoratorFunction, Renderer } from 'storybook/internal/csf'
|
|
4
4
|
import type { StoryCardProps, StoryCardStatus } from '../components/story_card.js'
|
|
5
5
|
import { StoryCardScope } from '../contexts/_story_card_scope.js'
|
|
@@ -124,6 +124,8 @@ export function withStoryCard<TRenderer extends Renderer = Renderer>({
|
|
|
124
124
|
return (Story) => <Story />
|
|
125
125
|
}
|
|
126
126
|
return (Story, { parameters, viewMode }) => {
|
|
127
|
+
if (viewMode === 'docs') return <Story />
|
|
128
|
+
|
|
127
129
|
// Get story card config from parameters if available
|
|
128
130
|
const storyCardParam = (parameters as Partial<StoryCardParam>).storyCard
|
|
129
131
|
// Decorator props override parameter values
|
|
@@ -137,22 +139,18 @@ export function withStoryCard<TRenderer extends Renderer = Renderer>({
|
|
|
137
139
|
// Fallback to docs description if no content provided
|
|
138
140
|
const content = finalContent ?? parameters.docs?.description?.story ?? parameters.docs?.description?.component
|
|
139
141
|
|
|
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]
|
|
151
|
-
)
|
|
152
|
-
|
|
153
|
-
if (viewMode === 'docs') return <Story />
|
|
154
142
|
if (!content && !finalTitle) return <Story />
|
|
155
143
|
|
|
156
|
-
return
|
|
144
|
+
return (
|
|
145
|
+
<StoryCardScope
|
|
146
|
+
Story={Story}
|
|
147
|
+
content={content}
|
|
148
|
+
title={finalTitle}
|
|
149
|
+
status={finalStatus}
|
|
150
|
+
appearance={finalAppearance}
|
|
151
|
+
className={finalClassName}
|
|
152
|
+
{...rest}
|
|
153
|
+
/>
|
|
154
|
+
)
|
|
157
155
|
}
|
|
158
156
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export * from '@repobuddy/test'
|
|
2
|
-
export type * from './arg-types/fn-to-arg
|
|
2
|
+
export type * from './arg-types/fn-to-arg.types.ts'
|
|
3
3
|
export * from './components/show_html.tsx'
|
|
4
4
|
export * from './components/story_card.tsx'
|
|
5
5
|
export * from './decorators/show_doc_source.tsx'
|
package/styles.css
CHANGED
|
File without changes
|