storybook-solidjs-vite 10.7.2 → 10.7.3
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
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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { a as createApplyDecorators, i as beforeAll, n as createMount, o as createStoryState, r as createDefaultRender, t as createRenderToCanvas } from "../render-to-canvas-5L3YXi18.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
|
package/package.json
CHANGED
|
@@ -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
|
+
}
|
|
@@ -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
|
+
});
|