Skip to content

Commit

Permalink
Address feedback
Browse files Browse the repository at this point in the history
- Re-order TOC items
- Fix conditional content
- Improve nextjs module API references
- Make example snippets more consistent
- Re-organize subpath imports section of module mocking guide
- Fix typos, grammar
  • Loading branch information
kylegach committed Apr 30, 2024
1 parent a9ce1d4 commit 01c6509
Show file tree
Hide file tree
Showing 17 changed files with 101 additions and 62 deletions.
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:

```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({
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: {
...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.

</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

0 comments on commit 01c6509

Please sign in to comment.