storybook-solidjs-vite 10.7.2 → 10.7.4
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/dist/index.js +29 -1
- package/dist/index.js.map +1 -1
- package/dist/{render-to-canvas-5L3YXi18.js → render-to-canvas-CUzXHGXU.js} +3 -2
- package/dist/{render-to-canvas-5L3YXi18.js.map → render-to-canvas-CUzXHGXU.js.map} +1 -1
- package/dist/renderer/solid-legacy.js +2 -2
- package/dist/renderer/solid-next.js +6 -3
- package/dist/renderer/solid-next.js.map +1 -1
- package/package.json +3 -3
- package/src/preview/define-preview.ts +3 -2
- package/src/preview/reactive-story-component.ts +73 -0
- package/src/renderer/shared/mount.ts +12 -1
- package/src/renderer/solid-next.ts +2 -1
- package/src/spec/preview/story-component.test.ts +279 -0
- package/src/spec/renderer/solid-next-mount.test.ts +37 -0
package/dist/index.js
CHANGED
|
@@ -3,6 +3,34 @@ import { t as defineMain } from "./public-api-BQ4r5wQn.js";
|
|
|
3
3
|
import { n as createDecorator, r as createJSXDecorator, t as IS_SOLID_JSX_FLAG } from "./public-api-Bp0rfkhU.js";
|
|
4
4
|
import { previewAddon } from "storybook-solidjs-vite/renderer/solid-legacy";
|
|
5
5
|
import { definePreview as definePreview$1 } from "storybook/internal/csf";
|
|
6
|
+
import * as solid from "solid-js";
|
|
7
|
+
import { composeStory } from "storybook/preview-api";
|
|
8
|
+
//#region src/preview/reactive-story-component.ts
|
|
9
|
+
const mergeArgs = "mergeProps" in solid ? solid.mergeProps : solid.merge;
|
|
10
|
+
function toArray(decorators) {
|
|
11
|
+
if (decorators == null) return [];
|
|
12
|
+
return Array.isArray(decorators) ? decorators : [decorators];
|
|
13
|
+
}
|
|
14
|
+
function createReactiveStoryComponent(story) {
|
|
15
|
+
let liveProps;
|
|
16
|
+
const composed = composeStory({
|
|
17
|
+
...story.input,
|
|
18
|
+
decorators: [(storyFn, context) => {
|
|
19
|
+
if (liveProps) context.args = mergeArgs(context.args ?? {}, liveProps);
|
|
20
|
+
return storyFn(context);
|
|
21
|
+
}, ...toArray(story.input.decorators)]
|
|
22
|
+
}, story.meta.input, void 0, story.meta.preview.composed);
|
|
23
|
+
const Component = (props) => {
|
|
24
|
+
liveProps = props;
|
|
25
|
+
try {
|
|
26
|
+
return composed();
|
|
27
|
+
} finally {
|
|
28
|
+
liveProps = void 0;
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
return Object.assign(Component, composed);
|
|
32
|
+
}
|
|
33
|
+
//#endregion
|
|
6
34
|
//#region src/preview/define-preview.ts
|
|
7
35
|
function createSolidDefinePreview(solidAnnotations) {
|
|
8
36
|
return ((input) => {
|
|
@@ -21,7 +49,7 @@ function createSolidDefinePreview(solidAnnotations) {
|
|
|
21
49
|
const defineStory = meta.story.bind(meta);
|
|
22
50
|
meta.story = (__input) => {
|
|
23
51
|
const story = defineStory(__input);
|
|
24
|
-
story.Component = story
|
|
52
|
+
story.Component = createReactiveStoryComponent(story);
|
|
25
53
|
return story;
|
|
26
54
|
};
|
|
27
55
|
return meta;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["definePreviewBase","solidDocsAnnotations"],"sources":["../src/preview/define-preview.ts","../src/index.ts"],"sourcesContent":["import { definePreview as definePreviewBase } from 'storybook/internal/csf';\n\nimport * as solidDocsAnnotations from '../renderer/docs';\n\nimport type { AddonTypes, InferTypes, Meta, Preview, PreviewAddon, Story } from 'storybook/internal/csf';\nimport type {\n Args,\n ArgsStoryFn,\n ComponentAnnotations,\n DecoratorFunction,\n ProjectAnnotations,\n Renderer,\n StoryAnnotations,\n} from 'storybook/internal/types';\nimport type { OmitIndexSignature, SetOptional, Simplify, UnionToIntersection } from 'type-fest';\nimport type { AddMocks, EmptyMetaArgs, SolidComponent, SolidTypes } from './public-api';\n\n/** Extracts and unions all args types from an array of decorators. */\ntype DecoratorsArgs<TRenderer extends Renderer, Decorators> = UnionToIntersection<\n Decorators extends DecoratorFunction<TRenderer, infer TArgs> ? TArgs : unknown\n>;\n\ntype InferArgs<TArgs, T, Decorators> = Simplify<\n TArgs & Simplify<OmitIndexSignature<DecoratorsArgs<SolidTypes & T, Decorators>>>\n>;\n\ntype InferSolidTypes<T, TArgs, Decorators> = SolidTypes & T & {\n args: Simplify<InferArgs<TArgs, T, Decorators>>;\n};\n\nexport type DefinePreviewInput<Addons extends PreviewAddon<never>[] = []> = {\n addons?: Addons;\n} & ProjectAnnotations<SolidTypes & InferTypes<Addons>>;\n\n/** @ts-expect-error We cannot implement the meta faithfully here, but that is okay. */\nexport interface SolidPreview<T extends AddonTypes> extends Preview<SolidTypes & T> {\n type: <R>() => SolidPreview<T & R>;\n\n meta: <\n TArgs extends Args,\n Decorators extends DecoratorFunction<SolidTypes & T, any>,\n TMetaArgs extends Partial<TArgs & T['args']>\n >(\n meta: {\n render?: ArgsStoryFn<SolidTypes & T, TArgs & T['args']>;\n component?: SolidComponent<TArgs>;\n decorators?: Decorators | Decorators[];\n args?: TMetaArgs;\n } & Omit<\n ComponentAnnotations<SolidTypes & T, TArgs>,\n 'decorators' | 'component' | 'args' | 'render'\n >\n ) => SolidMeta<\n InferSolidTypes<T, TArgs, Decorators>,\n Omit<ComponentAnnotations<InferSolidTypes<T, TArgs, Decorators>>, 'args'> & {\n args: Partial<TArgs> extends TMetaArgs ? EmptyMetaArgs : TMetaArgs;\n }\n >;\n}\n\nexport interface SolidMeta<T extends SolidTypes, MetaInput extends ComponentAnnotations<T>>\n /** @ts-expect-error SolidMeta requires two type parameters, but Meta's constraints differ */\n extends Meta<T, MetaInput> {\n story: (<\n TInput extends\n | (() => SolidTypes['storyResult'])\n | (StoryAnnotations<T, T['args']> & {\n render: () => SolidTypes['storyResult'];\n })\n >(\n story: TInput\n ) => SolidStory<T, TInput extends () => SolidTypes['storyResult'] ? { render: TInput } : TInput>) & (<\n TInput extends Simplify<\n StoryAnnotations<\n T,\n AddMocks<T['args'], MetaInput['args']>,\n SetOptional<T['args'], keyof T['args'] & keyof MetaInput['args']>\n >\n >\n >(\n story: TInput\n /** @ts-expect-error hard */\n ) => SolidStory<T, TInput>) & ((\n ..._args: Partial<T['args']> extends SetOptional<\n T['args'],\n keyof T['args'] & keyof MetaInput['args']\n >\n ? []\n : [never]\n ) => SolidStory<T, EmptyMetaArgs>);\n}\n\nexport interface SolidStory<T extends SolidTypes, TInput extends StoryAnnotations<T, T['args']>>\n extends Story<T, TInput> {\n Component: SolidComponent<Partial<T['args']>>;\n}\n\n/** Stable `definePreview` type for Solid 1 and Solid 2. */\nexport type SolidDefinePreview = <\n Addons extends PreviewAddon<never>[] = []\n>(\n input: DefinePreviewInput<Addons>\n) => SolidPreview<SolidTypes & InferTypes<Addons>>;\n\nexport function createSolidDefinePreview(\n solidAnnotations: PreviewAddon<never>\n): SolidDefinePreview {\n return (<Addons extends PreviewAddon<never>[] = []>(\n input: DefinePreviewInput<Addons>\n ) => {\n const { addons, ...projectAnnotations } = input;\n\n const preview = definePreviewBase({\n ...projectAnnotations,\n addons: [\n solidAnnotations,\n solidDocsAnnotations,\n ...(addons ?? []),\n ],\n });\n\n const defineMeta = preview.meta.bind(preview);\n\n preview.meta = (_input) => {\n const meta = defineMeta(_input);\n const defineStory = meta.story.bind(meta);\n\n meta.story = (__input: any) => {\n const story = defineStory(__input);\n\n // @ts-expect-error
|
|
1
|
+
{"version":3,"file":"index.js","names":["definePreviewBase","solidDocsAnnotations"],"sources":["../src/preview/reactive-story-component.ts","../src/preview/define-preview.ts","../src/index.ts"],"sourcesContent":["import * as solid from 'solid-js';\nimport { composeStory } from 'storybook/preview-api';\n\nimport type { ProjectAnnotations } from 'storybook/internal/types';\n\n/**\n * `__compose()` copies props with an object spread before the story function\n * runs. The only hook between that copy and the story is a decorator, so this\n * one puts the original props object back onto `context.args`. Solid's\n * `mergeProps` / `merge` keeps the getters. The story then renders once.\n */\n\ninterface ComposableStory {\n input: {\n decorators?: unknown;\n };\n meta: {\n input: Record<string, unknown>;\n preview: {\n composed: ProjectAnnotations<any>;\n };\n };\n}\n\ntype MergeArgs = (base: object, props: object) => object;\n\nconst mergeArgs: MergeArgs = 'mergeProps' in solid\n ? solid.mergeProps\n : (solid as { merge: MergeArgs }).merge;\n\nfunction toArray(decorators: unknown): any[] {\n if (decorators == null) {\n return [];\n }\n\n return Array.isArray(decorators) ? decorators : [decorators];\n}\n\nexport function createReactiveStoryComponent(story: ComposableStory) {\n let liveProps: object | undefined;\n\n const composed = composeStory(\n {\n ...story.input,\n decorators: [\n (storyFn: (context: { args: object }) => unknown, context: { args?: object }) => {\n if (liveProps) {\n context.args = mergeArgs(context.args ?? {}, liveProps);\n }\n\n return storyFn(context as { args: object });\n },\n ...toArray(story.input.decorators),\n ],\n },\n story.meta.input,\n undefined,\n story.meta.preview.composed,\n );\n\n const Component = (props?: object) => {\n liveProps = props;\n\n try {\n return composed();\n }\n finally {\n liveProps = undefined;\n }\n };\n\n return Object.assign(Component, composed);\n}\n","import { definePreview as definePreviewBase } from 'storybook/internal/csf';\n\nimport * as solidDocsAnnotations from '../renderer/docs';\nimport { createReactiveStoryComponent } from './reactive-story-component';\n\nimport type { AddonTypes, InferTypes, Meta, Preview, PreviewAddon, Story } from 'storybook/internal/csf';\nimport type {\n Args,\n ArgsStoryFn,\n ComponentAnnotations,\n DecoratorFunction,\n ProjectAnnotations,\n Renderer,\n StoryAnnotations,\n} from 'storybook/internal/types';\nimport type { OmitIndexSignature, SetOptional, Simplify, UnionToIntersection } from 'type-fest';\nimport type { AddMocks, EmptyMetaArgs, SolidComponent, SolidTypes } from './public-api';\n\n/** Extracts and unions all args types from an array of decorators. */\ntype DecoratorsArgs<TRenderer extends Renderer, Decorators> = UnionToIntersection<\n Decorators extends DecoratorFunction<TRenderer, infer TArgs> ? TArgs : unknown\n>;\n\ntype InferArgs<TArgs, T, Decorators> = Simplify<\n TArgs & Simplify<OmitIndexSignature<DecoratorsArgs<SolidTypes & T, Decorators>>>\n>;\n\ntype InferSolidTypes<T, TArgs, Decorators> = SolidTypes & T & {\n args: Simplify<InferArgs<TArgs, T, Decorators>>;\n};\n\nexport type DefinePreviewInput<Addons extends PreviewAddon<never>[] = []> = {\n addons?: Addons;\n} & ProjectAnnotations<SolidTypes & InferTypes<Addons>>;\n\n/** @ts-expect-error We cannot implement the meta faithfully here, but that is okay. */\nexport interface SolidPreview<T extends AddonTypes> extends Preview<SolidTypes & T> {\n type: <R>() => SolidPreview<T & R>;\n\n meta: <\n TArgs extends Args,\n Decorators extends DecoratorFunction<SolidTypes & T, any>,\n TMetaArgs extends Partial<TArgs & T['args']>\n >(\n meta: {\n render?: ArgsStoryFn<SolidTypes & T, TArgs & T['args']>;\n component?: SolidComponent<TArgs>;\n decorators?: Decorators | Decorators[];\n args?: TMetaArgs;\n } & Omit<\n ComponentAnnotations<SolidTypes & T, TArgs>,\n 'decorators' | 'component' | 'args' | 'render'\n >\n ) => SolidMeta<\n InferSolidTypes<T, TArgs, Decorators>,\n Omit<ComponentAnnotations<InferSolidTypes<T, TArgs, Decorators>>, 'args'> & {\n args: Partial<TArgs> extends TMetaArgs ? EmptyMetaArgs : TMetaArgs;\n }\n >;\n}\n\nexport interface SolidMeta<T extends SolidTypes, MetaInput extends ComponentAnnotations<T>>\n /** @ts-expect-error SolidMeta requires two type parameters, but Meta's constraints differ */\n extends Meta<T, MetaInput> {\n story: (<\n TInput extends\n | (() => SolidTypes['storyResult'])\n | (StoryAnnotations<T, T['args']> & {\n render: () => SolidTypes['storyResult'];\n })\n >(\n story: TInput\n ) => SolidStory<T, TInput extends () => SolidTypes['storyResult'] ? { render: TInput } : TInput>) & (<\n TInput extends Simplify<\n StoryAnnotations<\n T,\n AddMocks<T['args'], MetaInput['args']>,\n SetOptional<T['args'], keyof T['args'] & keyof MetaInput['args']>\n >\n >\n >(\n story: TInput\n /** @ts-expect-error hard */\n ) => SolidStory<T, TInput>) & ((\n ..._args: Partial<T['args']> extends SetOptional<\n T['args'],\n keyof T['args'] & keyof MetaInput['args']\n >\n ? []\n : [never]\n ) => SolidStory<T, EmptyMetaArgs>);\n}\n\nexport interface SolidStory<T extends SolidTypes, TInput extends StoryAnnotations<T, T['args']>>\n extends Story<T, TInput> {\n Component: SolidComponent<Partial<T['args']>>;\n}\n\n/** Stable `definePreview` type for Solid 1 and Solid 2. */\nexport type SolidDefinePreview = <\n Addons extends PreviewAddon<never>[] = []\n>(\n input: DefinePreviewInput<Addons>\n) => SolidPreview<SolidTypes & InferTypes<Addons>>;\n\nexport function createSolidDefinePreview(\n solidAnnotations: PreviewAddon<never>\n): SolidDefinePreview {\n return (<Addons extends PreviewAddon<never>[] = []>(\n input: DefinePreviewInput<Addons>\n ) => {\n const { addons, ...projectAnnotations } = input;\n\n const preview = definePreviewBase({\n ...projectAnnotations,\n addons: [\n solidAnnotations,\n solidDocsAnnotations,\n ...(addons ?? []),\n ],\n });\n\n const defineMeta = preview.meta.bind(preview);\n\n preview.meta = (_input) => {\n const meta = defineMeta(_input);\n const defineStory = meta.story.bind(meta);\n\n meta.story = (__input: any) => {\n const story = defineStory(__input);\n\n // @ts-expect-error factory stories keep input and meta off the public type\n story.Component = createReactiveStoryComponent(story);\n\n return story;\n };\n\n return meta;\n };\n\n return preview;\n }) as unknown as SolidDefinePreview;\n}\n","import { previewAddon } from 'storybook-solidjs-vite/renderer/solid-legacy';\n\nimport { createSolidDefinePreview, type SolidDefinePreview } from './preview/define-preview';\n\nexport * from './framework/public-api';\nexport * from './preview/public-api';\n\nexport const definePreview: SolidDefinePreview = createSolidDefinePreview(previewAddon);\n"],"mappings":";;;;;;;;AA0BA,MAAM,YAAuB,gBAAgB,QACvC,MAAM,aACL,MAA+B;AAEtC,SAAS,QAAQ,YAA4B;CACzC,IAAI,cAAc,MACd,OAAO,CAAC;CAGZ,OAAO,MAAM,QAAQ,UAAU,IAAI,aAAa,CAAC,UAAU;AAC/D;AAEA,SAAgB,6BAA6B,OAAwB;CACjE,IAAI;CAEJ,MAAM,WAAW,aACb;EACI,GAAG,MAAM;EACT,YAAY,EACP,SAAiD,YAA+B;GAC7E,IAAI,WACA,QAAQ,OAAO,UAAU,QAAQ,QAAQ,CAAC,GAAG,SAAS;GAG1D,OAAO,QAAQ,OAA2B;EAC9C,GACA,GAAG,QAAQ,MAAM,MAAM,UAAU,CACrC;CACJ,GACA,MAAM,KAAK,OACX,KAAA,GACA,MAAM,KAAK,QAAQ,QACvB;CAEA,MAAM,aAAa,UAAmB;EAClC,YAAY;EAEZ,IAAI;GACA,OAAO,SAAS;EACpB,UACQ;GACJ,YAAY,KAAA;EAChB;CACJ;CAEA,OAAO,OAAO,OAAO,WAAW,QAAQ;AAC5C;;;ACiCA,SAAgB,yBACZ,kBACkB;CAClB,SACI,UACC;EACD,MAAM,EAAE,QAAQ,GAAG,uBAAuB;EAE1C,MAAM,UAAUA,gBAAkB;GAC9B,GAAG;GACH,QAAQ;IACJ;IACAC;IACA,GAAI,UAAU,CAAC;GACnB;EACJ,CAAC;EAED,MAAM,aAAa,QAAQ,KAAK,KAAK,OAAO;EAE5C,QAAQ,QAAQ,WAAW;GACvB,MAAM,OAAO,WAAW,MAAM;GAC9B,MAAM,cAAc,KAAK,MAAM,KAAK,IAAI;GAExC,KAAK,SAAS,YAAiB;IAC3B,MAAM,QAAQ,YAAY,OAAO;IAGjC,MAAM,YAAY,6BAA6B,KAAK;IAEpD,OAAO;GACX;GAEA,OAAO;EACX;EAEA,OAAO;CACX;AACJ;;;ACvIA,MAAa,gBAAoC,yBAAyB,YAAY"}
|
|
@@ -105,7 +105,7 @@ function createDefaultRender(createComponent) {
|
|
|
105
105
|
//#endregion
|
|
106
106
|
//#region src/renderer/shared/mount.ts
|
|
107
107
|
/** Play/mount API: sync reactive args and globals, then render via `renderToCanvas`. */
|
|
108
|
-
function createMount({ storyStore }) {
|
|
108
|
+
function createMount({ storyStore, flush }) {
|
|
109
109
|
return (context) => {
|
|
110
110
|
const storyId = getStoryId(context);
|
|
111
111
|
const { forceRemount } = context;
|
|
@@ -115,6 +115,7 @@ function createMount({ storyStore }) {
|
|
|
115
115
|
if (forceRemount) storyStore.disposeStory(storyId);
|
|
116
116
|
if (!storyStore.isStoryRendered(storyId) || forceRemount) storyStore.resetStory(storyId);
|
|
117
117
|
storyStore.makeContextReactive(context);
|
|
118
|
+
flush?.();
|
|
118
119
|
await context.renderToCanvas();
|
|
119
120
|
return context.canvas;
|
|
120
121
|
};
|
|
@@ -152,4 +153,4 @@ function createRenderToCanvas(options) {
|
|
|
152
153
|
//#endregion
|
|
153
154
|
export { createApplyDecorators as a, beforeAll as i, createMount as n, createStoryState as o, createDefaultRender as r, createRenderToCanvas as t };
|
|
154
155
|
|
|
155
|
-
//# sourceMappingURL=render-to-canvas-
|
|
156
|
+
//# sourceMappingURL=render-to-canvas-CUzXHGXU.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"render-to-canvas-5L3YXi18.js","names":[],"sources":["../src/renderer/shared/story-store.ts","../src/renderer/shared/apply-decorators.ts","../src/renderer/shared/before-all.ts","../src/renderer/shared/default-render.ts","../src/renderer/shared/mount.ts","../src/renderer/shared/render-to-canvas.ts"],"sourcesContent":["import type { Args, Globals, StoryContext as InternalStoryContext } from 'storybook/internal/types';\nimport type { SolidRenderer } from '../../preview/public-api';\n\n/** Update store via `(prev) => next` (version layer applies reconcile/draft setter). */\ntype SetStoreFn<T extends object> = (update: (prev: T) => T) => void;\n\n/** Reactive store tuple returned by `createStore` (state + setter). */\ntype SolidStore<T extends object> = readonly [\n state: T,\n setState: SetStoreFn<T>\n];\n\ntype CreateStoreFn = <T extends object>(initial: T) => SolidStore<T>;\n\nexport interface StoryStateStore {\n isStoryRendered: (storyId: string) => boolean;\n resetStory: (storyId: string) => void;\n setRendered: (storyId: string, rendered: boolean) => void;\n setDisposeFn: (storyId: string, disposeFn: (() => void) | null) => void;\n makeContextReactive: (context: InternalStoryContext<SolidRenderer, Args>) => void;\n disposeStory: (storyId: string) => void;\n}\n\nexport function getStoryId(context: { id?: string; canvasElement?: { id?: string } }): string | undefined {\n const canvasId = context.canvasElement?.id;\n\n if (!canvasId || canvasId === 'storybook-root') {\n return context.id;\n }\n\n return canvasId;\n}\n\nexport function createStoryState(createStore: CreateStoreFn): StoryStateStore {\n const [globals, setGlobals] = createStore<Globals>({});\n const renderedStories = new Set<string>();\n const argsStores = new Map<string, SolidStore<Args>>();\n const disposers = new Map<string, () => void>();\n\n const getArgsStore = (storyId: string): SolidStore<Args> => {\n let store = argsStores.get(storyId);\n\n if (!store) {\n store = createStore({} as Args);\n argsStores.set(storyId, store);\n }\n\n return store;\n };\n\n return {\n isStoryRendered(storyId) {\n return renderedStories.has(storyId);\n },\n\n resetStory(storyId) {\n renderedStories.delete(storyId);\n disposers.delete(storyId);\n argsStores.delete(storyId);\n },\n\n setRendered(storyId, isRendered) {\n if (isRendered) {\n renderedStories.add(storyId);\n }\n else {\n renderedStories.delete(storyId);\n }\n },\n\n setDisposeFn(storyId, disposeFn) {\n if (disposeFn) {\n disposers.set(storyId, disposeFn);\n }\n else {\n disposers.delete(storyId);\n }\n },\n\n makeContextReactive(context) {\n const storyId = getStoryId(context);\n\n if (!storyId) {\n throw new Error('Story ID is required');\n }\n\n const [args, setArgs] = getArgsStore(storyId);\n\n // Update reactive stores with the new values\n setArgs(() => context.args);\n setGlobals(() => context.globals);\n\n // Sync the reactive stores to the context\n context.args = args;\n context.globals = globals;\n },\n\n disposeStory(storyId) {\n disposers.get(storyId)?.();\n disposers.delete(storyId);\n },\n };\n}\n","import { defaultDecorateStory } from 'storybook/preview-api';\n\nimport { IS_SOLID_JSX_FLAG, type Decorator, type SolidRenderer } from '../../preview/public-api';\nimport { getStoryId } from './story-store';\n\nimport type { LegacyStoryFn } from 'storybook/internal/types';\nimport type { StoryStateStore } from './story-store';\n\nexport function createApplyDecorators({\n storyStore,\n}: {\n storyStore: StoryStateStore;\n}): (\n storyFn: LegacyStoryFn<SolidRenderer>,\n rawDecorators: Decorator[]\n) => LegacyStoryFn<SolidRenderer> {\n return (storyFn, rawDecorators) => {\n const decorators = rawDecorators.map(\n (decorator): Decorator => {\n const originalFn = (decorator as Decorator & { originalFn?: Decorator }).originalFn;\n const isJSX = Boolean(\n decorator[IS_SOLID_JSX_FLAG] || originalFn?.[IS_SOLID_JSX_FLAG]\n );\n\n return (StoryFn, context) => {\n if (isJSX) {\n const storyId = getStoryId(context);\n\n if (storyId && storyStore.isStoryRendered(storyId)) {\n return StoryFn(context);\n }\n }\n\n return decorator(StoryFn, context);\n };\n }\n );\n\n return defaultDecorateStory((context) => {\n const storyId = getStoryId(context);\n\n if (storyId && storyStore.isStoryRendered(storyId)) {\n return null;\n }\n\n return storyFn(context);\n }, decorators);\n };\n}\n","import { configure } from 'storybook/test';\n\n/** Configures `storybook/test` wrappers so Solid updates flush after async play steps. */\nexport async function beforeAll() {\n try {\n configure({\n unstable_advanceTimersWrapper: (cb: () => any) => cb(),\n asyncWrapper: async(cb: () => any) => {\n const result = await cb();\n\n await new Promise<void>((resolve) => {\n setTimeout(resolve, 0);\n\n // @ts-expect-error global jest\n if (typeof jest !== 'undefined' && jest != null && ((setTimeout as any)._isMockFunction === true || Object.hasOwn(setTimeout, 'clock'))) {\n // @ts-expect-error global jest\n jest!.advanceTimersByTime(0);\n }\n });\n\n return result;\n },\n eventWrapper: (cb: () => any) => cb(),\n });\n }\n catch {\n // storybook/test might not be available; noop\n }\n}\n","import type { ArgsStoryFn } from 'storybook/internal/types';\nimport type { SolidRenderer } from '../../preview/public-api';\nimport type { SolidRuntimeCreateComponent } from './render-to-canvas';\n\n/** Default render for meta/stories with `component` + `args` and no custom `render` (CSF 3+). */\nexport function createDefaultRender(createComponent: SolidRuntimeCreateComponent): ArgsStoryFn<SolidRenderer> {\n return (_, context) => {\n if (!context.component) {\n throw new Error(\n `Unable to render story ${ context.id } as the component annotation is missing from the default export`\n );\n }\n\n return createComponent(context.component, context.args);\n };\n}\n","import { getStoryId } from './story-store';\n\nimport type { Args } from 'storybook/internal/types';\nimport type { StoryContext, StoryFnReturnType } from '../../preview/public-api';\nimport type { StoryStateStore } from './story-store';\n\n/** Play/mount API: sync reactive args and globals, then render via `renderToCanvas`. */\nexport function createMount({ storyStore }: { storyStore: StoryStateStore }) {\n return (context: StoryContext<Args>) => {\n const storyId = getStoryId(context);\n const { forceRemount } = context;\n\n if (!storyId) {\n throw new Error('Story ID is required');\n }\n\n return async(ui?: StoryFnReturnType) => {\n if (ui != null) {\n context.originalStoryFn = () => ui;\n }\n\n if (forceRemount) {\n storyStore.disposeStory(storyId);\n }\n\n if (!storyStore.isStoryRendered(storyId) || forceRemount) {\n storyStore.resetStory(storyId);\n }\n\n storyStore.makeContextReactive(context);\n\n await context.renderToCanvas();\n\n return context.canvas;\n };\n };\n}\n","import { getStoryId } from './story-store';\n\nimport type { RenderContext, SolidComponent, SolidRenderer } from '../../preview/public-api';\nimport type { StoryStateStore } from './story-store';\n\nexport type SolidRuntimeCreateComponent = (\n component: SolidComponent,\n props: object\n) => SolidRenderer['storyResult'];\n\nexport interface SolidRendererRuntime {\n storyStore: StoryStateStore;\n createComponent: SolidRuntimeCreateComponent;\n render: (\n renderFn: () => SolidRenderer['storyResult'],\n canvasElement: SolidRenderer['canvasElement']\n ) => () => void;\n}\n\nexport type StoryThunk = () => SolidRenderer['storyResult'];\n\nexport interface StoryAppContext {\n Story: StoryThunk;\n storyContext: RenderContext['storyContext'];\n showMain: RenderContext['showMain'];\n showException: RenderContext['showException'];\n storyId: string;\n}\n\ntype RenderToCanvasOptions = SolidRendererRuntime & {\n createStoryApp: (context: StoryAppContext) => SolidComponent;\n};\n\n/** Mounts or updates the story in the preview canvas. Returns cleanup when the story changes. */\nexport function createRenderToCanvas(options: RenderToCanvasOptions) {\n const { storyStore, createComponent, render, createStoryApp } = options;\n\n return async(\n renderContext: RenderContext,\n canvasElement: SolidRenderer['canvasElement']\n ) => {\n const storyId = getStoryId({ ...renderContext, canvasElement });\n\n if (!storyId) {\n throw new Error('Story ID is required');\n }\n\n const { storyContext, storyFn: StoryFn, showMain, showException } = renderContext;\n\n if (!storyStore.isStoryRendered(storyId)) {\n const App = createStoryApp({\n Story: StoryFn,\n storyContext,\n showMain,\n showException,\n storyId,\n });\n\n const disposeFn = render(() => createComponent(App, {}), canvasElement);\n\n storyStore.setDisposeFn(storyId, disposeFn);\n }\n else {\n StoryFn();\n }\n\n return () => {\n storyStore.disposeStory(storyId);\n storyStore.resetStory(storyId);\n };\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"render-to-canvas-CUzXHGXU.js","names":[],"sources":["../src/renderer/shared/story-store.ts","../src/renderer/shared/apply-decorators.ts","../src/renderer/shared/before-all.ts","../src/renderer/shared/default-render.ts","../src/renderer/shared/mount.ts","../src/renderer/shared/render-to-canvas.ts"],"sourcesContent":["import type { Args, Globals, StoryContext as InternalStoryContext } from 'storybook/internal/types';\nimport type { SolidRenderer } from '../../preview/public-api';\n\n/** Update store via `(prev) => next` (version layer applies reconcile/draft setter). */\ntype SetStoreFn<T extends object> = (update: (prev: T) => T) => void;\n\n/** Reactive store tuple returned by `createStore` (state + setter). */\ntype SolidStore<T extends object> = readonly [\n state: T,\n setState: SetStoreFn<T>\n];\n\ntype CreateStoreFn = <T extends object>(initial: T) => SolidStore<T>;\n\nexport interface StoryStateStore {\n isStoryRendered: (storyId: string) => boolean;\n resetStory: (storyId: string) => void;\n setRendered: (storyId: string, rendered: boolean) => void;\n setDisposeFn: (storyId: string, disposeFn: (() => void) | null) => void;\n makeContextReactive: (context: InternalStoryContext<SolidRenderer, Args>) => void;\n disposeStory: (storyId: string) => void;\n}\n\nexport function getStoryId(context: { id?: string; canvasElement?: { id?: string } }): string | undefined {\n const canvasId = context.canvasElement?.id;\n\n if (!canvasId || canvasId === 'storybook-root') {\n return context.id;\n }\n\n return canvasId;\n}\n\nexport function createStoryState(createStore: CreateStoreFn): StoryStateStore {\n const [globals, setGlobals] = createStore<Globals>({});\n const renderedStories = new Set<string>();\n const argsStores = new Map<string, SolidStore<Args>>();\n const disposers = new Map<string, () => void>();\n\n const getArgsStore = (storyId: string): SolidStore<Args> => {\n let store = argsStores.get(storyId);\n\n if (!store) {\n store = createStore({} as Args);\n argsStores.set(storyId, store);\n }\n\n return store;\n };\n\n return {\n isStoryRendered(storyId) {\n return renderedStories.has(storyId);\n },\n\n resetStory(storyId) {\n renderedStories.delete(storyId);\n disposers.delete(storyId);\n argsStores.delete(storyId);\n },\n\n setRendered(storyId, isRendered) {\n if (isRendered) {\n renderedStories.add(storyId);\n }\n else {\n renderedStories.delete(storyId);\n }\n },\n\n setDisposeFn(storyId, disposeFn) {\n if (disposeFn) {\n disposers.set(storyId, disposeFn);\n }\n else {\n disposers.delete(storyId);\n }\n },\n\n makeContextReactive(context) {\n const storyId = getStoryId(context);\n\n if (!storyId) {\n throw new Error('Story ID is required');\n }\n\n const [args, setArgs] = getArgsStore(storyId);\n\n // Update reactive stores with the new values\n setArgs(() => context.args);\n setGlobals(() => context.globals);\n\n // Sync the reactive stores to the context\n context.args = args;\n context.globals = globals;\n },\n\n disposeStory(storyId) {\n disposers.get(storyId)?.();\n disposers.delete(storyId);\n },\n };\n}\n","import { defaultDecorateStory } from 'storybook/preview-api';\n\nimport { IS_SOLID_JSX_FLAG, type Decorator, type SolidRenderer } from '../../preview/public-api';\nimport { getStoryId } from './story-store';\n\nimport type { LegacyStoryFn } from 'storybook/internal/types';\nimport type { StoryStateStore } from './story-store';\n\nexport function createApplyDecorators({\n storyStore,\n}: {\n storyStore: StoryStateStore;\n}): (\n storyFn: LegacyStoryFn<SolidRenderer>,\n rawDecorators: Decorator[]\n) => LegacyStoryFn<SolidRenderer> {\n return (storyFn, rawDecorators) => {\n const decorators = rawDecorators.map(\n (decorator): Decorator => {\n const originalFn = (decorator as Decorator & { originalFn?: Decorator }).originalFn;\n const isJSX = Boolean(\n decorator[IS_SOLID_JSX_FLAG] || originalFn?.[IS_SOLID_JSX_FLAG]\n );\n\n return (StoryFn, context) => {\n if (isJSX) {\n const storyId = getStoryId(context);\n\n if (storyId && storyStore.isStoryRendered(storyId)) {\n return StoryFn(context);\n }\n }\n\n return decorator(StoryFn, context);\n };\n }\n );\n\n return defaultDecorateStory((context) => {\n const storyId = getStoryId(context);\n\n if (storyId && storyStore.isStoryRendered(storyId)) {\n return null;\n }\n\n return storyFn(context);\n }, decorators);\n };\n}\n","import { configure } from 'storybook/test';\n\n/** Configures `storybook/test` wrappers so Solid updates flush after async play steps. */\nexport async function beforeAll() {\n try {\n configure({\n unstable_advanceTimersWrapper: (cb: () => any) => cb(),\n asyncWrapper: async(cb: () => any) => {\n const result = await cb();\n\n await new Promise<void>((resolve) => {\n setTimeout(resolve, 0);\n\n // @ts-expect-error global jest\n if (typeof jest !== 'undefined' && jest != null && ((setTimeout as any)._isMockFunction === true || Object.hasOwn(setTimeout, 'clock'))) {\n // @ts-expect-error global jest\n jest!.advanceTimersByTime(0);\n }\n });\n\n return result;\n },\n eventWrapper: (cb: () => any) => cb(),\n });\n }\n catch {\n // storybook/test might not be available; noop\n }\n}\n","import type { ArgsStoryFn } from 'storybook/internal/types';\nimport type { SolidRenderer } from '../../preview/public-api';\nimport type { SolidRuntimeCreateComponent } from './render-to-canvas';\n\n/** Default render for meta/stories with `component` + `args` and no custom `render` (CSF 3+). */\nexport function createDefaultRender(createComponent: SolidRuntimeCreateComponent): ArgsStoryFn<SolidRenderer> {\n return (_, context) => {\n if (!context.component) {\n throw new Error(\n `Unable to render story ${ context.id } as the component annotation is missing from the default export`\n );\n }\n\n return createComponent(context.component, context.args);\n };\n}\n","import { getStoryId } from './story-store';\n\nimport type { Args } from 'storybook/internal/types';\nimport type { StoryContext, StoryFnReturnType } from '../../preview/public-api';\nimport type { StoryStateStore } from './story-store';\n\n/** Play/mount API: sync reactive args and globals, then render via `renderToCanvas`. */\nexport function createMount({\n storyStore,\n flush,\n}: {\n storyStore: StoryStateStore;\n /**\n * Solid 2 commits store writes on `flush`. Call it once, after both args\n * and globals are updated, so a new reader does not observe the empty store.\n */\n flush?: () => void;\n}) {\n return (context: StoryContext<Args>) => {\n const storyId = getStoryId(context);\n const { forceRemount } = context;\n\n if (!storyId) {\n throw new Error('Story ID is required');\n }\n\n return async(ui?: StoryFnReturnType) => {\n if (ui != null) {\n context.originalStoryFn = () => ui;\n }\n\n if (forceRemount) {\n storyStore.disposeStory(storyId);\n }\n\n if (!storyStore.isStoryRendered(storyId) || forceRemount) {\n storyStore.resetStory(storyId);\n }\n\n storyStore.makeContextReactive(context);\n flush?.();\n\n await context.renderToCanvas();\n\n return context.canvas;\n };\n };\n}\n","import { getStoryId } from './story-store';\n\nimport type { RenderContext, SolidComponent, SolidRenderer } from '../../preview/public-api';\nimport type { StoryStateStore } from './story-store';\n\nexport type SolidRuntimeCreateComponent = (\n component: SolidComponent,\n props: object\n) => SolidRenderer['storyResult'];\n\nexport interface SolidRendererRuntime {\n storyStore: StoryStateStore;\n createComponent: SolidRuntimeCreateComponent;\n render: (\n renderFn: () => SolidRenderer['storyResult'],\n canvasElement: SolidRenderer['canvasElement']\n ) => () => void;\n}\n\nexport type StoryThunk = () => SolidRenderer['storyResult'];\n\nexport interface StoryAppContext {\n Story: StoryThunk;\n storyContext: RenderContext['storyContext'];\n showMain: RenderContext['showMain'];\n showException: RenderContext['showException'];\n storyId: string;\n}\n\ntype RenderToCanvasOptions = SolidRendererRuntime & {\n createStoryApp: (context: StoryAppContext) => SolidComponent;\n};\n\n/** Mounts or updates the story in the preview canvas. Returns cleanup when the story changes. */\nexport function createRenderToCanvas(options: RenderToCanvasOptions) {\n const { storyStore, createComponent, render, createStoryApp } = options;\n\n return async(\n renderContext: RenderContext,\n canvasElement: SolidRenderer['canvasElement']\n ) => {\n const storyId = getStoryId({ ...renderContext, canvasElement });\n\n if (!storyId) {\n throw new Error('Story ID is required');\n }\n\n const { storyContext, storyFn: StoryFn, showMain, showException } = renderContext;\n\n if (!storyStore.isStoryRendered(storyId)) {\n const App = createStoryApp({\n Story: StoryFn,\n storyContext,\n showMain,\n showException,\n storyId,\n });\n\n const disposeFn = render(() => createComponent(App, {}), canvasElement);\n\n storyStore.setDisposeFn(storyId, disposeFn);\n }\n else {\n StoryFn();\n }\n\n return () => {\n storyStore.disposeStory(storyId);\n storyStore.resetStory(storyId);\n };\n };\n}\n"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { a as createApplyDecorators, i as beforeAll, n as createMount, o as createStoryState, r as createDefaultRender, t as createRenderToCanvas } from "../render-to-canvas-
|
|
1
|
+
import { a as createApplyDecorators, i as beforeAll, n as createMount, o as createStoryState, r as createDefaultRender, t as createRenderToCanvas } from "../render-to-canvas-CUzXHGXU.js";
|
|
2
2
|
import { definePreviewAddon } from "storybook/internal/csf";
|
|
3
|
-
import { global } from "@storybook/global";
|
|
4
3
|
import { ErrorBoundary, createComponent, onCleanup, onMount } from "solid-js";
|
|
4
|
+
import { global } from "@storybook/global";
|
|
5
5
|
import { createStore, reconcile } from "solid-js/store";
|
|
6
6
|
import { render as render$1 } from "solid-js/web";
|
|
7
7
|
//#region src/renderer/solid-legacy.ts
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { a as createApplyDecorators, i as beforeAll, n as createMount, o as createStoryState, r as createDefaultRender, t as createRenderToCanvas } from "../render-to-canvas-
|
|
1
|
+
import { a as createApplyDecorators, i as beforeAll, n as createMount, o as createStoryState, r as createDefaultRender, t as createRenderToCanvas } from "../render-to-canvas-CUzXHGXU.js";
|
|
2
2
|
import { definePreviewAddon } from "storybook/internal/csf";
|
|
3
3
|
import { global } from "@storybook/global";
|
|
4
4
|
import { render as render$1 } from "@solidjs/web";
|
|
5
|
-
import { Errored, createComponent, createMemo, createStore, onSettled } from "solid-js";
|
|
5
|
+
import { Errored, createComponent, createMemo, createStore, flush, onSettled } from "solid-js";
|
|
6
6
|
//#region src/renderer/solid-next.ts
|
|
7
7
|
const SOLID_RENDERER_ID = "solid-next";
|
|
8
8
|
if (global.window) global.window.STORYBOOK_ENV = SOLID_RENDERER_ID;
|
|
@@ -23,7 +23,10 @@ const runtime = {
|
|
|
23
23
|
createComponent
|
|
24
24
|
};
|
|
25
25
|
const applyDecorators = createApplyDecorators({ storyStore });
|
|
26
|
-
const mount = createMount(
|
|
26
|
+
const mount = createMount({
|
|
27
|
+
storyStore,
|
|
28
|
+
flush
|
|
29
|
+
});
|
|
27
30
|
const render = createDefaultRender(runtime.createComponent);
|
|
28
31
|
const renderToCanvas = createRenderToCanvas({
|
|
29
32
|
...runtime,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"solid-next.js","names":["solidRender"],"sources":["../../src/renderer/solid-next.ts"],"sourcesContent":["import { render as solidRender } from '@solidjs/web';\nimport { global } from '@storybook/global';\nimport {\n createComponent,\n createMemo,\n createStore,\n Errored,\n onSettled,\n} from 'solid-js-next';\nimport { definePreviewAddon } from 'storybook/internal/csf';\n\nimport { createApplyDecorators } from './shared/apply-decorators';\nimport { beforeAll } from './shared/before-all';\nimport { createDefaultRender } from './shared/default-render';\nimport { createMount } from './shared/mount';\nimport { createRenderToCanvas } from './shared/render-to-canvas';\nimport { createStoryState } from './shared/story-store';\n\nimport type { ProjectAnnotations, Renderer } from 'storybook/internal/types';\nimport type { SolidComponent, SolidRenderer } from '../preview/public-api';\nimport type { SolidRendererRuntime, StoryThunk } from './shared/render-to-canvas';\n\nconst SOLID_RENDERER_ID = 'solid-next' as const;\n\nif (global.window) {\n global.window.STORYBOOK_ENV = SOLID_RENDERER_ID;\n}\n\nconst parameters = {\n renderer: SOLID_RENDERER_ID,\n};\n\nfunction trackStory(story: StoryThunk): SolidRenderer['storyResult'] {\n return createMemo(story) as unknown as SolidRenderer['storyResult'];\n}\n\nconst storyStore = createStoryState((initial: any) => {\n const [state, setStore] = createStore(initial);\n\n const setState = (update: (prev: any) => any) => {\n setStore(() => update(state));\n };\n\n return [state, setState];\n});\n\nconst runtime: SolidRendererRuntime = {\n storyStore,\n render: solidRender,\n createComponent: createComponent as SolidRendererRuntime['createComponent'],\n};\n\nconst applyDecorators = createApplyDecorators({ storyStore });\nconst mount = createMount(
|
|
1
|
+
{"version":3,"file":"solid-next.js","names":["solidRender"],"sources":["../../src/renderer/solid-next.ts"],"sourcesContent":["import { render as solidRender } from '@solidjs/web';\nimport { global } from '@storybook/global';\nimport {\n createComponent,\n createMemo,\n createStore,\n Errored,\n flush,\n onSettled,\n} from 'solid-js-next';\nimport { definePreviewAddon } from 'storybook/internal/csf';\n\nimport { createApplyDecorators } from './shared/apply-decorators';\nimport { beforeAll } from './shared/before-all';\nimport { createDefaultRender } from './shared/default-render';\nimport { createMount } from './shared/mount';\nimport { createRenderToCanvas } from './shared/render-to-canvas';\nimport { createStoryState } from './shared/story-store';\n\nimport type { ProjectAnnotations, Renderer } from 'storybook/internal/types';\nimport type { SolidComponent, SolidRenderer } from '../preview/public-api';\nimport type { SolidRendererRuntime, StoryThunk } from './shared/render-to-canvas';\n\nconst SOLID_RENDERER_ID = 'solid-next' as const;\n\nif (global.window) {\n global.window.STORYBOOK_ENV = SOLID_RENDERER_ID;\n}\n\nconst parameters = {\n renderer: SOLID_RENDERER_ID,\n};\n\nfunction trackStory(story: StoryThunk): SolidRenderer['storyResult'] {\n return createMemo(story) as unknown as SolidRenderer['storyResult'];\n}\n\nconst storyStore = createStoryState((initial: any) => {\n const [state, setStore] = createStore(initial);\n\n const setState = (update: (prev: any) => any) => {\n setStore(() => update(state));\n };\n\n return [state, setState];\n});\n\nconst runtime: SolidRendererRuntime = {\n storyStore,\n render: solidRender,\n createComponent: createComponent as SolidRendererRuntime['createComponent'],\n};\n\nconst applyDecorators = createApplyDecorators({ storyStore });\nconst mount = createMount({ storyStore, flush });\nconst render = createDefaultRender(runtime.createComponent);\nconst renderToCanvas = createRenderToCanvas({\n ...runtime,\n createStoryApp: ({ Story, storyContext, showMain, showException, storyId }) => {\n const renderStory = () => createComponent(() => trackStory(Story), {});\n\n return (() => {\n onSettled(() => {\n showMain();\n storyStore.setRendered(storyId, true);\n\n return () => {\n storyStore.setRendered(storyId, false);\n };\n });\n\n\n if (storyContext?.parameters?.['__isPortableStory']) {\n return renderStory();\n }\n\n return createComponent(Errored, {\n fallback: (err: () => unknown, _reset: () => void) => {\n const error = err() as Error;\n\n showException(error);\n\n return error as any;\n },\n children: renderStory(),\n });\n }) as SolidComponent;\n },\n});\n\nconst previewAddon = definePreviewAddon({\n applyDecorators,\n beforeAll,\n mount,\n parameters,\n render,\n renderToCanvas,\n} as unknown as ProjectAnnotations<Renderer>);\n\nexport {\n applyDecorators,\n beforeAll,\n mount,\n parameters,\n previewAddon,\n render,\n renderToCanvas,\n};\n"],"mappings":";;;;;;AAuBA,MAAM,oBAAoB;AAE1B,IAAI,OAAO,QACP,OAAO,OAAO,gBAAgB;AAGlC,MAAM,aAAa,EACf,UAAU,kBACd;AAEA,SAAS,WAAW,OAAiD;CACjE,OAAO,WAAW,KAAK;AAC3B;AAEA,MAAM,aAAa,kBAAkB,YAAiB;CAClD,MAAM,CAAC,OAAO,YAAY,YAAY,OAAO;CAE7C,MAAM,YAAY,WAA+B;EAC7C,eAAe,OAAO,KAAK,CAAC;CAChC;CAEA,OAAO,CAAC,OAAO,QAAQ;AAC3B,CAAC;AAED,MAAM,UAAgC;CAClC;CACA,QAAQA;CACS;AACrB;AAEA,MAAM,kBAAkB,sBAAsB,EAAE,WAAW,CAAC;AAC5D,MAAM,QAAQ,YAAY;CAAE;CAAY;AAAM,CAAC;AAC/C,MAAM,SAAS,oBAAoB,QAAQ,eAAe;AAC1D,MAAM,iBAAiB,qBAAqB;CACxC,GAAG;CACH,iBAAiB,EAAE,OAAO,cAAc,UAAU,eAAe,cAAc;EAC3E,MAAM,oBAAoB,sBAAsB,WAAW,KAAK,GAAG,CAAC,CAAC;EAErE,cAAc;GACV,gBAAgB;IACZ,SAAS;IACT,WAAW,YAAY,SAAS,IAAI;IAEpC,aAAa;KACT,WAAW,YAAY,SAAS,KAAK;IACzC;GACJ,CAAC;GAGD,IAAI,cAAc,aAAa,sBAC3B,OAAO,YAAY;GAGvB,OAAO,gBAAgB,SAAS;IAC5B,WAAW,KAAoB,WAAuB;KAClD,MAAM,QAAQ,IAAI;KAElB,cAAc,KAAK;KAEnB,OAAO;IACX;IACA,UAAU,YAAY;GAC1B,CAAC;EACL;CACJ;AACJ,CAAC;AAED,MAAM,eAAe,mBAAmB;CACpC;CACA;CACA;CACA;CACA;CACA;AACJ,CAA4C"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "storybook-solidjs-vite",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "10.7.
|
|
4
|
+
"version": "10.7.4",
|
|
5
5
|
"private": false,
|
|
6
6
|
"packageManager": "bun@1.3.14",
|
|
7
7
|
"description": "SolidJS integration with Storybook",
|
|
@@ -133,7 +133,7 @@
|
|
|
133
133
|
"@changesets/cli": "^3.0.1",
|
|
134
134
|
"@commitlint/cli": "^21.2.2",
|
|
135
135
|
"@commitlint/config-conventional": "^21.2.2",
|
|
136
|
-
"@solidjs/web": "2.0.0-rc.
|
|
136
|
+
"@solidjs/web": "2.0.0-rc.15",
|
|
137
137
|
"@tsconfig/strictest": "^2.0.8",
|
|
138
138
|
"@types/node": "^25.9.5",
|
|
139
139
|
"@types/semver": "^7.8.0",
|
|
@@ -144,7 +144,7 @@
|
|
|
144
144
|
"lint-staged": "^16.4.0",
|
|
145
145
|
"simple-git-hooks": "^2.13.1",
|
|
146
146
|
"solid-js": "^1.9.15",
|
|
147
|
-
"solid-js-next": "npm:solid-js@2.0.0-rc.
|
|
147
|
+
"solid-js-next": "npm:solid-js@2.0.0-rc.15",
|
|
148
148
|
"storybook": "^10.6.0",
|
|
149
149
|
"tsdown": "^0.23.0",
|
|
150
150
|
"type-fest": "^5.8.0",
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { definePreview as definePreviewBase } from 'storybook/internal/csf';
|
|
2
2
|
|
|
3
3
|
import * as solidDocsAnnotations from '../renderer/docs';
|
|
4
|
+
import { createReactiveStoryComponent } from './reactive-story-component';
|
|
4
5
|
|
|
5
6
|
import type { AddonTypes, InferTypes, Meta, Preview, PreviewAddon, Story } from 'storybook/internal/csf';
|
|
6
7
|
import type {
|
|
@@ -128,8 +129,8 @@ export function createSolidDefinePreview(
|
|
|
128
129
|
meta.story = (__input: any) => {
|
|
129
130
|
const story = defineStory(__input);
|
|
130
131
|
|
|
131
|
-
// @ts-expect-error
|
|
132
|
-
story.Component = story
|
|
132
|
+
// @ts-expect-error factory stories keep input and meta off the public type
|
|
133
|
+
story.Component = createReactiveStoryComponent(story);
|
|
133
134
|
|
|
134
135
|
return story;
|
|
135
136
|
};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import * as solid from 'solid-js';
|
|
2
|
+
import { composeStory } from 'storybook/preview-api';
|
|
3
|
+
|
|
4
|
+
import type { ProjectAnnotations } from 'storybook/internal/types';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* `__compose()` copies props with an object spread before the story function
|
|
8
|
+
* runs. The only hook between that copy and the story is a decorator, so this
|
|
9
|
+
* one puts the original props object back onto `context.args`. Solid's
|
|
10
|
+
* `mergeProps` / `merge` keeps the getters. The story then renders once.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
interface ComposableStory {
|
|
14
|
+
input: {
|
|
15
|
+
decorators?: unknown;
|
|
16
|
+
};
|
|
17
|
+
meta: {
|
|
18
|
+
input: Record<string, unknown>;
|
|
19
|
+
preview: {
|
|
20
|
+
composed: ProjectAnnotations<any>;
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
type MergeArgs = (base: object, props: object) => object;
|
|
26
|
+
|
|
27
|
+
const mergeArgs: MergeArgs = 'mergeProps' in solid
|
|
28
|
+
? solid.mergeProps
|
|
29
|
+
: (solid as { merge: MergeArgs }).merge;
|
|
30
|
+
|
|
31
|
+
function toArray(decorators: unknown): any[] {
|
|
32
|
+
if (decorators == null) {
|
|
33
|
+
return [];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return Array.isArray(decorators) ? decorators : [decorators];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function createReactiveStoryComponent(story: ComposableStory) {
|
|
40
|
+
let liveProps: object | undefined;
|
|
41
|
+
|
|
42
|
+
const composed = composeStory(
|
|
43
|
+
{
|
|
44
|
+
...story.input,
|
|
45
|
+
decorators: [
|
|
46
|
+
(storyFn: (context: { args: object }) => unknown, context: { args?: object }) => {
|
|
47
|
+
if (liveProps) {
|
|
48
|
+
context.args = mergeArgs(context.args ?? {}, liveProps);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return storyFn(context as { args: object });
|
|
52
|
+
},
|
|
53
|
+
...toArray(story.input.decorators),
|
|
54
|
+
],
|
|
55
|
+
},
|
|
56
|
+
story.meta.input,
|
|
57
|
+
undefined,
|
|
58
|
+
story.meta.preview.composed,
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
const Component = (props?: object) => {
|
|
62
|
+
liveProps = props;
|
|
63
|
+
|
|
64
|
+
try {
|
|
65
|
+
return composed();
|
|
66
|
+
}
|
|
67
|
+
finally {
|
|
68
|
+
liveProps = undefined;
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
return Object.assign(Component, composed);
|
|
73
|
+
}
|
|
@@ -5,7 +5,17 @@ import type { StoryContext, StoryFnReturnType } from '../../preview/public-api';
|
|
|
5
5
|
import type { StoryStateStore } from './story-store';
|
|
6
6
|
|
|
7
7
|
/** Play/mount API: sync reactive args and globals, then render via `renderToCanvas`. */
|
|
8
|
-
export function createMount({
|
|
8
|
+
export function createMount({
|
|
9
|
+
storyStore,
|
|
10
|
+
flush,
|
|
11
|
+
}: {
|
|
12
|
+
storyStore: StoryStateStore;
|
|
13
|
+
/**
|
|
14
|
+
* Solid 2 commits store writes on `flush`. Call it once, after both args
|
|
15
|
+
* and globals are updated, so a new reader does not observe the empty store.
|
|
16
|
+
*/
|
|
17
|
+
flush?: () => void;
|
|
18
|
+
}) {
|
|
9
19
|
return (context: StoryContext<Args>) => {
|
|
10
20
|
const storyId = getStoryId(context);
|
|
11
21
|
const { forceRemount } = context;
|
|
@@ -28,6 +38,7 @@ export function createMount({ storyStore }: { storyStore: StoryStateStore }) {
|
|
|
28
38
|
}
|
|
29
39
|
|
|
30
40
|
storyStore.makeContextReactive(context);
|
|
41
|
+
flush?.();
|
|
31
42
|
|
|
32
43
|
await context.renderToCanvas();
|
|
33
44
|
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
createMemo,
|
|
6
6
|
createStore,
|
|
7
7
|
Errored,
|
|
8
|
+
flush,
|
|
8
9
|
onSettled,
|
|
9
10
|
} from 'solid-js-next';
|
|
10
11
|
import { definePreviewAddon } from 'storybook/internal/csf';
|
|
@@ -51,7 +52,7 @@ const runtime: SolidRendererRuntime = {
|
|
|
51
52
|
};
|
|
52
53
|
|
|
53
54
|
const applyDecorators = createApplyDecorators({ storyStore });
|
|
54
|
-
const mount = createMount(
|
|
55
|
+
const mount = createMount({ storyStore, flush });
|
|
55
56
|
const render = createDefaultRender(runtime.createComponent);
|
|
56
57
|
const renderToCanvas = createRenderToCanvas({
|
|
57
58
|
...runtime,
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
import { createComponent, createRoot, createSignal } from 'solid-js';
|
|
2
|
+
import { createStore } from 'solid-js/store';
|
|
3
|
+
import { definePreviewAddon } from 'storybook/internal/csf';
|
|
4
|
+
import { describe, expect, it } from 'vitest';
|
|
5
|
+
|
|
6
|
+
import { createSolidDefinePreview } from '../../preview/define-preview';
|
|
7
|
+
import { createApplyDecorators } from '../../renderer/shared/apply-decorators';
|
|
8
|
+
import { createDefaultRender } from '../../renderer/shared/default-render';
|
|
9
|
+
|
|
10
|
+
const definePreview = createSolidDefinePreview(definePreviewAddon({
|
|
11
|
+
applyDecorators: createApplyDecorators({
|
|
12
|
+
storyStore: {
|
|
13
|
+
isStoryRendered: () => false,
|
|
14
|
+
},
|
|
15
|
+
} as any),
|
|
16
|
+
render: createDefaultRender(createComponent),
|
|
17
|
+
} as any));
|
|
18
|
+
|
|
19
|
+
describe('Story.Component', () => {
|
|
20
|
+
it('updates when props change and keeps the component mounted', () => {
|
|
21
|
+
const preview = definePreview({});
|
|
22
|
+
let mounts = 0;
|
|
23
|
+
let readLabel = () => '';
|
|
24
|
+
|
|
25
|
+
const Counter = (props: { label?: string }) => {
|
|
26
|
+
mounts += 1;
|
|
27
|
+
readLabel = () => props.label ?? '';
|
|
28
|
+
|
|
29
|
+
return null;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const meta = preview.meta({
|
|
33
|
+
component: Counter,
|
|
34
|
+
args: { label: 'from args' },
|
|
35
|
+
});
|
|
36
|
+
const Story = meta.story();
|
|
37
|
+
const [label, setLabel] = createSignal('Label');
|
|
38
|
+
const props = {
|
|
39
|
+
get label() {
|
|
40
|
+
return label();
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
createRoot((dispose) => {
|
|
45
|
+
createComponent(Story.Component, props);
|
|
46
|
+
expect(readLabel()).toBe('Label');
|
|
47
|
+
expect(mounts).toBe(1);
|
|
48
|
+
|
|
49
|
+
setLabel('New label');
|
|
50
|
+
|
|
51
|
+
expect(readLabel()).toBe('New label');
|
|
52
|
+
expect(mounts).toBe(1);
|
|
53
|
+
dispose();
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('keeps meta args when a prop is omitted', () => {
|
|
58
|
+
const preview = definePreview({});
|
|
59
|
+
let readLabel = () => '';
|
|
60
|
+
|
|
61
|
+
const Counter = (props: { label?: string }) => {
|
|
62
|
+
readLabel = () => props.label ?? '';
|
|
63
|
+
|
|
64
|
+
return null;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const Story = preview.meta({
|
|
68
|
+
component: Counter,
|
|
69
|
+
args: { label: 'from args' },
|
|
70
|
+
}).story();
|
|
71
|
+
|
|
72
|
+
createRoot((dispose) => {
|
|
73
|
+
createComponent(Story.Component, {});
|
|
74
|
+
expect(readLabel()).toBe('from args');
|
|
75
|
+
dispose();
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('tracks props for each mounted instance', () => {
|
|
80
|
+
const preview = definePreview({});
|
|
81
|
+
const readLabel: Array<() => string> = [];
|
|
82
|
+
|
|
83
|
+
const Counter = (props: { label?: string }) => {
|
|
84
|
+
readLabel.push(() => props.label ?? '');
|
|
85
|
+
|
|
86
|
+
return null;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const Story = preview.meta({ component: Counter }).story();
|
|
90
|
+
const [first, setFirst] = createSignal('one');
|
|
91
|
+
const [second, setSecond] = createSignal('two');
|
|
92
|
+
|
|
93
|
+
createRoot((dispose) => {
|
|
94
|
+
createComponent(Story.Component, {
|
|
95
|
+
get label() {
|
|
96
|
+
return first();
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
createComponent(Story.Component, {
|
|
100
|
+
get label() {
|
|
101
|
+
return second();
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
expect(readLabel.map(read => read())).toEqual(['one', 'two']);
|
|
106
|
+
|
|
107
|
+
setFirst('one-b');
|
|
108
|
+
|
|
109
|
+
expect(readLabel.map(read => read())).toEqual(['one-b', 'two']);
|
|
110
|
+
|
|
111
|
+
setSecond('two-b');
|
|
112
|
+
|
|
113
|
+
expect(readLabel.map(read => read())).toEqual(['one-b', 'two-b']);
|
|
114
|
+
dispose();
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('still runs story decorators and follows props through them', () => {
|
|
119
|
+
const preview = definePreview({});
|
|
120
|
+
let readLabel = () => '';
|
|
121
|
+
let decoratorRuns = 0;
|
|
122
|
+
const userDecorator = (storyFn: (context: { args: { label?: string } }) => unknown, context: { args: { label?: string } }) => {
|
|
123
|
+
decoratorRuns += 1;
|
|
124
|
+
|
|
125
|
+
return storyFn(context);
|
|
126
|
+
};
|
|
127
|
+
const annotations = {
|
|
128
|
+
decorators: [userDecorator],
|
|
129
|
+
};
|
|
130
|
+
const Story = preview.meta({
|
|
131
|
+
component: (props: { label?: string }) => {
|
|
132
|
+
readLabel = () => props.label ?? '';
|
|
133
|
+
|
|
134
|
+
return null;
|
|
135
|
+
},
|
|
136
|
+
}).story(annotations as never);
|
|
137
|
+
|
|
138
|
+
expect(annotations.decorators).toEqual([userDecorator]);
|
|
139
|
+
const [label, setLabel] = createSignal('Label');
|
|
140
|
+
|
|
141
|
+
createRoot((dispose) => {
|
|
142
|
+
createComponent(Story.Component, {
|
|
143
|
+
get label() {
|
|
144
|
+
return label();
|
|
145
|
+
},
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
expect(decoratorRuns).toBe(1);
|
|
149
|
+
expect(readLabel()).toBe('Label');
|
|
150
|
+
|
|
151
|
+
setLabel('New label');
|
|
152
|
+
|
|
153
|
+
expect(readLabel()).toBe('New label');
|
|
154
|
+
expect(decoratorRuns).toBe(1);
|
|
155
|
+
dispose();
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it('reads args from a custom render when those args change', () => {
|
|
160
|
+
const preview = definePreview({});
|
|
161
|
+
let readLabel = () => '';
|
|
162
|
+
const Story = preview.meta({
|
|
163
|
+
args: { label: 'from args' },
|
|
164
|
+
}).story({
|
|
165
|
+
render: (args: { label?: string }) => {
|
|
166
|
+
readLabel = () => args.label ?? '';
|
|
167
|
+
|
|
168
|
+
return null;
|
|
169
|
+
},
|
|
170
|
+
});
|
|
171
|
+
const [label, setLabel] = createSignal('Label');
|
|
172
|
+
|
|
173
|
+
createRoot((dispose) => {
|
|
174
|
+
createComponent(Story.Component, {
|
|
175
|
+
get label() {
|
|
176
|
+
return label();
|
|
177
|
+
},
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
expect(readLabel()).toBe('Label');
|
|
181
|
+
|
|
182
|
+
setLabel('New label');
|
|
183
|
+
|
|
184
|
+
expect(readLabel()).toBe('New label');
|
|
185
|
+
dispose();
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it('lets story args override meta args and keeps the rest from meta', () => {
|
|
190
|
+
const preview = definePreview({});
|
|
191
|
+
let readLabel = () => '';
|
|
192
|
+
let readHint = () => '';
|
|
193
|
+
|
|
194
|
+
const Story = preview.meta({
|
|
195
|
+
component: (props: { label?: string; hint?: string }) => {
|
|
196
|
+
readLabel = () => props.label ?? '';
|
|
197
|
+
readHint = () => props.hint ?? '';
|
|
198
|
+
|
|
199
|
+
return null;
|
|
200
|
+
},
|
|
201
|
+
args: { label: 'from meta', hint: 'meta hint' },
|
|
202
|
+
}).story({
|
|
203
|
+
args: { label: 'from story' },
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
createRoot((dispose) => {
|
|
207
|
+
createComponent(Story.Component, {});
|
|
208
|
+
|
|
209
|
+
expect(readLabel()).toBe('from story');
|
|
210
|
+
expect(readHint()).toBe('meta hint');
|
|
211
|
+
dispose();
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
it('lets component props override story args', () => {
|
|
216
|
+
const preview = definePreview({});
|
|
217
|
+
let readLabel = () => '';
|
|
218
|
+
let readHint = () => '';
|
|
219
|
+
const Story = preview.meta({
|
|
220
|
+
component: (props: { label?: string; hint?: string }) => {
|
|
221
|
+
readLabel = () => props.label ?? '';
|
|
222
|
+
readHint = () => props.hint ?? '';
|
|
223
|
+
|
|
224
|
+
return null;
|
|
225
|
+
},
|
|
226
|
+
args: { hint: 'meta hint' },
|
|
227
|
+
}).story({
|
|
228
|
+
args: { label: 'from story', hint: 'story hint' },
|
|
229
|
+
});
|
|
230
|
+
const [label, setLabel] = createSignal('from component');
|
|
231
|
+
|
|
232
|
+
createRoot((dispose) => {
|
|
233
|
+
createComponent(Story.Component, {
|
|
234
|
+
get label() {
|
|
235
|
+
return label();
|
|
236
|
+
},
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
expect(readLabel()).toBe('from component');
|
|
240
|
+
expect(readHint()).toBe('story hint');
|
|
241
|
+
|
|
242
|
+
setLabel('updated');
|
|
243
|
+
|
|
244
|
+
expect(readLabel()).toBe('updated');
|
|
245
|
+
expect(readHint()).toBe('story hint');
|
|
246
|
+
dispose();
|
|
247
|
+
});
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
it('snapshots args from meta.story and ignores later changes', () => {
|
|
251
|
+
const preview = definePreview({});
|
|
252
|
+
let readLabel = () => '';
|
|
253
|
+
const Counter = (props: { label?: string }) => {
|
|
254
|
+
readLabel = () => props.label ?? '';
|
|
255
|
+
|
|
256
|
+
return null;
|
|
257
|
+
};
|
|
258
|
+
const storyArgs = { label: 'original' };
|
|
259
|
+
const plain = preview.meta({ component: Counter }).story({ args: storyArgs });
|
|
260
|
+
|
|
261
|
+
storyArgs.label = 'changed';
|
|
262
|
+
|
|
263
|
+
const [storeArgs, setStoreArgs] = createStore({ label: 'from store' });
|
|
264
|
+
const stored = preview.meta({ component: Counter }).story({ args: storeArgs });
|
|
265
|
+
|
|
266
|
+
createRoot((dispose) => {
|
|
267
|
+
createComponent(plain.Component, {});
|
|
268
|
+
expect(readLabel()).toBe('original');
|
|
269
|
+
|
|
270
|
+
createComponent(stored.Component, {});
|
|
271
|
+
expect(readLabel()).toBe('from store');
|
|
272
|
+
|
|
273
|
+
setStoreArgs({ label: 'updated' });
|
|
274
|
+
|
|
275
|
+
expect(readLabel()).toBe('from store');
|
|
276
|
+
dispose();
|
|
277
|
+
});
|
|
278
|
+
});
|
|
279
|
+
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { createMemo, createRoot } from 'solid-js-next';
|
|
2
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
3
|
+
|
|
4
|
+
import { mount } from '../../renderer/solid-next';
|
|
5
|
+
|
|
6
|
+
// `@solidjs/web` imports `solid-js`, which is Solid 1 in this package.
|
|
7
|
+
vi.mock('@solidjs/web', () => ({
|
|
8
|
+
render: () => () => {},
|
|
9
|
+
}));
|
|
10
|
+
|
|
11
|
+
describe('Solid 2 mount', () => {
|
|
12
|
+
it('shows story args and globals to a reactive reader on first render', async() => {
|
|
13
|
+
let seen = '';
|
|
14
|
+
const context = {
|
|
15
|
+
id: 'repro-args--basic',
|
|
16
|
+
args: { user: { name: 'Ada' } },
|
|
17
|
+
globals: { theme: 'dark' },
|
|
18
|
+
forceRemount: false,
|
|
19
|
+
canvas: 'canvas',
|
|
20
|
+
renderToCanvas: async() => {
|
|
21
|
+
createRoot((dispose) => {
|
|
22
|
+
try {
|
|
23
|
+
const label = createMemo(() => `${context.args.user.name}/${context.globals.theme}`);
|
|
24
|
+
|
|
25
|
+
seen = label();
|
|
26
|
+
}
|
|
27
|
+
finally {
|
|
28
|
+
dispose();
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
},
|
|
32
|
+
} as any;
|
|
33
|
+
|
|
34
|
+
await expect(mount(context)()).resolves.toBe('canvas');
|
|
35
|
+
expect(seen).toBe('Ada/dark');
|
|
36
|
+
});
|
|
37
|
+
});
|