diff --git a/code/core/src/csf/csf-factories.test.ts b/code/core/src/csf/csf-factories.test.ts index b3c146c8f6dd..24c9572cc4dc 100644 --- a/code/core/src/csf/csf-factories.test.ts +++ b/code/core/src/csf/csf-factories.test.ts @@ -46,6 +46,27 @@ test('addon parameters are inferred', () => { }); }); +test('addon parameters and globals are inferred in decorator context', () => { + const typedMeta = preview.type<{ globals: { theme: 'light' | 'dark' } }>().meta({ + decorators: [ + (Story, { parameters, globals }) => { + parameters.foo?.value satisfies string | undefined; + parameters.bar?.value satisfies string | undefined; + globals.theme satisfies 'light' | 'dark'; + + // @ts-expect-error can not treat string parameter values as numbers + parameters.foo!.value satisfies number; + // @ts-expect-error can not treat typed globals as other values + globals.theme satisfies 'sepia'; + + return Story(); + }, + ], + }); + + typedMeta.story(); +}); + describe('test function', () => { test('without overrides', async () => { const MyStory = meta.story({ args: { label: 'foo' } }); diff --git a/code/core/src/csf/story.ts b/code/core/src/csf/story.ts index e2c8c4f57df5..aa86e4de07c3 100644 --- a/code/core/src/csf/story.ts +++ b/code/core/src/csf/story.ts @@ -213,6 +213,12 @@ export interface Renderer extends AddonTypes { csf4: boolean; } +type TypedParameters = Parameters & + (TRenderer['csf4'] extends true ? CoreTypes['parameters'] & TRenderer['parameters'] : unknown); + +type TypedGlobals = Globals & + (TRenderer['csf4'] extends true ? CoreTypes['globals'] & TRenderer['globals'] : unknown); + /** @deprecated - Use `Renderer` */ export type AnyFramework = Renderer; @@ -222,7 +228,7 @@ export interface StoryContextForEnhancers< > extends StoryIdentifier { component?: (TRenderer & { T: any })['component']; subcomponents?: Record; - parameters: Parameters; + parameters: TypedParameters; initialArgs: TArgs; argTypes: StrictArgTypes; } @@ -267,6 +273,7 @@ export interface Canvas {} export interface StoryContext extends StoryContextForEnhancers, Required> { + globals: TypedGlobals; loaded: Record; abortSignal: AbortSignal; canvasElement: TRenderer['canvasElement']; @@ -361,8 +368,7 @@ export interface BaseAnnotations; /** * Dynamic data that are provided (and possibly updated by) Storybook and its addons. @@ -441,8 +447,7 @@ export interface ProjectAnnotations< */ beforeAll?: BeforeAll; - initialGlobals?: Globals & - (TRenderer['csf4'] extends true ? CoreTypes['globals'] & TRenderer['globals'] : unknown); + initialGlobals?: TypedGlobals; globalTypes?: GlobalTypes; applyDecorators?: DecoratorApplicator; runStep?: StepRunner; @@ -536,8 +541,7 @@ export interface ComponentAnnotations< play?: PlayFunction; /** Override the globals values for all stories in this component */ - globals?: Globals & - (TRenderer['csf4'] extends true ? CoreTypes['globals'] & TRenderer['globals'] : unknown); + globals?: TypedGlobals; } export type StoryAnnotations< @@ -555,8 +559,7 @@ export type StoryAnnotations< play?: PlayFunction; /** Override the globals values for this story */ - globals?: Globals & - (TRenderer['csf4'] extends true ? CoreTypes['globals'] & TRenderer['globals'] : unknown); + globals?: TypedGlobals; /** @deprecated */ story?: Omit, 'story'>; diff --git a/code/core/src/preview-api/modules/store/csf/portable-stories.ts b/code/core/src/preview-api/modules/store/csf/portable-stories.ts index 717fc9dbef4a..bfd7dd37164f 100644 --- a/code/core/src/preview-api/modules/store/csf/portable-stories.ts +++ b/code/core/src/preview-api/modules/store/csf/portable-stories.ts @@ -160,7 +160,7 @@ export function composeStory