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

+