Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Docs: Module mocking, and more #26858

Merged
merged 48 commits into from May 6, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
1efccbe
WIP
kylegach Apr 15, 2024
0242371
Split into 3 pages
kylegach Apr 16, 2024
ff3f2e9
Add Modules section to Next.js docs
JReinhold Apr 16, 2024
92e1382
More mocking content
kylegach Apr 16, 2024
75b0820
Updates for Next.js
kylegach Apr 16, 2024
299a2c2
Simplify Building pages with Storybook guide
kylegach Apr 16, 2024
ec6742b
Update Decorators and Interaction tests pages
kylegach Apr 17, 2024
abc9b7c
Add missing TODOs
kylegach Apr 17, 2024
194a9bc
Add vite example
kylegach Apr 17, 2024
f2f4d13
clarify mock->original import limitations.
JReinhold Apr 17, 2024
e7c0676
add paragraph on mocking external modules.
JReinhold Apr 17, 2024
5d8ee28
add docs on `beforeEach`, `cleanup`, `parameters.test`
JReinhold Apr 17, 2024
4faf1ae
simplify subpath example in nextjs
JReinhold Apr 17, 2024
2ac03c4
Address feedback
kylegach Apr 18, 2024
b1da9c9
add getPackageAliases example
JReinhold Apr 18, 2024
71a4cea
add examples to next mock docs
JReinhold Apr 18, 2024
5bab73a
improve documentation for parameters.test.dangerouslyIgnoreUnhandledE…
JReinhold Apr 18, 2024
46c31eb
remove docs on default router and navigation contexts
JReinhold Apr 18, 2024
eef3290
add reasons for aliases in nextjs+jest
JReinhold Apr 18, 2024
f88bea5
Add back nextjs router/navigation override instructions
kylegach Apr 19, 2024
3f7e44c
Improve MSW snippets
kylegach Apr 19, 2024
8eaeb94
Address MSW snippets feedback
kylegach Apr 19, 2024
f4914e3
Prettify snippets
kylegach Apr 19, 2024
cf2be73
Add new actions to the bottom of the panel and auto scroll
kasperpeulen Apr 17, 2024
42773e3
Address review
kasperpeulen Apr 17, 2024
e850d36
add cache.mock entrypoint to nextjs
JReinhold Apr 17, 2024
401fd26
Try another mock naming convention
kasperpeulen Apr 16, 2024
9920353
Bind this
kasperpeulen Apr 16, 2024
b55673f
Update snapshot
kasperpeulen Apr 16, 2024
a033167
Fix name
kasperpeulen Apr 16, 2024
537fbb2
Hide some junk of next for nwo
kasperpeulen Apr 16, 2024
7cacdfa
Update code/frameworks/nextjs/src/export-mocks/navigation/index.ts
kasperpeulen Apr 16, 2024
af31a1f
Update code/frameworks/nextjs/src/export-mocks/navigation/index.ts
kasperpeulen Apr 16, 2024
789991f
Update code/frameworks/nextjs/src/export-mocks/navigation/index.ts
kasperpeulen Apr 16, 2024
17c5152
Update code/frameworks/nextjs/src/export-mocks/navigation/index.ts
kasperpeulen Apr 16, 2024
fc7e664
Hide some junk of next for now
kasperpeulen Apr 16, 2024
5fafb46
Show redirect and next/cache spies
kasperpeulen Apr 16, 2024
a9ce1d4
Apply suggestions from code review
kylegach Apr 30, 2024
01c6509
Address feedback
kylegach Apr 30, 2024
39689ac
Merge branch 'kasper/module-mocking' into module-mocking-docs
kylegach Apr 30, 2024
1dca308
Snippetize examples
kylegach May 1, 2024
6bf05a1
Remove unnecessary snippets
kylegach May 1, 2024
c715248
Prettify snippets
kylegach May 1, 2024
7be08e3
Address feedback
kylegach May 1, 2024
a9d9bfa
Update decorator snippets
kylegach May 3, 2024
d2f7fe8
Address feedback
kylegach May 3, 2024
dfa82ab
Address feedback
kylegach May 6, 2024
2a0d245
Merge branch 'next' into module-mocking-docs
kylegach May 6, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
26 changes: 26 additions & 0 deletions docs/configure/story-rendering.md
Expand Up @@ -8,6 +8,30 @@ In Storybook, your stories render in a particular “preview” iframe (also cal

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.

<If notRenderer={['angular', 'vue']}>

Here's an example of how you might use the preview file to initialize a library that must run before your components render:
kylegach marked this conversation as resolved.
Show resolved Hide resolved

```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;
```

</If>

<If renderer={['angular', 'vue']}>

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:

<!-- prettier-ignore-start -->
Expand All @@ -25,6 +49,8 @@ For example, with Vue, you can extend Storybook's application and register your

<!-- prettier-ignore-end -->

</If>

## Adding to &#60;head&#62;

If you need to add extra elements to the `head` of the preview iframe, for instance, to load static stylesheets, font files, or similar, you can create a file called [`.storybook/preview-head.html`](./index.md#configure-story-rendering) and add tags like this:
Expand Down
30 changes: 15 additions & 15 deletions docs/get-started/nextjs.md
Expand Up @@ -326,7 +326,7 @@ import { Preview } from '@storybook/react';
import { getRouter } from '@storybook/nextjs/router.mock';

const preview: Preview = {
paramters: {
parameters: {
nextjs: {
// 👇 Override the default router properties
router: {
Expand Down Expand Up @@ -487,7 +487,7 @@ import { Preview } from '@storybook/react';
import { getRouter } from '@storybook/nextjs/navigation.mock';

const preview: Preview = {
paramters: {
parameters: {
nextjs: {
// 👇 Override the default navigation properties
navigation: {
Expand Down Expand Up @@ -591,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": [
Expand Down Expand Up @@ -677,7 +677,7 @@ As an alternative to [module aliases](#module-aliases), you can use [subpath imp

To configure subpath imports, you define the `imports` property in your project's `package.json` file. This property maps the subpath to the actual file path. The example below configures subpath imports for all modules in the project:

```json
```jsonc
// package.json
{
"imports": {
Expand Down Expand Up @@ -741,11 +741,11 @@ export const getUserFromSession = fn(actual.getUserFromSession);

#### With subpath imports

If you're using [subpath imports](#subpath-imports), you can adjust your configuration to apply [conditions](../writing-stories/mocking-modules.md#conditional-imports) so that the mocked module is used inside Storybook. The example below configures subpath imports for four internal modules, which are then mocked in Storybook:
If you're using [subpath imports](#subpath-imports), you can adjust your configuration to apply [conditions](../writing-stories/mocking-modules.md#subpath-imports) so that the mocked module is used inside Storybook. The example below configures subpath imports for four internal modules, which are then mocked in Storybook:

<!-- TODO: Snippetize -->

```json
```jsonc
// package.json
{
"imports": {
Expand Down Expand Up @@ -827,7 +827,7 @@ module.exports = {

Calls to `getConfig` would return the following object when called within Storybook:

```json
```jsonc
// Runtime config
{
"serverRuntimeConfig": {},
Expand Down Expand Up @@ -862,7 +862,7 @@ Below is an example of how to add SVGR support to Storybook with this framework.

Storybook handles most [Typescript](https://www.typescriptlang.org/) configurations, but this framework adds additional support for Next.js's support for [Absolute Imports and Module path aliases](https://nextjs.org/docs/pages/building-your-application/configuring/absolute-imports-and-module-aliases). In short, it takes into account your `tsconfig.json`'s [baseUrl](https://www.typescriptlang.org/tsconfig#baseUrl) and [paths](https://www.typescriptlang.org/tsconfig#paths). Thus, a `tsconfig.json` like the one below would work out of the box.

```json
```jsonc
// tsconfig.json
{
"compilerOptions": {
Expand Down Expand Up @@ -918,7 +918,7 @@ In the future we will provide better mocking support in Storybook and support fo

You can test your stories in a Jest environment by using the [portable stories](../api/portable-stories-jest.md) API.

When using portable stories with Next.js, you need to mock the Next.js modules that your components depend on. You can use the [`@storybook/nextjs/export-mocks` module](#storybooknextjsexport-mocks) to generate the aliases needed to set up portable stories in a Jest environment. This is needed because, to replicate Next.js configuration, Storybook sets up aliases in Webpack to make testing and developing your components easier. If you make use of the advanced functionality like the built-in mocks for common Next.js modules, you need to set up this aliasing in your Jest environment as well.
When using portable stories with Next.js, you need to mock the Next.js modules on which your components depend. You can use the [`@storybook/nextjs/export-mocks` module](#storybooknextjsexport-mocks) to generate the aliases needed to set up portable stories in a Jest environment. This is needed because, to replicate Next.js configuration, Storybook sets up aliases in Webpack to make testing and developing your components easier. If you make use of the advanced functionality like the built-in mocks for common Next.js modules, you need to set up this aliasing in your Jest environment as well.

## Notes for Yarn v2 and v3 users

Expand Down Expand Up @@ -980,7 +980,7 @@ Make sure you are treating image imports the same way you treat them when using

Before using this framework, image imports would import the raw path to the image (e.g. `'static/media/stories/assets/logo.svg'`). Now image imports work the "Next.js way", meaning that you now get an object when importing an image. For example:

```json
```jsonc
// Image import object
{
"src": "static/media/stories/assets/logo.svg",
Expand Down Expand Up @@ -1059,7 +1059,7 @@ export default createJestConfig(config);

Type: `typeof import('next/cache')`

Exports mocks that replaces the actual implementation of `next/cache` exports. Use these to mock implementations or assert on mock calls in a story's [play function](../writing-stories/play-function.md).
This module exports mocked implementations of the `next/cache` module's exports. You can use it to create your own mock implementations or assert on mock calls in a story's [play function](../writing-stories/play-function.md).

<!-- TODO: Snippetize -->

Expand Down Expand Up @@ -1095,15 +1095,15 @@ export const Submitted: Story = {

Type: [`cookies`](https://nextjs.org/docs/app/api-reference/functions/cookies#cookiessetname-value-options), [`headers`](https://nextjs.org/docs/app/api-reference/functions/headers) and [`draftMode`](https://nextjs.org/docs/app/api-reference/functions/draft-mode) from Next.js

Exports _writable_ mocks that replaces the actual implementation of `next/headers` exports. Use this to set up cookies or headers that are read in your story, and to later assert that they have been called.
This module exports _writable_ mocked implementations of the `next/headers` module's exports. You can use it to set up cookies or headers that are read in your story, and to later assert that they have been called.

Next.js's default [`headers()`](https://nextjs.org/docs/app/api-reference/functions/headers) export is read-only, but this module exposes methods allowing you to write to the headers:

- **`headers().append(name: string, value: string)`**: Appends the value to the header if it exists already.
- **`headers().delete(name: string)`**: Deletes the header
- **`headers().set(name: string, value: string)`**: Sets the header to the value provided.

For cookies, you can use the existing API to write them, eg. `cookies().set('firstName', 'Jane')`.
For cookies, you can use the existing API to write them. E.g., `cookies().set('firstName', 'Jane')`.

Because `headers()`, `cookies()` and their sub-functions are all mocks you can use any [mock utilities](https://vitest.dev/api/mock.html) in your stories, like `headers().getAll.mock.calls`.

Expand Down Expand Up @@ -1145,7 +1145,7 @@ export const LoggedInEurope: Story = {

Type: `typeof import('next/navigation') & getRouter: () => ReturnType<typeof import('next/navigation')['useRouter']>`

Exports mocks that replaces the actual implementation of `next/navigation` exports. Also exports a `getRouter` function that returns a mocked version of [Next.js's `router` object from `useRouter`](https://nextjs.org/docs/app/api-reference/functions/use-router#userouter), so that the properties can be manipulated and asserted on. Use these to mock implementations or assert on mock calls in a story's [play function](../writing-stories/play-function.md).
This module exports mocked implementations of the `next/navigation` module's exports. It also exports a `getRouter` function that returns a mocked version of [Next.js's `router` object from `useRouter`](https://nextjs.org/docs/app/api-reference/functions/use-router#userouter), allowing the properties to be manipulated and asserted on. You can use it mock implementations or assert on mock calls in a story's [play function](../writing-stories/play-function.md).

<!-- TODO: Snippetize -->

Expand Down Expand Up @@ -1194,7 +1194,7 @@ export const GoBack: Story = {

Type: `typeof import('next/router') & getRouter: () => ReturnType<typeof import('next/router')['useRouter']>`

Exports mocks that replaces the actual implementation of `next/navigation` exports. Also exports a `getRouter` function that returns a mocked version of [Next.js's `router` object from `useRouter`](https://nextjs.org/docs/pages/api-reference/functions/use-router#router-object), so that the properties can be manipulated and asserted on. Use these to mock implementations or assert on mock calls in a story's [play function](../writing-stories/play-function.md).
This module exports mocked implementations of the `next/router` module's exports. It also exports a `getRouter` function that returns a mocked version of [Next.js's `router` object from `useRouter`](https://nextjs.org/docs/pages/api-reference/functions/use-router#router-object), allowing the properties to be manipulated and asserted on. You can use it mock implementations or assert on mock calls in a story's [play function](../writing-stories/play-function.md).

<!-- TODO: Snippetize -->

Expand Down
Expand Up @@ -59,8 +59,8 @@ export const MockedSuccess: Story = {
graphql.query('AllInfoQuery', () => {
return new HttpResponse.json({
kylegach marked this conversation as resolved.
Show resolved Hide resolved
data: {
allFilms: {
films,
AllInfo: {
...TestData,
},
},
});
Expand Down
Expand Up @@ -63,8 +63,8 @@ export const MockedSuccess = {
graphql.query('AllInfoQuery', () => {
return new HttpResponse.json({
data: {
allFilms: {
films,
AllInfo: {
kylegach marked this conversation as resolved.
Show resolved Hide resolved
...TestData,
},
},
});
Expand Down
Expand Up @@ -67,8 +67,8 @@ export const MockedSuccess: Story = {
graphql.query('AllInfoQuery', () => {
return new HttpResponse.json({
data: {
allFilms: {
films,
AllInfo: {
...TestData,
},
}
});
Expand Down
Expand Up @@ -67,8 +67,8 @@ export const MockedSuccess: Story = {
graphql.query('AllInfoQuery', () => {
return new HttpResponse.json({
data: {
allFilms: {
films,
AllInfo: {
...TestData,
},
}
});
Expand Down
Expand Up @@ -42,8 +42,8 @@ export const MockedSuccess = {
graphql.query('AllInfoQuery', () => {
return new HttpResponse.json({
data: {
allFilms: {
films,
AllInfo: {
...TestData,
},
},
});
Expand Down
Expand Up @@ -47,8 +47,8 @@ export const MockedSuccess: Story = {
graphql.query('AllInfoQuery', () => {
return new HttpResponse.json({
data: {
allFilms: {
films,
AllInfo: {
...TestData,
},
},
});
Expand Down
Expand Up @@ -47,8 +47,8 @@ export const MockedSuccess: Story = {
graphql.query('AllInfoQuery', () => {
return new HttpResponse.json({
data: {
allFilms: {
films,
AllInfo: {
...TestData,
},
},
});
Expand Down
4 changes: 2 additions & 2 deletions docs/snippets/vue/msw-addon-configure-handlers-graphql.js.mdx
Expand Up @@ -45,8 +45,8 @@ export const MockedSuccess = {
graphql.query('AllInfoQuery', () => {
return new HttpResponse.json({
data: {
allFilms: {
films,
AllInfo: {
...TestData,
},
},
});
Expand Down
Expand Up @@ -50,8 +50,8 @@ export const MockedSuccess: Story = {
graphql.query('AllInfoQuery', () => {
return new HttpResponse.json({
data: {
allFilms: {
films,
AllInfo: {
...TestData,
},
},
});
Expand Down
4 changes: 2 additions & 2 deletions docs/snippets/vue/msw-addon-configure-handlers-graphql.ts.mdx
Expand Up @@ -50,8 +50,8 @@ export const MockedSuccess: Story = {
graphql.query('AllInfoQuery', () => {
return new HttpResponse.json({
data: {
allFilms: {
films,
AllInfo: {
...TestData,
},
},
});
Expand Down
8 changes: 4 additions & 4 deletions docs/toc.js
Expand Up @@ -147,8 +147,8 @@ module.exports = {
type: 'menu',
children: [
{
pathSegment: 'mocking-providers',
title: 'Providers',
pathSegment: 'mocking-modules',
title: 'Modules',
type: 'link',
},
{
Expand All @@ -157,8 +157,8 @@ module.exports = {
type: 'link',
},
{
pathSegment: 'mocking-modules',
title: 'Modules',
pathSegment: 'mocking-providers',
title: 'Providers',
type: 'link',
},
],
Expand Down
10 changes: 5 additions & 5 deletions docs/writing-stories/build-pages-with-storybook.md
Expand Up @@ -73,19 +73,19 @@ This approach is beneficial when the various subcomponents export a complex list

## Mocking connected components

If you need to render a connected component in Storybook, you can mock the data or modules that component depends on. There are various layers in which you can do that.
Connected components are components that depend on external data or services. For example, a full page component is often a connected component. When you render a connected component in Storybook, you need to mock the data or modules that the component depends on. There are various layers in which you can do that.

### [Mocking providers](./mocking-providers.md)
### [Mocking imports](./mocking-modules.md)

Components can receive data or configuration from context providers. For example, a styled component might access its theme from a ThemeProvider or Redux uses React context to provide components access to app data. You can mock a provider and the value it's providing and wrap your component with it in your stories.
Components can depend on modules that are imported into the component file. These can be from external packages or internal to your project. When rendering those components in Storybook or testing them, you may want to mock those modules to control their behavior.

### [Mocking API Services](./mocking-network-requests.md)

For components that make network requests (e.g., fetching data from a REST or GraphQL API), you can mock those requests in your stories.

### [Mocking imports](./mocking-modules.md)
### [Mocking providers](./mocking-providers.md)

Components can also depend on modules that are imported into the component file. These can be from external packages or internal to your project. When rendering those components in Storybook or testing them, you may want to mock those modules to control their behavior.
Components can receive data or configuration from context providers. For example, a styled component might access its theme from a ThemeProvider or Redux uses React context to provide components access to app data. You can mock a provider and the value it's providing and wrap your component with it in your stories.

<IfRenderer renderer={['react', 'solid']}>

Expand Down
4 changes: 4 additions & 0 deletions docs/writing-stories/decorators.md
Expand Up @@ -80,8 +80,12 @@ const preview: Preview = {
export default preview;
```

<Callout variant="info" icon="💡">

For another example, see the section on [configuring the mock provider](./mocking-providers.md#configuring-the-mock-provider), which demonstrates how to use the same technique to change which theme is provided to the component.
kylegach marked this conversation as resolved.
Show resolved Hide resolved

</Callout>

### Using decorators to provide data

If your components are “connected” and require side-loaded data to render, you can use decorators to provide that data in a mocked way without having to refactor your components to take that data as an arg. There are several techniques to achieve this. Depending on exactly how you are loading that data. Read more in the [building pages in Storybook](./build-pages-with-storybook.md) section.
Expand Down