diff --git a/.circleci/config.yml b/.circleci/config.yml index d9846f300ccb..c71bbbbae66b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -768,22 +768,22 @@ workflows: requires: - build - create-sandboxes: - parallelism: 35 + parallelism: 37 requires: - build # - smoke-test-sandboxes: # disabled for now # requires: # - create-sandboxes - build-sandboxes: - parallelism: 35 + parallelism: 37 requires: - create-sandboxes - chromatic-sandboxes: - parallelism: 32 + parallelism: 34 requires: - build-sandboxes - e2e-production: - parallelism: 30 + parallelism: 32 requires: - build-sandboxes - e2e-dev: @@ -791,7 +791,7 @@ workflows: requires: - create-sandboxes - test-runner-production: - parallelism: 30 + parallelism: 32 requires: - build-sandboxes - test-portable-stories: diff --git a/MIGRATION.md b/MIGRATION.md index ffde34657039..22bd0a923f32 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -3899,7 +3899,7 @@ export default { We are replacing `@storybook/addon-knobs` with `@storybook/addon-controls`. - [Rationale & discussion](https://github.com/storybookjs/storybook/discussions/15060) -- [Migration notes](https://github.com/storybookjs/storybook/blob/next/addons/controls/README.md#how-do-i-migrate-from-addon-knobs) +- [Migration notes](https://github.com/storybookjs/storybook/blob/next/code/addons/controls/README.md#how-do-i-migrate-from-addon-knobs) #### Deprecated scoped blocks imports diff --git a/code/addons/docs/template/stories/docs2/resolved-react.stories.ts b/code/addons/docs/template/stories/docs2/resolved-react.stories.ts index 91f12041962b..20095ec6164b 100644 --- a/code/addons/docs/template/stories/docs2/resolved-react.stories.ts +++ b/code/addons/docs/template/stories/docs2/resolved-react.stories.ts @@ -44,6 +44,8 @@ export default { docs: { name: 'ResolvedReact', }, + // the version string changes with every release of React/Next.js/Preact, not worth snapshotting + chromatic: { disable: true }, }, }; diff --git a/code/addons/links/package.json b/code/addons/links/package.json index 7c4eb69bb10b..0155bf1635de 100644 --- a/code/addons/links/package.json +++ b/code/addons/links/package.json @@ -82,7 +82,7 @@ "typescript": "^5.3.2" }, "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta" }, "peerDependenciesMeta": { "react": { diff --git a/code/e2e-tests/addon-docs.spec.ts b/code/e2e-tests/addon-docs.spec.ts index 2713892fd042..b8b20bb46455 100644 --- a/code/e2e-tests/addon-docs.spec.ts +++ b/code/e2e-tests/addon-docs.spec.ts @@ -200,6 +200,11 @@ test.describe('addon-docs', () => { expectedReactVersionRange = /^17/; } else if (templateName.includes('react16')) { expectedReactVersionRange = /^16/; + } else if ( + templateName.includes('react-vite/prerelease') || + templateName.includes('react-webpack/prerelease') + ) { + expectedReactVersionRange = /^19/; } // Arrange - Get the actual versions diff --git a/code/frameworks/nextjs/package.json b/code/frameworks/nextjs/package.json index e07cb6a6db34..b86f8cb49e90 100644 --- a/code/frameworks/nextjs/package.json +++ b/code/frameworks/nextjs/package.json @@ -173,8 +173,8 @@ }, "peerDependencies": { "next": "^13.5.0 || ^14.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", "webpack": "^5.0.0" }, "peerDependenciesMeta": { diff --git a/code/frameworks/react-vite/package.json b/code/frameworks/react-vite/package.json index 37ec751658a0..9ad51376f118 100644 --- a/code/frameworks/react-vite/package.json +++ b/code/frameworks/react-vite/package.json @@ -64,8 +64,8 @@ "vite": "^4.0.0" }, "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", "vite": "^4.0.0 || ^5.0.0" }, "engines": { diff --git a/code/frameworks/react-webpack5/package.json b/code/frameworks/react-webpack5/package.json index 939b90176592..9b55765029d7 100644 --- a/code/frameworks/react-webpack5/package.json +++ b/code/frameworks/react-webpack5/package.json @@ -53,8 +53,8 @@ "@types/node": "^18.0.0" }, "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", "typescript": ">= 4.2.x" }, "peerDependenciesMeta": { diff --git a/code/lib/cli/src/sandbox-templates.ts b/code/lib/cli/src/sandbox-templates.ts index 9d8a839bfcce..14d5fe69d675 100644 --- a/code/lib/cli/src/sandbox-templates.ts +++ b/code/lib/cli/src/sandbox-templates.ts @@ -201,6 +201,28 @@ const baseTemplates = { }, skipTasks: ['bench'], }, + 'react-vite/prerelease-ts': { + name: 'React Prerelease (Vite | TypeScript)', + /** + * 1. Create a Vite project with the React template + * 2. Add React beta versions + * 3. Add resolutions for @types/react and @types/react-dom, see https://react.dev/blog/2024/04/25/react-19-upgrade-guide#installing + * 4. Add @types/react and @types/react-dom pointing to the beta packages + */ + script: ` + npm create vite --yes {{beforeDir}} -- --template react-ts && \ + cd {{beforeDir}} && \ + yarn add react@beta react-dom@beta && \ + jq '.resolutions += {"@types/react": "npm:types-react@beta", "@types/react-dom": "npm:types-react-dom@beta"}' package.json > tmp.json && mv tmp.json package.json && \ + yarn add --dev @types/react@npm:types-react@beta @types/react-dom@npm:types-react-dom@beta + `, + expected: { + framework: '@storybook/react-vite', + renderer: '@storybook/react', + builder: '@storybook/builder-vite', + }, + skipTasks: ['e2e-tests-dev', 'bench'], + }, 'react-webpack/18-ts': { name: 'React Latest (Webpack | TypeScript)', script: 'yarn create webpack5-react {{beforeDir}}', @@ -222,6 +244,26 @@ const baseTemplates = { }, skipTasks: ['e2e-tests-dev', 'bench'], }, + 'react-webpack/prerelease-ts': { + name: 'React Prerelease (Webpack | TypeScript)', + /** + * 1. Create a Webpack project with React beta versions + * 3. Add resolutions for @types/react and @types/react-dom, see https://react.dev/blog/2024/04/25/react-19-upgrade-guide#installing + * 4. Add @types/react and @types/react-dom pointing to the beta packages + */ + script: ` + yarn create webpack5-react {{beforeDir}} --version-react="beta" --version-react-dom="beta" && \ + cd {{beforeDir}} && \ + jq '.resolutions += {"@types/react": "npm:types-react@beta", "@types/react-dom": "npm:types-react-dom@beta"}' package.json > tmp.json && mv tmp.json package.json && \ + yarn add --dev @types/react@npm:types-react@beta @types/react-dom@npm:types-react-dom@beta + `, + expected: { + framework: '@storybook/react-webpack5', + renderer: '@storybook/react', + builder: '@storybook/builder-webpack5', + }, + skipTasks: ['e2e-tests-dev', 'bench'], + }, 'solid-vite/default-js': { name: 'SolidJS Latest (Vite | JavaScript)', script: 'npx degit solidjs/templates/js {{beforeDir}}', @@ -628,6 +670,8 @@ export const daily: TemplateKey[] = [ 'angular-cli/prerelease', 'cra/default-js', 'react-vite/default-js', + 'react-vite/prerelease-ts', + 'react-webpack/prerelease-ts', 'vue3-vite/default-js', 'vue-cli/default-js', 'lit-vite/default-js', diff --git a/code/lib/react-dom-shim/package.json b/code/lib/react-dom-shim/package.json index d9ba1aee82d5..0fb719944281 100644 --- a/code/lib/react-dom-shim/package.json +++ b/code/lib/react-dom-shim/package.json @@ -58,8 +58,8 @@ "typescript": "^5.3.2" }, "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta" }, "publishConfig": { "access": "public" diff --git a/code/lib/theming/package.json b/code/lib/theming/package.json index 3c528bd4f469..c5d0dc4cfb74 100644 --- a/code/lib/theming/package.json +++ b/code/lib/theming/package.json @@ -68,8 +68,8 @@ "typescript": "^5.3.2" }, "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta" }, "peerDependenciesMeta": { "react": { diff --git a/code/presets/react-webpack/package.json b/code/presets/react-webpack/package.json index 13a613129369..0633543f764a 100644 --- a/code/presets/react-webpack/package.json +++ b/code/presets/react-webpack/package.json @@ -84,8 +84,8 @@ "typescript": "^5.3.2" }, "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta" }, "peerDependenciesMeta": { "typescript": { diff --git a/code/renderers/react/package.json b/code/renderers/react/package.json index 9e077ff76419..ed92071b5ffe 100644 --- a/code/renderers/react/package.json +++ b/code/renderers/react/package.json @@ -86,8 +86,8 @@ "require-from-string": "^2.0.2" }, "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", "typescript": ">= 4.2.x" }, "peerDependenciesMeta": { diff --git a/code/ui/blocks/package.json b/code/ui/blocks/package.json index 523a3e1aed3c..a54a3726f998 100644 --- a/code/ui/blocks/package.json +++ b/code/ui/blocks/package.json @@ -75,8 +75,8 @@ "@types/color-convert": "^2.0.0" }, "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta" }, "peerDependenciesMeta": { "react": { diff --git a/code/ui/blocks/src/components/Source.tsx b/code/ui/blocks/src/components/Source.tsx index 5495d89c5981..76fb8f1c4f05 100644 --- a/code/ui/blocks/src/components/Source.tsx +++ b/code/ui/blocks/src/components/Source.tsx @@ -98,7 +98,7 @@ const Source: FunctionComponent = ({ language, code, dark, - format, + format = false, ...rest }) => { const { typography } = useTheme(); @@ -138,7 +138,4 @@ const Source: FunctionComponent = ({ ); }; -Source.defaultProps = { - format: false, -}; export { Source, StyledSyntaxHighlighter }; diff --git a/code/ui/components/package.json b/code/ui/components/package.json index bbbb9009a741..b1ea9cf01935 100644 --- a/code/ui/components/package.json +++ b/code/ui/components/package.json @@ -87,8 +87,8 @@ "use-resize-observer": "^9.1.0" }, "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta" }, "publishConfig": { "access": "public" diff --git a/code/ui/components/src/components/form/field/field.tsx b/code/ui/components/src/components/form/field/field.tsx index 19b21ae8d2f7..bf4e5f00c1db 100644 --- a/code/ui/components/src/components/form/field/field.tsx +++ b/code/ui/components/src/components/form/field/field.tsx @@ -38,7 +38,3 @@ export const Field = ({ label, children, ...props }: FieldProps) => ( {children} ); - -Field.defaultProps = { - label: undefined, -}; diff --git a/code/ui/components/src/components/tabs/tabs.tsx b/code/ui/components/src/components/tabs/tabs.tsx index 3d90fb3f9e58..de3eb731d3be 100644 --- a/code/ui/components/src/components/tabs/tabs.tsx +++ b/code/ui/components/src/components/tabs/tabs.tsx @@ -134,14 +134,14 @@ export interface TabsProps { export const Tabs: FC = memo( ({ children, - selected, + selected = null, actions, - absolute, - bordered, - tools, + absolute = false, + bordered = false, + tools = null, backgroundColor, - id: htmlId, - menuName, + id: htmlId = null, + menuName = 'Tabs', emptyState, showToolsWhenEmpty, }) => { @@ -206,15 +206,6 @@ export const Tabs: FC = memo( } ); Tabs.displayName = 'Tabs'; -Tabs.defaultProps = { - id: null, - children: null, - tools: null, - selected: null, - absolute: false, - bordered: false, - menuName: 'Tabs', -}; export interface TabsStateProps { children: TabsProps['children']; diff --git a/code/ui/components/src/components/tooltip/ListItem.tsx b/code/ui/components/src/components/tooltip/ListItem.tsx index 34921e65d5ae..5d5ebf763b99 100644 --- a/code/ui/components/src/components/tooltip/ListItem.tsx +++ b/code/ui/components/src/components/tooltip/ListItem.tsx @@ -186,17 +186,18 @@ export interface ListItemProps extends Omit, 'href' } const ListItem = ({ - loading, - title, - center, - right, - icon, - active, - disabled, + loading = false, + title = Loading state, + center = null, + right = null, + + active = false, + disabled = false, isIndented, - href, - onClick, - LinkWrapper, + href = null, + onClick = null, + icon, + LinkWrapper = null, ...rest }: ListItemProps) => { const itemProps = getItemProps(onClick, href, LinkWrapper); @@ -220,16 +221,4 @@ const ListItem = ({ ); }; -ListItem.defaultProps = { - loading: false, - title: Loading state, - center: null, - right: null, - active: false, - disabled: false, - href: null, - LinkWrapper: null, - onClick: null, -}; - export default ListItem; diff --git a/code/ui/components/src/components/tooltip/Tooltip.tsx b/code/ui/components/src/components/tooltip/Tooltip.tsx index d4ccde5e41df..6f14a148bfeb 100644 --- a/code/ui/components/src/components/tooltip/Tooltip.tsx +++ b/code/ui/components/src/components/tooltip/Tooltip.tsx @@ -126,7 +126,16 @@ export interface TooltipProps { export const Tooltip = React.forwardRef( ( - { placement, hasChrome, children, arrowProps, tooltipRef, color, withArrows, ...props }, + { + placement = 'top', + hasChrome = true, + children, + arrowProps = {}, + tooltipRef, + color, + withArrows, + ...props + }, ref ) => { return ( @@ -139,10 +148,3 @@ export const Tooltip = React.forwardRef( ); Tooltip.displayName = 'Tooltip'; -Tooltip.defaultProps = { - color: undefined, - tooltipRef: undefined, - hasChrome: true, - placement: 'top', - arrowProps: {}, -}; diff --git a/code/ui/components/src/components/tooltip/TooltipLinkList.tsx b/code/ui/components/src/components/tooltip/TooltipLinkList.tsx index 8c9d1b6564ad..fcb88ca25545 100644 --- a/code/ui/components/src/components/tooltip/TooltipLinkList.tsx +++ b/code/ui/components/src/components/tooltip/TooltipLinkList.tsx @@ -58,7 +58,7 @@ export interface TooltipLinkListProps { LinkWrapper?: LinkWrapperType; } -export const TooltipLinkList = ({ links, LinkWrapper }: TooltipLinkListProps) => { +export const TooltipLinkList = ({ links, LinkWrapper = null }: TooltipLinkListProps) => { const hasIcon = links.some((link) => link.icon); return ( @@ -68,7 +68,3 @@ export const TooltipLinkList = ({ links, LinkWrapper }: TooltipLinkListProps) => ); }; - -TooltipLinkList.defaultProps = { - LinkWrapper: ListItem.defaultProps.LinkWrapper, -}; diff --git a/code/ui/components/src/components/tooltip/TooltipMessage.tsx b/code/ui/components/src/components/tooltip/TooltipMessage.tsx index 1e47bb5481f5..7da93fc1474d 100644 --- a/code/ui/components/src/components/tooltip/TooltipMessage.tsx +++ b/code/ui/components/src/components/tooltip/TooltipMessage.tsx @@ -60,9 +60,3 @@ export const TooltipMessage = ({ title, desc, links }: TooltipMessageProps) => { ); }; - -TooltipMessage.defaultProps = { - title: null, - desc: null, - links: null, -}; diff --git a/code/ui/components/src/components/tooltip/WithTooltip.tsx b/code/ui/components/src/components/tooltip/WithTooltip.tsx index a95db5d76af2..b14015aa0d10 100644 --- a/code/ui/components/src/components/tooltip/WithTooltip.tsx +++ b/code/ui/components/src/components/tooltip/WithTooltip.tsx @@ -45,23 +45,42 @@ export interface WithTooltipPureProps // Pure, does not bind to the body const WithTooltipPure = ({ - svg, - trigger, - closeOnOutsideClick, - placement, - hasChrome, + svg = false, + trigger = 'click', + closeOnOutsideClick = false, + placement = 'top', + modifiers = [ + { + name: 'preventOverflow', + options: { + padding: 8, + }, + }, + { + name: 'offset', + options: { + offset: [8, 8], + }, + }, + { + name: 'arrow', + options: { + padding: 8, + }, + }, + ], + hasChrome = true, + defaultVisible = false, withArrows, offset, tooltip, children, closeOnTriggerHidden, mutationObserverOptions, - defaultVisible, delayHide, visible, interactive, delayShow, - modifiers, strategy, followCursor, onVisibleChange, @@ -120,35 +139,6 @@ const WithTooltipPure = ({ ); }; -WithTooltipPure.defaultProps = { - svg: false, - trigger: 'click', - closeOnOutsideClick: false, - placement: 'top', - modifiers: [ - { - name: 'preventOverflow', - options: { - padding: 8, - }, - }, - { - name: 'offset', - options: { - offset: [8, 8], - }, - }, - { - name: 'arrow', - options: { - padding: 8, - }, - }, - ], - hasChrome: true, - defaultVisible: false, -}; - export interface WithTooltipStateProps extends Omit { startOpen?: boolean; onVisibleChange?: (visible: boolean) => void | boolean; diff --git a/code/yarn.lock b/code/yarn.lock index d559b86e7189..b7a4e604c38b 100644 --- a/code/yarn.lock +++ b/code/yarn.lock @@ -5508,7 +5508,7 @@ __metadata: ts-dedent: "npm:^2.0.0" typescript: "npm:^5.3.2" peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta peerDependenciesMeta: react: optional: true @@ -5789,8 +5789,8 @@ __metadata: ts-dedent: "npm:^2.0.0" util-deprecate: "npm:^1.0.2" peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta peerDependenciesMeta: react: optional: true @@ -6054,8 +6054,8 @@ __metadata: use-resize-observer: "npm:^9.1.0" util-deprecate: "npm:^1.0.2" peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta languageName: unknown linkType: soft @@ -6548,8 +6548,8 @@ __metadata: webpack: "npm:^5.65.0" peerDependencies: next: ^13.5.0 || ^14.0.0 - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta webpack: ^5.0.0 dependenciesMeta: sharp: @@ -6685,8 +6685,8 @@ __metadata: typescript: "npm:^5.3.2" webpack: "npm:5" peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta peerDependenciesMeta: typescript: optional: true @@ -6812,8 +6812,8 @@ __metadata: "@storybook/types": "workspace:*" typescript: "npm:^5.3.2" peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta languageName: unknown linkType: soft @@ -6835,8 +6835,8 @@ __metadata: typescript: "npm:^5.3.2" vite: "npm:^4.0.0" peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta vite: ^4.0.0 || ^5.0.0 languageName: unknown linkType: soft @@ -6850,8 +6850,8 @@ __metadata: "@storybook/react": "workspace:*" "@types/node": "npm:^18.0.0" peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta typescript: ">= 4.2.x" peerDependenciesMeta: typescript: @@ -6892,8 +6892,8 @@ __metadata: type-fest: "npm:~2.19" util-deprecate: "npm:^1.0.2" peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta typescript: ">= 4.2.x" peerDependenciesMeta: typescript: @@ -7278,8 +7278,8 @@ __metadata: ts-dedent: "npm:^2.0.0" typescript: "npm:^5.3.2" peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta peerDependenciesMeta: react: optional: true diff --git a/docs/api/parameters.md b/docs/api/parameters.md index 506dc051b3df..f70fa3395962 100644 --- a/docs/api/parameters.md +++ b/docs/api/parameters.md @@ -141,6 +141,51 @@ When specifying a custom sorting function, the function behaves like a typical J See [the guide](../writing-stories/naming-components-and-hierarchy/#sorting-stories) for usage examples. +### `test` + +Type: + +```ts +{ + clearMocks?: boolean; + mockReset?: boolean; + restoreMocks?: boolean; + dangerouslyIgnoreUnhandledErrors?: boolean; +} +``` + +#### `clearMocks` + +Type: `boolean` + +Default: `false` + +[Similar to Vitest](https://vitest.dev/config/#clearmocks), it will call `.mockClear()` on all spies created with `fn()` from `@storybook/test` when a story unmounts. This will clear mock history, but not reset its implementation to the default one. + +#### `mockReset` + +Type: `boolean` + +Default: `false` + +[Similar to Vitest](https://vitest.dev/config/#mockreset), it will call `.mockReset()` on all spies created with `fn()` from `@storybook/test` when a story unmounts. This will clear mock history and reset its implementation to an empty function (will return `undefined`). + +#### `restoreMocks` + +Type: `boolean` + +Default: `true` + +[Similar to Vitest](https://vitest.dev/config/#restoremocks), it will call `.restoreMocks()` on all spies created with `fn()` from `@storybook/test` when a story unmounts. This will clear mock history and reset its implementation to the original one. + +#### `dangerouslyIgnoreUnhandledErrors` + +Type: `boolean` + +Default: `false` + +Unhandled errors might cause false positive assertions. Setting this to `true` will prevent the [play function](../writing-stories/play-function.md) from failing and showing a warning when unhandled errors are thrown during execution. + --- ### Essential addons diff --git a/docs/api/portable-stories-jest.md b/docs/api/portable-stories-jest.md index 3abe9798112c..21e35920ac5f 100644 --- a/docs/api/portable-stories-jest.md +++ b/docs/api/portable-stories-jest.md @@ -26,10 +26,11 @@ Normally, Storybok composes a story and its [annotations](#annotations) automati -**Using `Next.js`?** You need to do two things differently when using portable stories in Jest with Next.js projects: +**Using `Next.js`?** You need to do three things differently when using portable stories in Jest with Next.js projects: - Configure the [`next/jest.js` transformer](https://nextjs.org/docs/pages/building-your-application/testing/jest#manual-setup), which will handle all of the necessary Next.js configuration for you. - Import [`composeStories`](#composestories) or [`composeStory`](#composestory) from the `@storybook/nextjs` package (e.g. `import { composeStories } from '@storybook/nextjs'`). +- Set up [internal module aliases](../get-started/nextjs.md#storybooknextjsexport-mocks) to ensure the framework configuration works correctly and to be able to mock and assert on them. diff --git a/docs/configure/images-and-assets.md b/docs/configure/images-and-assets.md index 77ffed4231db..02f32cac96ca 100644 --- a/docs/configure/images-and-assets.md +++ b/docs/configure/images-and-assets.md @@ -42,8 +42,8 @@ Configure a directory (or a list of directories) where your assets live when sta diff --git a/docs/configure/story-rendering.md b/docs/configure/story-rendering.md index fff549e8ef4c..87f8bc8c8d9b 100644 --- a/docs/configure/story-rendering.md +++ b/docs/configure/story-rendering.md @@ -2,7 +2,54 @@ title: 'Story rendering' --- -In Storybook, your stories render in a particular “preview” iframe (Canvas tab) inside the larger Storybook web application. The JavaScript build configuration of the preview is controlled by a [webpack](../builders/webpack.md) config, but you also may want to directly control the rendered HTML to help your stories render correctly. +In Storybook, your stories render in a particular “preview” iframe (also called the Canvas) inside the larger Storybook web application. The JavaScript build configuration of the preview is controlled by a [builder](../builders/index.md) config, but you also may want to run some code for every story or directly control the rendered HTML to help your stories render correctly. + +## Running code for every story + +Code executed in the preview file (`.storybook/preview.js|ts`) runs for every story in your Storybook. This is useful for setting up global styles, initializing libraries, or anything else required to render your components. + + + +Here's an example of how you might use the preview file to initialize a library that must run before your components render: + +```ts +// .storybook/preview.ts +// Replace your-renderer with the renderer you are using (e.g., react, vue3) +import { Preview } from '@storybook/your-renderer'; + +import { initialize } from '../lib/your-library'; + +initialize(); + +const preview: Preview = { + // ... +}; + +export default preview; +``` + + + + + +For example, with Vue, you can extend Storybook's application and register your library (e.g., [Fontawesome](https://github.com/FortAwesome/vue-fontawesome)). Or with Angular, add the package ([localize](https://angular.io/api/localize)) into your `polyfills.ts` and import it: + + + + + + + + ## Adding to <head> diff --git a/docs/get-started/nextjs.md b/docs/get-started/nextjs.md index b17cf19661b3..957f58f8bb31 100644 --- a/docs/get-started/nextjs.md +++ b/docs/get-started/nextjs.md @@ -131,7 +131,7 @@ This framework allows you to use Next.js's [next/image](https://nextjs.org/docs/ [Local images](https://nextjs.org/docs/pages/building-your-application/optimizing/images#local-images) are supported. ```jsx -// index.js +// index.jsx import Image from 'next/image'; import profilePic from '../public/me.png'; @@ -158,7 +158,7 @@ function Home() { [Remote images](https://nextjs.org/docs/pages/building-your-application/optimizing/images#remote-images) are also supported. ```jsx -// index.js +// index.jsx import Image from 'next/image'; export default function Home() { @@ -301,41 +301,6 @@ The default values on the stubbed router are as follows (see [globals](../essent ```ts // Default router const defaultRouter = { - push(...args) { - action('nextRouter.push')(...args); - return Promise.resolve(true); - }, - replace(...args) { - action('nextRouter.replace')(...args); - return Promise.resolve(true); - }, - reload(...args) { - action('nextRouter.reload')(...args); - }, - back(...args) { - action('nextRouter.back')(...args); - }, - forward() { - action('nextRouter.forward')(); - }, - prefetch(...args) { - action('nextRouter.prefetch')(...args); - return Promise.resolve(); - }, - beforePopState(...args) { - action('nextRouter.beforePopState')(...args); - }, - events: { - on(...args) { - action('nextRouter.events.on')(...args); - }, - off(...args) { - action('nextRouter.events.off')(...args); - }, - emit(...args) { - action('nextRouter.events.emit')(...args); - }, - }, // The locale should be configured globally: https://storybook.js.org/docs/essentials/toolbars-and-globals#globals locale: globals?.locale, asPath: '/', @@ -350,20 +315,33 @@ const defaultRouter = { }; ``` -### Actions integration caveats - -If you override a function, you lose the automatic action tab integration and have to build it out yourself, which looks something like this (make sure you install the `@storybook/addon-actions` package): - - +Additionally, the [`router` object](https://nextjs.org/docs/pages/api-reference/functions/use-router#router-object) contains all of the original methods (such as `push()`, `replace()`, etc.) as mock functions that can be manipulated and asserted on using [regular mock APIs](https://vitest.dev/api/mock.html). - +To override these defaults, you can use [parameters](../writing-stories/parameters.md) and [`beforeEach`](../writing-stories/mocking-modules.md#setting-up-and-cleaning-up): - +```ts +// .storybook/preview.ts +import { Preview } from '@storybook/react'; +// 👇 Must use this import path to have mocks typed correctly +import { getRouter } from '@storybook/nextjs/router.mock'; + +const preview: Preview = { + parameters: { + nextjs: { + // 👇 Override the default router properties + router: { + basePath: '/app/', + }, + }, + }, + async beforeEach() { + // 👇 Manipulate the default router method mocks + getRouter().push.mockImplementation(() => { + /* ... */ + }); + }, +}; +``` ## Next.js navigation @@ -493,43 +471,38 @@ The default values on the stubbed navigation context are as follows: ```ts // Default navigation context const defaultNavigationContext = { - push(...args) { - action('nextNavigation.push')(...args); - }, - replace(...args) { - action('nextNavigation.replace')(...args); - }, - forward(...args) { - action('nextNavigation.forward')(...args); - }, - back(...args) { - action('nextNavigation.back')(...args); - }, - prefetch(...args) { - action('nextNavigation.prefetch')(...args); - }, - refresh: () => { - action('nextNavigation.refresh')(); - }, pathname: '/', query: {}, }; ``` -### Actions integration caveats - -If you override a function, you lose the automatic action tab integration and have to build it out yourself, which looks something like this (make sure you install the `@storybook/addon-actions` package): - - +Additionally, the [`router` object](https://nextjs.org/docs/app/api-reference/functions/use-router#userouter) contains all of the original methods (such as `push()`, `replace()`, etc.) as mock functions that can be manipulated and asserted on using [regular mock APIs](https://vitest.dev/api/mock.html). - +To override these defaults, you can use [parameters](../writing-stories/parameters.md) and [`beforeEach`](../writing-stories/mocking-modules.md#setting-up-and-cleaning-up): - +```ts +// .storybook/preview.ts +import { Preview } from '@storybook/react'; +// 👇 Must use this import path to have mocks typed correctly +import { getRouter } from '@storybook/nextjs/navigation.mock'; + +const preview: Preview = { + parameters: { + nextjs: { + // 👇 Override the default navigation properties + navigation: { + pathname: '/app/', + }, + }, + }, + async beforeEach() { + // 👇 Manipulate the default navigation method mocks + getRouter().push.mockImplementation(() => { + /* ... */ + }); + }, +}; +``` ## Next.js Head @@ -618,7 +591,7 @@ export default HelloWorld; You can use your own babel config too. This is an example of how you can customize styled-jsx. -```json +```jsonc // .babelrc (or whatever config file you use) { "presets": [ @@ -645,7 +618,7 @@ This allows for cool things like zero-config Tailwind! (See [Next.js' example](h [Absolute imports](https://nextjs.org/docs/pages/building-your-application/configuring/absolute-imports-and-module-aliases#absolute-imports) from the root directory are supported. ```jsx -// index.js +// index.jsx // All good! import Button from 'components/button'; // Also good! @@ -671,6 +644,135 @@ import 'styles/globals.scss'; // ... ``` + + +Absolute imports **cannot** be mocked in stories/tests. See the [Mocking modules](#mocking-modules) section for more information. + + + +## Module aliases + +[Module aliases](https://nextjs.org/docs/app/building-your-application/configuring/absolute-imports-and-module-aliases#module-aliases) are also supported. + +```jsx +// index.jsx +// All good! +import Button from '@/components/button'; +// Also good! +import styles from '@/styles/HomePage.module.css'; + +export default function HomePage() { + return ( + <> +

Hello World

+