diff --git a/code/frameworks/react-vite/src/preset.test.ts b/code/frameworks/react-vite/src/preset.test.ts new file mode 100644 index 000000000000..216d1db83147 --- /dev/null +++ b/code/frameworks/react-vite/src/preset.test.ts @@ -0,0 +1,55 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +vi.mock(import('./plugins/react-docgen.ts'), { spy: true }); + +import * as reactDocgenModule from './plugins/react-docgen.ts'; +import { viteFinal } from './preset.ts'; + +type ViteFinalOptions = Parameters[1]; + +const createTypescriptPresetOptions = ( + reactDocgenOptions: Record +): ViteFinalOptions => + ({ + presets: { + apply: async (name: string) => + name === 'typescript' ? { reactDocgen: 'react-docgen', reactDocgenOptions } : undefined, + }, + }) as ViteFinalOptions; + +describe('react-vite preset', () => { + beforeEach(() => { + vi.mocked(reactDocgenModule.reactDocgen) + .mockReset() + .mockResolvedValue({ name: 'storybook:react-docgen-plugin' }); + }); + + it('passes reactDocgenOptions to the react-docgen plugin', async () => { + const existingPlugin = { name: 'existing-plugin' }; + const reactDocgenOptions = { + exclude: [/node_modules\/.*/, /packages\/.*/], + }; + + const config = await viteFinal( + { plugins: [existingPlugin] }, + createTypescriptPresetOptions(reactDocgenOptions) + ); + + expect(reactDocgenModule.reactDocgen).toHaveBeenCalledWith({ + include: /\.(mjs|tsx?|jsx?)$/, + ...reactDocgenOptions, + }); + expect(config.plugins).toEqual([{ name: 'storybook:react-docgen-plugin' }, existingPlugin]); + }); + + it('allows reactDocgenOptions to override the default include pattern', async () => { + const reactDocgenOptions = { + include: /\.jsx$/, + exclude: [/packages\/.*/], + }; + + await viteFinal({}, createTypescriptPresetOptions(reactDocgenOptions)); + + expect(reactDocgenModule.reactDocgen).toHaveBeenCalledWith(reactDocgenOptions); + }); +}); diff --git a/code/frameworks/react-vite/src/preset.ts b/code/frameworks/react-vite/src/preset.ts index 1f752d510376..5c71a5e79835 100644 --- a/code/frameworks/react-vite/src/preset.ts +++ b/code/frameworks/react-vite/src/preset.ts @@ -7,14 +7,20 @@ export const core: PresetProperty<'core'> = { renderer: import.meta.resolve('@storybook/react/preset'), }; +type TypescriptPresetOptions = Pick< + NonNullable, + 'reactDocgen' | 'reactDocgenOptions' | 'reactDocgenTypescriptOptions' +>; + export const viteFinal: NonNullable = async (config, { presets }) => { const plugins = [...(config?.plugins ?? [])]; // Add docgen plugin - const { reactDocgen: reactDocgenOption, reactDocgenTypescriptOptions } = await presets.apply( - 'typescript', - {} - ); + const { + reactDocgen: reactDocgenOption, + reactDocgenOptions, + reactDocgenTypescriptOptions, + } = await presets.apply('typescript', {}); let typescriptPresent; try { @@ -42,6 +48,7 @@ export const viteFinal: NonNullable = async (confi // If react-docgen is specified, use it for everything, otherwise only use it for non-typescript files await reactDocgen({ include: reactDocgenOption === 'react-docgen' ? /\.(mjs|tsx?|jsx?)$/ : /\.(mjs|jsx?)$/, + ...reactDocgenOptions, }) ); } diff --git a/code/frameworks/react-vite/src/types.ts b/code/frameworks/react-vite/src/types.ts index 938139710843..e402e8dc5316 100644 --- a/code/frameworks/react-vite/src/types.ts +++ b/code/frameworks/react-vite/src/types.ts @@ -7,6 +7,7 @@ import type { import type { BuilderOptions, StorybookConfigVite } from '@storybook/builder-vite'; import type docgenTypescript from '@joshwooding/vite-plugin-react-docgen-typescript'; +import type { reactDocgen } from './plugins/react-docgen.ts'; type FrameworkName = CompatibleString<'@storybook/react-vite'>; type BuilderName = CompatibleString<'@storybook/builder-vite'>; @@ -26,6 +27,8 @@ export type FrameworkOptions = { legacyRootApi?: boolean; }; +export type ReactDocgenOptions = Parameters[0]; + type StorybookConfigFramework = { framework: | FrameworkName @@ -58,6 +61,8 @@ type TypescriptOptions = TypescriptOptionsBase & { * @default `'react-docgen'` */ reactDocgen: 'react-docgen-typescript' | 'react-docgen' | false; + /** Configures the built-in Vite `react-docgen` plugin */ + reactDocgenOptions: ReactDocgenOptions; /** Configures `@joshwooding/vite-plugin-react-docgen-typescript` */ reactDocgenTypescriptOptions: Parameters[0]; }; diff --git a/docs/_snippets/main-config-typescript-react-docgen-options.md b/docs/_snippets/main-config-typescript-react-docgen-options.md new file mode 100644 index 000000000000..0366465e9470 --- /dev/null +++ b/docs/_snippets/main-config-typescript-react-docgen-options.md @@ -0,0 +1,33 @@ +```ts filename=".storybook/main.ts" renderer="react" language="ts" tabTitle="CSF 3" +// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc. +import type { StorybookConfig } from '@storybook/your-framework'; + +const config: StorybookConfig = { + framework: '@storybook/your-framework', + stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'], + typescript: { + reactDocgen: 'react-docgen', + reactDocgenOptions: { + exclude: [/node_modules\/.*/, /packages\/.*/], + }, + }, +}; + +export default config; +``` + +```ts filename=".storybook/main.ts" renderer="react" language="ts" tabTitle="CSF Next 🧪" +// Replace your-framework with the framework you are using (e.g., react-vite, nextjs, nextjs-vite) +import { defineMain } from '@storybook/your-framework/node'; + +export default defineMain({ + framework: '@storybook/your-framework', + stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'], + typescript: { + reactDocgen: 'react-docgen', + reactDocgenOptions: { + exclude: [/node_modules\/.*/, /packages\/.*/], + }, + }, +}); +``` diff --git a/docs/api/main-config/main-config-typescript.mdx b/docs/api/main-config/main-config-typescript.mdx index c23f8e693a77..89d4a6d4f795 100644 --- a/docs/api/main-config/main-config-typescript.mdx +++ b/docs/api/main-config/main-config-typescript.mdx @@ -15,6 +15,10 @@ Type: check?: boolean; checkOptions?: CheckOptions; reactDocgen?: 'react-docgen' | 'react-docgen-typescript' | false; + reactDocgenOptions?: { + include?: string | RegExp | (string | RegExp)[]; + exclude?: string | RegExp | (string | RegExp)[]; + }; reactDocgenTypescriptOptions?: ReactDocgenTypescriptOptions; skipCompiler?: boolean; } @@ -75,6 +79,20 @@ Options to pass to `fork-ts-checker-webpack-plugin`, if [enabled](#check). See [ {/* prettier-ignore-end */} + ## `reactDocgenOptions` + + Type: `{ include?: string | RegExp | (string | RegExp)[]; exclude?: string | RegExp | (string | RegExp)[] }` + + Available for Vite-based React projects when `reactDocgen` is enabled. + + Configures the options passed to Storybook's built-in `react-docgen` Vite plugin, which can be useful in monorepos where you need to narrow the files that Storybook analyzes. + + {/* prettier-ignore-start */} + + + + {/* prettier-ignore-end */} + ## `reactDocgenTypescriptOptions` Type: `ReactDocgenTypescriptOptions` diff --git a/docs/configure/integration/typescript.mdx b/docs/configure/integration/typescript.mdx index ae0a3abf9f4d..59cfadbab450 100644 --- a/docs/configure/integration/typescript.mdx +++ b/docs/configure/integration/typescript.mdx @@ -43,6 +43,7 @@ See the [main configuration API reference](../../api/main-config/main-config.mdx | `check` | Available for Webpack-based projects.
Enables type checking within Storybook
`typescript: { check: true },` | | `checkOptions` | Requires the `check` option to be enabled.
Configures the [`fork-ts-checker-webpack-plugin`](https://github.com/TypeStrong/fork-ts-checker-webpack-plugin) plugin
`typescript: { checkOptions: {},},` | | `reactDocgen` | Configures the TypeScript parser used by Storybook.
Available options: `react-docgen` (default), `react-docgen-typescript`,` false`
`typescript: { reactDocgen: 'react-docgen'},` | + | `reactDocgenOptions` | Available for Vite-based React projects when `reactDocgen` is enabled.
Configures Storybook's built-in `react-docgen` Vite plugin, including custom `include` and `exclude` filters.
`typescript: { reactDocgen: 'react-docgen', reactDocgenOptions: {},},` | | `reactDocgenTypescriptOptions` | Requires the `reactDocgen`option to be `react-docgen-typescript`.
Configures the `react-docgen-typescript-plugin` plugin per builder
`typescript: { reactDocgen: 'react-docgen-typescript', reactDocgenTypescriptOptions: {},},` | | `skipCompiler` | Disables parsing Typescript files through the compiler
`typescript: { skipCompiler:false,},` |