From 8ceedc17dde6b299b3c6d6ac6757b6bc02806248 Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Wed, 29 Jan 2025 16:55:45 +0100 Subject: [PATCH 01/23] Base definePreview --- code/.eslintrc.js | 1 + code/core/src/types/index.ts | 1 + code/core/src/types/modules/csf-factories.ts | 92 ++++++++++++++++++++ code/core/src/types/modules/story.ts | 1 + code/frameworks/nextjs/src/index.ts | 14 +++ code/renderers/react/src/index.ts | 2 +- code/renderers/react/src/preview.tsx | 91 ++++++++----------- code/renderers/react/src/public-types.ts | 2 +- 8 files changed, 146 insertions(+), 58 deletions(-) create mode 100644 code/core/src/types/modules/csf-factories.ts diff --git a/code/.eslintrc.js b/code/.eslintrc.js index 4268ec993dd6..06c7c4fb137e 100644 --- a/code/.eslintrc.js +++ b/code/.eslintrc.js @@ -15,6 +15,7 @@ module.exports = { 'error', { devDependencies: true, peerDependencies: true }, ], + 'no-underscore-dangle': 'off', 'import/no-unresolved': 'off', // covered by typescript 'eslint-comments/disable-enable-pair': ['error', { allowWholeFile: true }], 'eslint-comments/no-unused-disable': 'error', diff --git a/code/core/src/types/index.ts b/code/core/src/types/index.ts index 1941cbe86872..b6a3088a0d64 100644 --- a/code/core/src/types/index.ts +++ b/code/core/src/types/index.ts @@ -1,6 +1,7 @@ /// export * from './modules/csf'; +export * from './modules/csf-factories'; export * from './modules/addons'; export * from './modules/story'; export * from './modules/core-common'; diff --git a/code/core/src/types/modules/csf-factories.ts b/code/core/src/types/modules/csf-factories.ts new file mode 100644 index 000000000000..d997ac7fe9ff --- /dev/null +++ b/code/core/src/types/modules/csf-factories.ts @@ -0,0 +1,92 @@ +import { composeConfigs, normalizeProjectAnnotations } from '@storybook/core/preview-api'; + +import type { Args, ComponentAnnotations, Renderer, StoryAnnotations } from './csf'; +import type { + NormalizedComponentAnnotations, + NormalizedProjectAnnotations, + NormalizedStoryAnnotations, + ProjectAnnotations, +} from './story'; + +export interface Preview { + readonly _tag: 'Preview'; + input: ProjectAnnotations; + composed: NormalizedProjectAnnotations; + + meta(input: ComponentAnnotations): Meta; +} + +export function definePreview( + preview: Preview['input'] +): Preview { + return { + _tag: 'Preview', + input: preview, + get composed() { + const { addons, ...rest } = preview; + return normalizeProjectAnnotations(composeConfigs([...(addons ?? []), rest])); + }, + meta(meta: ComponentAnnotations) { + return defineMeta(meta, this); + }, + }; +} + +function isPreview(input: unknown): input is Preview { + return input != null && typeof input === 'object' && '_tag' in input && input?._tag === 'Preview'; +} + +export interface Meta { + readonly _tag: 'Meta'; + input: ComponentAnnotations; + composed: NormalizedComponentAnnotations; + preview: Preview; + + story(input: ComponentAnnotations): Story; +} + +function isMeta(input: unknown): input is Meta { + return input != null && typeof input === 'object' && '_tag' in input && input?._tag === 'Meta'; +} + +function defineMeta( + input: ComponentAnnotations, + preview: Preview +): Meta { + return { + _tag: 'Meta', + input, + preview, + get composed(): never { + throw new Error('Not implemented'); + }, + story(story: StoryAnnotations) { + return defineStory(story, this); + }, + }; +} + +export interface Story { + readonly _tag: 'Story'; + input: StoryAnnotations; + composed: NormalizedStoryAnnotations; + meta: Meta; +} + +function defineStory( + input: ComponentAnnotations, + meta: Meta +): Story { + return { + _tag: 'Story', + input, + meta, + get composed(): never { + throw new Error('Not implemented'); + }, + }; +} + +function isStory(input: unknown): input is Meta { + return input != null && typeof input === 'object' && '_tag' in input && input?._tag === 'Story'; +} diff --git a/code/core/src/types/modules/story.ts b/code/core/src/types/modules/story.ts index a823863defaf..6cb1ef2e874e 100644 --- a/code/core/src/types/modules/story.ts +++ b/code/core/src/types/modules/story.ts @@ -46,6 +46,7 @@ export type RenderToCanvas = ( export interface ProjectAnnotations extends CsfProjectAnnotations { + addons?: ProjectAnnotations[]; testingLibraryRender?: (...args: never[]) => { unmount: () => void }; renderToCanvas?: RenderToCanvas; /* @deprecated use renderToCanvas */ diff --git a/code/frameworks/nextjs/src/index.ts b/code/frameworks/nextjs/src/index.ts index a904f93ec89d..41f13da3975f 100644 --- a/code/frameworks/nextjs/src/index.ts +++ b/code/frameworks/nextjs/src/index.ts @@ -1,2 +1,16 @@ +import type { ReactPreview } from '@storybook/react'; +import { definePreview as definePreviewBase } from '@storybook/react'; + +import * as nextPreview from './preview'; + export * from './types'; export * from './portable-stories'; + +export function definePreview(preview: NextPreview['input']) { + return definePreviewBase({ + ...preview, + addons: [nextPreview, ...(preview.addons ?? [])], + }) as NextPreview; +} + +interface NextPreview extends ReactPreview {} diff --git a/code/renderers/react/src/index.ts b/code/renderers/react/src/index.ts index 66c491981bce..fa1b92e92149 100644 --- a/code/renderers/react/src/index.ts +++ b/code/renderers/react/src/index.ts @@ -5,7 +5,7 @@ export * from './public-types'; export * from './portable-stories'; -export { definePreview } from './preview'; +export * from './preview'; // optimization: stop HMR propagation in webpack diff --git a/code/renderers/react/src/preview.tsx b/code/renderers/react/src/preview.tsx index 40fdb3307316..a6b7fb61b44d 100644 --- a/code/renderers/react/src/preview.tsx +++ b/code/renderers/react/src/preview.tsx @@ -1,74 +1,53 @@ -import type { ComponentProps, ComponentType } from 'react'; +import type { ComponentType } from 'react'; -import { composeConfigs } from 'storybook/internal/preview-api'; -import { normalizeProjectAnnotations } from 'storybook/internal/preview-api'; import type { Args, ComponentAnnotations, - NormalizedProjectAnnotations, - ProjectAnnotations, - Renderer, + Meta, + Preview, + Story, StoryAnnotations, } from 'storybook/internal/types'; +import { definePreview as definePreviewBase } from 'storybook/internal/types'; -import type { SetOptional } from 'type-fest'; +import type { ArgsStoryFn } from '@storybook/csf'; + +import type { AddMocks } from 'src/public-types'; +import type { Exact, SetOptional } from 'type-fest'; import * as reactAnnotations from './entry-preview'; import * as reactDocsAnnotations from './entry-preview-docs'; import type { ReactRenderer } from './types'; -export function definePreview(config: PreviewConfigData) { - return new PreviewConfig({ - ...config, - addons: [reactAnnotations, reactDocsAnnotations, ...(config.addons ?? [])], - }); +export function definePreview(preview: ReactPreview['input']) { + return definePreviewBase({ + ...preview, + addons: [reactAnnotations, reactDocsAnnotations, ...(preview.addons ?? [])], + }) as ReactPreview; } -interface PreviewConfigData extends ProjectAnnotations { - addons?: ProjectAnnotations[]; +export interface ReactPreview extends Preview { + meta, TMetaArgs>>( + meta: { + render?: ArgsStoryFn; + component?: ComponentType; + args?: TMetaArgs; + } & ComponentAnnotations + ): ReactMeta<{ args: TArgs }, { args: TMetaArgs }>; } -class PreviewConfig { - readonly input: NormalizedProjectAnnotations; - - constructor(data: PreviewConfigData) { - const { addons, ...rest } = data; - this.input = normalizeProjectAnnotations(composeConfigs([...(addons ?? []), rest])); - } - - readonly meta = < - TComponent extends ComponentType, - TMetaArgs extends Partial>, - >( - meta: ComponentAnnotations & { component: TComponent; args: TMetaArgs } - ) => { - return new Meta, TMetaArgs>(meta, this); - }; - - readonly isCSFFactoryPreview = true; +interface ReactMeta< + Context extends { args: Args }, + MetaInput extends ComponentAnnotations, +> extends Meta { + story( + story: StoryAnnotations< + ReactRenderer, + // TODO: infer mocks from story itself as well + AddMocks, + SetOptional + > + ): ReactStory; } -class Meta { - readonly input: ComponentAnnotations; - - readonly config: PreviewConfig; - - constructor(annotations: ComponentAnnotations, config: PreviewConfig) { - this.input = annotations; - this.config = config; - } - - readonly story = ( - story: StoryAnnotations> - ) => new Story(story as any, this, this.config); -} - -class Story { - constructor( - public input: StoryAnnotations, - public meta: Meta, - public config: PreviewConfig - ) {} - - readonly isCSFFactory = true; -} +interface ReactStory extends Story {} diff --git a/code/renderers/react/src/public-types.ts b/code/renderers/react/src/public-types.ts index 8a2f7003ec94..2f2b2d1de816 100644 --- a/code/renderers/react/src/public-types.ts +++ b/code/renderers/react/src/public-types.ts @@ -66,7 +66,7 @@ export type StoryObj = [TMetaOrCmpOrArgs] extends [ : StoryAnnotations; // This performs a downcast to function types that are mocks, when a mock fn is given to meta args. -type AddMocks = Simplify<{ +export type AddMocks = Simplify<{ [T in keyof TArgs]: T extends keyof DefaultArgs ? // eslint-disable-next-line @typescript-eslint/ban-types DefaultArgs[T] extends (...args: any) => any & { mock: {} } // allow any function with a mock object From 2085d4b6434a214599d8e9f35de391efd6faf905 Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Thu, 30 Jan 2025 12:26:48 +0100 Subject: [PATCH 02/23] Add type tests --- code/core/src/manager/globals/exports.ts | 6 +- .../react/src/csf-factories.test.tsx | 238 +++++++++++++++++- code/renderers/react/src/preview.tsx | 26 +- 3 files changed, 258 insertions(+), 12 deletions(-) diff --git a/code/core/src/manager/globals/exports.ts b/code/core/src/manager/globals/exports.ts index 4e34bb61244c..037bf2504c57 100644 --- a/code/core/src/manager/globals/exports.ts +++ b/code/core/src/manager/globals/exports.ts @@ -963,9 +963,9 @@ export default { 'UPDATE_QUERY_PARAMS', 'UPDATE_STORY_ARGS', ], - 'storybook/internal/types': ['Addon_TypesEnum'], - '@storybook/types': ['Addon_TypesEnum'], - '@storybook/core/types': ['Addon_TypesEnum'], + 'storybook/internal/types': ['Addon_TypesEnum', 'definePreview'], + '@storybook/types': ['Addon_TypesEnum', 'definePreview'], + '@storybook/core/types': ['Addon_TypesEnum', 'definePreview'], 'storybook/internal/manager-errors': [ 'Category', 'ProviderDoesNotExtendBaseProviderError', diff --git a/code/renderers/react/src/csf-factories.test.tsx b/code/renderers/react/src/csf-factories.test.tsx index 3b1928051eb4..ccaf6d405421 100644 --- a/code/renderers/react/src/csf-factories.test.tsx +++ b/code/renderers/react/src/csf-factories.test.tsx @@ -1,7 +1,26 @@ +// @vitest-environment happy-dom +// this file tests Typescript types that's why there are no assertions +import { describe, it } from 'vitest'; import { expect, test } from 'vitest'; -import { Button } from './__test__/Button'; +import type { KeyboardEventHandler, ReactElement, ReactNode } from 'react'; +import React from 'react'; + +import type { Args, StrictArgs } from 'storybook/internal/types'; + +import type { Canvas } from '@storybook/csf'; +import type { Mock } from '@storybook/test'; +import { fn } from '@storybook/test'; + +import { expectTypeOf } from 'expect-type'; + import { definePreview } from './preview'; +import type { Decorator } from './public-types'; + +type ButtonProps = { label: string; disabled: boolean }; +const Button: (props: ButtonProps) => ReactElement = () => <>; + +const preview = definePreview({}); test('csf factories', () => { const config = definePreview({ @@ -12,13 +31,224 @@ test('csf factories', () => { ], }); - const meta = config.meta({ component: Button, args: { primary: true } }); + const meta = config.meta({ component: Button, args: { disabled: true } }); const MyStory = meta.story({ args: { - children: 'Hello world', + label: 'Hello world', + }, + }); + + expect(MyStory.input.args?.label).toBe('Hello world'); +}); + +describe('Args can be provided in multiple ways', () => { + it('✅ All required args may be provided in meta', () => { + const meta = preview.meta({ + component: Button, + args: { label: 'good', disabled: false }, + }); + + const Basic = meta.story({}); + }); + + it('✅ Required args may be provided partial in meta and the story', () => { + const meta = preview.meta({ + component: Button, + args: { label: 'good' }, + }); + const Basic = meta.story({ + args: { disabled: false }, + }); + }); + + it('❌ The combined shape of meta args and story args must match the required args.', () => { + { + const meta = preview.meta({ component: Button }); + const Basic = meta.story({ + // @ts-expect-error disabled not provided ❌ + args: { label: 'good' }, + }); + } + { + const meta = preview.meta({ + component: Button, + args: { label: 'good' }, + }); + // @ts-expect-error disabled not provided ❌ + const Basic = meta.story({}); + } + { + const meta = preview.meta({ component: Button }); + const Basic = meta.story({ + // @ts-expect-error disabled not provided ❌ + args: { label: 'good' }, + }); + } + }); +}); + +it('✅ Void functions are not changed', () => { + interface CmpProps { + label: string; + disabled: boolean; + onClick(): void; + onKeyDown: KeyboardEventHandler; + onLoading: (s: string) => ReactElement; + submitAction(): void; + } + + const Cmp: (props: CmpProps) => ReactElement = () => <>; + + const meta = preview.meta({ + component: Cmp, + args: { label: 'good' }, + }); + + const Basic = meta.story({ + args: { + disabled: false, + onLoading: () =>
Loading...
, + onKeyDown: fn(), + onClick: fn(), + submitAction: fn(), }, }); +}); + +type ThemeData = 'light' | 'dark'; +declare const Theme: (props: { theme: ThemeData; children?: ReactNode }) => ReactElement; + +describe('Story args can be inferred', () => { + it('Correct args are inferred when type is widened for render function', () => { + const meta = preview.meta({ + component: Button, + args: { disabled: false }, + render: (args: ButtonProps & { theme: ThemeData }, { component }) => { + // component is not null as it is provided in meta - expect(MyStory.input.args?.children).toBe('Hello world'); + const Component = component!; + return ( + + + + ); + }, + }); + + const Basic = meta.story({ args: { theme: 'light', label: 'good' } }); + }); + + const withDecorator: Decorator<{ decoratorArg: number }> = (Story, { args }) => ( + <> + Decorator: {args.decoratorArg} + + + ); + + it('Correct args are inferred when type is widened for decorators', () => { + const meta = preview.meta({ + component: Button, + args: { disabled: false }, + decorators: [withDecorator], + }); + + const Basic = meta.story({ args: { decoratorArg: 0, label: 'good' } }); + }); + + it('Correct args are inferred when type is widened for multiple decorators', () => { + type Props = ButtonProps & { decoratorArg: number; decoratorArg2: string }; + + const secondDecorator: Decorator<{ decoratorArg2: string }> = (Story, { args }) => ( + <> + Decorator: {args.decoratorArg2} + + + ); + + // decorator is not using args + const thirdDecorator: Decorator = (Story) => ( + <> + + + ); + + // decorator is not using args + const fourthDecorator: Decorator = (Story) => ( + <> + + + ); + + const meta = preview.meta({ + component: Button, + args: { disabled: false }, + decorators: [withDecorator, secondDecorator, thirdDecorator, fourthDecorator], + }); + + const Basic = meta.story({ + args: { decoratorArg: 0, decoratorArg2: '', label: 'good' }, + }); + }); +}); + +it('Components without Props can be used, issue #21768', () => { + const Component = () => <>Foo; + const withDecorator: Decorator = (Story) => ( + <> + + + ); + + const meta = preview.meta({ + component: Component, + decorators: [withDecorator], + }); + + const Basic = meta.story({}); +}); + +it('Meta is broken when using discriminating types, issue #23629', () => { + type TestButtonProps = { + text: string; + } & ( + | { + id?: string; + onClick?: (e: unknown, id: string | undefined) => void; + } + | { + id: string; + onClick: (e: unknown, id: string) => void; + } + ); + const TestButton: React.FC = ({ text }) => { + return

{text}

; + }; + + preview.meta({ + title: 'Components/Button', + component: TestButton, + args: { + text: 'Button', + }, + }); +}); + +it('Infer mock function given to args in meta.', () => { + type Props = { label: string; onClick: () => void; onRender: () => JSX.Element }; + const TestButton = (props: Props) => <>; + + const meta = preview.meta({ + component: TestButton, + args: { label: 'label', onClick: fn(), onRender: () => <>some jsx }, + }); + + const Basic = meta.story({ + play: async ({ args, mount }) => { + const canvas = await mount(); + expectTypeOf(canvas).toEqualTypeOf(); + expectTypeOf(args.onClick).toEqualTypeOf(); + expectTypeOf(args.onRender).toEqualTypeOf<() => JSX.Element>(); + }, + }); }); diff --git a/code/renderers/react/src/preview.tsx b/code/renderers/react/src/preview.tsx index a6b7fb61b44d..ba53e054ff5b 100644 --- a/code/renderers/react/src/preview.tsx +++ b/code/renderers/react/src/preview.tsx @@ -10,10 +10,10 @@ import type { } from 'storybook/internal/types'; import { definePreview as definePreviewBase } from 'storybook/internal/types'; -import type { ArgsStoryFn } from '@storybook/csf'; +import type { ArgsStoryFn, DecoratorFunction, LoaderFunction, Renderer } from '@storybook/csf'; import type { AddMocks } from 'src/public-types'; -import type { Exact, SetOptional } from 'type-fest'; +import type { RemoveIndexSignature, SetOptional, Simplify, UnionToIntersection } from 'type-fest'; import * as reactAnnotations from './entry-preview'; import * as reactDocsAnnotations from './entry-preview-docs'; @@ -27,15 +27,31 @@ export function definePreview(preview: ReactPreview['input']) { } export interface ReactPreview extends Preview { - meta, TMetaArgs>>( + meta< + TArgs extends Args, + Decorators extends DecoratorFunction, + // Try to make Exact, TMetaArgs> work + TMetaArgs extends Partial, + >( meta: { render?: ArgsStoryFn; component?: ComponentType; + decorators?: Decorators | Decorators[]; args?: TMetaArgs; - } & ComponentAnnotations - ): ReactMeta<{ args: TArgs }, { args: TMetaArgs }>; + } & Omit, 'decorators'> + ): ReactMeta< + { + args: Simplify< + TArgs & Simplify>> + >; + }, + { args: Partial extends TMetaArgs ? {} : TMetaArgs } + >; } +type DecoratorsArgs = UnionToIntersection< + Decorators extends DecoratorFunction ? TArgs : unknown +>; interface ReactMeta< Context extends { args: Args }, MetaInput extends ComponentAnnotations, From b93d96a575b08cafc20979840eca598308e3f0b1 Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Thu, 30 Jan 2025 12:53:04 +0100 Subject: [PATCH 03/23] Make storybook/internal/csf --- code/core/package.json | 8 ++++++++ code/core/scripts/entries.ts | 1 + .../{types/modules/csf-factories.ts => csf/index.ts} | 11 +++++++---- code/core/src/manager/globals/exports.ts | 6 +++--- code/core/src/types/index.ts | 1 - code/lib/cli/core/csf/index.cjs | 1 + code/lib/cli/core/csf/index.d.ts | 2 ++ code/lib/cli/core/csf/index.js | 1 + code/lib/cli/package.json | 8 ++++++++ code/renderers/react/src/preview.tsx | 11 +++++------ 10 files changed, 36 insertions(+), 14 deletions(-) rename code/core/src/{types/modules/csf-factories.ts => csf/index.ts} (96%) create mode 100644 code/lib/cli/core/csf/index.cjs create mode 100644 code/lib/cli/core/csf/index.d.ts create mode 100644 code/lib/cli/core/csf/index.js diff --git a/code/core/package.json b/code/core/package.json index 76008e354161..e6139d3f7667 100644 --- a/code/core/package.json +++ b/code/core/package.json @@ -97,6 +97,11 @@ "import": "./dist/csf-tools/index.js", "require": "./dist/csf-tools/index.cjs" }, + "./csf": { + "types": "./dist/csf/index.d.ts", + "import": "./dist/csf/index.js", + "require": "./dist/csf/index.cjs" + }, "./common": { "types": "./dist/common/index.d.ts", "import": "./dist/common/index.js", @@ -219,6 +224,9 @@ "csf-tools": [ "./dist/csf-tools/index.d.ts" ], + "csf": [ + "./dist/csf/index.d.ts" + ], "common": [ "./dist/common/index.d.ts" ], diff --git a/code/core/scripts/entries.ts b/code/core/scripts/entries.ts index a8588f13fd86..2b90e2c4b8a4 100644 --- a/code/core/scripts/entries.ts +++ b/code/core/scripts/entries.ts @@ -25,6 +25,7 @@ export const getEntries = (cwd: string) => { define('src/channels/index.ts', ['browser', 'node'], true), define('src/types/index.ts', ['browser', 'node'], true, ['react']), define('src/csf-tools/index.ts', ['node'], true), + define('src/csf/index.ts', ['browser', 'node'], true), define('src/common/index.ts', ['node'], true), define('src/builder-manager/index.ts', ['node'], true), define('src/telemetry/index.ts', ['node'], true), diff --git a/code/core/src/types/modules/csf-factories.ts b/code/core/src/csf/index.ts similarity index 96% rename from code/core/src/types/modules/csf-factories.ts rename to code/core/src/csf/index.ts index d997ac7fe9ff..20d0643b1b39 100644 --- a/code/core/src/types/modules/csf-factories.ts +++ b/code/core/src/csf/index.ts @@ -1,12 +1,15 @@ -import { composeConfigs, normalizeProjectAnnotations } from '@storybook/core/preview-api'; - -import type { Args, ComponentAnnotations, Renderer, StoryAnnotations } from './csf'; import type { + Args, + ComponentAnnotations, NormalizedComponentAnnotations, NormalizedProjectAnnotations, NormalizedStoryAnnotations, ProjectAnnotations, -} from './story'; + Renderer, + StoryAnnotations, +} from '@storybook/core/types'; + +import { composeConfigs, normalizeProjectAnnotations } from '@storybook/core/preview-api'; export interface Preview { readonly _tag: 'Preview'; diff --git a/code/core/src/manager/globals/exports.ts b/code/core/src/manager/globals/exports.ts index 037bf2504c57..4e34bb61244c 100644 --- a/code/core/src/manager/globals/exports.ts +++ b/code/core/src/manager/globals/exports.ts @@ -963,9 +963,9 @@ export default { 'UPDATE_QUERY_PARAMS', 'UPDATE_STORY_ARGS', ], - 'storybook/internal/types': ['Addon_TypesEnum', 'definePreview'], - '@storybook/types': ['Addon_TypesEnum', 'definePreview'], - '@storybook/core/types': ['Addon_TypesEnum', 'definePreview'], + 'storybook/internal/types': ['Addon_TypesEnum'], + '@storybook/types': ['Addon_TypesEnum'], + '@storybook/core/types': ['Addon_TypesEnum'], 'storybook/internal/manager-errors': [ 'Category', 'ProviderDoesNotExtendBaseProviderError', diff --git a/code/core/src/types/index.ts b/code/core/src/types/index.ts index b6a3088a0d64..1941cbe86872 100644 --- a/code/core/src/types/index.ts +++ b/code/core/src/types/index.ts @@ -1,7 +1,6 @@ /// export * from './modules/csf'; -export * from './modules/csf-factories'; export * from './modules/addons'; export * from './modules/story'; export * from './modules/core-common'; diff --git a/code/lib/cli/core/csf/index.cjs b/code/lib/cli/core/csf/index.cjs new file mode 100644 index 000000000000..19b144387019 --- /dev/null +++ b/code/lib/cli/core/csf/index.cjs @@ -0,0 +1 @@ +module.exports = require('@storybook/core/csf'); diff --git a/code/lib/cli/core/csf/index.d.ts b/code/lib/cli/core/csf/index.d.ts new file mode 100644 index 000000000000..2cc38939bb85 --- /dev/null +++ b/code/lib/cli/core/csf/index.d.ts @@ -0,0 +1,2 @@ +export * from '@storybook/core/csf'; +export type * from '@storybook/core/csf'; diff --git a/code/lib/cli/core/csf/index.js b/code/lib/cli/core/csf/index.js new file mode 100644 index 000000000000..ed1380489e93 --- /dev/null +++ b/code/lib/cli/core/csf/index.js @@ -0,0 +1 @@ +export * from '@storybook/core/csf'; diff --git a/code/lib/cli/package.json b/code/lib/cli/package.json index 53cf6294030e..09e35b685f74 100644 --- a/code/lib/cli/package.json +++ b/code/lib/cli/package.json @@ -103,6 +103,11 @@ "import": "./core/types/index.js", "require": "./core/types/index.cjs" }, + "./internal/csf": { + "types": "./core/csf/index.d.ts", + "import": "./core/csf/index.js", + "require": "./core/csf/index.cjs" + }, "./internal/csf-tools": { "types": "./core/csf-tools/index.d.ts", "import": "./core/csf-tools/index.js", @@ -233,6 +238,9 @@ "internal/core-server": [ "./core/core-server/index.d.ts" ], + "internal/csf": [ + "./core/csf/index.d.ts" + ], "internal/csf-tools": [ "./core/csf-tools/index.d.ts" ], diff --git a/code/renderers/react/src/preview.tsx b/code/renderers/react/src/preview.tsx index ba53e054ff5b..d2c435bf121c 100644 --- a/code/renderers/react/src/preview.tsx +++ b/code/renderers/react/src/preview.tsx @@ -1,16 +1,15 @@ import type { ComponentType } from 'react'; +import { definePreview as definePreviewBase } from 'storybook/internal/csf'; +import type { Meta, Preview, Story } from 'storybook/internal/csf'; import type { Args, + ArgsStoryFn, ComponentAnnotations, - Meta, - Preview, - Story, + DecoratorFunction, + Renderer, StoryAnnotations, } from 'storybook/internal/types'; -import { definePreview as definePreviewBase } from 'storybook/internal/types'; - -import type { ArgsStoryFn, DecoratorFunction, LoaderFunction, Renderer } from '@storybook/csf'; import type { AddMocks } from 'src/public-types'; import type { RemoveIndexSignature, SetOptional, Simplify, UnionToIntersection } from 'type-fest'; From fccc0df808bb449ab8f894d614b07f9fa201ceb2 Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Thu, 30 Jan 2025 14:42:49 +0100 Subject: [PATCH 04/23] Adjust runtime --- .../src/codegen-modern-iframe-script.ts | 7 ++-- .../templates/virtualModuleModernEntry.js | 11 +++--- code/core/src/csf/index.ts | 8 ++--- .../preview-web/docs-context/DocsContext.ts | 6 ++-- .../modules/store/csf/csf-factory-utils.ts | 35 +++++-------------- 5 files changed, 23 insertions(+), 44 deletions(-) diff --git a/code/builders/builder-vite/src/codegen-modern-iframe-script.ts b/code/builders/builder-vite/src/codegen-modern-iframe-script.ts index 69b05dc5e744..31e1e93f3468 100644 --- a/code/builders/builder-vite/src/codegen-modern-iframe-script.ts +++ b/code/builders/builder-vite/src/codegen-modern-iframe-script.ts @@ -24,10 +24,9 @@ export async function generateModernIframeScriptCode(options: Options, projectRo const getPreviewAnnotationsFunction = ` const getProjectAnnotations = async (hmrPreviewAnnotationModules = []) => { const preview = await import('${previewFileUrl}'); - const csfFactoryPreview = getCsfFactoryPreview(preview); - - if (csfFactoryPreview) { - return csfFactoryPreview.input; + + if (isPreview(preview.default)) { + return preview.default.composed; } const configs = await Promise.all([${previewAnnotationURLs diff --git a/code/builders/builder-webpack5/templates/virtualModuleModernEntry.js b/code/builders/builder-webpack5/templates/virtualModuleModernEntry.js index 423bee0c36d0..0ed2fe15fa85 100644 --- a/code/builders/builder-webpack5/templates/virtualModuleModernEntry.js +++ b/code/builders/builder-webpack5/templates/virtualModuleModernEntry.js @@ -1,4 +1,5 @@ import { createBrowserChannel } from 'storybook/internal/channels'; +import { isPreview } from 'storybook/internal/csf'; import { PreviewWeb, addons, composeConfigs } from 'storybook/internal/preview-api'; import { global } from '@storybook/global'; @@ -8,14 +9,10 @@ import { importFn } from '{{storiesFilename}}'; const getProjectAnnotations = () => { const previewAnnotations = ['{{previewAnnotations_requires}}']; // the last one in this array is the user preview - const preview = previewAnnotations[previewAnnotations.length - 1]; + const userPreview = previewAnnotations[previewAnnotations.length - 1]?.default; - const csfFactoryPreview = Object.values(preview).find((module) => { - return 'isCSFFactoryPreview' in module; - }); - - if (csfFactoryPreview) { - return csfFactoryPreview.annotations; + if (isPreview(userPreview)) { + return userPreview.composed; } return composeConfigs(previewAnnotations); diff --git a/code/core/src/csf/index.ts b/code/core/src/csf/index.ts index 20d0643b1b39..f913c8b205ae 100644 --- a/code/core/src/csf/index.ts +++ b/code/core/src/csf/index.ts @@ -11,7 +11,7 @@ import type { import { composeConfigs, normalizeProjectAnnotations } from '@storybook/core/preview-api'; -export interface Preview { +export interface Preview { readonly _tag: 'Preview'; input: ProjectAnnotations; composed: NormalizedProjectAnnotations; @@ -35,7 +35,7 @@ export function definePreview( }; } -function isPreview(input: unknown): input is Preview { +export function isPreview(input: unknown): input is Preview { return input != null && typeof input === 'object' && '_tag' in input && input?._tag === 'Preview'; } @@ -48,7 +48,7 @@ export interface Meta { story(input: ComponentAnnotations): Story; } -function isMeta(input: unknown): input is Meta { +export function isMeta(input: unknown): input is Meta { return input != null && typeof input === 'object' && '_tag' in input && input?._tag === 'Meta'; } @@ -90,6 +90,6 @@ function defineStory( }; } -function isStory(input: unknown): input is Meta { +export function isStory(input: unknown): input is Story { return input != null && typeof input === 'object' && '_tag' in input && input?._tag === 'Story'; } diff --git a/code/core/src/preview-api/modules/preview-web/docs-context/DocsContext.ts b/code/core/src/preview-api/modules/preview-web/docs-context/DocsContext.ts index c84bf2e16e12..d70abc89646a 100644 --- a/code/core/src/preview-api/modules/preview-web/docs-context/DocsContext.ts +++ b/code/core/src/preview-api/modules/preview-web/docs-context/DocsContext.ts @@ -11,9 +11,10 @@ import type { StoryName, } from '@storybook/core/types'; +import { isStory } from 'core/src/csf'; import { dedent } from 'ts-dedent'; -import { type StoryStore, isCsfFactory } from '../../store'; +import { type StoryStore } from '../../store'; import type { DocsContextProps } from './DocsContextProps'; export class DocsContext implements DocsContextProps { @@ -163,8 +164,7 @@ export class DocsContext implements DocsContextProps } const story = this.exportToStory.get( - // TODO: @kasperpeulen will fix this once csf factory types are defined - isCsfFactory(moduleExportOrType) ? (moduleExportOrType as any).input : moduleExportOrType + isStory(moduleExportOrType) ? moduleExportOrType.input : moduleExportOrType ); if (story) { diff --git a/code/core/src/preview-api/modules/store/csf/csf-factory-utils.ts b/code/core/src/preview-api/modules/store/csf/csf-factory-utils.ts index def96170fca1..741a1842d4a5 100644 --- a/code/core/src/preview-api/modules/store/csf/csf-factory-utils.ts +++ b/code/core/src/preview-api/modules/store/csf/csf-factory-utils.ts @@ -1,27 +1,11 @@ -/* eslint-disable @typescript-eslint/naming-convention */ - -/* eslint-disable no-underscore-dangle */ +import { isStory } from '@storybook/core/csf'; import type { Args, ComponentAnnotations, LegacyStoryAnnotationsOrFn, - ModuleExports, ProjectAnnotations, Renderer, - StoryAnnotations, -} from '@storybook/types'; - -export function getCsfFactoryPreview(preview: ModuleExports): ProjectAnnotations | null { - return Object.values(preview).find(isCsfFactory) ?? null; -} - -export function isCsfFactory(target: StoryAnnotations | ProjectAnnotations) { - return ( - target != null && - typeof target === 'object' && - ('isCSFFactory' in target || 'isCSFFactoryPreview' in target) - ); -} +} from '@storybook/core/types'; export function getCsfFactoryAnnotations< TRenderer extends Renderer = Renderer, @@ -31,12 +15,11 @@ export function getCsfFactoryAnnotations< meta?: ComponentAnnotations, projectAnnotations?: ProjectAnnotations ) { - const _isCsfFactory = isCsfFactory(story); - - return { - // TODO: @kasperpeulen will fix this once csf factory types are defined - story: _isCsfFactory ? (story as any)?.input : story, - meta: _isCsfFactory ? (story as any)?.meta?.input : meta, - preview: _isCsfFactory ? (story as any)?.config?.input : projectAnnotations, - }; + return isStory(story) + ? { + story: story.input, + meta: story.meta.input, + preview: story.meta.preview.composed, + } + : { story, meta, preview: projectAnnotations }; } From bd5f88c536dbb7b8e591a4404270c6c39ff6d178 Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Thu, 30 Jan 2025 15:25:18 +0100 Subject: [PATCH 05/23] Adjust runtime --- .../builder-vite/src/codegen-modern-iframe-script.ts | 1 + code/core/src/csf/index.ts | 2 +- .../src/preview-api/modules/store/csf/processCSFFile.ts | 7 ++++--- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/code/builders/builder-vite/src/codegen-modern-iframe-script.ts b/code/builders/builder-vite/src/codegen-modern-iframe-script.ts index 31e1e93f3468..a953acdaee91 100644 --- a/code/builders/builder-vite/src/codegen-modern-iframe-script.ts +++ b/code/builders/builder-vite/src/codegen-modern-iframe-script.ts @@ -79,6 +79,7 @@ export async function generateModernIframeScriptCode(options: Options, projectRo setup(); import { composeConfigs, PreviewWeb, ClientApi, getCsfFactoryPreview } from 'storybook/internal/preview-api'; + import { isPreview } from 'storybook/internal/csf'; import { importFn } from '${SB_VIRTUAL_FILES.VIRTUAL_STORIES_FILE}'; diff --git a/code/core/src/csf/index.ts b/code/core/src/csf/index.ts index f913c8b205ae..91b503856c56 100644 --- a/code/core/src/csf/index.ts +++ b/code/core/src/csf/index.ts @@ -90,6 +90,6 @@ function defineStory( }; } -export function isStory(input: unknown): input is Story { +export function isStory(input: unknown): input is Story { return input != null && typeof input === 'object' && '_tag' in input && input?._tag === 'Story'; } diff --git a/code/core/src/preview-api/modules/store/csf/processCSFFile.ts b/code/core/src/preview-api/modules/store/csf/processCSFFile.ts index 9242d7cf8d18..b29d7d67d9e5 100644 --- a/code/core/src/preview-api/modules/store/csf/processCSFFile.ts +++ b/code/core/src/preview-api/modules/store/csf/processCSFFile.ts @@ -1,3 +1,4 @@ +import { isStory } from '@storybook/core/csf'; import type { ComponentTitle, Parameters, Path, Renderer } from '@storybook/core/types'; import type { CSFFile, ModuleExports, NormalizedComponentAnnotations } from '@storybook/core/types'; import { isExportStory } from '@storybook/csf'; @@ -46,10 +47,10 @@ export function processCSFFile( // eslint-disable-next-line @typescript-eslint/naming-convention const { default: defaultExport, __namedExportsOrder, ...namedExports } = moduleExports; - const firstStory: any = Object.values(namedExports)[0]; + const firstStory = Object.values(namedExports)[0]; // CSF4 // TODO: @kasperpeulen will fix this once csf factory types are defined - if (!defaultExport && 'isCSFFactory' in firstStory) { + if (isStory(firstStory)) { const meta: NormalizedComponentAnnotations = normalizeComponentAnnotations(firstStory.meta.input, title, importPath); checkDisallowedParameters(meta.parameters); @@ -65,7 +66,7 @@ export function processCSFFile( } }); - csfFile.projectAnnotations = firstStory.config.input; + csfFile.projectAnnotations = firstStory.meta.preview.composed; return csfFile; } From c5cb4b1b4823aaa469ad0a63d6b370f8c1752193 Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Thu, 30 Jan 2025 15:26:15 +0100 Subject: [PATCH 06/23] Fix TODO --- code/core/src/preview-api/modules/store/csf/processCSFFile.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/code/core/src/preview-api/modules/store/csf/processCSFFile.ts b/code/core/src/preview-api/modules/store/csf/processCSFFile.ts index b29d7d67d9e5..9ebb75d6e7d5 100644 --- a/code/core/src/preview-api/modules/store/csf/processCSFFile.ts +++ b/code/core/src/preview-api/modules/store/csf/processCSFFile.ts @@ -48,8 +48,6 @@ export function processCSFFile( const { default: defaultExport, __namedExportsOrder, ...namedExports } = moduleExports; const firstStory = Object.values(namedExports)[0]; - // CSF4 - // TODO: @kasperpeulen will fix this once csf factory types are defined if (isStory(firstStory)) { const meta: NormalizedComponentAnnotations = normalizeComponentAnnotations(firstStory.meta.input, title, importPath); From 209cdeb83c19118e4f3df0db4e50b4fcf4647730 Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Thu, 30 Jan 2025 15:31:13 +0100 Subject: [PATCH 07/23] Use default export for preview --- code/.storybook/preview.tsx | 3 +-- code/.storybook/storybook.setup.ts | 5 ++--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/code/.storybook/preview.tsx b/code/.storybook/preview.tsx index f26260d48339..739c2ea7ba43 100644 --- a/code/.storybook/preview.tsx +++ b/code/.storybook/preview.tsx @@ -130,7 +130,6 @@ const ThemedSetRoot = () => { return null; }; -// eslint-disable-next-line no-underscore-dangle const preview = (window as any).__STORYBOOK_PREVIEW__ as PreviewWeb | undefined; const channel = (window as any).__STORYBOOK_ADDONS_CHANNEL__ as Channel | undefined; const loaders = [ @@ -373,7 +372,7 @@ const parameters = { }, }; -export const config = definePreview({ +export default definePreview({ addons: [ addonThemes(), addonEssentials(), diff --git a/code/.storybook/storybook.setup.ts b/code/.storybook/storybook.setup.ts index 3521dcafb8cc..2fe58c6dbe96 100644 --- a/code/.storybook/storybook.setup.ts +++ b/code/.storybook/storybook.setup.ts @@ -3,17 +3,16 @@ import { beforeAll, vi, expect as vitestExpect } from 'vitest'; import { setProjectAnnotations } from '@storybook/react'; import { userEvent as storybookEvent, expect as storybookExpect } from '@storybook/test'; -import { config } from './preview'; +import preview from './preview'; vi.spyOn(console, 'warn').mockImplementation((...args) => console.log(...args)); const annotations = setProjectAnnotations([ - config.input, + preview.composed, { // experiment with injecting Vitest's interactivity API over our userEvent while tests run in browser mode // https://vitest.dev/guide/browser/interactivity-api.html loaders: async (context) => { - // eslint-disable-next-line no-underscore-dangle if (globalThis.__vitest_browser__) { const vitest = await import('@vitest/browser/context'); const { userEvent: browserEvent } = vitest; From d5b592a5c7d18800951cc1144259040070f53646 Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Thu, 30 Jan 2025 15:59:20 +0100 Subject: [PATCH 08/23] Fix type inference issue --- .../components/Button/Button.stories.tsx | 4 ++-- .../mocks/csf4-variances.stories.tsx | 4 ++-- code/renderers/react/src/preview.tsx | 18 +++++++++++------- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/code/core/src/components/components/Button/Button.stories.tsx b/code/core/src/components/components/Button/Button.stories.tsx index c2724412aa18..78e061d5d1a7 100644 --- a/code/core/src/components/components/Button/Button.stories.tsx +++ b/code/core/src/components/components/Button/Button.stories.tsx @@ -3,10 +3,10 @@ import React from 'react'; import { FaceHappyIcon } from '@storybook/icons'; -import { config } from '../../../../../.storybook/preview'; +import preview from '../../../../../.storybook/preview'; import { Button } from './Button'; -const meta = config.meta({ +const meta = preview.meta({ id: 'button-component', title: 'Button', component: Button, diff --git a/code/core/src/core-server/utils/save-story/mocks/csf4-variances.stories.tsx b/code/core/src/core-server/utils/save-story/mocks/csf4-variances.stories.tsx index fd9003947612..51f78d90ed92 100644 --- a/code/core/src/core-server/utils/save-story/mocks/csf4-variances.stories.tsx +++ b/code/core/src/core-server/utils/save-story/mocks/csf4-variances.stories.tsx @@ -1,7 +1,7 @@ // @ts-expect-error this is just a mock file -import { config } from '#.storybook/preview'; +import preview from '#.storybook/preview'; -const meta = config.meta({ +const meta = preview.meta({ title: 'MyComponent', args: { initial: 'foo', diff --git a/code/renderers/react/src/preview.tsx b/code/renderers/react/src/preview.tsx index d2c435bf121c..498521c241f0 100644 --- a/code/renderers/react/src/preview.tsx +++ b/code/renderers/react/src/preview.tsx @@ -55,13 +55,17 @@ interface ReactMeta< Context extends { args: Args }, MetaInput extends ComponentAnnotations, > extends Meta { - story( - story: StoryAnnotations< - ReactRenderer, - // TODO: infer mocks from story itself as well - AddMocks, - SetOptional - > + story< + const TInput extends Simplify< + StoryAnnotations< + ReactRenderer, + // TODO: infer mocks from story itself as well + AddMocks, + SetOptional + > + >, + >( + story: TInput ): ReactStory; } From 1f5a406ee6f9618a9fba4977fe33ded19959c8e1 Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Thu, 30 Jan 2025 16:06:52 +0100 Subject: [PATCH 09/23] Fix --- .../builder-vite/src/codegen-modern-iframe-script.ts | 2 +- code/core/src/preview-api/index.ts | 7 +------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/code/builders/builder-vite/src/codegen-modern-iframe-script.ts b/code/builders/builder-vite/src/codegen-modern-iframe-script.ts index a953acdaee91..7b8f82a3f330 100644 --- a/code/builders/builder-vite/src/codegen-modern-iframe-script.ts +++ b/code/builders/builder-vite/src/codegen-modern-iframe-script.ts @@ -78,7 +78,7 @@ export async function generateModernIframeScriptCode(options: Options, projectRo setup(); - import { composeConfigs, PreviewWeb, ClientApi, getCsfFactoryPreview } from 'storybook/internal/preview-api'; + import { composeConfigs, PreviewWeb, ClientApi } from 'storybook/internal/preview-api'; import { isPreview } from 'storybook/internal/csf'; import { importFn } from '${SB_VIRTUAL_FILES.VIRTUAL_STORIES_FILE}'; diff --git a/code/core/src/preview-api/index.ts b/code/core/src/preview-api/index.ts index 907897730377..12ee8d71fba3 100644 --- a/code/core/src/preview-api/index.ts +++ b/code/core/src/preview-api/index.ts @@ -60,12 +60,7 @@ export { } from './store'; /** CSF API */ -export { - createPlaywrightTest, - getCsfFactoryPreview, - getCsfFactoryAnnotations, - isCsfFactory, -} from './modules/store/csf'; +export { createPlaywrightTest, getCsfFactoryAnnotations } from './modules/store/csf'; export type { PropDescriptor } from './store'; From 1f4c92fdf457b58ae6e5ea8886e3fcc3bf3b819d Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Fri, 31 Jan 2025 09:05:44 +0100 Subject: [PATCH 10/23] Fix import --- .../preview-api/modules/preview-web/docs-context/DocsContext.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/core/src/preview-api/modules/preview-web/docs-context/DocsContext.ts b/code/core/src/preview-api/modules/preview-web/docs-context/DocsContext.ts index d70abc89646a..d8d83bed4deb 100644 --- a/code/core/src/preview-api/modules/preview-web/docs-context/DocsContext.ts +++ b/code/core/src/preview-api/modules/preview-web/docs-context/DocsContext.ts @@ -1,4 +1,5 @@ import type { Channel } from '@storybook/core/channels'; +import { isStory } from '@storybook/core/csf'; import type { CSFFile, ModuleExport, @@ -11,7 +12,6 @@ import type { StoryName, } from '@storybook/core/types'; -import { isStory } from 'core/src/csf'; import { dedent } from 'ts-dedent'; import { type StoryStore } from '../../store'; From 1a356cbb820bb61120f7792daaa1c97d97556ebf Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Fri, 31 Jan 2025 09:32:06 +0100 Subject: [PATCH 11/23] Fix build --- code/addons/test/src/vitest-plugin/test-utils.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/code/addons/test/src/vitest-plugin/test-utils.ts b/code/addons/test/src/vitest-plugin/test-utils.ts index 765ab38d5a1b..92b09a49623a 100644 --- a/code/addons/test/src/vitest-plugin/test-utils.ts +++ b/code/addons/test/src/vitest-plugin/test-utils.ts @@ -1,6 +1,4 @@ /* eslint-disable @typescript-eslint/naming-convention */ - -/* eslint-disable no-underscore-dangle */ import { type RunnerTask, type TaskMeta, type TestContext } from 'vitest'; import { @@ -33,7 +31,7 @@ export const testStory = ( const annotations = getCsfFactoryAnnotations(story, meta); const composedStory = composeStory( annotations.story, - annotations.meta, + annotations.meta!, { initialGlobals: (await getInitialGlobals?.()) ?? {}, tags: await getTags?.() }, annotations.preview, exportName From 03ec5932446094b24c8dccc7c01dceb41719ad5c Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Fri, 31 Jan 2025 09:43:11 +0100 Subject: [PATCH 12/23] Fix test --- code/renderers/react/src/__test__/Button.csf4.stories.tsx | 2 +- .../__snapshots__/portable-stories-factory.test.tsx.snap | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/code/renderers/react/src/__test__/Button.csf4.stories.tsx b/code/renderers/react/src/__test__/Button.csf4.stories.tsx index 067e481a3d6a..f84f6233a559 100644 --- a/code/renderers/react/src/__test__/Button.csf4.stories.tsx +++ b/code/renderers/react/src/__test__/Button.csf4.stories.tsx @@ -88,7 +88,7 @@ export const CSF3Button = meta.story({ }); export const CSF3ButtonWithRender = meta.story({ - ...CSF3Button, + ...CSF3Button.input, render: (args) => (

I am a custom render function

diff --git a/code/renderers/react/src/__test__/__snapshots__/portable-stories-factory.test.tsx.snap b/code/renderers/react/src/__test__/__snapshots__/portable-stories-factory.test.tsx.snap index 4e8d3a043beb..3f00ff746281 100644 --- a/code/renderers/react/src/__test__/__snapshots__/portable-stories-factory.test.tsx.snap +++ b/code/renderers/react/src/__test__/__snapshots__/portable-stories-factory.test.tsx.snap @@ -52,7 +52,7 @@ exports[`Renders CSF3ButtonWithRender story 1`] = ` class="storybook-button storybook-button--medium storybook-button--secondary" type="button" > - Children coming from meta args + foo
From cecfd850f3f073aed1d8b06b2cb1c1e240827839 Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Fri, 31 Jan 2025 10:18:25 +0100 Subject: [PATCH 13/23] Fix eslint --- code/.eslintrc.js | 1 - code/core/src/csf/index.ts | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/code/.eslintrc.js b/code/.eslintrc.js index 06c7c4fb137e..4268ec993dd6 100644 --- a/code/.eslintrc.js +++ b/code/.eslintrc.js @@ -15,7 +15,6 @@ module.exports = { 'error', { devDependencies: true, peerDependencies: true }, ], - 'no-underscore-dangle': 'off', 'import/no-unresolved': 'off', // covered by typescript 'eslint-comments/disable-enable-pair': ['error', { allowWholeFile: true }], 'eslint-comments/no-unused-disable': 'error', diff --git a/code/core/src/csf/index.ts b/code/core/src/csf/index.ts index 91b503856c56..0c0ea4cbe21a 100644 --- a/code/core/src/csf/index.ts +++ b/code/core/src/csf/index.ts @@ -1,3 +1,4 @@ +/* eslint-disable no-underscore-dangle */ import type { Args, ComponentAnnotations, From 815c7784911eaa5b9ae606f50536ee83988d0a91 Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Fri, 31 Jan 2025 11:59:31 +0100 Subject: [PATCH 14/23] Fix eslint --- code/.storybook/preview.tsx | 1 + code/.storybook/storybook.setup.ts | 1 + code/addons/test/src/vitest-plugin/test-utils.ts | 2 ++ 3 files changed, 4 insertions(+) diff --git a/code/.storybook/preview.tsx b/code/.storybook/preview.tsx index 739c2ea7ba43..1555e13ef113 100644 --- a/code/.storybook/preview.tsx +++ b/code/.storybook/preview.tsx @@ -130,6 +130,7 @@ const ThemedSetRoot = () => { return null; }; +// eslint-disable-next-line no-underscore-dangle const preview = (window as any).__STORYBOOK_PREVIEW__ as PreviewWeb | undefined; const channel = (window as any).__STORYBOOK_ADDONS_CHANNEL__ as Channel | undefined; const loaders = [ diff --git a/code/.storybook/storybook.setup.ts b/code/.storybook/storybook.setup.ts index 2fe58c6dbe96..80160218a314 100644 --- a/code/.storybook/storybook.setup.ts +++ b/code/.storybook/storybook.setup.ts @@ -13,6 +13,7 @@ const annotations = setProjectAnnotations([ // experiment with injecting Vitest's interactivity API over our userEvent while tests run in browser mode // https://vitest.dev/guide/browser/interactivity-api.html loaders: async (context) => { + // eslint-disable-next-line no-underscore-dangle if (globalThis.__vitest_browser__) { const vitest = await import('@vitest/browser/context'); const { userEvent: browserEvent } = vitest; diff --git a/code/addons/test/src/vitest-plugin/test-utils.ts b/code/addons/test/src/vitest-plugin/test-utils.ts index 92b09a49623a..f8259d4445fb 100644 --- a/code/addons/test/src/vitest-plugin/test-utils.ts +++ b/code/addons/test/src/vitest-plugin/test-utils.ts @@ -1,4 +1,6 @@ /* eslint-disable @typescript-eslint/naming-convention */ + +/* eslint-disable no-underscore-dangle */ import { type RunnerTask, type TaskMeta, type TestContext } from 'vitest'; import { From 43ac42571ad5a51612d6d00942628cd8878d6bd0 Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Fri, 31 Jan 2025 12:16:50 +0100 Subject: [PATCH 15/23] Fix vitest --- scripts/tasks/sandbox-parts.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/tasks/sandbox-parts.ts b/scripts/tasks/sandbox-parts.ts index 563ca02ee478..e3f66447f2e8 100644 --- a/scripts/tasks/sandbox-parts.ts +++ b/scripts/tasks/sandbox-parts.ts @@ -429,7 +429,7 @@ export async function setupVitest(details: TemplateDetails, options: PassedOptio import projectAnnotations from './preview' // setProjectAnnotations still kept to support non-CSF4 story tests - const annotations = setProjectAnnotations(projectAnnotations.input) + const annotations = setProjectAnnotations(projectAnnotations.composed) beforeAll(annotations.beforeAll) ` ); From 70429c2392e565a0f0dad6094bac57d7c4c73caf Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Fri, 31 Jan 2025 13:44:55 +0100 Subject: [PATCH 16/23] Re-export definePreview --- code/frameworks/react-vite/src/index.ts | 2 ++ code/frameworks/react-webpack5/src/index.ts | 1 + 2 files changed, 3 insertions(+) diff --git a/code/frameworks/react-vite/src/index.ts b/code/frameworks/react-vite/src/index.ts index fcb073fefcd6..54688d096160 100644 --- a/code/frameworks/react-vite/src/index.ts +++ b/code/frameworks/react-vite/src/index.ts @@ -1 +1,3 @@ +export { definePreview } from '@storybook/react'; + export * from './types'; diff --git a/code/frameworks/react-webpack5/src/index.ts b/code/frameworks/react-webpack5/src/index.ts index fcb073fefcd6..84081fa8f85d 100644 --- a/code/frameworks/react-webpack5/src/index.ts +++ b/code/frameworks/react-webpack5/src/index.ts @@ -1 +1,2 @@ export * from './types'; +export { definePreview } from '@storybook/react'; From a838e73c0cc20041802e26a4fe9274a27ecde684 Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Fri, 31 Jan 2025 14:18:05 +0100 Subject: [PATCH 17/23] Adjust codemod --- code/.storybook/preview.tsx | 2 +- code/core/src/csf-tools/ConfigFile.test.ts | 18 +++++++++--------- .../experimental-nextjs-vite/src/index.ts | 14 ++++++++++++++ .../codemod/helpers/config-to-csf-factory.ts | 7 ------- scripts/tasks/sandbox-parts.ts | 14 ++++++++++++-- 5 files changed, 36 insertions(+), 19 deletions(-) diff --git a/code/.storybook/preview.tsx b/code/.storybook/preview.tsx index 1555e13ef113..048343067be2 100644 --- a/code/.storybook/preview.tsx +++ b/code/.storybook/preview.tsx @@ -17,12 +17,12 @@ import { import { DocsContext } from '@storybook/blocks'; import { global } from '@storybook/global'; import type { Decorator, Loader, ReactRenderer } from '@storybook/react'; -import { definePreview } from '@storybook/react'; // TODO add empty preview // import * as storysource from '@storybook/addon-storysource'; // import * as designs from '@storybook/addon-designs/preview'; import addonTest from '@storybook/experimental-addon-test'; +import { definePreview } from '@storybook/react-vite'; import addonA11y from '@storybook/addon-a11y'; import addonEssentials from '@storybook/addon-essentials'; diff --git a/code/core/src/csf-tools/ConfigFile.test.ts b/code/core/src/csf-tools/ConfigFile.test.ts index 6425a0ced108..bd9c6b97d482 100644 --- a/code/core/src/csf-tools/ConfigFile.test.ts +++ b/code/core/src/csf-tools/ConfigFile.test.ts @@ -247,7 +247,7 @@ describe('ConfigFile', () => { describe('factory config', () => { it('parses correctly', () => { const source = dedent` - import { definePreview } from '@storybook/react-vite/preview'; + import { definePreview } from '@storybook/react-vite'; const config = definePreview({ framework: 'foo', @@ -262,7 +262,7 @@ describe('ConfigFile', () => { getField( ['core', 'builder'], dedent` - import { definePreview } from '@storybook/react-vite/preview'; + import { definePreview } from '@storybook/react-vite'; export const foo = definePreview({ core: { builder: 'webpack5' } }); ` ) @@ -273,7 +273,7 @@ describe('ConfigFile', () => { getField( ['tags'], dedent` - import { definePreview } from '@storybook/react-vite/preview'; + import { definePreview } from '@storybook/react-vite'; const parameters = {}; export const config = definePreview({ parameters, @@ -528,14 +528,14 @@ describe('ConfigFile', () => { ['core', 'builder'], 'webpack5', dedent` - import { definePreview } from '@storybook/react-vite/preview'; + import { definePreview } from '@storybook/react-vite'; export const foo = definePreview({ addons: [], }); ` ) ).toMatchInlineSnapshot(` - import { definePreview } from '@storybook/react-vite/preview'; + import { definePreview } from '@storybook/react-vite'; export const foo = definePreview({ addons: [], @@ -551,14 +551,14 @@ describe('ConfigFile', () => { ['core', 'builder'], 'webpack5', dedent` - import { definePreview } from '@storybook/react-vite/preview'; + import { definePreview } from '@storybook/react-vite'; export const foo = definePreview({ core: { foo: 'bar' }, }); ` ) ).toMatchInlineSnapshot(` - import { definePreview } from '@storybook/react-vite/preview'; + import { definePreview } from '@storybook/react-vite'; export const foo = definePreview({ core: { foo: 'bar', @@ -573,14 +573,14 @@ describe('ConfigFile', () => { ['core', 'builder'], 'webpack5', dedent` - import { definePreview } from '@storybook/react-vite/preview'; + import { definePreview } from '@storybook/react-vite'; export const foo = definePreview({ core: { builder: 'webpack4' }, }); ` ) ).toMatchInlineSnapshot(` - import { definePreview } from '@storybook/react-vite/preview'; + import { definePreview } from '@storybook/react-vite'; export const foo = definePreview({ core: { builder: 'webpack5' }, }); diff --git a/code/frameworks/experimental-nextjs-vite/src/index.ts b/code/frameworks/experimental-nextjs-vite/src/index.ts index 32476387c88c..f620bc6df0a0 100644 --- a/code/frameworks/experimental-nextjs-vite/src/index.ts +++ b/code/frameworks/experimental-nextjs-vite/src/index.ts @@ -1,5 +1,10 @@ +import type { ReactPreview } from '@storybook/react'; +import { definePreview as definePreviewBase } from '@storybook/react'; + import type vitePluginStorybookNextJs from 'vite-plugin-storybook-nextjs'; +import * as nextPreview from './preview'; + export * from './types'; export * from './portable-stories'; @@ -8,3 +13,12 @@ export * from './portable-stories'; declare module '@storybook/experimental-nextjs-vite/vite-plugin' { export const storybookNextJsPlugin: typeof vitePluginStorybookNextJs; } + +export function definePreview(preview: NextPreview['input']) { + return definePreviewBase({ + ...preview, + addons: [nextPreview, ...(preview.addons ?? [])], + }) as NextPreview; +} + +interface NextPreview extends ReactPreview {} diff --git a/code/lib/cli-storybook/src/codemod/helpers/config-to-csf-factory.ts b/code/lib/cli-storybook/src/codemod/helpers/config-to-csf-factory.ts index 4231c3a6fd2a..34d62b3c7f2a 100644 --- a/code/lib/cli-storybook/src/codemod/helpers/config-to-csf-factory.ts +++ b/code/lib/cli-storybook/src/codemod/helpers/config-to-csf-factory.ts @@ -27,13 +27,6 @@ export async function configToCsfFactory( } const methodName = configType === 'main' ? 'defineMain' : 'definePreview'; - // TODO: remove this later, it's just a quick workaround for preview imports - // while it is part of @storybook/react and not @storybook/react-vite - frameworkPackage = - configType === 'preview' && frameworkPackage === '@storybook/react-vite' - ? '@storybook/react' - : frameworkPackage; - const programNode = config._ast.program; const hasNamedExports = Object.keys(config._exportDecls).length > 0; diff --git a/scripts/tasks/sandbox-parts.ts b/scripts/tasks/sandbox-parts.ts index e3f66447f2e8..74da77ec18b5 100644 --- a/scripts/tasks/sandbox-parts.ts +++ b/scripts/tasks/sandbox-parts.ts @@ -816,7 +816,12 @@ export const extendPreview: Task['run'] = async ({ template, sandboxDir }) => { const previewConfig = await readConfig({ cwd: sandboxDir, fileName: 'preview' }); if ( - template.expected.framework === '@storybook/react-vite' && + [ + '@storybook/react-vite', + '@storybook/react-webpack5', + '@storybook/nextjs', + '@storybook/experimental-nextjs-vite', + ].includes(template.expected.framework) && !template.skipTasks.includes('vitest-integration') ) { previewConfig.setImport(null, '../src/stories/components'); @@ -838,7 +843,12 @@ export const extendPreview: Task['run'] = async ({ template, sandboxDir }) => { export const runMigrations: Task['run'] = async ({ sandboxDir, template }, { dryRun, debug }) => { if ( - template.expected.framework === '@storybook/react-vite' && + [ + '@storybook/react-vite', + '@storybook/react-webpack5', + '@storybook/nextjs', + '@storybook/experimental-nextjs-vite', + ].includes(template.expected.framework) && !template.skipTasks.includes('vitest-integration') ) { await executeCLIStep(steps.automigrate, { From dc79392a1a6d53533a771cc35282f98c49e6ebd0 Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Fri, 31 Jan 2025 14:25:39 +0100 Subject: [PATCH 18/23] Move csf4 stories --- .../react-vite => renderers/react}/template/stories/csf4.mdx | 0 .../react}/template/stories/csf4.stories.tsx | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename code/{frameworks/react-vite => renderers/react}/template/stories/csf4.mdx (100%) rename code/{frameworks/react-vite => renderers/react}/template/stories/csf4.stories.tsx (81%) diff --git a/code/frameworks/react-vite/template/stories/csf4.mdx b/code/renderers/react/template/stories/csf4.mdx similarity index 100% rename from code/frameworks/react-vite/template/stories/csf4.mdx rename to code/renderers/react/template/stories/csf4.mdx diff --git a/code/frameworks/react-vite/template/stories/csf4.stories.tsx b/code/renderers/react/template/stories/csf4.stories.tsx similarity index 81% rename from code/frameworks/react-vite/template/stories/csf4.stories.tsx rename to code/renderers/react/template/stories/csf4.stories.tsx index 4bb4a60cbf35..49a02100096b 100644 --- a/code/frameworks/react-vite/template/stories/csf4.stories.tsx +++ b/code/renderers/react/template/stories/csf4.stories.tsx @@ -2,7 +2,7 @@ import config from '#.storybook/preview'; const meta = config.meta({ - component: globalThis.Components.Button, + component: (globalThis as any).Components.Button, args: { label: 'Hello world!', }, From 3847a11e6b8baa8d7522267be351b5a2f00d1778 Mon Sep 17 00:00:00 2001 From: Yann Braga Date: Fri, 31 Jan 2025 14:53:21 +0100 Subject: [PATCH 19/23] add csfFactory modification option for sandboxes --- .../cli-storybook/src/sandbox-templates.ts | 12 +++++++++++ scripts/tasks/sandbox-parts.ts | 20 ++----------------- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/code/lib/cli-storybook/src/sandbox-templates.ts b/code/lib/cli-storybook/src/sandbox-templates.ts index 0324635b1ffe..ae52a6e5f1bf 100644 --- a/code/lib/cli-storybook/src/sandbox-templates.ts +++ b/code/lib/cli-storybook/src/sandbox-templates.ts @@ -75,6 +75,7 @@ export type Template = { disableDocs?: boolean; extraDependencies?: string[]; editAddons?: (addons: string[]) => string[]; + useCsfFactory?: boolean; }; /** * Flag to indicate that this template is a secondary template, which is used mainly to test @@ -136,6 +137,7 @@ const baseTemplates = { builder: '@storybook/builder-webpack5', }, modifications: { + useCsfFactory: true, extraDependencies: ['prop-types'], }, }, @@ -149,6 +151,7 @@ const baseTemplates = { builder: '@storybook/builder-webpack5', }, modifications: { + useCsfFactory: true, mainConfig: { features: { experimentalRSC: true, @@ -169,6 +172,7 @@ const baseTemplates = { builder: '@storybook/builder-webpack5', }, modifications: { + useCsfFactory: true, mainConfig: { features: { experimentalRSC: true, @@ -189,6 +193,7 @@ const baseTemplates = { builder: '@storybook/builder-webpack5', }, modifications: { + useCsfFactory: true, mainConfig: { features: { experimentalRSC: true, @@ -209,6 +214,7 @@ const baseTemplates = { builder: '@storybook/builder-webpack5', }, modifications: { + useCsfFactory: true, mainConfig: { features: { experimentalRSC: true, @@ -229,6 +235,7 @@ const baseTemplates = { builder: '@storybook/builder-vite', }, modifications: { + useCsfFactory: true, mainConfig: { framework: '@storybook/experimental-nextjs-vite', features: { @@ -255,6 +262,7 @@ const baseTemplates = { builder: '@storybook/builder-vite', }, modifications: { + useCsfFactory: true, mainConfig: { framework: '@storybook/experimental-nextjs-vite', features: { @@ -298,6 +306,7 @@ const baseTemplates = { builder: '@storybook/builder-vite', }, modifications: { + useCsfFactory: true, extraDependencies: ['prop-types'], mainConfig: { features: { @@ -329,6 +338,7 @@ const baseTemplates = { builder: '@storybook/builder-vite', }, modifications: { + useCsfFactory: true, extraDependencies: ['prop-types'], mainConfig: { features: { @@ -347,6 +357,7 @@ const baseTemplates = { builder: '@storybook/builder-webpack5', }, modifications: { + useCsfFactory: true, extraDependencies: ['prop-types'], }, skipTasks: ['e2e-tests-dev', 'bench', 'vitest-integration'], @@ -385,6 +396,7 @@ const baseTemplates = { builder: '@storybook/builder-webpack5', }, modifications: { + useCsfFactory: true, extraDependencies: ['prop-types'], }, skipTasks: ['e2e-tests-dev', 'bench', 'vitest-integration'], diff --git a/scripts/tasks/sandbox-parts.ts b/scripts/tasks/sandbox-parts.ts index 74da77ec18b5..9c8887fad2b6 100644 --- a/scripts/tasks/sandbox-parts.ts +++ b/scripts/tasks/sandbox-parts.ts @@ -815,15 +815,7 @@ export const extendPreview: Task['run'] = async ({ template, sandboxDir }) => { logger.log('📝 Extending preview.js'); const previewConfig = await readConfig({ cwd: sandboxDir, fileName: 'preview' }); - if ( - [ - '@storybook/react-vite', - '@storybook/react-webpack5', - '@storybook/nextjs', - '@storybook/experimental-nextjs-vite', - ].includes(template.expected.framework) && - !template.skipTasks.includes('vitest-integration') - ) { + if (template.modifications.useCsfFactory) { previewConfig.setImport(null, '../src/stories/components'); previewConfig.setImport({ namespace: 'coreAnnotations' }, '../template-stories/core/preview'); previewConfig.setImport( @@ -842,15 +834,7 @@ export const extendPreview: Task['run'] = async ({ template, sandboxDir }) => { }; export const runMigrations: Task['run'] = async ({ sandboxDir, template }, { dryRun, debug }) => { - if ( - [ - '@storybook/react-vite', - '@storybook/react-webpack5', - '@storybook/nextjs', - '@storybook/experimental-nextjs-vite', - ].includes(template.expected.framework) && - !template.skipTasks.includes('vitest-integration') - ) { + if (template.modifications.useCsfFactory) { await executeCLIStep(steps.automigrate, { cwd: sandboxDir, argument: 'csf-factories', From b5b0420014fc9d53bf45a67deef25ce4c66b730e Mon Sep 17 00:00:00 2001 From: Yann Braga Date: Fri, 31 Jan 2025 14:59:26 +0100 Subject: [PATCH 20/23] fix tests --- .../src/codemod/helpers/config-to-csf-factory.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/code/lib/cli-storybook/src/codemod/helpers/config-to-csf-factory.test.ts b/code/lib/cli-storybook/src/codemod/helpers/config-to-csf-factory.test.ts index 85174836901b..6be5462c51ee 100644 --- a/code/lib/cli-storybook/src/codemod/helpers/config-to-csf-factory.test.ts +++ b/code/lib/cli-storybook/src/codemod/helpers/config-to-csf-factory.test.ts @@ -156,7 +156,7 @@ describe('preview specific functionality', () => { }; `) ).resolves.toMatchInlineSnapshot(` - import { definePreview } from '@storybook/react'; + import { definePreview } from '@storybook/react-vite'; export default definePreview({ tags: ['test'], @@ -167,7 +167,7 @@ describe('preview specific functionality', () => { it('should remove legacy preview type imports', async () => { await expect( transform(dedent` - import type { Preview } from '@storybook/react' + import type { Preview } from '@storybook/react-vite' const preview: Preview = { tags: [] @@ -175,7 +175,7 @@ describe('preview specific functionality', () => { export default preview; `) ).resolves.toMatchInlineSnapshot(` - import { definePreview } from '@storybook/react'; + import { definePreview } from '@storybook/react-vite'; export default definePreview({ tags: [], From aa305bb946b048b29942c50ffe5a2a4758c4d8c4 Mon Sep 17 00:00:00 2001 From: Yann Braga Date: Fri, 31 Jan 2025 16:09:57 +0100 Subject: [PATCH 21/23] fix issues --- code/frameworks/nextjs/package.json | 2 +- scripts/tasks/sandbox-parts.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/code/frameworks/nextjs/package.json b/code/frameworks/nextjs/package.json index 0a8750f46c0d..1c827f099204 100644 --- a/code/frameworks/nextjs/package.json +++ b/code/frameworks/nextjs/package.json @@ -87,7 +87,7 @@ "import": "./dist/export-mocks/router/index.mjs", "require": "./dist/export-mocks/router/index.js" }, - "./main": { + "./node": { "types": "./dist/node/index.d.ts", "node": "./dist/node/index.js", "import": "./dist/node/index.mjs", diff --git a/scripts/tasks/sandbox-parts.ts b/scripts/tasks/sandbox-parts.ts index 9c8887fad2b6..07db3deff32c 100644 --- a/scripts/tasks/sandbox-parts.ts +++ b/scripts/tasks/sandbox-parts.ts @@ -815,7 +815,7 @@ export const extendPreview: Task['run'] = async ({ template, sandboxDir }) => { logger.log('📝 Extending preview.js'); const previewConfig = await readConfig({ cwd: sandboxDir, fileName: 'preview' }); - if (template.modifications.useCsfFactory) { + if (template.modifications?.useCsfFactory) { previewConfig.setImport(null, '../src/stories/components'); previewConfig.setImport({ namespace: 'coreAnnotations' }, '../template-stories/core/preview'); previewConfig.setImport( @@ -834,7 +834,7 @@ export const extendPreview: Task['run'] = async ({ template, sandboxDir }) => { }; export const runMigrations: Task['run'] = async ({ sandboxDir, template }, { dryRun, debug }) => { - if (template.modifications.useCsfFactory) { + if (template.modifications?.useCsfFactory) { await executeCLIStep(steps.automigrate, { cwd: sandboxDir, argument: 'csf-factories', From ce00ea5f9b852113149b4c8e0b8f07a35a0ce783 Mon Sep 17 00:00:00 2001 From: Yann Braga Date: Fri, 31 Jan 2025 16:22:33 +0100 Subject: [PATCH 22/23] fix sandboxes --- code/lib/cli-storybook/src/sandbox-templates.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/code/lib/cli-storybook/src/sandbox-templates.ts b/code/lib/cli-storybook/src/sandbox-templates.ts index ae52a6e5f1bf..d066b4b34438 100644 --- a/code/lib/cli-storybook/src/sandbox-templates.ts +++ b/code/lib/cli-storybook/src/sandbox-templates.ts @@ -107,6 +107,7 @@ const baseTemplates = { skipTasks: ['e2e-tests-dev', 'bench', 'vitest-integration'], modifications: { + useCsfFactory: true, extraDependencies: ['prop-types'], mainConfig: (config) => { const stories = config.getFieldValue>(['stories']); @@ -288,6 +289,7 @@ const baseTemplates = { builder: '@storybook/builder-vite', }, modifications: { + useCsfFactory: true, extraDependencies: ['prop-types'], mainConfig: { features: { From 0f00a1861181cee62e7764dff5197d3a3bacb66e Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Tue, 4 Feb 2025 12:57:19 +0100 Subject: [PATCH 23/23] Fix react-webpack/17-ts --- code/lib/cli-storybook/src/sandbox-templates.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/code/lib/cli-storybook/src/sandbox-templates.ts b/code/lib/cli-storybook/src/sandbox-templates.ts index d066b4b34438..1b5ff4b4279e 100644 --- a/code/lib/cli-storybook/src/sandbox-templates.ts +++ b/code/lib/cli-storybook/src/sandbox-templates.ts @@ -374,6 +374,7 @@ const baseTemplates = { builder: '@storybook/builder-webpack5', }, modifications: { + useCsfFactory: true, extraDependencies: ['prop-types'], }, skipTasks: ['e2e-tests-dev', 'bench', 'vitest-integration'],