diff --git a/code/addons/interactions/src/preview.ts b/code/addons/interactions/src/preview.ts index 5ef2520b6b71..aff3b3c59c50 100644 --- a/code/addons/interactions/src/preview.ts +++ b/code/addons/interactions/src/preview.ts @@ -1,10 +1,14 @@ -import type { PlayFunction, PlayFunctionContext, StepLabel } from '@storybook/types'; +import type { PlayFunction, StepLabel, StoryContext } from '@storybook/types'; import { instrument } from '@storybook/instrumenter'; export const { step: runStep } = instrument( { - step: (label: StepLabel, play: PlayFunction, context: PlayFunctionContext) => - play(context), + // It seems like the label is unused, but the instrumenter has access to it + // The context will be bounded later in StoryRender, so that the user can write just: + // await step("label", (context) => { + // // labeled step + // }); + step: (label: StepLabel, play: PlayFunction, context: StoryContext) => play(context), }, { intercept: true } ); diff --git a/code/addons/links/package.json b/code/addons/links/package.json index 3052208e2246..2be96a5f6d90 100644 --- a/code/addons/links/package.json +++ b/code/addons/links/package.json @@ -67,7 +67,7 @@ "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/addon-bundle.ts" }, "dependencies": { - "@storybook/csf": "^0.1.8", + "@storybook/csf": "0.1.10--canary.d841bb4.0", "@storybook/global": "^5.0.0", "ts-dedent": "^2.0.0" }, diff --git a/code/frameworks/angular/src/client/decorators.test.ts b/code/frameworks/angular/src/client/decorators.test.ts index 72a6f6836713..f31678fbd0c1 100644 --- a/code/frameworks/angular/src/client/decorators.test.ts +++ b/code/frameworks/angular/src/client/decorators.test.ts @@ -24,8 +24,12 @@ const defaultContext: Addon_StoryContext = { viewMode: 'story', abortSignal: undefined, canvasElement: undefined, + step: undefined, + context: undefined, }; +defaultContext.context = defaultContext; + class MockModule {} class MockModuleTwo {} class MockService {} diff --git a/code/lib/codemod/package.json b/code/lib/codemod/package.json index 97bc4d24046c..b70bc3016fcd 100644 --- a/code/lib/codemod/package.json +++ b/code/lib/codemod/package.json @@ -57,7 +57,7 @@ "@babel/core": "^7.24.4", "@babel/preset-env": "^7.24.4", "@babel/types": "^7.24.0", - "@storybook/csf": "^0.1.8", + "@storybook/csf": "0.1.10--canary.d841bb4.0", "@storybook/csf-tools": "workspace:*", "@storybook/node-logger": "workspace:*", "@storybook/types": "workspace:*", diff --git a/code/lib/core-events/package.json b/code/lib/core-events/package.json index 24fefcf9b258..d4bd5bf31d26 100644 --- a/code/lib/core-events/package.json +++ b/code/lib/core-events/package.json @@ -78,7 +78,7 @@ "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "dependencies": { - "@storybook/csf": "^0.1.8", + "@storybook/csf": "0.1.10--canary.d841bb4.0", "ts-dedent": "^2.0.0" }, "devDependencies": { diff --git a/code/lib/core-server/package.json b/code/lib/core-server/package.json index 0b4738c6e908..adec4bbecd9d 100644 --- a/code/lib/core-server/package.json +++ b/code/lib/core-server/package.json @@ -63,7 +63,7 @@ "@storybook/channels": "workspace:*", "@storybook/core-common": "workspace:*", "@storybook/core-events": "workspace:*", - "@storybook/csf": "^0.1.8", + "@storybook/csf": "0.1.10--canary.d841bb4.0", "@storybook/csf-tools": "workspace:*", "@storybook/docs-mdx": "3.1.0-next.0", "@storybook/global": "^5.0.0", diff --git a/code/lib/csf-tools/package.json b/code/lib/csf-tools/package.json index 026383952aeb..07718df5b847 100644 --- a/code/lib/csf-tools/package.json +++ b/code/lib/csf-tools/package.json @@ -46,7 +46,7 @@ "@babel/parser": "^7.24.4", "@babel/traverse": "^7.24.1", "@babel/types": "^7.24.0", - "@storybook/csf": "^0.1.8", + "@storybook/csf": "0.1.10--canary.d841bb4.0", "@storybook/types": "workspace:*", "fs-extra": "^11.1.0", "recast": "^0.23.5", diff --git a/code/lib/instrumenter/src/instrumenter.test.ts b/code/lib/instrumenter/src/instrumenter.test.ts index bbd01e5f064f..6209f51b6f08 100644 --- a/code/lib/instrumenter/src/instrumenter.test.ts +++ b/code/lib/instrumenter/src/instrumenter.test.ts @@ -175,6 +175,27 @@ describe('Instrumenter', () => { ); }); + it('handles circular references', () => { + const { fn } = instrument({ fn: (...args: any) => {} }); + const obj = { key: 'value', obj: {}, array: [] as any[] }; + obj.obj = obj; + obj.array = [obj]; + + expect(() => fn(obj)).not.toThrow(); + + expect(callSpy.mock.calls[0][0].args).toMatchInlineSnapshot(` + [ + { + "array": [ + "[Circular]", + ], + "key": "value", + "obj": "[Circular]", + }, + ] + `); + }); + it('provides metadata about the call in the event', () => { const { obj } = instrument({ obj: { fn: () => {} } }); obj.fn(); diff --git a/code/lib/instrumenter/src/instrumenter.ts b/code/lib/instrumenter/src/instrumenter.ts index f0a11336666b..641577d57f18 100644 --- a/code/lib/instrumenter/src/instrumenter.ts +++ b/code/lib/instrumenter/src/instrumenter.ts @@ -411,13 +411,21 @@ export class Instrumenter { invoke(fn: Function, object: Record, call: Call, options: Options) { const { callRefsByResult, renderPhase } = this.getState(call.storyId); - // Map complex values to a JSON-serializable representation. - const serializeValues = (value: any): any => { + // TODO This function should not needed anymore, as the channel already serializes values with telejson + // Possibly we need to add HTMLElement support to telejson though + // Keeping this function here, as removing it means we need to refactor the deserializing that happens in addon-interactions + const maximumDepth = 25; // mimicks the max depth of telejson + const serializeValues = (value: any, depth: number, seen: unknown[]): any => { + if (seen.includes(value)) return '[Circular]'; + seen = [...seen, value]; + + if (depth > maximumDepth) return '...'; + if (callRefsByResult.has(value)) { return callRefsByResult.get(value); } if (value instanceof Array) { - return value.map(serializeValues); + return value.map((it) => serializeValues(it, ++depth, seen)); } if (value instanceof Date) { return { __date__: { value: value.toISOString() } }; @@ -452,13 +460,16 @@ export class Instrumenter { } if (Object.prototype.toString.call(value) === '[object Object]') { return Object.fromEntries( - Object.entries(value).map(([key, val]) => [key, serializeValues(val)]) + Object.entries(value).map(([key, val]) => [key, serializeValues(val, ++depth, seen)]) ); } return value; }; - const info: Call = { ...call, args: call.args.map(serializeValues) }; + const info: Call = { + ...call, + args: call.args.map((arg) => serializeValues(arg, 0, [])), + }; // Mark any ancestor calls as "chained upon" so we won't attempt to defer it later. call.path.forEach((ref: any) => { diff --git a/code/lib/manager-api/package.json b/code/lib/manager-api/package.json index 49270d11882d..36eef08b6735 100644 --- a/code/lib/manager-api/package.json +++ b/code/lib/manager-api/package.json @@ -47,7 +47,7 @@ "@storybook/channels": "workspace:*", "@storybook/client-logger": "workspace:*", "@storybook/core-events": "workspace:*", - "@storybook/csf": "^0.1.8", + "@storybook/csf": "0.1.10--canary.d841bb4.0", "@storybook/global": "^5.0.0", "@storybook/icons": "^1.2.5", "@storybook/router": "workspace:*", diff --git a/code/lib/preview-api/package.json b/code/lib/preview-api/package.json index 75415839d0d2..1256f3d16b11 100644 --- a/code/lib/preview-api/package.json +++ b/code/lib/preview-api/package.json @@ -47,7 +47,7 @@ "@storybook/channels": "workspace:*", "@storybook/client-logger": "workspace:*", "@storybook/core-events": "workspace:*", - "@storybook/csf": "^0.1.8", + "@storybook/csf": "0.1.10--canary.d841bb4.0", "@storybook/global": "^5.0.0", "@storybook/types": "workspace:*", "@types/qs": "^6.9.5", diff --git a/code/lib/preview-api/src/modules/preview-web/PreviewWeb.mockdata.ts b/code/lib/preview-api/src/modules/preview-web/PreviewWeb.mockdata.ts index ded5833ed667..202e17f291ef 100644 --- a/code/lib/preview-api/src/modules/preview-web/PreviewWeb.mockdata.ts +++ b/code/lib/preview-api/src/modules/preview-web/PreviewWeb.mockdata.ts @@ -11,8 +11,15 @@ import { STORY_THREW_EXCEPTION, } from '@storybook/core-events'; -import type { ModuleImportFn, StoryIndex, TeardownRenderToCanvas } from '@storybook/types'; +import type { + ModuleImportFn, + ProjectAnnotations, + Renderer, + StoryIndex, + TeardownRenderToCanvas, +} from '@storybook/types'; import type { RenderPhase } from './render/StoryRender'; +import { composeConfigs } from '../store'; export const componentOneExports = { default: { @@ -65,7 +72,7 @@ export const docsRenderer = { unmount: vi.fn(), }; export const teardownrenderToCanvas: Mock<[TeardownRenderToCanvas]> = vi.fn(); -export const projectAnnotations = { +const rawProjectAnnotations = { initialGlobals: { a: 'b' }, globalTypes: {}, decorators: [vi.fn((s) => s())], @@ -73,6 +80,10 @@ export const projectAnnotations = { renderToCanvas: vi.fn().mockReturnValue(teardownrenderToCanvas), parameters: { docs: { renderer: () => docsRenderer } }, }; +export const projectAnnotations = composeConfigs([ + rawProjectAnnotations, +]) as ProjectAnnotations & typeof rawProjectAnnotations; + export const getProjectAnnotations = vi.fn(() => projectAnnotations as any); export const storyIndex: StoryIndex = { diff --git a/code/lib/preview-api/src/modules/preview-web/PreviewWeb.test.ts b/code/lib/preview-api/src/modules/preview-web/PreviewWeb.test.ts index f02240c5abda..830450dd6a23 100644 --- a/code/lib/preview-api/src/modules/preview-web/PreviewWeb.test.ts +++ b/code/lib/preview-api/src/modules/preview-web/PreviewWeb.test.ts @@ -949,7 +949,7 @@ describe('PreviewWeb', () => { forceRemount: true, storyContext: expect.objectContaining({ loaded: { l: 8 }, // This is the value returned by the *first* loader call - args: { foo: 'a', new: 'arg', one: 'mapped-1' }, + args: { foo: 'a', one: 'mapped-1' }, }), }), 'story-element' diff --git a/code/lib/preview-api/src/modules/preview-web/docs-context/DocsContext.ts b/code/lib/preview-api/src/modules/preview-web/docs-context/DocsContext.ts index b22ad4afe444..d299889bcaa1 100644 --- a/code/lib/preview-api/src/modules/preview-web/docs-context/DocsContext.ts +++ b/code/lib/preview-api/src/modules/preview-web/docs-context/DocsContext.ts @@ -4,7 +4,6 @@ import type { ModuleExport, ModuleExports, PreparedStory, - StoryContextForLoaders, StoryId, StoryName, ResolvedModuleExportType, @@ -232,8 +231,9 @@ export class DocsContext implements DocsContextProps getStoryContext = (story: PreparedStory) => { return { ...this.store.getStoryContext(story), + loaded: {}, viewMode: 'docs', - } as StoryContextForLoaders; + }; }; loadStory = (id: StoryId) => { diff --git a/code/lib/preview-api/src/modules/preview-web/render/StoryRender.ts b/code/lib/preview-api/src/modules/preview-web/render/StoryRender.ts index 8ade66fc8942..d7a1abe740f3 100644 --- a/code/lib/preview-api/src/modules/preview-web/render/StoryRender.ts +++ b/code/lib/preview-api/src/modules/preview-web/render/StoryRender.ts @@ -6,10 +6,8 @@ import type { PreparedStory, TeardownRenderToCanvas, StoryContext, - StoryContextForLoaders, StoryId, StoryRenderOptions, - ViewMode, } from '@storybook/types'; import type { Channel } from '@storybook/channels'; import { @@ -71,7 +69,7 @@ export class StoryRender implements Render, private callbacks: RenderContextCallbacks, public id: StoryId, - public viewMode: ViewMode, + public viewMode: StoryContext['viewMode'], public renderOptions: StoryRenderOptions = { autoplay: true, forceInitialArgs: false }, story?: PreparedStory ) { @@ -165,6 +163,7 @@ export class StoryRender implements Render implements Render>; - await this.runPhase(abortSignal, 'loading', async () => { - loadedContext = await applyLoaders({ - ...this.storyContext(), - viewMode: this.viewMode, - // TODO add this to CSF - canvasElement, - } as unknown as StoryContextForLoaders); - }); - if (abortSignal.aborted) return; - - const renderStoryContext: StoryContext = { - ...loadedContext!, - // By this stage, it is possible that new args/globals have been received for this story - // and we need to ensure we render it with the new values + const context: StoryContext = { ...this.storyContext(), + viewMode: this.viewMode, abortSignal, - // We should consider parameterizing the story types with TRenderer['canvasElement'] in the future - canvasElement: canvasElement as any, + canvasElement, + loaded: {}, + step: (label, play) => runStep(label, play, context), + context: null!, }; - await this.runPhase(abortSignal, 'beforeEach', async () => { - const cleanupCallbacks = await applyBeforeEach(renderStoryContext); - this.store.addCleanupCallbacks(story, cleanupCallbacks); - }); - - if (abortSignal.aborted) return; + context.context = context; const renderContext: RenderContext = { componentId, @@ -226,10 +209,22 @@ export class StoryRender implements Render unboundStoryFn(renderStoryContext), + storyContext: context, + storyFn: () => unboundStoryFn(context), unboundStoryFn, }; + await this.runPhase(abortSignal, 'loading', async () => { + context.loaded = await applyLoaders(context); + }); + + if (abortSignal.aborted) return; + + await this.runPhase(abortSignal, 'beforeEach', async () => { + const cleanupCallbacks = await applyBeforeEach(context); + this.store.addCleanupCallbacks(story, cleanupCallbacks); + }); + + if (abortSignal.aborted) return; await this.runPhase(abortSignal, 'rendering', async () => { const teardown = await this.renderToScreen(renderContext, canvasElement); @@ -253,7 +248,7 @@ export class StoryRender implements Render { - await playFunction(renderContext.storyContext); + await playFunction(context); }); if (!ignoreUnhandledErrors && unhandledErrors.size > 0) { await this.runPhase(abortSignal, 'errored'); diff --git a/code/lib/preview-api/src/modules/store/StoryStore.test.ts b/code/lib/preview-api/src/modules/store/StoryStore.test.ts index 51d7664b468d..b508cca90d3b 100644 --- a/code/lib/preview-api/src/modules/store/StoryStore.test.ts +++ b/code/lib/preview-api/src/modules/store/StoryStore.test.ts @@ -5,6 +5,7 @@ import { prepareStory } from './csf/prepareStory'; import { processCSFFile } from './csf/processCSFFile'; import { StoryStore } from './StoryStore'; import type { HooksContext } from './hooks'; +import { composeConfigs } from './csf/composeConfigs'; // Spy on prepareStory/processCSFFile vi.mock('./csf/prepareStory', async (importOriginal) => { @@ -41,12 +42,14 @@ const importFn = vi.fn(async (path) => { return path === './src/ComponentOne.stories.js' ? componentOneExports : componentTwoExports; }); -const projectAnnotations: ProjectAnnotations = { - globals: { a: 'b' }, - globalTypes: { a: { type: 'string' } }, - argTypes: { a: { type: 'string' } }, - render: vi.fn(), -}; +const projectAnnotations: ProjectAnnotations = composeConfigs([ + { + globals: { a: 'b' }, + globalTypes: { a: { type: 'string' } }, + argTypes: { a: { type: 'string' } }, + render: vi.fn(), + }, +]); const storyIndex: StoryIndex = { v: 5, @@ -660,6 +663,7 @@ describe('StoryStore', () => { "fileName": "./src/ComponentOne.stories.js", }, "playFunction": undefined, + "runStep": [Function], "story": "A", "storyFn": [Function], "subcomponents": undefined, @@ -707,6 +711,7 @@ describe('StoryStore', () => { "fileName": "./src/ComponentOne.stories.js", }, "playFunction": undefined, + "runStep": [Function], "story": "B", "storyFn": [Function], "subcomponents": undefined, @@ -754,6 +759,7 @@ describe('StoryStore', () => { "fileName": "./src/ComponentTwo.stories.js", }, "playFunction": undefined, + "runStep": [Function], "story": "C", "storyFn": [Function], "subcomponents": undefined, diff --git a/code/lib/preview-api/src/modules/store/StoryStore.ts b/code/lib/preview-api/src/modules/store/StoryStore.ts index 8e2c6e444f80..05702d9b2630 100644 --- a/code/lib/preview-api/src/modules/store/StoryStore.ts +++ b/code/lib/preview-api/src/modules/store/StoryStore.ts @@ -17,7 +17,6 @@ import type { V3CompatIndexEntry, StoryContext, StoryContextForEnhancers, - StoryContextForLoaders, StoryId, PreparedMeta, } from '@storybook/types'; @@ -226,10 +225,7 @@ export class StoryStore { // A prepared story does not include args, globals or hooks. These are stored in the story store // and updated separtely to the (immutable) story. - getStoryContext( - story: PreparedStory, - { forceInitialArgs = false } = {} - ): Omit { + getStoryContext(story: PreparedStory, { forceInitialArgs = false } = {}) { return prepareContext({ ...story, args: forceInitialArgs ? story.initialArgs : this.args.get(story.id), @@ -369,6 +365,12 @@ export class StoryStore { storyFn: (update) => { const context = { ...this.getStoryContext(story), + abortSignal: new AbortController().signal, + canvasElement: null!, + loaded: {}, + step: (label, play) => story.runStep(label, play, context), + context: null!, + canvas: {}, viewMode: 'story', } as StoryContext; diff --git a/code/lib/preview-api/src/modules/store/csf/normalizeProjectAnnotations.ts b/code/lib/preview-api/src/modules/store/csf/normalizeProjectAnnotations.ts index 52ce9f31ab22..37af21e7eca6 100644 --- a/code/lib/preview-api/src/modules/store/csf/normalizeProjectAnnotations.ts +++ b/code/lib/preview-api/src/modules/store/csf/normalizeProjectAnnotations.ts @@ -13,6 +13,10 @@ import { normalizeInputTypes } from './normalizeInputTypes'; import { normalizeArrays } from './normalizeArrays'; import { combineParameters } from '../parameters'; +// TODO(kasperpeulen) Consolidate this function with composeConfigs +// As composeConfigs is the real normalizer, and always run before normalizeProjectAnnotations +// tmeasday: Alternatively we could get rid of composeConfigs and just pass ProjectAnnotations[] around -- and do the composing here. +// That makes sense to me as it avoids the need for both WP + Vite to call composeConfigs at the right time. export function normalizeProjectAnnotations({ argTypes, globalTypes, @@ -48,6 +52,6 @@ export function normalizeProjectAnnotations({ inferControls, ], initialGlobals: combineParameters(initialGlobals, globals), - ...annotations, + ...(annotations as NormalizedProjectAnnotations), }; } diff --git a/code/lib/preview-api/src/modules/store/csf/portable-stories.ts b/code/lib/preview-api/src/modules/store/csf/portable-stories.ts index 75e8d16cce33..bb69130559dc 100644 --- a/code/lib/preview-api/src/modules/store/csf/portable-stories.ts +++ b/code/lib/preview-api/src/modules/store/csf/portable-stories.ts @@ -8,14 +8,12 @@ import type { ComponentAnnotations, LegacyStoryAnnotationsOrFn, NamedOrDefaultProjectAnnotations, - ComposedStoryPlayFn, ComposeStoryFn, Store_CSFExports, StoryContext, Parameters, ComposedStoryFn, StrictArgTypes, - PlayFunctionContext, ProjectAnnotations, } from '@storybook/types'; @@ -104,18 +102,20 @@ export function composeStory story.runStep(label, play, context), + canvasElement: globalThis?.document?.body, + context: null!, ...story, }; + context.context = context; + const playFunction = story.playFunction - ? async (extraContext: Partial>) => - story.playFunction!({ - ...context, - ...extraContext, - canvasElement: extraContext?.canvasElement ?? globalThis.document?.body, - }) + ? async (extraContext?: Partial>>) => { + Object.assign(context, extraContext); + return story.playFunction!(context); + } : undefined; let previousCleanupsDone = false; @@ -159,8 +159,7 @@ export function composeStory, parameters: story.parameters as Parameters, argTypes: story.argTypes as StrictArgTypes, - play: playFunction as ComposedStoryPlayFn | undefined, + play: playFunction, tags: story.tags, } ); diff --git a/code/lib/preview-api/src/modules/store/csf/prepareStory.test.ts b/code/lib/preview-api/src/modules/store/csf/prepareStory.test.ts index a574080558c5..7818d2bd4752 100644 --- a/code/lib/preview-api/src/modules/store/csf/prepareStory.test.ts +++ b/code/lib/preview-api/src/modules/store/csf/prepareStory.test.ts @@ -1,10 +1,21 @@ -import { describe, beforeEach, it, expect, vi } from 'vitest'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; import { global } from '@storybook/global'; -import type { Renderer, ArgsEnhancer, PlayFunctionContext, SBScalarType } from '@storybook/types'; +import type { + ArgsEnhancer, + NormalizedComponentAnnotations, + NormalizedStoryAnnotations, + PreparedStory, + ProjectAnnotations, + Renderer, + SBScalarType, + StoryContext, +} from '@storybook/types'; import { addons, HooksContext } from '../../addons'; import { UNTARGETED } from '../args'; -import { prepareStory, prepareMeta, prepareContext } from './prepareStory'; +import { prepareMeta, prepareStory as realPrepareStory, prepareContext } from './prepareStory'; +import { composeConfigs } from './composeConfigs'; +import { normalizeProjectAnnotations } from './normalizeProjectAnnotations'; vi.mock('@storybook/global', async (importOriginal) => ({ global: { @@ -15,21 +26,42 @@ vi.mock('@storybook/global', async (importOriginal) => ({ const id = 'id'; const name = 'name'; const title = 'title'; -const render = (args: any) => {}; +const render = () => {}; const moduleExport = {}; const stringType: SBScalarType = { name: 'string' }; const numberType: SBScalarType = { name: 'number' }; const booleanType: SBScalarType = { name: 'boolean' }; +// Normalize the project annotations to mimick live behavior +export function prepareStory( + storyAnnotations: NormalizedStoryAnnotations, + componentAnnotations: NormalizedComponentAnnotations, + projectAnnotations: ProjectAnnotations +): PreparedStory { + return realPrepareStory( + storyAnnotations, + componentAnnotations, + normalizeProjectAnnotations(composeConfigs([projectAnnotations])) + ); +} // Extra fields that must be added to the story context after enhancers -const storyContextExtras = () => ({ - hooks: new HooksContext(), - viewMode: 'story' as const, - loaded: {}, - abortSignal: new AbortController().signal, - canvasElement: {}, -}); +const addExtraContext = ( + context: PreparedStory & Pick +): StoryContext => { + const extraContext = { + ...context, + hooks: new HooksContext(), + viewMode: 'story' as const, + loaded: {}, + abortSignal: new AbortController().signal, + canvasElement: {}, + step: vi.fn(), + context: undefined! as StoryContext, + }; + extraContext.context = extraContext; + return extraContext; +}; describe('prepareStory', () => { describe('tags', () => { @@ -93,7 +125,7 @@ describe('prepareStory', () => { ); expect(parameters).toEqual({ - __isArgsStory: true, + __isArgsStory: false, a: 'story', b: { name: 'component' }, c: { name: 'global' }, @@ -102,7 +134,11 @@ describe('prepareStory', () => { }); it('sets a value even if annotations do not have parameters', () => { - const { parameters } = prepareStory({ id, name, moduleExport }, { id, title }, { render }); + const { parameters } = prepareStory( + { id, name, moduleExport }, + { id, title }, + { render: (args: any) => {} } + ); expect(parameters).toEqual({ __isArgsStory: true }); }); @@ -322,6 +358,7 @@ describe('prepareStory', () => { }); describe('applyLoaders', () => { + const abortSignal = new AbortController().signal; it('awaits the result of a loader', async () => { const loader = vi.fn(async () => new Promise((r) => setTimeout(() => r({ foo: 7 }), 100))); const { applyLoaders } = prepareStory( @@ -330,14 +367,15 @@ describe('prepareStory', () => { { render } ); - const storyContext = { context: 'value' } as any; - const loadedContext = await applyLoaders({ ...storyContext }); + const storyContext = { abortSignal } as StoryContext; + const loaded = await applyLoaders(storyContext); - expect(loader).toHaveBeenCalledWith({ ...storyContext, loaded: {} }); - expect(loadedContext).toEqual({ - context: 'value', - loaded: { foo: 7 }, - }); + expect(loader).toHaveBeenCalledWith(storyContext); + expect(loaded).toMatchInlineSnapshot(` + { + "foo": 7, + } + `); }); it('loaders are composed in the right order', async () => { @@ -351,13 +389,13 @@ describe('prepareStory', () => { { render, loaders: [globalLoader] } ); - const storyContext = { context: 'value' } as any; - const loadedContext = await applyLoaders(storyContext); - - expect(loadedContext).toEqual({ - context: 'value', - loaded: { foo: 5, bar: 3, baz: 1 }, - }); + expect(await applyLoaders({ abortSignal } as StoryContext)).toMatchInlineSnapshot(` + { + "bar": 3, + "baz": 1, + "foo": 5, + } + `); }); it('later loaders override earlier loaders', async () => { @@ -372,13 +410,12 @@ describe('prepareStory', () => { { render } ); - const storyContext = { context: 'value' } as any; - const loadedContext = await applyLoaders(storyContext); - - expect(loadedContext).toEqual({ - context: 'value', - loaded: { foo: 3 }, - }); + expect(await applyLoaders({ abortSignal: abortSignal } as StoryContext)) + .toMatchInlineSnapshot(` + { + "foo": 3, + } + `); }); }); @@ -401,7 +438,7 @@ describe('prepareStory', () => { ); const context = prepareContext({ args: story.initialArgs, globals: {}, ...story }); - story.undecoratedStoryFn({ ...context, ...storyContextExtras() }); + story.undecoratedStoryFn(addExtraContext(context)); expect(renderMock).toHaveBeenCalledWith( { one: 'mapped', two: 2, three: 3 }, expect.objectContaining({ args: { one: 'mapped', two: 2, three: 3 } }) @@ -473,7 +510,7 @@ describe('prepareStory', () => { const hooks = new HooksContext(); const context = prepareContext({ args: story.initialArgs, globals: {}, ...story }); - story.unboundStoryFn({ ...context, ...storyContextExtras(), hooks }); + story.unboundStoryFn(addExtraContext(context)); expect(ctx1).toMatchObject({ unmappedArgs: { one: 1 }, args: { one: 'mapped-1' } }); expect(ctx2).toMatchObject({ unmappedArgs: { one: 1 }, args: { one: 'mapped-1' } }); @@ -548,7 +585,7 @@ describe('prepareStory', () => { ); const context = prepareContext({ args: firstStory.initialArgs, globals: {}, ...firstStory }); - firstStory.unboundStoryFn({ ...context, ...storyContextExtras() }); + firstStory.unboundStoryFn(addExtraContext(context)); expect(renderMock).toHaveBeenCalledWith( { a: 1 }, expect.objectContaining({ args: { a: 1 }, allArgs: { a: 1, b: 2 } }) @@ -570,7 +607,7 @@ describe('prepareStory', () => { ); const context = prepareContext({ args: firstStory.initialArgs, globals: {}, ...firstStory }); - firstStory.unboundStoryFn({ ...context, ...storyContextExtras() }); + firstStory.unboundStoryFn(addExtraContext(context)); expect(renderMock).toHaveBeenCalledWith( { a: 1 }, expect.objectContaining({ args: { a: 1 }, allArgs: { a: 1, b: 2 } }) @@ -592,7 +629,7 @@ describe('prepareStory', () => { ); const context = prepareContext({ args: firstStory.initialArgs, globals: {}, ...firstStory }); - firstStory.unboundStoryFn({ ...context, ...storyContextExtras() }); + firstStory.unboundStoryFn(addExtraContext(context)); expect(renderMock).toHaveBeenCalledWith( { a: 1 }, expect.objectContaining({ argsByTarget: { [UNTARGETED]: { a: 1 }, foo: { b: 2 } } }) @@ -614,7 +651,7 @@ describe('prepareStory', () => { ); const context = prepareContext({ args: firstStory.initialArgs, globals: {}, ...firstStory }); - firstStory.unboundStoryFn({ ...context, ...storyContextExtras() }); + firstStory.unboundStoryFn(addExtraContext(context)); expect(renderMock).toHaveBeenCalledWith( {}, expect.objectContaining({ argsByTarget: { foo: { b: 2 } } }) @@ -634,7 +671,7 @@ describe('prepareStory', () => { ); const context = prepareContext({ args: firstStory.initialArgs, globals: {}, ...firstStory }); - firstStory.unboundStoryFn({ ...context, ...storyContextExtras() }); + firstStory.unboundStoryFn(addExtraContext(context)); expect(renderMock).toHaveBeenCalledWith({}, expect.objectContaining({ argsByTarget: {} })); }); }); @@ -653,7 +690,7 @@ describe('playFunction', () => { { render } ); - await playFunction!({} as PlayFunctionContext); + await playFunction?.({} as StoryContext); expect(play).toHaveBeenCalled(); expect(inner).toHaveBeenCalled(); }); @@ -667,16 +704,19 @@ describe('playFunction', () => { await step('label', stepPlay); }); const runStep = vi.fn((label, p, c) => p(c)); - const { playFunction } = prepareStory( + const { playFunction, runStep: preparedRunStep } = prepareStory( { id, name, play, moduleExport }, { id, title }, { render, runStep } ); - await playFunction!({} as PlayFunctionContext); + const context: Partial = { + step: (label, playFn) => preparedRunStep(label, playFn, context as StoryContext), + }; + await playFunction?.(context as StoryContext); expect(play).toHaveBeenCalled(); expect(stepPlay).toHaveBeenCalled(); - expect(runStep).toBeCalledWith('label', stepPlay, expect.any(Object)); + expect(runStep).toBeCalledWith('label', expect.any(Function), expect.any(Object)); }); }); @@ -689,7 +729,7 @@ describe('moduleExport', () => { }); describe('prepareMeta', () => { - it('returns the same as prepareStory', () => { + it('returns a similar object as prepareStory', () => { const meta = { id, title, @@ -710,8 +750,6 @@ describe('prepareMeta', () => { nested: { name: 'nested', type: booleanType, a: 'story' }, }, }; - const preparedStory = prepareStory({ id, name, moduleExport }, meta, { render }); - const preparedMeta = prepareMeta(meta, { render }, {}); // omitting the properties from preparedStory that are not in preparedMeta const { @@ -723,12 +761,20 @@ describe('prepareMeta', () => { unboundStoryFn, undecoratedStoryFn, playFunction, - // eslint-disable-next-line @typescript-eslint/naming-convention - parameters: { __isArgsStory, ...parameters }, - ...expectedPreparedMeta - } = preparedStory; + runStep, + ...preparedStory + } = prepareStory({ id, name, moduleExport }, meta, { render }); + + const preparedMeta = prepareMeta( + meta, + normalizeProjectAnnotations(composeConfigs([{ render }])), + {} + ); + + // prepareMeta doesn't explicitly set this parameter to false + // eslint-disable-next-line no-underscore-dangle + preparedMeta.parameters.__isArgsStory = false; - expect(preparedMeta).toMatchObject({ ...expectedPreparedMeta, parameters }); - expect(Object.keys(preparedMeta)).toHaveLength(Object.keys(expectedPreparedMeta).length + 1); + expect(preparedMeta).toEqual(preparedStory); }); }); diff --git a/code/lib/preview-api/src/modules/store/csf/prepareStory.ts b/code/lib/preview-api/src/modules/store/csf/prepareStory.ts index ce99cef8f1e0..aa6e1d797dd0 100644 --- a/code/lib/preview-api/src/modules/store/csf/prepareStory.ts +++ b/code/lib/preview-api/src/modules/store/csf/prepareStory.ts @@ -1,4 +1,4 @@ -/* eslint-disable @typescript-eslint/no-loop-func,no-underscore-dangle */ +/* eslint-disable no-underscore-dangle */ import { global } from '@storybook/global'; import type { Args, @@ -8,12 +8,9 @@ import type { NormalizedProjectAnnotations, NormalizedStoryAnnotations, Parameters, - PlayFunction, - PlayFunctionContext, PreparedMeta, PreparedStory, Renderer, - StepLabel, StoryContext, StoryContextForEnhancers, StoryContextForLoaders, @@ -50,9 +47,9 @@ export function prepareStory( ); const applyLoaders = async ( - context: StoryContextForLoaders - ): Promise & { loaded: StoryContext['loaded'] }> => { - let updatedContext = { ...context, loaded: {} }; + context: StoryContext + ): Promise['loaded']> => { + const loaded = {}; for (const loaders of [ ...('__STORYBOOK_TEST_LOADERS__' in global && Array.isArray(global.__STORYBOOK_TEST_LOADERS__) ? [global.__STORYBOOK_TEST_LOADERS__] @@ -61,12 +58,11 @@ export function prepareStory( normalizeArrays(componentAnnotations.loaders), normalizeArrays(storyAnnotations.loaders), ]) { - const loadResults = await Promise.all(loaders.map((loader) => loader(updatedContext))); - const loaded: Record = Object.assign({}, ...loadResults); - updatedContext = { ...updatedContext, loaded: { ...updatedContext.loaded, ...loaded } }; + if (context.abortSignal.aborted) return loaded; + const loadResults = await Promise.all(loaders.map((loader) => loader(context))); + Object.assign(loaded, ...loadResults); } - - return updatedContext; + return loaded; }; const applyBeforeEach = async (context: StoryContext): Promise => { @@ -76,6 +72,7 @@ export function prepareStory( ...normalizeArrays(componentAnnotations.beforeEach), ...normalizeArrays(storyAnnotations.beforeEach), ]) { + if (context.abortSignal.aborted) return cleanupCallbacks; const cleanup = await beforeEach(context); if (cleanup) cleanupCallbacks.push(cleanup); } @@ -106,20 +103,7 @@ export function prepareStory( const decoratedStoryFn = applyHooks(applyDecorators)(undecoratedStoryFn, decorators); const unboundStoryFn = (context: StoryContext) => decoratedStoryFn(context); - const play = storyAnnotations?.play || componentAnnotations.play; - - const playFunction = - play && - (async (storyContext: StoryContext) => { - const playFunctionContext: PlayFunctionContext = { - ...storyContext, - // eslint-disable-next-line @typescript-eslint/no-shadow - step: (label: StepLabel, play: PlayFunction) => - // TODO: We know runStep is defined, we need a proper normalized annotations type - runStep!(label, play, playFunctionContext), - }; - return play(playFunctionContext); - }); + const playFunction = storyAnnotations?.play ?? componentAnnotations?.play; return { ...partialAnnotations, @@ -133,6 +117,7 @@ export function prepareStory( applyLoaders, applyBeforeEach, playFunction, + runStep, }; } export function prepareMeta( diff --git a/code/lib/preview-api/src/modules/store/csf/stepRunners.test.ts b/code/lib/preview-api/src/modules/store/csf/stepRunners.test.ts index 4858cfa7e995..e60976c2ac3f 100644 --- a/code/lib/preview-api/src/modules/store/csf/stepRunners.test.ts +++ b/code/lib/preview-api/src/modules/store/csf/stepRunners.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect, vi } from 'vitest'; -import type { PlayFunctionContext, StepRunner } from '@storybook/types'; +import type { StoryContext, StepRunner } from '@storybook/types'; import { composeStepRunners } from './stepRunners'; describe('stepRunners', () => { @@ -21,10 +21,10 @@ describe('stepRunners', () => { const composed = composeStepRunners([firstStepRunner, secondStepRunner]); const playFnA = vi.fn(); - const playContextA = {} as PlayFunctionContext; + const playContextA = {} as StoryContext; await composed('a', playFnA, playContextA); const playFnB = vi.fn(); - const playContextB = {} as PlayFunctionContext; + const playContextB = {} as StoryContext; await composed('b', playFnB, playContextB); expect(playFnA).toHaveBeenCalledTimes(1); @@ -47,10 +47,10 @@ describe('stepRunners', () => { const composed = composeStepRunners([]); const playFnA = vi.fn(); - const playContextA = {} as PlayFunctionContext; + const playContextA = {} as StoryContext; await composed('a', playFnA, playContextA); const playFnB = vi.fn(); - const playContextB = {} as PlayFunctionContext; + const playContextB = {} as StoryContext; await composed('b', playFnB, playContextB); expect(playFnA).toHaveBeenCalledTimes(1); diff --git a/code/lib/source-loader/package.json b/code/lib/source-loader/package.json index 94eb630d9aab..c9f90b9df49d 100644 --- a/code/lib/source-loader/package.json +++ b/code/lib/source-loader/package.json @@ -45,7 +45,7 @@ "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "dependencies": { - "@storybook/csf": "^0.1.8", + "@storybook/csf": "0.1.10--canary.d841bb4.0", "@storybook/types": "workspace:*", "estraverse": "^5.2.0", "lodash": "^4.17.21", diff --git a/code/lib/test/template/stories/before-each.stories.ts b/code/lib/test/template/stories/before-each.stories.ts index 657179a0c597..301264f8a8f8 100644 --- a/code/lib/test/template/stories/before-each.stories.ts +++ b/code/lib/test/template/stories/before-each.stories.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/naming-convention,storybook/prefer-pascal-case */ import { expect, mocked, getByRole, spyOn, userEvent } from '@storybook/test'; const meta = { @@ -40,3 +41,37 @@ export const BeforeEachOrder = { ]); }, }; + +export const before_each_and_loaders_can_extend_context = { + parameters: { chromatic: { disable: true } }, + loaders(context) { + context.foo = ['bar']; + }, + beforeEach(context) { + context.foo = [...context.foo, 'baz']; + }, + async play({ foo }) { + await expect(foo).toEqual(['bar', 'baz']); + }, +}; + +export const context_prop_is_available = { + parameters: { chromatic: { disable: true } }, + async play({ context, canvasElement }) { + await expect(context.canvasElement).toEqual(canvasElement); + }, +}; + +export const step_and_canvas_element_can_be_used_in_loaders_and_before_each = { + parameters: { chromatic: { disable: true } }, + loaders({ step, canvasElement }) { + step('loaders', async () => { + await expect(canvasElement).toBeInTheDocument(); + }); + }, + beforeEach({ step, canvasElement }) { + step('before each', async () => { + await expect(canvasElement).toBeInTheDocument(); + }); + }, +}; diff --git a/code/lib/types/package.json b/code/lib/types/package.json index 226acaedbbb1..81057ffcd97f 100644 --- a/code/lib/types/package.json +++ b/code/lib/types/package.json @@ -49,7 +49,7 @@ "file-system-cache": "2.3.0" }, "devDependencies": { - "@storybook/csf": "^0.1.8", + "@storybook/csf": "0.1.10--canary.d841bb4.0", "@types/fs-extra": "^11.0.1", "@types/node": "^18.0.0", "typescript": "^5.3.2" diff --git a/code/lib/types/src/modules/addons.ts b/code/lib/types/src/modules/addons.ts index c1325e3f07ec..90011c5f1eec 100644 --- a/code/lib/types/src/modules/addons.ts +++ b/code/lib/types/src/modules/addons.ts @@ -83,11 +83,11 @@ export type Addon_StoryContext = StoryContextForFramework; export type Addon_StoryContextUpdate = Partial; -type Addon_ReturnTypeFramework = { +interface Addon_ReturnTypeFramework extends Renderer { component: any; storyResult: ReturnType; canvasElement: any; -}; +} export type Addon_PartialStoryFn = PartialStoryFnForFramework< Addon_ReturnTypeFramework >; diff --git a/code/lib/types/src/modules/composedStory.ts b/code/lib/types/src/modules/composedStory.ts index f4b49f24793a..b1cd46589c3a 100644 --- a/code/lib/types/src/modules/composedStory.ts +++ b/code/lib/types/src/modules/composedStory.ts @@ -1,9 +1,9 @@ /* eslint-disable @typescript-eslint/naming-convention */ import type { - PlayFunction, ProjectAnnotations, Renderer, + StoryContext, StoryId, StrictArgTypes, Tag, @@ -35,14 +35,6 @@ export type PartialArgsStoryFn = T extends (...args: infer P) => infer R - ? (...args: { [K in keyof P]?: Partial }) => R - : never; - -export type ComposedStoryPlayFn< - TRenderer extends Renderer = Renderer, - TArgs = Args, -> = MakeAllParametersOptional>>; /** * A story that got recomposed for portable stories, containing all the necessary data to be rendered in external environments */ @@ -52,7 +44,7 @@ export type ComposedStoryFn< > = PartialArgsStoryFn & { args: TArgs; id: StoryId; - play?: ComposedStoryPlayFn; + play?: (context?: Partial>>) => Promise; load: () => Promise; storyName: string; parameters: Parameters; diff --git a/code/lib/types/src/modules/docs.ts b/code/lib/types/src/modules/docs.ts index 4e8e1c2f3a7f..62184e057ff2 100644 --- a/code/lib/types/src/modules/docs.ts +++ b/code/lib/types/src/modules/docs.ts @@ -1,5 +1,5 @@ import type { Channel } from '@storybook/channels'; -import type { Renderer, StoryContextForLoaders, StoryId, StoryName, Parameters } from './csf'; +import type { Renderer, StoryContext, StoryId, StoryName, Parameters } from './csf'; import type { ModuleExport, ModuleExports, @@ -91,7 +91,9 @@ export interface DocsContextProps { /** * Get the story context of the referenced story. */ - getStoryContext: (story: PreparedStory) => StoryContextForLoaders; + getStoryContext: ( + story: PreparedStory + ) => Omit, 'abortSignal' | 'canvasElement' | 'step' | 'context'>; /** * Asyncronously load an arbitrary story by id. */ diff --git a/code/lib/types/src/modules/story.ts b/code/lib/types/src/modules/story.ts index 041aaed2e709..db994f5c40ec 100644 --- a/code/lib/types/src/modules/story.ts +++ b/code/lib/types/src/modules/story.ts @@ -4,6 +4,7 @@ import type { DecoratorFunction, LoaderFunction, CleanupCallback, + StepRunner, } from '@storybook/csf'; import type { @@ -16,7 +17,6 @@ import type { StoryAnnotations, StoryContext, StoryContextForEnhancers, - StoryContextForLoaders, StoryFn, StoryId, StoryIdentifier, @@ -41,12 +41,12 @@ export type RenderToCanvas = ( element: TRenderer['canvasElement'] ) => MaybePromise; -export type ProjectAnnotations = CsfProjectAnnotations & { +export interface ProjectAnnotations + extends CsfProjectAnnotations { renderToCanvas?: RenderToCanvas; - /* @deprecated use renderToCanvas */ renderToDOM?: RenderToCanvas; -}; +} type NamedExportsOrDefault = TExport | { default: TExport }; @@ -55,12 +55,13 @@ export type NamedOrDefaultProjectAnnotations = Omit< ProjectAnnotations, - 'decorators' | 'loaders' + 'decorators' | 'loaders' | 'runStep' > & { argTypes?: StrictArgTypes; globalTypes?: StrictGlobalTypes; decorators?: DecoratorFunction[]; loaders?: LoaderFunction[]; + runStep: StepRunner; }; export type NormalizedComponentAnnotations = Omit< @@ -101,11 +102,10 @@ export type PreparedStory = originalStoryFn: StoryFn; undecoratedStoryFn: LegacyStoryFn; unboundStoryFn: LegacyStoryFn; - applyLoaders: ( - context: StoryContextForLoaders - ) => Promise & { loaded: StoryContext['loaded'] }>; + applyLoaders: (context: StoryContext) => Promise['loaded']>; applyBeforeEach: (context: StoryContext) => Promise; playFunction?: (context: StoryContext) => Promise | void; + runStep: StepRunner; }; export type PreparedMeta = Omit< diff --git a/code/package.json b/code/package.json index 69553500b02d..f2dadf392dcc 100644 --- a/code/package.json +++ b/code/package.json @@ -126,7 +126,7 @@ "@storybook/core-events": "workspace:*", "@storybook/core-server": "workspace:*", "@storybook/core-webpack": "workspace:*", - "@storybook/csf": "^0.1.8", + "@storybook/csf": "0.1.10--canary.d841bb4.0", "@storybook/csf-plugin": "workspace:*", "@storybook/csf-tools": "workspace:*", "@storybook/docs-tools": "workspace:*", @@ -293,5 +293,6 @@ "Dependency Upgrades" ] ] - } + }, + "packageManager": "yarn@4.3.1" } diff --git a/code/renderers/react/package.json b/code/renderers/react/package.json index ba8b41f1181d..b540ba0198ea 100644 --- a/code/renderers/react/package.json +++ b/code/renderers/react/package.json @@ -87,6 +87,7 @@ "util-deprecate": "^1.0.2" }, "devDependencies": { + "@storybook/blocks": "workspace:*", "@storybook/test": "workspace:*", "@types/babel-plugin-react-docgen": "^4", "@types/semver": "^7.3.4", diff --git a/code/renderers/server/package.json b/code/renderers/server/package.json index cdf3421a8b86..437d1d937941 100644 --- a/code/renderers/server/package.json +++ b/code/renderers/server/package.json @@ -46,7 +46,7 @@ "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "dependencies": { - "@storybook/csf": "^0.1.8", + "@storybook/csf": "0.1.10--canary.d841bb4.0", "@storybook/csf-tools": "workspace:*", "@storybook/global": "^5.0.0", "@storybook/preview-api": "workspace:*", diff --git a/code/ui/blocks/package.json b/code/ui/blocks/package.json index 39f5d36c9149..d34881e2a9d9 100644 --- a/code/ui/blocks/package.json +++ b/code/ui/blocks/package.json @@ -48,7 +48,7 @@ "@storybook/client-logger": "workspace:*", "@storybook/components": "workspace:*", "@storybook/core-events": "workspace:*", - "@storybook/csf": "^0.1.8", + "@storybook/csf": "0.1.10--canary.d841bb4.0", "@storybook/docs-tools": "workspace:*", "@storybook/global": "^5.0.0", "@storybook/icons": "^1.2.5", diff --git a/code/ui/blocks/src/blocks/Source.tsx b/code/ui/blocks/src/blocks/Source.tsx index 59906aa77c43..e5fa68e82636 100644 --- a/code/ui/blocks/src/blocks/Source.tsx +++ b/code/ui/blocks/src/blocks/Source.tsx @@ -1,12 +1,6 @@ import type { ComponentProps, FC } from 'react'; import React, { useContext } from 'react'; -import type { - StoryId, - PreparedStory, - ModuleExport, - Args, - StoryContextForLoaders, -} from '@storybook/types'; +import type { StoryId, PreparedStory, ModuleExport, Args } from '@storybook/types'; import { SourceType } from '@storybook/docs-tools'; import type { SourceCodeProps } from '../components/Source'; @@ -24,7 +18,10 @@ type SourceParameters = SourceCodeProps & { /** * Transform the detected source for display */ - transform?: (code: string, storyContext: StoryContextForLoaders) => string; + transform?: ( + code: string, + storyContext: ReturnType + ) => string; /** * Internal: set by our CSF loader (`enrichCsf` in `@storybook/csf-tools`). */ @@ -75,7 +72,7 @@ const getSnippet = ({ transformFromProps, }: { snippet: string; - storyContext: StoryContextForLoaders; + storyContext: ReturnType; typeFromProps: SourceType; transformFromProps?: SourceProps['transform']; }): string => { diff --git a/code/ui/components/package.json b/code/ui/components/package.json index be82bb3941a0..52c5c8874920 100644 --- a/code/ui/components/package.json +++ b/code/ui/components/package.json @@ -62,7 +62,7 @@ "@radix-ui/react-dialog": "^1.0.5", "@radix-ui/react-slot": "^1.0.2", "@storybook/client-logger": "workspace:*", - "@storybook/csf": "^0.1.8", + "@storybook/csf": "0.1.10--canary.d841bb4.0", "@storybook/global": "^5.0.0", "@storybook/icons": "^1.2.5", "@storybook/theming": "workspace:*", diff --git a/code/yarn.lock b/code/yarn.lock index 5c5fabfd1554..a053e517969e 100644 --- a/code/yarn.lock +++ b/code/yarn.lock @@ -5288,7 +5288,7 @@ __metadata: dependencies: "@storybook/client-logger": "workspace:*" "@storybook/core-events": "workspace:*" - "@storybook/csf": "npm:^0.1.8" + "@storybook/csf": "npm:0.1.10--canary.d841bb4.0" "@storybook/global": "npm:^5.0.0" "@storybook/manager-api": "workspace:*" "@storybook/preview-api": "workspace:*" @@ -5555,7 +5555,7 @@ __metadata: "@storybook/client-logger": "workspace:*" "@storybook/components": "workspace:*" "@storybook/core-events": "workspace:*" - "@storybook/csf": "npm:^0.1.8" + "@storybook/csf": "npm:0.1.10--canary.d841bb4.0" "@storybook/docs-tools": "workspace:*" "@storybook/global": "npm:^5.0.0" "@storybook/icons": "npm:^1.2.5" @@ -5785,7 +5785,7 @@ __metadata: "@babel/core": "npm:^7.24.4" "@babel/preset-env": "npm:^7.24.4" "@babel/types": "npm:^7.24.0" - "@storybook/csf": "npm:^0.1.8" + "@storybook/csf": "npm:0.1.10--canary.d841bb4.0" "@storybook/csf-tools": "workspace:*" "@storybook/node-logger": "workspace:*" "@storybook/types": "workspace:*" @@ -5822,7 +5822,7 @@ __metadata: "@radix-ui/react-scroll-area": "npm:^1.0.5" "@radix-ui/react-slot": "npm:^1.0.2" "@storybook/client-logger": "workspace:*" - "@storybook/csf": "npm:^0.1.8" + "@storybook/csf": "npm:0.1.10--canary.d841bb4.0" "@storybook/global": "npm:^5.0.0" "@storybook/icons": "npm:^1.2.5" "@storybook/test": "workspace:*" @@ -5905,7 +5905,7 @@ __metadata: version: 0.0.0-use.local resolution: "@storybook/core-events@workspace:lib/core-events" dependencies: - "@storybook/csf": "npm:^0.1.8" + "@storybook/csf": "npm:0.1.10--canary.d841bb4.0" chalk: "npm:^4.1.0" ts-dedent: "npm:^2.0.0" typescript: "npm:^5.3.2" @@ -5925,7 +5925,7 @@ __metadata: "@storybook/channels": "workspace:*" "@storybook/core-common": "workspace:*" "@storybook/core-events": "workspace:*" - "@storybook/csf": "npm:^0.1.8" + "@storybook/csf": "npm:0.1.10--canary.d841bb4.0" "@storybook/csf-tools": "workspace:*" "@storybook/docs-mdx": "npm:3.1.0-next.0" "@storybook/global": "npm:^5.0.0" @@ -6007,7 +6007,7 @@ __metadata: "@babel/parser": "npm:^7.24.4" "@babel/traverse": "npm:^7.24.1" "@babel/types": "npm:^7.24.0" - "@storybook/csf": "npm:^0.1.8" + "@storybook/csf": "npm:0.1.10--canary.d841bb4.0" "@storybook/types": "workspace:*" "@types/fs-extra": "npm:^11.0.1" "@types/js-yaml": "npm:^4.0.5" @@ -6019,6 +6019,15 @@ __metadata: languageName: unknown linkType: soft +"@storybook/csf@npm:0.1.10--canary.d841bb4.0": + version: 0.1.10--canary.d841bb4.0 + resolution: "@storybook/csf@npm:0.1.10--canary.d841bb4.0" + dependencies: + type-fest: "npm:^2.19.0" + checksum: 10c0/35a9b5cf88a3378fb362d0a1988265ec218aa98d54b82b56e78a4c1142b3beed6e2ee42bd8731bac009fd8133e085c92af43541028468865b51c0b1220ded338 + languageName: node + linkType: hard + "@storybook/csf@npm:^0.0.1": version: 0.0.1 resolution: "@storybook/csf@npm:0.0.1" @@ -6028,15 +6037,6 @@ __metadata: languageName: node linkType: hard -"@storybook/csf@npm:^0.1.8": - version: 0.1.8 - resolution: "@storybook/csf@npm:0.1.8" - dependencies: - type-fest: "npm:^2.19.0" - checksum: 10c0/4fd08bd65205395aba693f053706d5c880db5102543744c4e13768902a615df9a479ac03f80fe33297bd5457a0d0ef655cd33d41a0e379764dd6f0c6b9a7d88c - languageName: node - linkType: hard - "@storybook/docs-mdx@npm:3.1.0-next.0": version: 3.1.0-next.0 resolution: "@storybook/docs-mdx@npm:3.1.0-next.0" @@ -6206,7 +6206,7 @@ __metadata: "@storybook/channels": "workspace:*" "@storybook/client-logger": "workspace:*" "@storybook/core-events": "workspace:*" - "@storybook/csf": "npm:^0.1.8" + "@storybook/csf": "npm:0.1.10--canary.d841bb4.0" "@storybook/global": "npm:^5.0.0" "@storybook/icons": "npm:^1.2.5" "@storybook/router": "workspace:*" @@ -6546,7 +6546,7 @@ __metadata: "@storybook/client-logger": "workspace:*" "@storybook/core-common": "workspace:*" "@storybook/core-events": "workspace:*" - "@storybook/csf": "npm:^0.1.8" + "@storybook/csf": "npm:0.1.10--canary.d841bb4.0" "@storybook/global": "npm:^5.0.0" "@storybook/types": "workspace:*" "@types/qs": "npm:^6.9.5" @@ -6654,6 +6654,7 @@ __metadata: version: 0.0.0-use.local resolution: "@storybook/react@workspace:renderers/react" dependencies: + "@storybook/blocks": "workspace:*" "@storybook/client-logger": "workspace:*" "@storybook/docs-tools": "workspace:*" "@storybook/global": "npm:^5.0.0" @@ -6733,7 +6734,7 @@ __metadata: "@storybook/core-events": "workspace:*" "@storybook/core-server": "workspace:*" "@storybook/core-webpack": "workspace:*" - "@storybook/csf": "npm:^0.1.8" + "@storybook/csf": "npm:0.1.10--canary.d841bb4.0" "@storybook/csf-plugin": "workspace:*" "@storybook/csf-tools": "workspace:*" "@storybook/docs-tools": "workspace:*" @@ -6885,7 +6886,7 @@ __metadata: version: 0.0.0-use.local resolution: "@storybook/server@workspace:renderers/server" dependencies: - "@storybook/csf": "npm:^0.1.8" + "@storybook/csf": "npm:0.1.10--canary.d841bb4.0" "@storybook/csf-tools": "workspace:*" "@storybook/global": "npm:^5.0.0" "@storybook/preview-api": "workspace:*" @@ -6902,7 +6903,7 @@ __metadata: version: 0.0.0-use.local resolution: "@storybook/source-loader@workspace:lib/source-loader" dependencies: - "@storybook/csf": "npm:^0.1.8" + "@storybook/csf": "npm:0.1.10--canary.d841bb4.0" "@storybook/types": "workspace:*" estraverse: "npm:^5.2.0" lodash: "npm:^4.17.21" @@ -7072,7 +7073,7 @@ __metadata: resolution: "@storybook/types@workspace:lib/types" dependencies: "@storybook/channels": "workspace:*" - "@storybook/csf": "npm:^0.1.8" + "@storybook/csf": "npm:0.1.10--canary.d841bb4.0" "@types/express": "npm:^4.17.21" "@types/fs-extra": "npm:^11.0.1" "@types/node": "npm:^18.0.0" diff --git a/docs/_snippets/my-component-play-function-composition.md b/docs/_snippets/my-component-play-function-composition.md index 6052d455a55d..e3239b113329 100644 --- a/docs/_snippets/my-component-play-function-composition.md +++ b/docs/_snippets/my-component-play-function-composition.md @@ -33,12 +33,12 @@ export const SecondStory: Story = { }; export const CombinedStories: Story = { - play: async ({ canvasElement }) => { + play: async ({ context, canvasElement }) => { const canvas = within(canvasElement); // Runs the FirstStory and Second story play function before running this story's play function - await FirstStory.play({ canvasElement }); - await SecondStory.play({ canvasElement }); + await FirstStory.play(context); + await SecondStory.play(context); await userEvent.type(canvas.getByTestId('another-element'), 'random value'); }, }; @@ -74,8 +74,8 @@ export const SecondStory = { }; export const CombinedStories = { - play: async (context) => { - const canvas = within(context.canvasElement); + play: async ({ context, canvasElement }) => { + const canvas = within(canvasElement); // Runs the FirstStory and Second story play function before running this story's play function await FirstStory.play(context); @@ -121,12 +121,12 @@ export const SecondStory: Story = { }; export const CombinedStories: Story = { - play: async ({ canvasElement }) => { + play: async ({ context, canvasElement }) => { const canvas = within(canvasElement); // Runs the FirstStory and Second story play function before running this story's play function - await FirstStory.play({ canvasElement }); - await SecondStory.play({ canvasElement }); + await FirstStory.play(context); + await SecondStory.play(context); await userEvent.type(canvas.getByTestId('another-element'), 'random value'); }, }; @@ -168,8 +168,8 @@ export const SecondStory: Story = { }; export const CombinedStories: Story = { - play: async (context) => { - const canvas = within(context.canvasElement); + play: async ({ context, canvasElement }) => { + const canvas = within(canvasElement); // Runs the FirstStory and Second story play function before running this story's play function await FirstStory.play(context); @@ -207,8 +207,8 @@ export const SecondStory = { }; export const CombinedStories = { - play: async (context) => { - const canvas = within(context.canvasElement); + play: async ({ context, canvasElement }) => { + const canvas = within(canvasElement); // Runs the FirstStory and Second story play function before running this story's play function await FirstStory.play(context); @@ -250,8 +250,8 @@ export const SecondStory: Story = { }; export const CombinedStories: Story = { - play: async (context) => { - const canvas = within(context.canvasElement); + play: async ({ context, canvasElement }) => { + const canvas = within(canvasElement); // Runs the FirstStory and Second story play function before running this story's play function await FirstStory.play(context);