From 4fdafc1c1744a8daf913e838d702cbfb70225d79 Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Wed, 18 Dec 2024 22:39:24 +0100 Subject: [PATCH 01/16] rename packageDir --- code/addons/test/src/vitest-plugin/index.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/code/addons/test/src/vitest-plugin/index.ts b/code/addons/test/src/vitest-plugin/index.ts index e73549ded6d9..a2dca99999be 100644 --- a/code/addons/test/src/vitest-plugin/index.ts +++ b/code/addons/test/src/vitest-plugin/index.ts @@ -64,7 +64,7 @@ const getStoryGlobsAndFiles = async ( }; }; -const packageDir = dirname(require.resolve('@storybook/experimental-addon-test/package.json')); +const PACKAGE_DIR = dirname(require.resolve('@storybook/experimental-addon-test/package.json')); export const storybookTest = async (options?: UserOptions): Promise => { const finalOptions = { @@ -153,7 +153,7 @@ export const storybookTest = async (options?: UserOptions): Promise => { const baseConfig: Omit = { test: { setupFiles: [ - join(packageDir, 'dist/vitest-plugin/setup-file.mjs'), + join(PACKAGE_DIR, 'dist/vitest-plugin/setup-file.mjs'), // if the existing setupFiles is a string, we have to include it otherwise we're overwriting it typeof inputConfig_ONLY_MUTATE_WHEN_STRICTLY_NEEDED_OR_YOU_WILL_BE_FIRED.test ?.setupFiles === 'string' && @@ -162,7 +162,7 @@ export const storybookTest = async (options?: UserOptions): Promise => { ...(finalOptions.storybookScript ? { - globalSetup: [join(packageDir, 'dist/vitest-plugin/global-setup.mjs')], + globalSetup: [join(PACKAGE_DIR, 'dist/vitest-plugin/global-setup.mjs')], } : {}), From 5110c71ce64dfee466d089b536b51dd0291df082 Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Wed, 18 Dec 2024 22:40:02 +0100 Subject: [PATCH 02/16] don't enforce pre on the vitest plugin --- code/addons/test/src/vitest-plugin/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/code/addons/test/src/vitest-plugin/index.ts b/code/addons/test/src/vitest-plugin/index.ts index a2dca99999be..9bb3baf66ed2 100644 --- a/code/addons/test/src/vitest-plugin/index.ts +++ b/code/addons/test/src/vitest-plugin/index.ts @@ -116,7 +116,6 @@ export const storybookTest = async (options?: UserOptions): Promise => { return { name: 'vite-plugin-storybook-test', - enforce: 'pre', async transformIndexHtml(html) { const [headHtmlSnippet, bodyHtmlSnippet] = await Promise.all([ presets.apply('previewHead'), From a079a2871ef655be58e01b0e6b5cab09f40a6c97 Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Wed, 18 Dec 2024 22:40:58 +0100 Subject: [PATCH 03/16] selectively optimize modules to not let vite optimize everything (it was trying to optimize the vitest plugin itself) --- code/addons/test/src/vitest-plugin/index.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/code/addons/test/src/vitest-plugin/index.ts b/code/addons/test/src/vitest-plugin/index.ts index 9bb3baf66ed2..fe86d6945fe4 100644 --- a/code/addons/test/src/vitest-plugin/index.ts +++ b/code/addons/test/src/vitest-plugin/index.ts @@ -243,7 +243,9 @@ export const storybookTest = async (options?: UserOptions): Promise => { optimizeDeps: { include: [ - '@storybook/experimental-addon-test/**', + '@storybook/experimental-addon-test/internal/setup-file', + '@storybook/experimental-addon-test/internal/global-setup', + '@storybook/experimental-addon-test/internal/test-utils', ...(frameworkName?.includes('react') || frameworkName?.includes('nextjs') ? ['react-dom/test-utils'] : []), From 8bc928b6918c441bdab28c94485d3b9663fd8dfe Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Wed, 18 Dec 2024 22:41:09 +0100 Subject: [PATCH 04/16] add plugins from viteFinal --- code/addons/test/src/vitest-plugin/index.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/code/addons/test/src/vitest-plugin/index.ts b/code/addons/test/src/vitest-plugin/index.ts index fe86d6945fe4..ab10f6d67c82 100644 --- a/code/addons/test/src/vitest-plugin/index.ts +++ b/code/addons/test/src/vitest-plugin/index.ts @@ -66,7 +66,7 @@ const getStoryGlobsAndFiles = async ( const PACKAGE_DIR = dirname(require.resolve('@storybook/experimental-addon-test/package.json')); -export const storybookTest = async (options?: UserOptions): Promise => { +export const storybookTest = async (options?: UserOptions): Promise => { const finalOptions = { ...defaultOptions, ...options, @@ -109,12 +109,14 @@ export const storybookTest = async (options?: UserOptions): Promise => { getStoryGlobsAndFiles(presets, directories), presets.apply('framework', undefined), presets.apply('env', {}), - presets.apply('viteFinal', {}), + presets.apply<{ plugins?: Plugin[] }>('viteFinal', {}), presets.apply('staticDirs', []), extractTagsFromPreview(finalOptions.configDir), ]); - return { + const plugins = [...viteConfigFromStorybook.plugins].filter(Boolean) as Plugin[]; + + plugins.push({ name: 'vite-plugin-storybook-test', async transformIndexHtml(html) { const [headHtmlSnippet, bodyHtmlSnippet] = await Promise.all([ @@ -318,7 +320,9 @@ export const storybookTest = async (options?: UserOptions): Promise => { }); } }, - }; + }); + + return plugins; }; export default storybookTest; From 89036fedc9f2758a455738ed9e88f492e30e80d5 Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Wed, 18 Dec 2024 22:49:52 +0100 Subject: [PATCH 05/16] don't exclude stories.svelte files from svelte sandboxes --- scripts/tasks/sandbox-parts.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/tasks/sandbox-parts.ts b/scripts/tasks/sandbox-parts.ts index 98f05c95f9d3..bdd27181f947 100644 --- a/scripts/tasks/sandbox-parts.ts +++ b/scripts/tasks/sandbox-parts.ts @@ -504,7 +504,6 @@ export async function setupVitest(details: TemplateDetails, options: PassedOptio ...defaultExclude, // TODO: investigate TypeError: Cannot read properties of null (reading 'useContext') "**/*argtypes*", - ${template.expected.renderer === '@storybook/svelte' ? '"**/*.stories.svelte",' : ''} ], /** * TODO: Either fix or acknowledge limitation of: From 2259584940a601b3fa27acdda48f599f7ab365e7 Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Thu, 19 Dec 2024 09:14:51 +0100 Subject: [PATCH 06/16] support undefined plugins array --- code/addons/test/src/vitest-plugin/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/addons/test/src/vitest-plugin/index.ts b/code/addons/test/src/vitest-plugin/index.ts index ab10f6d67c82..e81e49ed32a3 100644 --- a/code/addons/test/src/vitest-plugin/index.ts +++ b/code/addons/test/src/vitest-plugin/index.ts @@ -114,7 +114,7 @@ export const storybookTest = async (options?: UserOptions): Promise => extractTagsFromPreview(finalOptions.configDir), ]); - const plugins = [...viteConfigFromStorybook.plugins].filter(Boolean) as Plugin[]; + const plugins = [...(viteConfigFromStorybook.plugins ?? [])]; plugins.push({ name: 'vite-plugin-storybook-test', From 4bce136e053236cf41634b5470a6c3bcb807915e Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Thu, 19 Dec 2024 15:31:02 +0100 Subject: [PATCH 07/16] remove vite plugins unnecessary for testing --- code/addons/test/src/vitest-plugin/index.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/code/addons/test/src/vitest-plugin/index.ts b/code/addons/test/src/vitest-plugin/index.ts index e81e49ed32a3..74a223199751 100644 --- a/code/addons/test/src/vitest-plugin/index.ts +++ b/code/addons/test/src/vitest-plugin/index.ts @@ -24,7 +24,10 @@ import picocolors from 'picocolors'; import sirv from 'sirv'; import { convertPathToPattern } from 'tinyglobby'; import { dedent } from 'ts-dedent'; +import type { PluginOption } from 'vite'; +// ! Relative import to prebundle it without needing to depend on the Vite builder +import { withoutVitePlugins } from '../../../../builders/builder-vite/src/utils/without-vite-plugins'; import type { InternalOptions, UserOptions } from './types'; const WORKING_DIR = process.cwd(); @@ -114,7 +117,18 @@ export const storybookTest = async (options?: UserOptions): Promise => extractTagsFromPreview(finalOptions.configDir), ]); - const plugins = [...(viteConfigFromStorybook.plugins ?? [])]; + // filter out plugins that we know are unnecesary for tests, eg. docgen plugins + const plugins = (await withoutVitePlugins( + (viteConfigFromStorybook.plugins as unknown as PluginOption[]) ?? [], + [ + 'storybook:package-deduplication', + 'storybook:mdx-plugin', + 'storybook:react-docgen-plugin', + 'storybook:svelte-docgen-plugin', + 'storybook:vue-component-meta-plugin', + 'storybook:vue-docgen-plugin', + ] + )) as unknown as Plugin[]; plugins.push({ name: 'vite-plugin-storybook-test', From 20f525437be94c7fb4e0b5118361e508b06c597e Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Thu, 19 Dec 2024 20:44:09 +0100 Subject: [PATCH 08/16] also ignore @joshwooding/vite-plugin-react-docgen-typescript --- code/addons/test/src/vitest-plugin/index.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/code/addons/test/src/vitest-plugin/index.ts b/code/addons/test/src/vitest-plugin/index.ts index 74a223199751..a27ac4270905 100644 --- a/code/addons/test/src/vitest-plugin/index.ts +++ b/code/addons/test/src/vitest-plugin/index.ts @@ -121,9 +121,10 @@ export const storybookTest = async (options?: UserOptions): Promise => const plugins = (await withoutVitePlugins( (viteConfigFromStorybook.plugins as unknown as PluginOption[]) ?? [], [ - 'storybook:package-deduplication', - 'storybook:mdx-plugin', + 'storybook:package-deduplication', // addon-docs + 'storybook:mdx-plugin', // addon-docs 'storybook:react-docgen-plugin', + 'vite:react-docgen-typescript', // aka @joshwooding/vite-plugin-react-docgen-typescript 'storybook:svelte-docgen-plugin', 'storybook:vue-component-meta-plugin', 'storybook:vue-docgen-plugin', From c38eed3fa36da925594627b1d0062cdea907c929 Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Thu, 19 Dec 2024 20:44:52 +0100 Subject: [PATCH 09/16] don't add plugins during postinstall --- code/addons/test/src/postinstall.ts | 49 +++-------------------------- 1 file changed, 4 insertions(+), 45 deletions(-) diff --git a/code/addons/test/src/postinstall.ts b/code/addons/test/src/postinstall.ts index bb22c7517127..47662b7e41bc 100644 --- a/code/addons/test/src/postinstall.ts +++ b/code/addons/test/src/postinstall.ts @@ -233,8 +233,6 @@ export default async function postInstall(options: PostinstallOptions) { } } - const vitestInfo = getVitestPluginInfo(info.frameworkPackageName); - if (info.frameworkPackageName === '@storybook/nextjs') { printInfo( '🍿 Just so you know...', @@ -414,7 +412,7 @@ export default async function postInstall(options: PostinstallOptions) { browserWorkspaceFile, dedent` import { defineWorkspace } from 'vitest/config'; - import { storybookTest } from '@storybook/experimental-addon-test/vitest-plugin';${vitestInfo.frameworkPluginImport} + import { storybookTest } from '@storybook/experimental-addon-test/vitest-plugin'; // More info at: https://storybook.js.org/docs/writing-tests/vitest-plugin export default defineWorkspace([ @@ -424,7 +422,7 @@ export default async function postInstall(options: PostinstallOptions) { plugins: [ // The plugin will run tests for the stories defined in your Storybook config // See options at: https://storybook.js.org/docs/writing-tests/vitest-plugin#storybooktest - storybookTest({ configDir: '${options.configDir}' }),${vitestInfo.frameworkPluginDocs + vitestInfo.frameworkPluginCall} + storybookTest({ configDir: '${options.configDir}' }), ], test: { name: 'storybook', @@ -454,14 +452,14 @@ export default async function postInstall(options: PostinstallOptions) { newVitestConfigFile, dedent` import { defineConfig } from 'vitest/config'; - import { storybookTest } from '@storybook/experimental-addon-test/vitest-plugin';${vitestInfo.frameworkPluginImport} + import { storybookTest } from '@storybook/experimental-addon-test/vitest-plugin'; // More info at: https://storybook.js.org/docs/writing-tests/vitest-plugin export default defineConfig({ plugins: [ // The plugin will run tests for the stories defined in your Storybook config // See options at: https://storybook.js.org/docs/writing-tests/vitest-plugin#storybooktest - storybookTest({ configDir: '${options.configDir}' }),${vitestInfo.frameworkPluginDocs + vitestInfo.frameworkPluginCall} + storybookTest({ configDir: '${options.configDir}' }), ], test: { name: 'storybook', @@ -496,45 +494,6 @@ export default async function postInstall(options: PostinstallOptions) { logger.line(1); } -const getVitestPluginInfo = (framework: string) => { - let frameworkPluginImport = ''; - let frameworkPluginCall = ''; - let frameworkPluginDocs = ''; - - if (framework === '@storybook/nextjs' || framework === '@storybook/experimental-nextjs-vite') { - frameworkPluginImport = - "import { storybookNextJsPlugin } from '@storybook/experimental-nextjs-vite/vite-plugin';"; - frameworkPluginDocs = - '// More info at: https://github.com/storybookjs/vite-plugin-storybook-nextjs'; - frameworkPluginCall = 'storybookNextJsPlugin()'; - } - - if (framework === '@storybook/sveltekit') { - frameworkPluginImport = - "import { storybookSveltekitPlugin } from '@storybook/sveltekit/vite-plugin';"; - frameworkPluginCall = 'storybookSveltekitPlugin()'; - } - - if (framework === '@storybook/vue3-vite') { - frameworkPluginImport = - "import { storybookVuePlugin } from '@storybook/vue3-vite/vite-plugin';"; - frameworkPluginCall = 'storybookVuePlugin()'; - } - - if (framework === '@storybook/react-native-web-vite') { - frameworkPluginImport = - "import { storybookReactNativeWeb } from '@storybook/react-native-web-vite/vite-plugin';"; - frameworkPluginCall = 'storybookReactNativeWeb()'; - } - - // spaces for file indentation - frameworkPluginImport = `\n${frameworkPluginImport}`; - frameworkPluginDocs = frameworkPluginDocs ? `\n ${frameworkPluginDocs}` : ''; - frameworkPluginCall = frameworkPluginCall ? `\n ${frameworkPluginCall},` : ''; - - return { frameworkPluginImport, frameworkPluginCall, frameworkPluginDocs }; -}; - async function getStorybookInfo({ configDir, packageManager: pkgMgr }: PostinstallOptions) { const packageManager = JsPackageManagerFactory.getPackageManager({ force: pkgMgr }); const packageJson = await packageManager.retrievePackageJson(); From 470c04cc6fde97dbb0152dc15ada47f363c9a126 Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Thu, 19 Dec 2024 20:48:16 +0100 Subject: [PATCH 10/16] don't tell users to add framework plugins in docs --- docs/_snippets/vitest-plugin-vitest-config.md | 6 ------ docs/_snippets/vitest-plugin-vitest-workspace.md | 8 -------- 2 files changed, 14 deletions(-) diff --git a/docs/_snippets/vitest-plugin-vitest-config.md b/docs/_snippets/vitest-plugin-vitest-config.md index b3f271a6e636..867d362b8626 100644 --- a/docs/_snippets/vitest-plugin-vitest-config.md +++ b/docs/_snippets/vitest-plugin-vitest-config.md @@ -1,8 +1,6 @@ ```ts filename="vitest.config.ts" renderer="react" import { defineConfig, mergeConfig } from 'vitest/config'; import { storybookTest } from '@storybook/experimental-addon-test/vitest-plugin'; -// 👇 If you're using Next.js, apply this framework plugin as well -// import { storybookNextJsPlugin } from '@storybook/experimental-nextjs-vite/vite-plugin'; import viteConfig from './vite.config'; @@ -15,7 +13,6 @@ export default mergeConfig( // The --ci flag will skip prompts and not open a browser storybookScript: 'yarn storybook --ci', }), - // storybookNextJsPlugin(), ], test: { // Enable browser mode @@ -68,8 +65,6 @@ export default mergeConfig( ```ts filename="vitest.config.ts" renderer="svelte" import { defineConfig, mergeConfig } from 'vitest/config'; import { storybookTest } from '@storybook/experimental-addon-test/vitest-plugin'; -// 👇 If you're using Sveltekit, apply this framework plugin as well -// import { storybookSveltekitPlugin } from '@storybook/sveltekit/vite-plugin'; import viteConfig from './vite.config'; @@ -82,7 +77,6 @@ export default mergeConfig( // The --ci flag will skip prompts and not open a browser storybookScript: 'yarn storybook --ci', }), - // storybookSveltekitPlugin(), ], test: { // Enable browser mode diff --git a/docs/_snippets/vitest-plugin-vitest-workspace.md b/docs/_snippets/vitest-plugin-vitest-workspace.md index 46b5a3e22edd..0d543005dbcf 100644 --- a/docs/_snippets/vitest-plugin-vitest-workspace.md +++ b/docs/_snippets/vitest-plugin-vitest-workspace.md @@ -1,8 +1,6 @@ ```ts filename="vitest.workspace.ts" renderer="react" import { defineWorkspace } from 'vitest/config'; import { storybookTest } from '@storybook/experimental-addon-test/vitest-plugin'; -// 👇 If you're using Next.js, apply this framework plugin as well -// import { storybookNextJsPlugin } from '@storybook/experimental-nextjs-vite/vite-plugin'; export default defineWorkspace([ // This is the path to your existing Vitest config file @@ -16,7 +14,6 @@ export default defineWorkspace([ // The --ci flag will skip prompts and not open a browser storybookScript: 'yarn storybook --ci', }), - // storybookNextJsPlugin(), ], test: { name: 'storybook', @@ -37,7 +34,6 @@ export default defineWorkspace([ ```ts filename="vitest.config.ts" renderer="vue" import { defineConfig, mergeConfig } from 'vitest/config'; import { storybookTest } from '@storybook/experimental-addon-test/vitest-plugin'; -import { storybookVuePlugin } from '@storybook/vue3-vite/vite-plugin'; import viteConfig from './vite.config'; @@ -53,7 +49,6 @@ export default defineWorkspace([ // The --ci flag will skip prompts and not open a browser storybookScript: 'yarn storybook --ci', }), - storybookVuePlugin(), ], test: { name: 'storybook', @@ -74,8 +69,6 @@ export default defineWorkspace([ ```ts filename="vitest.config.ts" renderer="svelte" import { defineConfig, mergeConfig } from 'vitest/config'; import { storybookTest } from '@storybook/experimental-addon-test/vitest-plugin'; -// 👇 If you're using Sveltekit, apply this framework plugin as well -// import { storybookSveltekitPlugin } from '@storybook/sveltekit/vite-plugin'; import viteConfig from './vite.config'; @@ -91,7 +84,6 @@ export default defineWorkspace([ // The --ci flag will skip prompts and not open a browser storybookScript: 'yarn storybook --ci', }), - // storybookSveltekitPlugin(), ], test: { name: 'storybook', From 21873f1b5bb0220bdb2337fa4fc53a6eb8c84abc Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Thu, 19 Dec 2024 20:56:12 +0100 Subject: [PATCH 11/16] add migration note about removing duplicate vite configurations --- MIGRATION.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/MIGRATION.md b/MIGRATION.md index 28b4bc5d6ddc..da9f0e8f588c 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -4,7 +4,8 @@ - [Added source code panel to docs](#added-source-code-panel-to-docs) - [Addon-a11y: Component test integration](#addon-a11y-component-test-integration) - [Addon-a11y: Deprecated `parameters.a11y.manual`](#addon-a11y-deprecated-parametersa11ymanual) - - [Indexing behavior of @storybook/experimental-addon-test is changed](#indexing-behavior-of-storybookexperimental-addon-test-is-changed) + - [Addon-test: You should no longer copy the content of `viteFinal` to your configuration](#addon-test-you-should-no-longer-copy-the-content-of-vitefinal-to-your-configuration) + - [Addon-test: Indexing behavior of @storybook/experimental-addon-test is changed](#addon-test-indexing-behavior-of-storybookexperimental-addon-test-is-changed) - [From version 8.2.x to 8.3.x](#from-version-82x-to-83x) - [Removed `experimental_SIDEBAR_BOTTOM` and deprecated `experimental_SIDEBAR_TOP` addon types](#removed-experimental_sidebar_bottom-and-deprecated-experimental_sidebar_top-addon-types) - [New parameters format for addon backgrounds](#new-parameters-format-for-addon-backgrounds) @@ -468,7 +469,13 @@ beforeAll(annotations.beforeAll); We have deprecated `parameters.a11y.manual` in 8.5. Please use `globals.a11y.manual` instead. -### Indexing behavior of @storybook/experimental-addon-test is changed +### Addon-test: You should no longer copy the content of `viteFinal` to your configuration + +In version 8.4 of `@storybook/experimental-addon-test`, it was required to copy any custom configuration you had in `viteFinal` in `main.ts`, to the Vitest Storybook project. This is no longer necessary, as the Storybook Test plugin will automatically include your `viteFinal` configuration. You should remove any configurations you might already have in `viteFinal` to remove duplicates. + +This is especially the case for any plugins you might have, as they could now end up being loaded twice, which is likely to cause errors when running tests. In 8.4 we documented and automatically added some Vite plugins from Storybook frameworks like `@storybook/experimental-nextjs-vite` and `@storybook/sveltekit` - **these needs to be removed as well**. + +### Addon-test: Indexing behavior of @storybook/experimental-addon-test is changed The Storybook test addon used to index stories based on the `test.include` field in the Vitest config file. This caused indexing issues with Storybook, because stories could have been indexed by Storybook and not Vitest, and vice versa. Starting in Storybook 8.5.0-alpha.18, we changed the indexing behavior so that it always uses the globs defined in the `stories` field in `.storybook/main.js` for a more consistent experience. It is now discouraged to use `test.include`, please remove it. From 4512a5aceaeda653875762d6126235b9b528f6fa Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Thu, 19 Dec 2024 21:07:43 +0100 Subject: [PATCH 12/16] remove docs on manual Framework plugins --- docs/writing-tests/test-addon.mdx | 85 +------------------------------ 1 file changed, 1 insertion(+), 84 deletions(-) diff --git a/docs/writing-tests/test-addon.mdx b/docs/writing-tests/test-addon.mdx index 351aa557428a..a3ccc005ae58 100644 --- a/docs/writing-tests/test-addon.mdx +++ b/docs/writing-tests/test-addon.mdx @@ -68,90 +68,7 @@ For some project setups, the `add` command may be unable to automate the addon a 1. Configure Vitest to use [browser mode](https://vitest.dev/guide/browser/). 1. Install the addon, `@storybook/experimental-addon-test`, in your project and [register it in your Storybook configuration](http://storybook.js.org/docs/addons/install-addons#manual-installation). 1. Create a test setup file, `.storybook/vitest.setup.ts`. You can use the [example setup file](#example-vitest-setup) as a guide. -1. Adjust your Vitest configuration to include the plugin(s) and reference the setup file. You can use the [example configuration files](#example-configuration-files) as a guide. - -#### Framework plugins - -Some Storybook frameworks require additional setup to enable the framework's features to work with Vitest. Each of those frameworks exports a Vite plugin that you can use to configure your project correctly: - - - If you're using Next.js, first install the `@storybook/experimental-nextjs-vite` package: - - {/* prettier-ignore-start */} - - - - {/* prettier-ignore-end */} - - Then apply the plugin from `@storybook/experimental-nextjs-vite/vite-plugin`: - - ```js title="vitest.config.ts" - import { defineConfig, mergeConfig } from 'vitest/config'; - import { storybookTest } from '@storybook/experimental-addon-test/vitest-plugin'; - import { storybookNextJsPlugin } from '@storybook/experimental-nextjs-vite/vite-plugin'; - - import viteConfig from './vite.config'; - - export default mergeConfig( - viteConfig, - defineConfig({ - plugins: [ - storybookTest(), - storybookNextJsPlugin(), // 👈 Apply the framework plugin here - ], - // ... - }) - ); - ``` - - - - Vue projects should apply the plugin from `@storybook/vue3-vite/vite-plugin`: - - ```js title="vitest.config.ts" - import { defineConfig, mergeConfig } from 'vitest/config'; - import { storybookTest } from '@storybook/experimental-addon-test/vitest-plugin'; - import { storybookVuePlugin } from '@storybook/vue3-vite/vite-plugin'; - - import viteConfig from './vite.config' - - export default mergeConfig( - viteConfig, - defineConfig({ - plugins: [ - storybookTest(), - storybookVuePlugin(), // 👈 Apply the framework plugin here - ], - // ... - }) - ); - ``` - - - - If you're using SvelteKit, apply the plugin from `@storybook/sveltekit/vite-plugin`: - - ```js title="vitest.config.ts" - import { defineConfig, mergeConfig } from 'vitest/config'; - import { storybookTest } from '@storybook/experimental-addon-test/vitest-plugin'; - import { storybookSveltekitPlugin } from '@storybook/sveltekit/vite-plugin'; - - import viteConfig from './vite.config'; - - export default mergeConfig( - viteConfig, - defineConfig({ - plugins: [ - storybookTest(), - storybookSveltekitPlugin(), // 👈 Apply the framework plugin here - ], - // ... - }) - ); - ``` - - -The above example uses the framework's plugin in a Vitest configuration file. You can also use it in a Vitest workspace file, if that is how your project is configured. +1. Adjust your Vitest configuration to include the plugin and reference the setup file. You can use the [example configuration files](#example-configuration-files) as a guide. ### Example configuration files From a045f4e8583797e1a8b1dcb6236d0bd0260023dc Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Thu, 19 Dec 2024 21:08:00 +0100 Subject: [PATCH 13/16] don't add framework plugins to sandboxes --- scripts/tasks/sandbox-parts.ts | 31 ------------------------------- 1 file changed, 31 deletions(-) diff --git a/scripts/tasks/sandbox-parts.ts b/scripts/tasks/sandbox-parts.ts index bdd27181f947..9603bf2b0204 100644 --- a/scripts/tasks/sandbox-parts.ts +++ b/scripts/tasks/sandbox-parts.ts @@ -368,34 +368,6 @@ async function linkPackageStories( ); } -const getVitestPluginInfo = (details: TemplateDetails) => { - let frameworkPluginImport = ''; - let frameworkPluginCall = ''; - - const framework = details.template.expected.framework; - const isNextjs = framework.includes('nextjs'); - const isSveltekit = framework.includes('sveltekit'); - - if (isNextjs) { - frameworkPluginImport = - "import { storybookNextJsPlugin } from '@storybook/experimental-nextjs-vite/vite-plugin'"; - frameworkPluginCall = 'storybookNextJsPlugin()'; - } - - if (isSveltekit) { - frameworkPluginImport = - "import { storybookSveltekitPlugin } from '@storybook/sveltekit/vite-plugin'"; - frameworkPluginCall = 'storybookSveltekitPlugin()'; - } - - if (framework === '@storybook/vue3-vite') { - frameworkPluginImport = "import { storybookVuePlugin } from '@storybook/vue3-vite/vite-plugin'"; - frameworkPluginCall = 'storybookVuePlugin()'; - } - - return { frameworkPluginImport, frameworkPluginCall }; -}; - export async function setupVitest(details: TemplateDetails, options: PassedOptionValues) { const { sandboxDir, template } = details; const packageJsonPath = join(sandboxDir, 'package.json'); @@ -420,7 +392,6 @@ export async function setupVitest(details: TemplateDetails, options: PassedOptio const isVue = template.expected.renderer === '@storybook/vue3'; const isNextjs = template.expected.framework.includes('nextjs'); - const { frameworkPluginCall, frameworkPluginImport } = getVitestPluginInfo(details); // const isAngular = template.expected.framework === '@storybook/angular'; const portableStoriesFrameworks = [ @@ -467,7 +438,6 @@ export async function setupVitest(details: TemplateDetails, options: PassedOptio dedent` import { defineWorkspace, defaultExclude } from "vitest/config"; import { storybookTest } from "@storybook/experimental-addon-test/vitest-plugin"; - ${frameworkPluginImport} export default defineWorkspace([ { @@ -479,7 +449,6 @@ export async function setupVitest(details: TemplateDetails, options: PassedOptio include: ["vitest"], }, }), - ${frameworkPluginCall} ], ${ isNextjs From 5bb9a7fb0254c7138b4c0a501740457a3fa31c22 Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Fri, 20 Dec 2024 10:31:43 +0100 Subject: [PATCH 14/16] fix react-vite and nextjs-vite viteFinals discarding configs from previous presets --- code/addons/test/src/vitest-plugin/index.ts | 5 +++-- code/frameworks/experimental-nextjs-vite/src/preset.ts | 7 ++++--- code/frameworks/react-vite/src/preset.ts | 4 ++-- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/code/addons/test/src/vitest-plugin/index.ts b/code/addons/test/src/vitest-plugin/index.ts index a27ac4270905..c55d9fbb5216 100644 --- a/code/addons/test/src/vitest-plugin/index.ts +++ b/code/addons/test/src/vitest-plugin/index.ts @@ -131,7 +131,7 @@ export const storybookTest = async (options?: UserOptions): Promise => ] )) as unknown as Plugin[]; - plugins.push({ + const storybookTestPlugin: Plugin = { name: 'vite-plugin-storybook-test', async transformIndexHtml(html) { const [headHtmlSnippet, bodyHtmlSnippet] = await Promise.all([ @@ -335,8 +335,9 @@ export const storybookTest = async (options?: UserOptions): Promise => }); } }, - }); + }; + plugins.push(storybookTestPlugin); return plugins; }; diff --git a/code/frameworks/experimental-nextjs-vite/src/preset.ts b/code/frameworks/experimental-nextjs-vite/src/preset.ts index 633f62a5dceb..2cbddf07f26e 100644 --- a/code/frameworks/experimental-nextjs-vite/src/preset.ts +++ b/code/frameworks/experimental-nextjs-vite/src/preset.ts @@ -36,12 +36,13 @@ export const previewAnnotations: PresetProperty<'previewAnnotations'> = (entry = export const viteFinal: StorybookConfigVite['viteFinal'] = async (config, options) => { const reactConfig = await reactViteFinal(config, options); - const { plugins = [] } = reactConfig; const { nextConfigPath } = await options.presets.apply('frameworkOptions'); const nextDir = nextConfigPath ? path.dirname(nextConfigPath) : undefined; - plugins.push(vitePluginStorybookNextjs({ dir: nextDir })); - return reactConfig; + return { + ...reactConfig, + plugins: [...(reactConfig?.plugins ?? []), vitePluginStorybookNextjs({ dir: nextDir })], + }; }; diff --git a/code/frameworks/react-vite/src/preset.ts b/code/frameworks/react-vite/src/preset.ts index a01721dadacc..593fac66a5db 100644 --- a/code/frameworks/react-vite/src/preset.ts +++ b/code/frameworks/react-vite/src/preset.ts @@ -13,7 +13,7 @@ export const core: PresetProperty<'core'> = { }; export const viteFinal: NonNullable = async (config, { presets }) => { - const { plugins = [] } = config; + const plugins = [...(config?.plugins ?? [])]; // Add docgen plugin const { reactDocgen: reactDocgenOption, reactDocgenTypescriptOptions } = await presets.apply( @@ -51,5 +51,5 @@ export const viteFinal: NonNullable = async (confi ); } - return config; + return { ...config, plugins }; }; From e1cd01a67c2883a5dafd913ffe4bae2b2de43559 Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Fri, 20 Dec 2024 10:49:10 +0100 Subject: [PATCH 15/16] cleanup viteFinals --- .../react-native-web-vite/src/preset.ts | 39 +++++++++---------- .../src/plugins/mock-sveltekit-stores.ts | 2 +- code/frameworks/sveltekit/src/preset.ts | 25 ++++++------ 3 files changed, 31 insertions(+), 35 deletions(-) diff --git a/code/frameworks/react-native-web-vite/src/preset.ts b/code/frameworks/react-native-web-vite/src/preset.ts index 44e847171dda..d1ab9a5afb04 100644 --- a/code/frameworks/react-native-web-vite/src/preset.ts +++ b/code/frameworks/react-native-web-vite/src/preset.ts @@ -67,28 +67,25 @@ export const viteFinal: StorybookConfig['viteFinal'] = async (config, options) = const { pluginReactOptions = {} } = await options.presets.apply('frameworkOptions'); - const reactConfig = await reactViteFinal(config, options); + const { plugins = [], ...reactConfigWithoutPlugins } = await reactViteFinal(config, options); - const { plugins = [] } = reactConfig; - - plugins.unshift( - tsconfigPaths(), - flowPlugin({ - exclude: [/node_modules\/(?!react-native|@react-native)/], - }), - react({ - babel: { - babelrc: false, - configFile: false, - }, - jsxRuntime: 'automatic', - ...pluginReactOptions, - }) - ); - - plugins.push(reactNativeWeb()); - - return mergeConfig(reactConfig, { + return mergeConfig(reactConfigWithoutPlugins, { + plugins: [ + tsconfigPaths(), + flowPlugin({ + exclude: [/node_modules\/(?!react-native|@react-native)/], + }), + react({ + babel: { + babelrc: false, + configFile: false, + }, + jsxRuntime: 'automatic', + ...pluginReactOptions, + }), + ...plugins, + reactNativeWeb(), + ], optimizeDeps: { esbuildOptions: { plugins: [esbuildFlowPlugin(new RegExp(/\.(flow|jsx?)$/), (_path: string) => 'jsx')], diff --git a/code/frameworks/sveltekit/src/plugins/mock-sveltekit-stores.ts b/code/frameworks/sveltekit/src/plugins/mock-sveltekit-stores.ts index 98ceb6cc7e5c..39a5b2030a5b 100644 --- a/code/frameworks/sveltekit/src/plugins/mock-sveltekit-stores.ts +++ b/code/frameworks/sveltekit/src/plugins/mock-sveltekit-stores.ts @@ -7,7 +7,7 @@ import type { Plugin } from 'vite'; const filename = __filename ?? fileURLToPath(import.meta.url); const dir = dirname(filename); -export async function mockSveltekitStores() { +export function mockSveltekitStores() { return { name: 'storybook:sveltekit-mock-stores', config: () => ({ diff --git a/code/frameworks/sveltekit/src/preset.ts b/code/frameworks/sveltekit/src/preset.ts index 263a6b35429d..d0674661595f 100644 --- a/code/frameworks/sveltekit/src/preset.ts +++ b/code/frameworks/sveltekit/src/preset.ts @@ -25,17 +25,16 @@ export const previewAnnotations: PresetProperty<'previewAnnotations'> = (entry = export const viteFinal: NonNullable = async (config, options) => { const baseConfig = await svelteViteFinal(config, options); - let { plugins = [] } = baseConfig; - - // disable specific plugins that are not compatible with Storybook - plugins = ( - await withoutVitePlugins(plugins, [ - 'vite-plugin-sveltekit-compile', - 'vite-plugin-sveltekit-guard', - ]) - ) - .concat(configOverrides()) - .concat(await mockSveltekitStores()); - - return { ...baseConfig, plugins }; + return { + ...baseConfig, + plugins: [ + // disable specific plugins that are not compatible with Storybook + ...(await withoutVitePlugins(baseConfig.plugins ?? [], [ + 'vite-plugin-sveltekit-compile', + 'vite-plugin-sveltekit-guard', + ])), + configOverrides(), + mockSveltekitStores(), + ], + }; }; From 3c0886bb4423723713903edc4b964a18405efc1c Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Wed, 8 Jan 2025 11:14:56 +0100 Subject: [PATCH 16/16] merge rnw preset changes --- .../react-native-web-vite/src/preset.ts | 47 +++++++++++++++++-- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/code/frameworks/react-native-web-vite/src/preset.ts b/code/frameworks/react-native-web-vite/src/preset.ts index a7aa10e8c95c..df94a85d1434 100644 --- a/code/frameworks/react-native-web-vite/src/preset.ts +++ b/code/frameworks/react-native-web-vite/src/preset.ts @@ -70,21 +70,62 @@ export const viteFinal: StorybookConfig['viteFinal'] = async (config, options) = const isDevelopment = options.configType !== 'PRODUCTION'; - const reactConfig = await reactViteFinal(config, options); + const { plugins = [], ...reactConfigWithoutPlugins } = await reactViteFinal(config, options); return mergeConfig(reactConfigWithoutPlugins, { plugins: [ tsconfigPaths(), + + // fix for react native packages shipping with flow types untranspiled flowPlugin({ exclude: [/node_modules\/(?!react-native|@react-native)/], }), react({ + ...pluginReactOptions, + jsxRuntime: pluginReactOptions.jsxRuntime || 'automatic', babel: { babelrc: false, configFile: false, + ...pluginReactOptions.babel, + }, + }), + + // we need to add this extra babel config because the react plugin doesn't allow + // for transpiling node_modules. We need this because many react native packages are un-transpiled. + // see this pr for more context: https://github.com/vitejs/vite-plugin-react/pull/306 + // However we keep the react plugin to get the fast refresh and the other stuff its doing + babel({ + ...pluginBabelOptions, + include: pluginBabelOptions.include || [/node_modules\/(react-native|@react-native)/], + exclude: pluginBabelOptions.exclude, + babelConfig: { + ...pluginBabelOptions.babelConfig, + babelrc: false, + configFile: false, + presets: [ + [ + '@babel/preset-react', + { + development: isDevelopment, + runtime: 'automatic', + ...(pluginBabelOptions.presetReact || {}), + }, + ], + ...(pluginBabelOptions.babelConfig?.presets || []), + ], + plugins: [ + [ + // this is a fix for reanimated not working in production + '@babel/plugin-transform-modules-commonjs', + { + strict: false, + strictMode: false, // prevent "use strict" injections + allowTopLevelThis: true, // dont rewrite global `this` -> `undefined` + }, + ], + ...(pluginBabelOptions.babelConfig?.plugins || []), + ], }, - jsxRuntime: 'automatic', - ...pluginReactOptions, }), ...plugins, reactNativeWeb(),