diff --git a/.circleci/config.yml b/.circleci/config.yml index 60a5b6db83f6..385972ef21d8 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1011,7 +1011,7 @@ workflows: requires: - create-sandboxes - vitest-integration: - parallelism: 11 + parallelism: 13 requires: - create-sandboxes - test-portable-stories: @@ -1106,7 +1106,7 @@ workflows: requires: - create-sandboxes - vitest-integration: - parallelism: 6 + parallelism: 7 requires: - create-sandboxes - test-portable-stories: diff --git a/.circleci/src/workflows/daily.yml b/.circleci/src/workflows/daily.yml index be7814aba520..0f31c5279609 100644 --- a/.circleci/src/workflows/daily.yml +++ b/.circleci/src/workflows/daily.yml @@ -54,7 +54,7 @@ jobs: requires: - create-sandboxes - vitest-integration: - parallelism: 11 + parallelism: 13 requires: - create-sandboxes - test-portable-stories: diff --git a/.circleci/src/workflows/merged.yml b/.circleci/src/workflows/merged.yml index 34b8fc22a199..bdf687d408a4 100644 --- a/.circleci/src/workflows/merged.yml +++ b/.circleci/src/workflows/merged.yml @@ -54,7 +54,7 @@ jobs: requires: - create-sandboxes - vitest-integration: - parallelism: 6 + parallelism: 7 requires: - create-sandboxes - test-portable-stories: diff --git a/CHANGELOG.md b/CHANGELOG.md index 9717bfe4e938..8ef6c6a401a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## 10.0.5 + +- Core: Add reentry guard to focus patch - [#32655](https://github.com/storybookjs/storybook/pull/32655), thanks @ia319! +- Nextjs Vite: Update internal plugin to support `svgr` use cases - [#32957](https://github.com/storybookjs/storybook/pull/32957), thanks @yannbf! + ## 10.0.4 - CLI: Fix issue with running Storybook after being initialized - [#32929](https://github.com/storybookjs/storybook/pull/32929), thanks @yannbf! diff --git a/CHANGELOG.prerelease.md b/CHANGELOG.prerelease.md index a30651a9a756..83a07ed7e133 100644 --- a/CHANGELOG.prerelease.md +++ b/CHANGELOG.prerelease.md @@ -1,3 +1,11 @@ +## 10.1.0-alpha.7 + +- CSF: Fix export interface declaration for NextPreview - [#32914](https://github.com/storybookjs/storybook/pull/32914), thanks @icopp! +- Controls: Add range validation in Number Control - [#32539](https://github.com/storybookjs/storybook/pull/32539), thanks @ia319! +- Fix: Export interface declaration for ReactMeta - [#32915](https://github.com/storybookjs/storybook/pull/32915), thanks @icopp! +- React: Improve error messages in component manifest - [#32954](https://github.com/storybookjs/storybook/pull/32954), thanks @kasperpeulen! +- Vitest Addon: Add support for Preact - [#32948](https://github.com/storybookjs/storybook/pull/32948), thanks @yannbf! + ## 10.1.0-alpha.6 - Core: Add reentry guard to focus patch - [#32655](https://github.com/storybookjs/storybook/pull/32655), thanks @ia319! diff --git a/code/addons/docs/src/blocks/controls/Number.tsx b/code/addons/docs/src/blocks/controls/Number.tsx index c9b0b8c1332f..5c8757c5fab6 100644 --- a/code/addons/docs/src/blocks/controls/Number.tsx +++ b/code/addons/docs/src/blocks/controls/Number.tsx @@ -49,11 +49,31 @@ export const NumberControl: FC = ({ if (Number.isNaN(result)) { setParseError(new Error(`'${event.target.value}' is not a number`)); } else { - onChange(result); + // Initialize the final value as the user's input + let finalValue = result; + + // Clamp to minimum: if finalValue is less than min, use min + if (typeof min === 'number' && finalValue < min) { + finalValue = min; + } + + // Clamp to maximum: if finalValue is greater than max, use max + if (typeof max === 'number' && finalValue > max) { + finalValue = max; + } + + // Pass the clamped final value to the onChange callback + onChange(finalValue); + // Clear any previous parse errors setParseError(null); + + // If the value was clamped, update the input display to the final value + if (finalValue !== result) { + setInputValue(String(finalValue)); + } } }, - [onChange, setParseError] + [onChange, setParseError, min, max] ); const onForceVisible = useCallback(() => { diff --git a/code/addons/vitest/src/constants.ts b/code/addons/vitest/src/constants.ts index 7ef81afb73d3..05c92bc41a23 100644 --- a/code/addons/vitest/src/constants.ts +++ b/code/addons/vitest/src/constants.ts @@ -22,6 +22,7 @@ export const SUPPORTED_FRAMEWORKS = [ '@storybook/nextjs', '@storybook/nextjs-vite', '@storybook/react-vite', + '@storybook/preact-vite', '@storybook/svelte-vite', '@storybook/vue3-vite', '@storybook/html-vite', diff --git a/code/frameworks/nextjs/src/index.ts b/code/frameworks/nextjs/src/index.ts index 88aecbb315e6..8bb41e89c3f7 100644 --- a/code/frameworks/nextjs/src/index.ts +++ b/code/frameworks/nextjs/src/index.ts @@ -23,4 +23,4 @@ export function definePreview[]>( }); } -interface NextPreview extends ReactPreview {} +export interface NextPreview extends ReactPreview {} diff --git a/code/lib/cli-storybook/src/sandbox-templates.ts b/code/lib/cli-storybook/src/sandbox-templates.ts index 49914b4088d9..c9f9302cfbf3 100644 --- a/code/lib/cli-storybook/src/sandbox-templates.ts +++ b/code/lib/cli-storybook/src/sandbox-templates.ts @@ -615,7 +615,7 @@ export const baseTemplates = { modifications: { extraDependencies: ['preact-render-to-string'], }, - skipTasks: ['e2e-tests', 'bench', 'vitest-integration'], + skipTasks: ['e2e-tests', 'bench'], }, 'preact-vite/default-ts': { name: 'Preact Latest (Vite | TypeScript)', @@ -628,7 +628,7 @@ export const baseTemplates = { modifications: { extraDependencies: ['preact-render-to-string'], }, - skipTasks: ['e2e-tests', 'bench', 'vitest-integration'], + skipTasks: ['e2e-tests', 'bench'], }, 'qwik-vite/default-ts': { name: 'Qwik CLI Latest (Vite | TypeScript)', diff --git a/code/package.json b/code/package.json index 0cf4d6974a6c..e9aa7d43f237 100644 --- a/code/package.json +++ b/code/package.json @@ -283,5 +283,6 @@ "Dependency Upgrades" ] ] - } + }, + "deferredNextVersion": "10.1.0-alpha.7" } diff --git a/code/renderers/react/src/componentManifest/generateCodeSnippet.ts b/code/renderers/react/src/componentManifest/generateCodeSnippet.ts index 3afb9a1d62d9..f89e21868681 100644 --- a/code/renderers/react/src/componentManifest/generateCodeSnippet.ts +++ b/code/renderers/react/src/componentManifest/generateCodeSnippet.ts @@ -118,17 +118,26 @@ export function getCodeSnippet( ? metaPath.get('properties').filter((p) => p.isObjectProperty()) : []; - const getRenderPath = (object: NodePath[]) => - object - .filter((p) => keyOf(p.node) === 'render') - .map((p) => p.get('value')) - .find( - (v): v is NodePath => - v.isArrowFunctionExpression() || v.isFunctionExpression() + const getRenderPath = (object: NodePath[]) => { + const renderPath = object.find((p) => keyOf(p.node) === 'render')?.get('value'); + + if (renderPath?.isIdentifier()) { + componentName = renderPath.node.name; + } + if ( + renderPath && + !(renderPath.isArrowFunctionExpression() || renderPath.isFunctionExpression()) + ) { + throw renderPath.buildCodeFrameError( + 'Expected render to be an arrow function or function expression' ); + } + + return renderPath; + }; - const renderPath = getRenderPath(storyProps); const metaRenderPath = getRenderPath(metaProps); + const renderPath = getRenderPath(storyProps); storyFn ??= renderPath ?? metaRenderPath; diff --git a/code/renderers/react/src/componentManifest/generator.ts b/code/renderers/react/src/componentManifest/generator.ts index fee51c167004..9d9da2ce7cf8 100644 --- a/code/renderers/react/src/componentManifest/generator.ts +++ b/code/renderers/react/src/componentManifest/generator.ts @@ -132,21 +132,23 @@ export const componentManifestGenerator: PresetPropertyFn< } satisfies Partial; if (!component?.reactDocgen) { - const error = !component + const error = !csf._meta?.component ? { - name: 'No meta.component specified', - message: 'Specify meta.component for the component to be included in the manifest.', + name: 'No component found', + message: + 'We could not detect the component from your story file. Specify meta.component.', } : { name: 'No component import found', - message: `No component file found for the "${component.componentName}" component.`, + message: `No component file found for the "${csf.meta.component}" component.`, }; return { ...base, error: { name: error.name, message: - csf._metaStatementPath?.buildCodeFrameError(error.message).message ?? error.message, + (csf._metaStatementPath?.buildCodeFrameError(error.message).message ?? + error.message) + `\n\n${entry.importPath}:\n${storyFile}`, }, }; } diff --git a/code/renderers/react/src/componentManifest/getComponentImports.ts b/code/renderers/react/src/componentManifest/getComponentImports.ts index 259c810d159a..defe9e8b17dd 100644 --- a/code/renderers/react/src/componentManifest/getComponentImports.ts +++ b/code/renderers/react/src/componentManifest/getComponentImports.ts @@ -198,7 +198,7 @@ export const getComponents = ({ }); } } catch (e) { - logger.error(e); + logger.debug(e); } if (path) { const reactDocgen = getReactDocgen(path, component); diff --git a/code/renderers/react/src/componentManifest/reactDocgen.ts b/code/renderers/react/src/componentManifest/reactDocgen.ts index 45c457b26aa6..7ade97fade6a 100644 --- a/code/renderers/react/src/componentManifest/reactDocgen.ts +++ b/code/renderers/react/src/componentManifest/reactDocgen.ts @@ -3,6 +3,7 @@ import { dirname, sep } from 'node:path'; import { babelParse, types as t } from 'storybook/internal/babel'; import { getProjectRoot, supportedExtensions } from 'storybook/internal/common'; +import { logger } from 'storybook/internal/node-logger'; import * as find from 'empathic/find'; import { @@ -12,6 +13,7 @@ import { makeFsImporter, parse, } from 'react-docgen'; +import { dedent } from 'ts-dedent'; import * as TsconfigPaths from 'tsconfig-paths'; import { type ComponentRef } from './getComponentImports'; @@ -33,6 +35,9 @@ const defaultResolver = new docgenResolver.FindExportedDefinitionsResolver(); const handlers = [...defaultHandlers, actualNameHandler, exportNameHandler]; export function getMatchingDocgen(docgens: DocObj[], component: ComponentRef) { + if (docgens.length === 0) { + return; + } if (docgens.length === 1) { return docgens[0]; } @@ -91,75 +96,141 @@ export const parseWithReactDocgen = cached( const getExportPaths = cached( (code: string, filePath: string) => { - const ast = (() => { - try { - return babelParse(code); - } catch (_) { - return undefined; - } - })(); - - if (!ast) { - return [] as string[]; + let ast; + try { + ast = babelParse(code); + } catch (_) { + return []; } + const basedir = dirname(filePath); const body = ast.program.body; return body - .flatMap((n) => - t.isExportAllDeclaration(n) - ? [n.source.value] - : t.isExportNamedDeclaration(n) && !!n.source && !n.declaration - ? [n.source.value] + .flatMap((statement) => + t.isExportAllDeclaration(statement) + ? [statement.source.value] + : t.isExportNamedDeclaration(statement) && !!statement.source && !statement.declaration + ? [statement.source.value] : [] ) - .map((s) => matchPath(s, basedir)) - .map((s) => { + .map((id) => matchPath(id, basedir)) + .flatMap((id) => { try { - return cachedResolveImport(s, { basedir }); - } catch { - return undefined; + return [cachedResolveImport(id, { basedir })]; + } catch (e) { + logger.debug(e); + return []; } - }) - .filter((p): p is string => !!p && !p.includes('node_modules')); + }); }, { name: 'getExportPaths' } ); const gatherDocgensForPath = cached( ( - filePath: string, + path: string, depth: number - ): { docgens: DocObj[]; analyzed: { path: string; code: string }[] } => { - if (depth > 5 || filePath.includes('node_modules')) { - return { docgens: [], analyzed: [] }; + ): { + docgens: DocObj[]; + errors: { path: string; code: string; name: string; message: string }[]; + } => { + if (path.includes('node_modules')) { + return { + docgens: [], + errors: [ + { + path, + code: '/* File in node_modules */', + name: 'Component file in node_modules', + message: dedent` + Component files in node_modules are not supported. + The distributed files in node_modules usually don't contain the necessary comments or types needed to analyze component information. + Configure TypeScript path aliases to map your package name to the source file instead. + + Example (tsconfig.json): + { + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@design-system/button": ["src/components/Button.tsx"], + "@design-system/*": ["src/components/*"] + } + } + } + + Then import using: + import { Button } from '@design-system/button' + + Storybook resolves tsconfig paths automatically. + `, + }, + ], + }; } - let code: string | undefined; + let code; try { - code = cachedReadFileSync(filePath, 'utf-8') as string; - } catch {} + code = cachedReadFileSync(path, 'utf-8') as string; + } catch { + return { + docgens: [], + errors: [ + { + path, + code: '/* File not found or unreadable */', + name: 'Component file could not be read', + message: `Could not read the component file located at "${path}".\nPrefer relative imports if possible.`, + }, + ], + }; + } - if (!code) { - return { docgens: [], analyzed: [{ path: filePath, code: '/* File not found */' }] }; + if (depth > 5) { + return { + docgens: [], + errors: [ + { + path, + code, + name: 'Max re-export depth exceeded', + message: dedent` + Traversal stopped after 5 steps while following re-exports starting from this file. + This usually indicates a deep or circular re-export chain. Try one of the following: + - Import the component file directly (e.g., src/components/Button.tsx), + - Reduce the number of re-export hops. + `, + }, + ], + }; } - const reexportResults = getExportPaths(code, filePath).map((p) => - gatherDocgensForPath(p, depth + 1) - ); - const fromReexports = reexportResults.flatMap((r) => r.docgens); - const analyzedChildren = reexportResults.flatMap((r) => r.analyzed); + const exportPaths = getExportPaths(code, path).map((p) => gatherDocgensForPath(p, depth + 1)); + const docgens = exportPaths.flatMap((r) => r.docgens); + const errors = exportPaths.flatMap((r) => r.errors); - let locals: DocObj[]; try { - locals = parseWithReactDocgen(code as string, filePath); - } catch { - locals = []; + return { + docgens: [...parseWithReactDocgen(code, path), ...docgens], + errors, + }; + } catch (e) { + const message = e instanceof Error ? e.message : String(e); + return { + docgens, + errors: [ + { + path, + code, + name: 'No component definition found', + message: dedent` + ${message} + You can debug your component file in this playground: https://react-docgen.dev/playground + `, + }, + ...errors, + ], + }; } - - return { - docgens: [...locals, ...fromReexports], - analyzed: [{ path: filePath, code }, ...analyzedChildren], - }; }, { name: 'gatherDocgensWithTrace', key: (filePath) => filePath } ); @@ -171,39 +242,25 @@ export const getReactDocgen = cached( ): | { type: 'success'; data: DocObj } | { type: 'error'; error: { name: string; message: string } } => { - if (path.includes('node_modules')) { - return { - type: 'error', - error: { - name: 'Component file in node_modules', - message: `Component files in node_modules are not supported. Please import your component file directly.`, - }, - }; - } - - const docgenWithInfo = gatherDocgensForPath(path, 0); - const docgens = docgenWithInfo.docgens; - - const noCompDefError = { - type: 'error' as const, - error: { - name: 'No component definition found', - message: - `Could not find a component definition.\n` + - `Prefer relative imports if possible.\n` + - `Avoid pointing to transpiled files.\n` + - `You can debug your component file in this playground: https://react-docgen.dev/playground\n\n` + - docgenWithInfo.analyzed.map(({ path, code }) => `File: ${path}\n${code}`).join('\n'), - }, - }; - - if (!docgens || docgens.length === 0) { - return noCompDefError; - } + const { docgens, errors } = gatherDocgensForPath(path, 0); const docgen = getMatchingDocgen(docgens, component); + if (!docgen) { - return noCompDefError; + const error = { + name: errors.at(-1)?.name ?? 'No component definition found', + message: errors + .map( + (e) => dedent` + File: ${e.path} + Error: + ${e.message} + Code: + ${e.code}` + ) + .join('\n\n'), + }; + return { type: 'error', error }; } return { type: 'success', data: docgen }; }, diff --git a/code/renderers/react/src/preview.tsx b/code/renderers/react/src/preview.tsx index 8021aa5cc2b7..ebcb0ffb960e 100644 --- a/code/renderers/react/src/preview.tsx +++ b/code/renderers/react/src/preview.tsx @@ -84,7 +84,7 @@ type DecoratorsArgs = UnionToIntersectio Decorators extends DecoratorFunction ? TArgs : unknown >; -interface ReactMeta> +export interface ReactMeta> /** @ts-expect-error hard */ extends Meta { // Required args don't need to be provided when the user uses an empty render diff --git a/docs/_snippets/vitest-plugin-vitest-config.md b/docs/_snippets/vitest-plugin-vitest-config.md index 7dfedbbb76c2..96b96b9e1548 100644 --- a/docs/_snippets/vitest-plugin-vitest-config.md +++ b/docs/_snippets/vitest-plugin-vitest-config.md @@ -1,5 +1,5 @@ ```ts filename="vitest.config.ts" renderer="react" tabTitle="Vitest 4" -import { defineConfig, defineProject, mergeConfig } from 'vitest/config'; +import { defineConfig, mergeConfig } from 'vitest/config'; import { playwright } from '@vitest/browser-playwright'; import { storybookTest } from '@storybook/addon-vitest/vitest-plugin'; @@ -17,7 +17,8 @@ export default mergeConfig( test: { // Use `workspace` field in Vitest < 3.2 projects: [ - defineProject({ + { + extends: true, plugins: [ storybookTest({ // The location of your Storybook config, main.js|ts @@ -39,7 +40,7 @@ export default mergeConfig( }, setupFiles: ['./.storybook/vitest.setup.ts'], }, - }), + }, ], }, }), @@ -47,7 +48,7 @@ export default mergeConfig( ``` ```ts filename="vitest.config.ts" renderer="react" tabTitle="Vitest 3" -import { defineConfig, defineProject, mergeConfig } from 'vitest/config'; +import { defineConfig, mergeConfig } from 'vitest/config'; import { storybookTest } from '@storybook/addon-vitest/vitest-plugin'; @@ -64,7 +65,8 @@ export default mergeConfig( test: { // Use `workspace` field in Vitest < 3.2 projects: [ - defineProject({ + { + extends: true, plugins: [ storybookTest({ // The location of your Storybook config, main.js|ts @@ -86,7 +88,7 @@ export default mergeConfig( }, setupFiles: ['./.storybook/vitest.setup.ts'], }, - }), + }, ], }, }), @@ -94,7 +96,7 @@ export default mergeConfig( ``` ```ts filename="vitest.config.ts" renderer="vue" tabTitle="Vitest 4" -import { defineConfig, defineProject, mergeConfig } from 'vitest/config'; +import { defineConfig, mergeConfig } from 'vitest/config'; import { playwright } from '@vitest/browser-playwright'; import { storybookTest } from '@storybook/addon-vitest/vitest-plugin'; @@ -112,7 +114,8 @@ export default mergeConfig( test: { // Use `workspace` field in Vitest < 3.2 projects: [ - defineProject({ + { + extends: true, plugins: [ storybookTest({ // The location of your Storybook config, main.js|ts @@ -134,7 +137,7 @@ export default mergeConfig( }, setupFiles: ['./.storybook/vitest.setup.ts'], }, - }), + }, ], }, }), @@ -142,7 +145,7 @@ export default mergeConfig( ``` ```ts filename="vitest.config.ts" renderer="vue" tabTitle="Vitest 3" -import { defineConfig, defineProject, mergeConfig } from 'vitest/config'; +import { defineConfig, mergeConfig } from 'vitest/config'; import { storybookTest } from '@storybook/addon-vitest/vitest-plugin'; @@ -159,7 +162,8 @@ export default mergeConfig( test: { // Use `workspace` field in Vitest < 3.2 projects: [ - defineProject({ + { + extends: true, plugins: [ storybookTest({ // The location of your Storybook config, main.js|ts @@ -181,7 +185,7 @@ export default mergeConfig( }, setupFiles: ['./.storybook/vitest.setup.ts'], }, - }), + }, ], }, }), @@ -189,7 +193,7 @@ export default mergeConfig( ``` ```ts filename="vitest.config.ts" renderer="svelte" tabTitle="Vitest 4" -import { defineConfig, defineProject, mergeConfig } from 'vitest/config'; +import { defineConfig, mergeConfig } from 'vitest/config'; import { playwright } from '@vitest/browser-playwright'; import { storybookTest } from '@storybook/addon-vitest/vitest-plugin'; @@ -207,7 +211,8 @@ export default mergeConfig( test: { // Use `workspace` field in Vitest < 3.2 projects: [ - defineProject({ + { + extends: true, plugins: [ storybookTest({ // The location of your Storybook config, main.js|ts @@ -229,7 +234,7 @@ export default mergeConfig( }, setupFiles: ['./.storybook/vitest.setup.ts'], }, - }), + }, ], }, }), @@ -237,7 +242,7 @@ export default mergeConfig( ``` ```ts filename="vitest.config.ts" renderer="svelte" tabTitle="Vitest 3" -import { defineConfig, defineProject, mergeConfig } from 'vitest/config'; +import { defineConfig, mergeConfig } from 'vitest/config'; import { storybookTest } from '@storybook/addon-vitest/vitest-plugin'; @@ -254,7 +259,8 @@ export default mergeConfig( test: { // Use `workspace` field in Vitest < 3.2 projects: [ - defineProject({ + { + extends: true, plugins: [ storybookTest({ // The location of your Storybook config, main.js|ts @@ -276,7 +282,7 @@ export default mergeConfig( }, setupFiles: ['./.storybook/vitest.setup.ts'], }, - }), + }, ], }, }), @@ -284,7 +290,7 @@ export default mergeConfig( ``` ```ts filename="vitest.config.ts" renderer="web-components" tabTitle="Vitest 4" -import { defineConfig, defineProject, mergeConfig } from 'vitest/config'; +import { defineConfig, mergeConfig } from 'vitest/config'; import { playwright } from '@vitest/browser-playwright'; import { storybookTest } from '@storybook/addon-vitest/vitest-plugin'; @@ -302,7 +308,8 @@ export default mergeConfig( test: { // Use `workspace` field in Vitest < 3.2 projects: [ - defineProject({ + { + extends: true, plugins: [ storybookTest({ // The location of your Storybook config, main.js|ts @@ -324,7 +331,7 @@ export default mergeConfig( }, setupFiles: ['./.storybook/vitest.setup.ts'], }, - }), + }, ], }, }), @@ -332,7 +339,7 @@ export default mergeConfig( ``` ```ts filename="vitest.config.ts" renderer="web-components" tabTitle="Vitest 3" -import { defineConfig, defineProject, mergeConfig } from 'vitest/config'; +import { defineConfig, mergeConfig } from 'vitest/config'; import { storybookTest } from '@storybook/addon-vitest/vitest-plugin'; @@ -349,7 +356,8 @@ export default mergeConfig( test: { // Use `workspace` field in Vitest < 3.2 projects: [ - defineProject({ + { + extends: true, plugins: [ storybookTest({ // The location of your Storybook config, main.js|ts @@ -371,7 +379,7 @@ export default mergeConfig( }, setupFiles: ['./.storybook/vitest.setup.ts'], }, - }), + }, ], }, }), diff --git a/docs/versions/next.json b/docs/versions/next.json index a7c7b18932bd..fa4ab0f27d6e 100644 --- a/docs/versions/next.json +++ b/docs/versions/next.json @@ -1 +1 @@ -{"version":"10.1.0-alpha.6","info":{"plain":"- Core: Add reentry guard to focus patch - [#32655](https://github.com/storybookjs/storybook/pull/32655), thanks @ia319!\n- Nextjs Vite: Update internal plugin to support `svgr` use cases - [#32957](https://github.com/storybookjs/storybook/pull/32957), thanks @yannbf!"}} \ No newline at end of file +{"version":"10.1.0-alpha.7","info":{"plain":"- CSF: Fix export interface declaration for NextPreview - [#32914](https://github.com/storybookjs/storybook/pull/32914), thanks @icopp!\n- Controls: Add range validation in Number Control - [#32539](https://github.com/storybookjs/storybook/pull/32539), thanks @ia319!\n- Fix: Export interface declaration for ReactMeta - [#32915](https://github.com/storybookjs/storybook/pull/32915), thanks @icopp!\n- React: Improve error messages in component manifest - [#32954](https://github.com/storybookjs/storybook/pull/32954), thanks @kasperpeulen!\n- Vitest Addon: Add support for Preact - [#32948](https://github.com/storybookjs/storybook/pull/32948), thanks @yannbf!"}} \ No newline at end of file diff --git a/docs/writing-tests/integrations/vitest-addon.mdx b/docs/writing-tests/integrations/vitest-addon.mdx index 80e131d82fd2..50148d992e2c 100644 --- a/docs/writing-tests/integrations/vitest-addon.mdx +++ b/docs/writing-tests/integrations/vitest-addon.mdx @@ -5,11 +5,11 @@ sidebar: title: Vitest addon --- - + -The Vitest addon is currently only supported in [React](?renderer=react), [Vue](?renderer=vue), [Svelte](?renderer=svelte), and [Web Components](?renderer=web-components) projects, which use the [Vite builder](../builders/vite.mdx) (or the [Next.js framework with Vite](../get-started/frameworks/nextjs.mdx#with-vite)). +The Vitest addon is currently only supported in [React](?renderer=react), [Preact](?renderer=preact), [Vue](?renderer=vue), [Svelte](?renderer=svelte), and [Web Components](?renderer=web-components) projects, which use the [Vite builder](../builders/vite.mdx) (or the [Next.js framework with Vite](../get-started/frameworks/nextjs.mdx#with-vite)). If you are using a different renderer (such as Angular) or the Webpack builder, you can use the [Storyboook test runner](./test-runner.mdx) to test your stories. @@ -18,7 +18,7 @@ If you are using a different renderer (such as Angular) or the Webpack builder, {/* End non-supported renderers */} - + Storybook's Vitest addon allows you to test your components directly inside Storybook. On its own, it transforms your [stories](../../writing-stories/index.mdx) into component tests, which test the rendering and behavior of your components in a real browser environment. It can also calculate project [coverage](../test-coverage.mdx) provided by your stories. @@ -34,7 +34,7 @@ You can also run tests in watch mode, which will automatically re-run tests when Before installing, make sure your project meets the following requirements: -- A Storybook framework that uses Vite (e.g. [`vue3-vite`](../../get-started/frameworks/vue3-vite.mdx), [`react-vite`](../../get-started/frameworks/react-vite.mdx), [`nextjs-vite`](../../get-started/frameworks/nextjs.mdx#with-vite), [`sveltekit`](../../get-started/frameworks/sveltekit.mdx), etc.) +- A Storybook framework that uses Vite (e.g. [`vue3-vite`](../../get-started/frameworks/vue3-vite.mdx), [`react-vite`](../../get-started/frameworks/react-vite.mdx), [`preact-vite`](../../get-started/frameworks/preact-vite.mdx), [`nextjs-vite`](../../get-started/frameworks/nextjs.mdx#with-vite), [`sveltekit`](../../get-started/frameworks/sveltekit.mdx), etc.) - Vitest ≥ 3.0 - If you're not yet using Vitest, it will be installed and configured for you when you install the addon - (optional) MSW ≥ 2.0