From c9dc4e01a05b21d9163c7d29d88ea31fd1468c20 Mon Sep 17 00:00:00 2001
From: Jorge Ji
Date: Tue, 12 Mar 2024 19:57:43 +0000
Subject: [PATCH 001/117] Fix #25759: Panel resizing is not applied when done
too fast When trying to resize any panel too fast, the changes would not be
applied. The fix was done by changing a comparison condition in
"useLayoutEffect()". Instead of using a variable
("prevManagerLayoutStateRef") that was also modified by another function
("useEffect()") that could execute at the same time, it now uses a variable
that is only modified by the own function, making it so that the supposed
layout values are not changed after they are set.
---
code/ui/manager/src/components/layout/Layout.tsx | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/code/ui/manager/src/components/layout/Layout.tsx b/code/ui/manager/src/components/layout/Layout.tsx
index a17636ceadbd..f0c087b2dcad 100644
--- a/code/ui/manager/src/components/layout/Layout.tsx
+++ b/code/ui/manager/src/components/layout/Layout.tsx
@@ -81,7 +81,7 @@ const useLayoutSyncingState = ({
useLayoutEffect(() => {
if (
internalDraggingSizeState.isDragging || // wait with syncing managerLayoutState until user is done dragging
- layoutStateIsEqual(prevManagerLayoutStateRef.current, internalDraggingSizeState) // don't sync managerLayoutState if it doesn't differ from internalDraggingSizeState
+ layoutStateIsEqual(managerLayoutState, internalDraggingSizeState) // don't sync managerLayoutState if it doesn't differ from internalDraggingSizeStatee)
) {
return;
}
@@ -95,7 +95,7 @@ const useLayoutSyncingState = ({
...nextState,
};
setManagerLayoutState(nextState);
- }, [internalDraggingSizeState, setManagerLayoutState]);
+ }, [internalDraggingSizeState, managerLayoutState, setManagerLayoutState]);
const isPagesShown =
managerLayoutState.viewMode !== 'story' && managerLayoutState.viewMode !== 'docs';
From 32e854a5a89d9c3353d458ca1027bd61dfd27584 Mon Sep 17 00:00:00 2001
From: Michael Shilman
Date: Sun, 24 Mar 2024 23:35:44 +0800
Subject: [PATCH 002/117] Add metaTags, tag removal
---
code/addons/links/package.json | 2 +-
code/lib/codemod/package.json | 2 +-
code/lib/core-server/package.json | 2 +-
.../src/utils/StoryIndexGenerator.ts | 23 +++++++---
code/lib/csf-tools/package.json | 2 +-
code/lib/manager-api/package.json | 2 +-
code/lib/preview-api/package.json | 2 +-
.../src/modules/store/csf/prepareStory.ts | 8 +++-
.../template/stories/tags.stories.ts | 4 ++
code/lib/source-loader/package.json | 2 +-
code/lib/types/package.json | 2 +-
code/lib/types/src/modules/indexer.ts | 1 +
code/package.json | 2 +-
code/renderers/server/package.json | 2 +-
code/ui/blocks/package.json | 2 +-
code/ui/components/package.json | 2 +-
code/yarn.lock | 42 +++++++++----------
17 files changed, 62 insertions(+), 40 deletions(-)
diff --git a/code/addons/links/package.json b/code/addons/links/package.json
index ed71883923f1..6a135b3c576a 100644
--- a/code/addons/links/package.json
+++ b/code/addons/links/package.json
@@ -67,7 +67,7 @@
"prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/addon-bundle.ts"
},
"dependencies": {
- "@storybook/csf": "^0.1.2",
+ "@storybook/csf": "0.1.4--canary.80.632d12b.0",
"@storybook/global": "^5.0.0",
"ts-dedent": "^2.0.0"
},
diff --git a/code/lib/codemod/package.json b/code/lib/codemod/package.json
index 1213fe7dd30f..8c355eb93454 100644
--- a/code/lib/codemod/package.json
+++ b/code/lib/codemod/package.json
@@ -57,7 +57,7 @@
"@babel/core": "^7.23.2",
"@babel/preset-env": "^7.23.2",
"@babel/types": "^7.23.0",
- "@storybook/csf": "^0.1.2",
+ "@storybook/csf": "0.1.4--canary.80.632d12b.0",
"@storybook/csf-tools": "workspace:*",
"@storybook/node-logger": "workspace:*",
"@storybook/types": "workspace:*",
diff --git a/code/lib/core-server/package.json b/code/lib/core-server/package.json
index b1c6880972ad..1526c391dff0 100644
--- a/code/lib/core-server/package.json
+++ b/code/lib/core-server/package.json
@@ -62,7 +62,7 @@
"@storybook/channels": "workspace:*",
"@storybook/core-common": "workspace:*",
"@storybook/core-events": "workspace:*",
- "@storybook/csf": "^0.1.2",
+ "@storybook/csf": "0.1.4--canary.80.632d12b.0",
"@storybook/csf-tools": "workspace:*",
"@storybook/docs-mdx": "3.0.0",
"@storybook/global": "^5.0.0",
diff --git a/code/lib/core-server/src/utils/StoryIndexGenerator.ts b/code/lib/core-server/src/utils/StoryIndexGenerator.ts
index a650f04c1ffa..0e036f685355 100644
--- a/code/lib/core-server/src/utils/StoryIndexGenerator.ts
+++ b/code/lib/core-server/src/utils/StoryIndexGenerator.ts
@@ -23,7 +23,7 @@ import { userOrAutoTitleFromSpecifier, sortStoriesV7 } from '@storybook/preview-
import { commonGlobOptions, normalizeStoryPath } from '@storybook/core-common';
import { logger, once } from '@storybook/node-logger';
import { getStorySortParameter } from '@storybook/csf-tools';
-import { storyNameFromExport, toId } from '@storybook/csf';
+import { storyNameFromExport, toId, combineTags } from '@storybook/csf';
import { analyze } from '@storybook/docs-mdx';
import dedent from 'ts-dedent';
import { autoName } from './autoName';
@@ -55,6 +55,7 @@ export type StoryIndexGeneratorOptions = {
};
export const AUTODOCS_TAG = 'autodocs';
+export const NO_AUTODOCS_TAG = `-${AUTODOCS_TAG}`;
export const STORIES_MDX_TAG = 'stories-mdx';
export const PLAY_FN_TAG = 'play-fn';
@@ -291,7 +292,7 @@ export class StoryIndexGenerator {
const title = input.title ?? defaultMakeTitle();
// eslint-disable-next-line no-underscore-dangle
const id = input.__id ?? toId(input.metaId ?? title, storyNameFromExport(input.exportName));
- const tags = (input.tags || []).concat('story');
+ const tags = combineTags(...(input.tags ?? []), ...(input.metaTags ?? []), 'story');
return {
type: 'story',
@@ -300,6 +301,7 @@ export class StoryIndexGenerator {
name,
title,
importPath,
+ metaTags: input.metaTags ?? [],
tags,
};
});
@@ -312,7 +314,9 @@ export class StoryIndexGenerator {
const hasAutodocsTag = entries.some((entry) => entry.tags.includes(AUTODOCS_TAG));
const isStoriesMdx = entries.some((entry) => entry.tags.includes(STORIES_MDX_TAG));
const createDocEntry =
- autodocs === true || (autodocs === 'tag' && hasAutodocsTag) || isStoriesMdx;
+ (autodocs === true && !entries.some((entry) => entry.tags.includes(NO_AUTODOCS_TAG))) ||
+ (autodocs === 'tag' && hasAutodocsTag) ||
+ isStoriesMdx;
if (createDocEntry && this.options.build?.test?.disableAutoDocs !== true) {
const name = this.options.docs.defaultName ?? 'Docs';
@@ -326,7 +330,12 @@ export class StoryIndexGenerator {
name,
importPath,
type: 'docs',
- tags: [...metaTags, 'docs', ...(!hasAutodocsTag && !isStoriesMdx ? [AUTODOCS_TAG] : [])],
+ metaTags,
+ tags: combineTags(
+ ...metaTags,
+ 'docs',
+ ...(!hasAutodocsTag && !isStoriesMdx ? [AUTODOCS_TAG] : [])
+ ),
storiesImports: [],
});
}
@@ -433,7 +442,11 @@ export class StoryIndexGenerator {
storiesImports: sortedDependencies.map((dep) => dep.entries[0].importPath),
type: 'docs',
// FIXME: update this to use the index entry's metaTags once we update this to run on `IndexInputs`
- tags: [...(result.tags || []), csfEntry ? 'attached-mdx' : 'unattached-mdx', 'docs'],
+ tags: combineTags(
+ ...(result.tags || []),
+ csfEntry ? 'attached-mdx' : 'unattached-mdx',
+ 'docs'
+ ),
};
return docsEntry;
} catch (err) {
diff --git a/code/lib/csf-tools/package.json b/code/lib/csf-tools/package.json
index 7aff878d6a68..39d063d32d01 100644
--- a/code/lib/csf-tools/package.json
+++ b/code/lib/csf-tools/package.json
@@ -46,7 +46,7 @@
"@babel/parser": "^7.23.0",
"@babel/traverse": "^7.23.2",
"@babel/types": "^7.23.0",
- "@storybook/csf": "^0.1.2",
+ "@storybook/csf": "0.1.4--canary.80.632d12b.0",
"@storybook/types": "workspace:*",
"fs-extra": "^11.1.0",
"recast": "^0.23.5",
diff --git a/code/lib/manager-api/package.json b/code/lib/manager-api/package.json
index d9b7b6b39cf2..9fd9941ef00f 100644
--- a/code/lib/manager-api/package.json
+++ b/code/lib/manager-api/package.json
@@ -47,7 +47,7 @@
"@storybook/channels": "workspace:*",
"@storybook/client-logger": "workspace:*",
"@storybook/core-events": "workspace:*",
- "@storybook/csf": "^0.1.2",
+ "@storybook/csf": "0.1.4--canary.80.632d12b.0",
"@storybook/global": "^5.0.0",
"@storybook/icons": "^1.2.5",
"@storybook/router": "workspace:*",
diff --git a/code/lib/preview-api/package.json b/code/lib/preview-api/package.json
index 8c64089acdec..23bd408491db 100644
--- a/code/lib/preview-api/package.json
+++ b/code/lib/preview-api/package.json
@@ -47,7 +47,7 @@
"@storybook/channels": "workspace:*",
"@storybook/client-logger": "workspace:*",
"@storybook/core-events": "workspace:*",
- "@storybook/csf": "^0.1.2",
+ "@storybook/csf": "0.1.4--canary.80.632d12b.0",
"@storybook/global": "^5.0.0",
"@storybook/types": "workspace:*",
"@types/qs": "^6.9.5",
diff --git a/code/lib/preview-api/src/modules/store/csf/prepareStory.ts b/code/lib/preview-api/src/modules/store/csf/prepareStory.ts
index 0e5ea37500cd..ad35eff50f07 100644
--- a/code/lib/preview-api/src/modules/store/csf/prepareStory.ts
+++ b/code/lib/preview-api/src/modules/store/csf/prepareStory.ts
@@ -20,7 +20,7 @@ import type {
StoryContextForLoaders,
StrictArgTypes,
} from '@storybook/types';
-import { includeConditionalArg } from '@storybook/csf';
+import { includeConditionalArg, combineTags } from '@storybook/csf';
import { applyHooks } from '../../addons';
import { combineParameters } from '../parameters';
@@ -140,7 +140,11 @@ function preparePartialAnnotations(
// anything at render time. The assumption is that as we don't load all the stories at once, this
// will have a limited cost. If this proves misguided, we can refactor it.
- const tags = [...(storyAnnotations?.tags || componentAnnotations.tags || []), 'story'];
+ const tags = combineTags(
+ ...(storyAnnotations?.tags ?? []),
+ ...(componentAnnotations.tags ?? []),
+ 'story'
+ );
const parameters: Parameters = combineParameters(
projectAnnotations.parameters,
diff --git a/code/lib/preview-api/template/stories/tags.stories.ts b/code/lib/preview-api/template/stories/tags.stories.ts
index 61ffe8811429..9af5be843d5f 100644
--- a/code/lib/preview-api/template/stories/tags.stories.ts
+++ b/code/lib/preview-api/template/stories/tags.stories.ts
@@ -37,3 +37,7 @@ export const TestOnly = {
export const DevOnly = {
tags: ['dev-only'],
};
+
+export const TagRemoval = {
+ tags: ['-component-two'],
+};
diff --git a/code/lib/source-loader/package.json b/code/lib/source-loader/package.json
index dca73497297f..c0578007e242 100644
--- a/code/lib/source-loader/package.json
+++ b/code/lib/source-loader/package.json
@@ -45,7 +45,7 @@
"prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts"
},
"dependencies": {
- "@storybook/csf": "^0.1.2",
+ "@storybook/csf": "0.1.4--canary.80.632d12b.0",
"@storybook/types": "workspace:*",
"estraverse": "^5.2.0",
"lodash": "^4.17.21",
diff --git a/code/lib/types/package.json b/code/lib/types/package.json
index c936f0d7514d..c09dfb384b39 100644
--- a/code/lib/types/package.json
+++ b/code/lib/types/package.json
@@ -49,7 +49,7 @@
"file-system-cache": "2.3.0"
},
"devDependencies": {
- "@storybook/csf": "^0.1.2",
+ "@storybook/csf": "0.1.4--canary.80.632d12b.0",
"@types/fs-extra": "^11.0.1",
"@types/node": "^18.0.0",
"typescript": "^5.3.2"
diff --git a/code/lib/types/src/modules/indexer.ts b/code/lib/types/src/modules/indexer.ts
index 1064354eefe0..42f0e21640e8 100644
--- a/code/lib/types/src/modules/indexer.ts
+++ b/code/lib/types/src/modules/indexer.ts
@@ -73,6 +73,7 @@ export interface BaseIndexEntry {
id: StoryId;
name: StoryName;
title: ComponentTitle;
+ metaTags?: Tag[];
tags?: Tag[];
importPath: Path;
}
diff --git a/code/package.json b/code/package.json
index 6e2885c0d0c1..c882ef4cf53d 100644
--- a/code/package.json
+++ b/code/package.json
@@ -127,7 +127,7 @@
"@storybook/core-events": "workspace:*",
"@storybook/core-server": "workspace:*",
"@storybook/core-webpack": "workspace:*",
- "@storybook/csf": "^0.1.2",
+ "@storybook/csf": "0.1.4--canary.80.632d12b.0",
"@storybook/csf-plugin": "workspace:*",
"@storybook/csf-tools": "workspace:*",
"@storybook/docs-tools": "workspace:*",
diff --git a/code/renderers/server/package.json b/code/renderers/server/package.json
index 321308cbbf11..5a18737a65ca 100644
--- a/code/renderers/server/package.json
+++ b/code/renderers/server/package.json
@@ -46,7 +46,7 @@
"prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts"
},
"dependencies": {
- "@storybook/csf": "^0.1.2",
+ "@storybook/csf": "0.1.4--canary.80.632d12b.0",
"@storybook/csf-tools": "workspace:*",
"@storybook/global": "^5.0.0",
"@storybook/preview-api": "workspace:*",
diff --git a/code/ui/blocks/package.json b/code/ui/blocks/package.json
index 683c5fbf25d7..a7b11a866577 100644
--- a/code/ui/blocks/package.json
+++ b/code/ui/blocks/package.json
@@ -48,7 +48,7 @@
"@storybook/client-logger": "workspace:*",
"@storybook/components": "workspace:*",
"@storybook/core-events": "workspace:*",
- "@storybook/csf": "^0.1.2",
+ "@storybook/csf": "0.1.4--canary.80.632d12b.0",
"@storybook/docs-tools": "workspace:*",
"@storybook/global": "^5.0.0",
"@storybook/icons": "^1.2.5",
diff --git a/code/ui/components/package.json b/code/ui/components/package.json
index 56a624b9e90c..1160d1a0d126 100644
--- a/code/ui/components/package.json
+++ b/code/ui/components/package.json
@@ -61,7 +61,7 @@
"dependencies": {
"@radix-ui/react-slot": "^1.0.2",
"@storybook/client-logger": "workspace:*",
- "@storybook/csf": "^0.1.2",
+ "@storybook/csf": "0.1.4--canary.80.632d12b.0",
"@storybook/global": "^5.0.0",
"@storybook/icons": "^1.2.5",
"@storybook/theming": "workspace:*",
diff --git a/code/yarn.lock b/code/yarn.lock
index 664e1ed4eb15..e8d2a6a1df31 100644
--- a/code/yarn.lock
+++ b/code/yarn.lock
@@ -5256,7 +5256,7 @@ __metadata:
dependencies:
"@storybook/client-logger": "workspace:*"
"@storybook/core-events": "workspace:*"
- "@storybook/csf": "npm:^0.1.2"
+ "@storybook/csf": "npm:0.1.4--canary.80.632d12b.0"
"@storybook/global": "npm:^5.0.0"
"@storybook/manager-api": "workspace:*"
"@storybook/preview-api": "workspace:*"
@@ -5524,7 +5524,7 @@ __metadata:
"@storybook/client-logger": "workspace:*"
"@storybook/components": "workspace:*"
"@storybook/core-events": "workspace:*"
- "@storybook/csf": "npm:^0.1.2"
+ "@storybook/csf": "npm:0.1.4--canary.80.632d12b.0"
"@storybook/docs-tools": "workspace:*"
"@storybook/global": "npm:^5.0.0"
"@storybook/icons": "npm:^1.2.5"
@@ -5755,7 +5755,7 @@ __metadata:
"@babel/core": "npm:^7.23.2"
"@babel/preset-env": "npm:^7.23.2"
"@babel/types": "npm:^7.23.0"
- "@storybook/csf": "npm:^0.1.2"
+ "@storybook/csf": "npm:0.1.4--canary.80.632d12b.0"
"@storybook/csf-tools": "workspace:*"
"@storybook/node-logger": "workspace:*"
"@storybook/types": "workspace:*"
@@ -5791,7 +5791,7 @@ __metadata:
"@radix-ui/react-scroll-area": "npm:^1.0.5"
"@radix-ui/react-slot": "npm:^1.0.2"
"@storybook/client-logger": "workspace:*"
- "@storybook/csf": "npm:^0.1.2"
+ "@storybook/csf": "npm:0.1.4--canary.80.632d12b.0"
"@storybook/global": "npm:^5.0.0"
"@storybook/icons": "npm:^1.2.5"
"@storybook/test": "workspace:*"
@@ -5884,7 +5884,7 @@ __metadata:
"@storybook/channels": "workspace:*"
"@storybook/core-common": "workspace:*"
"@storybook/core-events": "workspace:*"
- "@storybook/csf": "npm:^0.1.2"
+ "@storybook/csf": "npm:0.1.4--canary.80.632d12b.0"
"@storybook/csf-tools": "workspace:*"
"@storybook/docs-mdx": "npm:3.0.0"
"@storybook/global": "npm:^5.0.0"
@@ -5964,7 +5964,7 @@ __metadata:
"@babel/parser": "npm:^7.23.0"
"@babel/traverse": "npm:^7.23.2"
"@babel/types": "npm:^7.23.0"
- "@storybook/csf": "npm:^0.1.2"
+ "@storybook/csf": "npm:0.1.4--canary.80.632d12b.0"
"@storybook/types": "workspace:*"
"@types/fs-extra": "npm:^11.0.1"
"@types/js-yaml": "npm:^4.0.5"
@@ -5976,6 +5976,15 @@ __metadata:
languageName: unknown
linkType: soft
+"@storybook/csf@npm:0.1.4--canary.80.632d12b.0":
+ version: 0.1.4--canary.80.632d12b.0
+ resolution: "@storybook/csf@npm:0.1.4--canary.80.632d12b.0"
+ dependencies:
+ type-fest: "npm:^2.19.0"
+ checksum: 10c0/8e2c9562d874ea78ad578eac3132bcc0dc4116e52a0cea7921888796ffa4225f106d4286bce92655584ae1ad1cf5150fd891af6cec5eb261a2e5ba5ecc4fa879
+ languageName: node
+ linkType: hard
+
"@storybook/csf@npm:^0.0.1":
version: 0.0.1
resolution: "@storybook/csf@npm:0.0.1"
@@ -5985,15 +5994,6 @@ __metadata:
languageName: node
linkType: hard
-"@storybook/csf@npm:^0.1.2":
- version: 0.1.2
- resolution: "@storybook/csf@npm:0.1.2"
- dependencies:
- type-fest: "npm:^2.19.0"
- checksum: 10c0/b51a55292e5d2af8b1d135a28ecaa94f8860ddfedcb393adfa2cca1ee23853156066f737d8be1cb5412f572781aa525dc0b2f6e4a6f6ce805489f0149efe837c
- languageName: node
- linkType: hard
-
"@storybook/docs-mdx@npm:3.0.0":
version: 3.0.0
resolution: "@storybook/docs-mdx@npm:3.0.0"
@@ -6161,7 +6161,7 @@ __metadata:
"@storybook/channels": "workspace:*"
"@storybook/client-logger": "workspace:*"
"@storybook/core-events": "workspace:*"
- "@storybook/csf": "npm:^0.1.2"
+ "@storybook/csf": "npm:0.1.4--canary.80.632d12b.0"
"@storybook/global": "npm:^5.0.0"
"@storybook/icons": "npm:^1.2.5"
"@storybook/router": "workspace:*"
@@ -6493,7 +6493,7 @@ __metadata:
"@storybook/client-logger": "workspace:*"
"@storybook/core-common": "workspace:*"
"@storybook/core-events": "workspace:*"
- "@storybook/csf": "npm:^0.1.2"
+ "@storybook/csf": "npm:0.1.4--canary.80.632d12b.0"
"@storybook/global": "npm:^5.0.0"
"@storybook/types": "workspace:*"
"@types/qs": "npm:^6.9.5"
@@ -6678,7 +6678,7 @@ __metadata:
"@storybook/core-events": "workspace:*"
"@storybook/core-server": "workspace:*"
"@storybook/core-webpack": "workspace:*"
- "@storybook/csf": "npm:^0.1.2"
+ "@storybook/csf": "npm:0.1.4--canary.80.632d12b.0"
"@storybook/csf-plugin": "workspace:*"
"@storybook/csf-tools": "workspace:*"
"@storybook/docs-tools": "workspace:*"
@@ -6833,7 +6833,7 @@ __metadata:
version: 0.0.0-use.local
resolution: "@storybook/server@workspace:renderers/server"
dependencies:
- "@storybook/csf": "npm:^0.1.2"
+ "@storybook/csf": "npm:0.1.4--canary.80.632d12b.0"
"@storybook/csf-tools": "workspace:*"
"@storybook/global": "npm:^5.0.0"
"@storybook/preview-api": "workspace:*"
@@ -6850,7 +6850,7 @@ __metadata:
version: 0.0.0-use.local
resolution: "@storybook/source-loader@workspace:lib/source-loader"
dependencies:
- "@storybook/csf": "npm:^0.1.2"
+ "@storybook/csf": "npm:0.1.4--canary.80.632d12b.0"
"@storybook/types": "workspace:*"
estraverse: "npm:^5.2.0"
lodash: "npm:^4.17.21"
@@ -7028,7 +7028,7 @@ __metadata:
resolution: "@storybook/types@workspace:lib/types"
dependencies:
"@storybook/channels": "workspace:*"
- "@storybook/csf": "npm:^0.1.2"
+ "@storybook/csf": "npm:0.1.4--canary.80.632d12b.0"
"@types/express": "npm:^4.7.0"
"@types/fs-extra": "npm:^11.0.1"
"@types/node": "npm:^18.0.0"
From ab64211167ab223a01c06d119f6084e056ce817d Mon Sep 17 00:00:00 2001
From: Michael Shilman
Date: Tue, 26 Mar 2024 13:12:56 +0800
Subject: [PATCH 003/117] Switch tag removal prefix from `-` to `!`
---
code/addons/links/package.json | 2 +-
code/lib/codemod/package.json | 2 +-
code/lib/core-server/package.json | 2 +-
.../src/utils/StoryIndexGenerator.ts | 2 +-
code/lib/csf-tools/package.json | 2 +-
code/lib/manager-api/package.json | 2 +-
code/lib/preview-api/package.json | 2 +-
.../template/stories/tags.stories.ts | 2 +-
code/lib/source-loader/package.json | 2 +-
code/lib/types/package.json | 2 +-
code/package.json | 2 +-
code/renderers/server/package.json | 2 +-
code/ui/blocks/package.json | 2 +-
code/ui/components/package.json | 2 +-
code/yarn.lock | 32 +++++++++----------
15 files changed, 30 insertions(+), 30 deletions(-)
diff --git a/code/addons/links/package.json b/code/addons/links/package.json
index 6a135b3c576a..6718bb96b849 100644
--- a/code/addons/links/package.json
+++ b/code/addons/links/package.json
@@ -67,7 +67,7 @@
"prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/addon-bundle.ts"
},
"dependencies": {
- "@storybook/csf": "0.1.4--canary.80.632d12b.0",
+ "@storybook/csf": "0.1.4--canary.80.aedde7f.0",
"@storybook/global": "^5.0.0",
"ts-dedent": "^2.0.0"
},
diff --git a/code/lib/codemod/package.json b/code/lib/codemod/package.json
index 8c355eb93454..954ed90c5aa3 100644
--- a/code/lib/codemod/package.json
+++ b/code/lib/codemod/package.json
@@ -57,7 +57,7 @@
"@babel/core": "^7.23.2",
"@babel/preset-env": "^7.23.2",
"@babel/types": "^7.23.0",
- "@storybook/csf": "0.1.4--canary.80.632d12b.0",
+ "@storybook/csf": "0.1.4--canary.80.aedde7f.0",
"@storybook/csf-tools": "workspace:*",
"@storybook/node-logger": "workspace:*",
"@storybook/types": "workspace:*",
diff --git a/code/lib/core-server/package.json b/code/lib/core-server/package.json
index 1526c391dff0..8a785c9d4e3e 100644
--- a/code/lib/core-server/package.json
+++ b/code/lib/core-server/package.json
@@ -62,7 +62,7 @@
"@storybook/channels": "workspace:*",
"@storybook/core-common": "workspace:*",
"@storybook/core-events": "workspace:*",
- "@storybook/csf": "0.1.4--canary.80.632d12b.0",
+ "@storybook/csf": "0.1.4--canary.80.aedde7f.0",
"@storybook/csf-tools": "workspace:*",
"@storybook/docs-mdx": "3.0.0",
"@storybook/global": "^5.0.0",
diff --git a/code/lib/core-server/src/utils/StoryIndexGenerator.ts b/code/lib/core-server/src/utils/StoryIndexGenerator.ts
index 0e036f685355..8b1fbbe738d7 100644
--- a/code/lib/core-server/src/utils/StoryIndexGenerator.ts
+++ b/code/lib/core-server/src/utils/StoryIndexGenerator.ts
@@ -55,7 +55,7 @@ export type StoryIndexGeneratorOptions = {
};
export const AUTODOCS_TAG = 'autodocs';
-export const NO_AUTODOCS_TAG = `-${AUTODOCS_TAG}`;
+export const NO_AUTODOCS_TAG = `!${AUTODOCS_TAG}`;
export const STORIES_MDX_TAG = 'stories-mdx';
export const PLAY_FN_TAG = 'play-fn';
diff --git a/code/lib/csf-tools/package.json b/code/lib/csf-tools/package.json
index 39d063d32d01..bc45285b5242 100644
--- a/code/lib/csf-tools/package.json
+++ b/code/lib/csf-tools/package.json
@@ -46,7 +46,7 @@
"@babel/parser": "^7.23.0",
"@babel/traverse": "^7.23.2",
"@babel/types": "^7.23.0",
- "@storybook/csf": "0.1.4--canary.80.632d12b.0",
+ "@storybook/csf": "0.1.4--canary.80.aedde7f.0",
"@storybook/types": "workspace:*",
"fs-extra": "^11.1.0",
"recast": "^0.23.5",
diff --git a/code/lib/manager-api/package.json b/code/lib/manager-api/package.json
index 9fd9941ef00f..51621f8445d1 100644
--- a/code/lib/manager-api/package.json
+++ b/code/lib/manager-api/package.json
@@ -47,7 +47,7 @@
"@storybook/channels": "workspace:*",
"@storybook/client-logger": "workspace:*",
"@storybook/core-events": "workspace:*",
- "@storybook/csf": "0.1.4--canary.80.632d12b.0",
+ "@storybook/csf": "0.1.4--canary.80.aedde7f.0",
"@storybook/global": "^5.0.0",
"@storybook/icons": "^1.2.5",
"@storybook/router": "workspace:*",
diff --git a/code/lib/preview-api/package.json b/code/lib/preview-api/package.json
index 23bd408491db..c3705da914f7 100644
--- a/code/lib/preview-api/package.json
+++ b/code/lib/preview-api/package.json
@@ -47,7 +47,7 @@
"@storybook/channels": "workspace:*",
"@storybook/client-logger": "workspace:*",
"@storybook/core-events": "workspace:*",
- "@storybook/csf": "0.1.4--canary.80.632d12b.0",
+ "@storybook/csf": "0.1.4--canary.80.aedde7f.0",
"@storybook/global": "^5.0.0",
"@storybook/types": "workspace:*",
"@types/qs": "^6.9.5",
diff --git a/code/lib/preview-api/template/stories/tags.stories.ts b/code/lib/preview-api/template/stories/tags.stories.ts
index 9af5be843d5f..b6b89c37ec6c 100644
--- a/code/lib/preview-api/template/stories/tags.stories.ts
+++ b/code/lib/preview-api/template/stories/tags.stories.ts
@@ -39,5 +39,5 @@ export const DevOnly = {
};
export const TagRemoval = {
- tags: ['-component-two'],
+ tags: ['!component-two'],
};
diff --git a/code/lib/source-loader/package.json b/code/lib/source-loader/package.json
index c0578007e242..624a582ef899 100644
--- a/code/lib/source-loader/package.json
+++ b/code/lib/source-loader/package.json
@@ -45,7 +45,7 @@
"prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts"
},
"dependencies": {
- "@storybook/csf": "0.1.4--canary.80.632d12b.0",
+ "@storybook/csf": "0.1.4--canary.80.aedde7f.0",
"@storybook/types": "workspace:*",
"estraverse": "^5.2.0",
"lodash": "^4.17.21",
diff --git a/code/lib/types/package.json b/code/lib/types/package.json
index c09dfb384b39..fe29111a2043 100644
--- a/code/lib/types/package.json
+++ b/code/lib/types/package.json
@@ -49,7 +49,7 @@
"file-system-cache": "2.3.0"
},
"devDependencies": {
- "@storybook/csf": "0.1.4--canary.80.632d12b.0",
+ "@storybook/csf": "0.1.4--canary.80.aedde7f.0",
"@types/fs-extra": "^11.0.1",
"@types/node": "^18.0.0",
"typescript": "^5.3.2"
diff --git a/code/package.json b/code/package.json
index c882ef4cf53d..44ed3a2544ad 100644
--- a/code/package.json
+++ b/code/package.json
@@ -127,7 +127,7 @@
"@storybook/core-events": "workspace:*",
"@storybook/core-server": "workspace:*",
"@storybook/core-webpack": "workspace:*",
- "@storybook/csf": "0.1.4--canary.80.632d12b.0",
+ "@storybook/csf": "0.1.4--canary.80.aedde7f.0",
"@storybook/csf-plugin": "workspace:*",
"@storybook/csf-tools": "workspace:*",
"@storybook/docs-tools": "workspace:*",
diff --git a/code/renderers/server/package.json b/code/renderers/server/package.json
index 5a18737a65ca..70fda2b74aff 100644
--- a/code/renderers/server/package.json
+++ b/code/renderers/server/package.json
@@ -46,7 +46,7 @@
"prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts"
},
"dependencies": {
- "@storybook/csf": "0.1.4--canary.80.632d12b.0",
+ "@storybook/csf": "0.1.4--canary.80.aedde7f.0",
"@storybook/csf-tools": "workspace:*",
"@storybook/global": "^5.0.0",
"@storybook/preview-api": "workspace:*",
diff --git a/code/ui/blocks/package.json b/code/ui/blocks/package.json
index a7b11a866577..f93dfeeb03d1 100644
--- a/code/ui/blocks/package.json
+++ b/code/ui/blocks/package.json
@@ -48,7 +48,7 @@
"@storybook/client-logger": "workspace:*",
"@storybook/components": "workspace:*",
"@storybook/core-events": "workspace:*",
- "@storybook/csf": "0.1.4--canary.80.632d12b.0",
+ "@storybook/csf": "0.1.4--canary.80.aedde7f.0",
"@storybook/docs-tools": "workspace:*",
"@storybook/global": "^5.0.0",
"@storybook/icons": "^1.2.5",
diff --git a/code/ui/components/package.json b/code/ui/components/package.json
index 1160d1a0d126..470a71dda15d 100644
--- a/code/ui/components/package.json
+++ b/code/ui/components/package.json
@@ -61,7 +61,7 @@
"dependencies": {
"@radix-ui/react-slot": "^1.0.2",
"@storybook/client-logger": "workspace:*",
- "@storybook/csf": "0.1.4--canary.80.632d12b.0",
+ "@storybook/csf": "0.1.4--canary.80.aedde7f.0",
"@storybook/global": "^5.0.0",
"@storybook/icons": "^1.2.5",
"@storybook/theming": "workspace:*",
diff --git a/code/yarn.lock b/code/yarn.lock
index e8d2a6a1df31..6faa6a67ad53 100644
--- a/code/yarn.lock
+++ b/code/yarn.lock
@@ -5256,7 +5256,7 @@ __metadata:
dependencies:
"@storybook/client-logger": "workspace:*"
"@storybook/core-events": "workspace:*"
- "@storybook/csf": "npm:0.1.4--canary.80.632d12b.0"
+ "@storybook/csf": "npm:0.1.4--canary.80.aedde7f.0"
"@storybook/global": "npm:^5.0.0"
"@storybook/manager-api": "workspace:*"
"@storybook/preview-api": "workspace:*"
@@ -5524,7 +5524,7 @@ __metadata:
"@storybook/client-logger": "workspace:*"
"@storybook/components": "workspace:*"
"@storybook/core-events": "workspace:*"
- "@storybook/csf": "npm:0.1.4--canary.80.632d12b.0"
+ "@storybook/csf": "npm:0.1.4--canary.80.aedde7f.0"
"@storybook/docs-tools": "workspace:*"
"@storybook/global": "npm:^5.0.0"
"@storybook/icons": "npm:^1.2.5"
@@ -5755,7 +5755,7 @@ __metadata:
"@babel/core": "npm:^7.23.2"
"@babel/preset-env": "npm:^7.23.2"
"@babel/types": "npm:^7.23.0"
- "@storybook/csf": "npm:0.1.4--canary.80.632d12b.0"
+ "@storybook/csf": "npm:0.1.4--canary.80.aedde7f.0"
"@storybook/csf-tools": "workspace:*"
"@storybook/node-logger": "workspace:*"
"@storybook/types": "workspace:*"
@@ -5791,7 +5791,7 @@ __metadata:
"@radix-ui/react-scroll-area": "npm:^1.0.5"
"@radix-ui/react-slot": "npm:^1.0.2"
"@storybook/client-logger": "workspace:*"
- "@storybook/csf": "npm:0.1.4--canary.80.632d12b.0"
+ "@storybook/csf": "npm:0.1.4--canary.80.aedde7f.0"
"@storybook/global": "npm:^5.0.0"
"@storybook/icons": "npm:^1.2.5"
"@storybook/test": "workspace:*"
@@ -5884,7 +5884,7 @@ __metadata:
"@storybook/channels": "workspace:*"
"@storybook/core-common": "workspace:*"
"@storybook/core-events": "workspace:*"
- "@storybook/csf": "npm:0.1.4--canary.80.632d12b.0"
+ "@storybook/csf": "npm:0.1.4--canary.80.aedde7f.0"
"@storybook/csf-tools": "workspace:*"
"@storybook/docs-mdx": "npm:3.0.0"
"@storybook/global": "npm:^5.0.0"
@@ -5964,7 +5964,7 @@ __metadata:
"@babel/parser": "npm:^7.23.0"
"@babel/traverse": "npm:^7.23.2"
"@babel/types": "npm:^7.23.0"
- "@storybook/csf": "npm:0.1.4--canary.80.632d12b.0"
+ "@storybook/csf": "npm:0.1.4--canary.80.aedde7f.0"
"@storybook/types": "workspace:*"
"@types/fs-extra": "npm:^11.0.1"
"@types/js-yaml": "npm:^4.0.5"
@@ -5976,12 +5976,12 @@ __metadata:
languageName: unknown
linkType: soft
-"@storybook/csf@npm:0.1.4--canary.80.632d12b.0":
- version: 0.1.4--canary.80.632d12b.0
- resolution: "@storybook/csf@npm:0.1.4--canary.80.632d12b.0"
+"@storybook/csf@npm:0.1.4--canary.80.aedde7f.0":
+ version: 0.1.4--canary.80.aedde7f.0
+ resolution: "@storybook/csf@npm:0.1.4--canary.80.aedde7f.0"
dependencies:
type-fest: "npm:^2.19.0"
- checksum: 10c0/8e2c9562d874ea78ad578eac3132bcc0dc4116e52a0cea7921888796ffa4225f106d4286bce92655584ae1ad1cf5150fd891af6cec5eb261a2e5ba5ecc4fa879
+ checksum: 10c0/14bf5d4f7cb44a2534a2ea37e88350a44d43e33218184d57e7cfcf1e029967454fb16118e21ce5ebce21e1d783fe807723d8d8babf7b2cc806207e2345033748
languageName: node
linkType: hard
@@ -6161,7 +6161,7 @@ __metadata:
"@storybook/channels": "workspace:*"
"@storybook/client-logger": "workspace:*"
"@storybook/core-events": "workspace:*"
- "@storybook/csf": "npm:0.1.4--canary.80.632d12b.0"
+ "@storybook/csf": "npm:0.1.4--canary.80.aedde7f.0"
"@storybook/global": "npm:^5.0.0"
"@storybook/icons": "npm:^1.2.5"
"@storybook/router": "workspace:*"
@@ -6493,7 +6493,7 @@ __metadata:
"@storybook/client-logger": "workspace:*"
"@storybook/core-common": "workspace:*"
"@storybook/core-events": "workspace:*"
- "@storybook/csf": "npm:0.1.4--canary.80.632d12b.0"
+ "@storybook/csf": "npm:0.1.4--canary.80.aedde7f.0"
"@storybook/global": "npm:^5.0.0"
"@storybook/types": "workspace:*"
"@types/qs": "npm:^6.9.5"
@@ -6678,7 +6678,7 @@ __metadata:
"@storybook/core-events": "workspace:*"
"@storybook/core-server": "workspace:*"
"@storybook/core-webpack": "workspace:*"
- "@storybook/csf": "npm:0.1.4--canary.80.632d12b.0"
+ "@storybook/csf": "npm:0.1.4--canary.80.aedde7f.0"
"@storybook/csf-plugin": "workspace:*"
"@storybook/csf-tools": "workspace:*"
"@storybook/docs-tools": "workspace:*"
@@ -6833,7 +6833,7 @@ __metadata:
version: 0.0.0-use.local
resolution: "@storybook/server@workspace:renderers/server"
dependencies:
- "@storybook/csf": "npm:0.1.4--canary.80.632d12b.0"
+ "@storybook/csf": "npm:0.1.4--canary.80.aedde7f.0"
"@storybook/csf-tools": "workspace:*"
"@storybook/global": "npm:^5.0.0"
"@storybook/preview-api": "workspace:*"
@@ -6850,7 +6850,7 @@ __metadata:
version: 0.0.0-use.local
resolution: "@storybook/source-loader@workspace:lib/source-loader"
dependencies:
- "@storybook/csf": "npm:0.1.4--canary.80.632d12b.0"
+ "@storybook/csf": "npm:0.1.4--canary.80.aedde7f.0"
"@storybook/types": "workspace:*"
estraverse: "npm:^5.2.0"
lodash: "npm:^4.17.21"
@@ -7028,7 +7028,7 @@ __metadata:
resolution: "@storybook/types@workspace:lib/types"
dependencies:
"@storybook/channels": "workspace:*"
- "@storybook/csf": "npm:0.1.4--canary.80.632d12b.0"
+ "@storybook/csf": "npm:0.1.4--canary.80.aedde7f.0"
"@types/express": "npm:^4.7.0"
"@types/fs-extra": "npm:^11.0.1"
"@types/node": "npm:^18.0.0"
From b5b17f5dee27f16fcfc2cf51eec7d2dbd27d5486 Mon Sep 17 00:00:00 2001
From: Michael Shilman
Date: Fri, 29 Mar 2024 21:39:17 +0800
Subject: [PATCH 004/117] Add project-level tags
---
code/addons/links/package.json | 2 +-
code/lib/codemod/package.json | 2 +-
code/lib/core-server/package.json | 2 +-
.../src/utils/StoryIndexGenerator.ts | 57 ++++++++++++-------
code/lib/csf-tools/package.json | 2 +-
code/lib/manager-api/package.json | 2 +-
code/lib/preview-api/package.json | 2 +-
.../src/modules/store/csf/composeConfigs.ts | 1 +
.../src/modules/store/csf/prepareStory.ts | 7 ++-
code/lib/source-loader/package.json | 2 +-
code/lib/types/package.json | 2 +-
code/package.json | 2 +-
code/renderers/server/package.json | 2 +-
code/ui/blocks/package.json | 2 +-
code/ui/components/package.json | 2 +-
code/yarn.lock | 32 +++++------
16 files changed, 73 insertions(+), 48 deletions(-)
diff --git a/code/addons/links/package.json b/code/addons/links/package.json
index 6718bb96b849..8a2c600d574c 100644
--- a/code/addons/links/package.json
+++ b/code/addons/links/package.json
@@ -67,7 +67,7 @@
"prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/addon-bundle.ts"
},
"dependencies": {
- "@storybook/csf": "0.1.4--canary.80.aedde7f.0",
+ "@storybook/csf": "0.1.4--canary.80.f469755.0",
"@storybook/global": "^5.0.0",
"ts-dedent": "^2.0.0"
},
diff --git a/code/lib/codemod/package.json b/code/lib/codemod/package.json
index 954ed90c5aa3..35e5ebab28c9 100644
--- a/code/lib/codemod/package.json
+++ b/code/lib/codemod/package.json
@@ -57,7 +57,7 @@
"@babel/core": "^7.23.2",
"@babel/preset-env": "^7.23.2",
"@babel/types": "^7.23.0",
- "@storybook/csf": "0.1.4--canary.80.aedde7f.0",
+ "@storybook/csf": "0.1.4--canary.80.f469755.0",
"@storybook/csf-tools": "workspace:*",
"@storybook/node-logger": "workspace:*",
"@storybook/types": "workspace:*",
diff --git a/code/lib/core-server/package.json b/code/lib/core-server/package.json
index 8a785c9d4e3e..6e88034f3591 100644
--- a/code/lib/core-server/package.json
+++ b/code/lib/core-server/package.json
@@ -62,7 +62,7 @@
"@storybook/channels": "workspace:*",
"@storybook/core-common": "workspace:*",
"@storybook/core-events": "workspace:*",
- "@storybook/csf": "0.1.4--canary.80.aedde7f.0",
+ "@storybook/csf": "0.1.4--canary.80.f469755.0",
"@storybook/csf-tools": "workspace:*",
"@storybook/docs-mdx": "3.0.0",
"@storybook/global": "^5.0.0",
diff --git a/code/lib/core-server/src/utils/StoryIndexGenerator.ts b/code/lib/core-server/src/utils/StoryIndexGenerator.ts
index 8b1fbbe738d7..0aaba41efd67 100644
--- a/code/lib/core-server/src/utils/StoryIndexGenerator.ts
+++ b/code/lib/core-server/src/utils/StoryIndexGenerator.ts
@@ -22,7 +22,7 @@ import type {
import { userOrAutoTitleFromSpecifier, sortStoriesV7 } from '@storybook/preview-api';
import { commonGlobOptions, normalizeStoryPath } from '@storybook/core-common';
import { logger, once } from '@storybook/node-logger';
-import { getStorySortParameter } from '@storybook/csf-tools';
+import { getStorySortParameter, loadConfig } from '@storybook/csf-tools';
import { storyNameFromExport, toId, combineTags } from '@storybook/csf';
import { analyze } from '@storybook/docs-mdx';
import dedent from 'ts-dedent';
@@ -156,8 +156,11 @@ export class StoryIndexGenerator {
this.specifierToCache.set(specifier, cache)
);
+ const previewCode = await this.getPreviewCode();
+ const projectTags = previewCode && this.getProjectTags(previewCode);
+
// Extract stories for each file
- await this.ensureExtracted();
+ await this.ensureExtracted({ projectTags });
}
/**
@@ -211,7 +214,11 @@ export class StoryIndexGenerator {
return /(? {
+ async ensureExtracted({
+ projectTags,
+ }: {
+ projectTags?: Tag[];
+ }): Promise<(IndexEntry | ErrorEntry)[]> {
// First process all the story files. Then, in a second pass,
// process the docs files. The reason for this is that the docs
// files may use the `` syntax, which requires
@@ -234,19 +241,26 @@ export class StoryIndexGenerator {
);
return Object.values(cache).flatMap((entry): (IndexEntry | ErrorEntry)[] => {
if (!entry) return [];
- if (entry.type === 'docs') return [entry];
+ if (entry.type === 'docs') return [this.addProjectTags(entry, projectTags)];
if (entry.type === 'error') return [entry];
return entry.entries.map((item) => {
- if (item.type === 'docs') return item;
+ if (item.type === 'docs') return this.addProjectTags(item, projectTags);
// Drop the meta id as it isn't part of the index, we just used it for record keeping in `extractDocs`
const { metaId, ...existing } = item;
- return existing;
+ return this.addProjectTags(existing, projectTags);
});
});
});
}
+ addProjectTags(entry: IndexEntry, projectTags?: Tag[]) {
+ return {
+ ...entry,
+ metaTags: combineTags(...(projectTags || []), ...(entry.metaTags || [])),
+ };
+ }
+
findDependencies(absoluteImports: Path[]) {
return [...this.specifierToCache.values()].flatMap((cache: SpecifierStoriesCache) =>
Object.entries(cache)
@@ -535,10 +549,8 @@ export class StoryIndexGenerator {
return betterEntry;
}
- async sortStories(entries: StoryIndex['entries']) {
+ async sortStories(entries: StoryIndex['entries'], storySortParameter: any) {
const sortableStories = Object.values(entries);
-
- const storySortParameter = await this.getStorySortParameter();
const fileNameOrder = this.storyFileNames();
sortStoriesV7(sortableStories, storySortParameter, fileNameOrder);
@@ -555,9 +567,12 @@ export class StoryIndexGenerator {
if (this.lastIndex) return this.lastIndex;
if (this.lastError) throw this.lastError;
+ const previewCode = await this.getPreviewCode();
+ const projectTags = previewCode && this.getProjectTags(previewCode);
+
// Extract any entries that are currently missing
// Pull out each file's stories into a list of stories, to be composed and sorted
- const storiesList = await this.ensureExtracted();
+ const storiesList = await this.ensureExtracted({ projectTags });
try {
const errorEntries = storiesList.filter((entry) => entry.type === 'error');
@@ -581,7 +596,10 @@ export class StoryIndexGenerator {
});
if (duplicateErrors.length) throw new MultipleIndexingError(duplicateErrors);
- const sorted = await this.sortStories(indexEntries);
+ const sorted = await this.sortStories(
+ indexEntries,
+ previewCode && getStorySortParameter(previewCode)
+ );
this.lastIndex = {
v: 4,
@@ -641,17 +659,18 @@ export class StoryIndexGenerator {
this.lastError = null;
}
- async getStorySortParameter() {
- const previewFile = ['js', 'jsx', 'ts', 'tsx', 'mjs', 'cjs']
+ async getPreviewCode() {
+ const previewFile = ['js', 'jsx', 'ts', 'tsx', 'mjs', 'cjs', 'mts']
.map((ext) => path.join(this.options.configDir, `preview.${ext}`))
.find((fname) => fs.existsSync(fname));
- let storySortParameter;
- if (previewFile) {
- const previewCode = (await fs.readFile(previewFile, 'utf-8')).toString();
- storySortParameter = await getStorySortParameter(previewCode);
- }
- return storySortParameter;
+ return previewFile && (await fs.readFile(previewFile, 'utf-8')).toString();
+ }
+
+ getProjectTags(previewCode: string) {
+ const projectAnnotations = loadConfig(previewCode).parse();
+ const projectTags = projectAnnotations.getFieldValue(['tags']);
+ return projectTags;
}
// Get the story file names in "imported order"
diff --git a/code/lib/csf-tools/package.json b/code/lib/csf-tools/package.json
index bc45285b5242..1f8b41d7b51b 100644
--- a/code/lib/csf-tools/package.json
+++ b/code/lib/csf-tools/package.json
@@ -46,7 +46,7 @@
"@babel/parser": "^7.23.0",
"@babel/traverse": "^7.23.2",
"@babel/types": "^7.23.0",
- "@storybook/csf": "0.1.4--canary.80.aedde7f.0",
+ "@storybook/csf": "0.1.4--canary.80.f469755.0",
"@storybook/types": "workspace:*",
"fs-extra": "^11.1.0",
"recast": "^0.23.5",
diff --git a/code/lib/manager-api/package.json b/code/lib/manager-api/package.json
index 51621f8445d1..abced2447faf 100644
--- a/code/lib/manager-api/package.json
+++ b/code/lib/manager-api/package.json
@@ -47,7 +47,7 @@
"@storybook/channels": "workspace:*",
"@storybook/client-logger": "workspace:*",
"@storybook/core-events": "workspace:*",
- "@storybook/csf": "0.1.4--canary.80.aedde7f.0",
+ "@storybook/csf": "0.1.4--canary.80.f469755.0",
"@storybook/global": "^5.0.0",
"@storybook/icons": "^1.2.5",
"@storybook/router": "workspace:*",
diff --git a/code/lib/preview-api/package.json b/code/lib/preview-api/package.json
index c3705da914f7..cb995ab59c37 100644
--- a/code/lib/preview-api/package.json
+++ b/code/lib/preview-api/package.json
@@ -47,7 +47,7 @@
"@storybook/channels": "workspace:*",
"@storybook/client-logger": "workspace:*",
"@storybook/core-events": "workspace:*",
- "@storybook/csf": "0.1.4--canary.80.aedde7f.0",
+ "@storybook/csf": "0.1.4--canary.80.f469755.0",
"@storybook/global": "^5.0.0",
"@storybook/types": "workspace:*",
"@types/qs": "^6.9.5",
diff --git a/code/lib/preview-api/src/modules/store/csf/composeConfigs.ts b/code/lib/preview-api/src/modules/store/csf/composeConfigs.ts
index e5785a6a3f01..29823816beaf 100644
--- a/code/lib/preview-api/src/modules/store/csf/composeConfigs.ts
+++ b/code/lib/preview-api/src/modules/store/csf/composeConfigs.ts
@@ -63,5 +63,6 @@ export function composeConfigs(
renderToDOM: getSingletonField(moduleExportList, 'renderToDOM'), // deprecated
applyDecorators: getSingletonField(moduleExportList, 'applyDecorators'),
runStep: composeStepRunners(stepRunners),
+ tags: getArrayField(moduleExportList, 'tags'),
};
}
diff --git a/code/lib/preview-api/src/modules/store/csf/prepareStory.ts b/code/lib/preview-api/src/modules/store/csf/prepareStory.ts
index ad35eff50f07..4b8035f606d3 100644
--- a/code/lib/preview-api/src/modules/store/csf/prepareStory.ts
+++ b/code/lib/preview-api/src/modules/store/csf/prepareStory.ts
@@ -125,6 +125,10 @@ export function prepareMeta(
projectAnnotations: NormalizedProjectAnnotations,
moduleExport: ModuleExport
): PreparedMeta {
+ console.log('prepareMeta', {
+ metaTags: componentAnnotations.tags,
+ projectAnnotations,
+ });
return {
...preparePartialAnnotations(undefined, componentAnnotations, projectAnnotations),
moduleExport,
@@ -141,8 +145,9 @@ function preparePartialAnnotations(
// will have a limited cost. If this proves misguided, we can refactor it.
const tags = combineTags(
- ...(storyAnnotations?.tags ?? []),
+ ...(projectAnnotations.tags ?? []),
...(componentAnnotations.tags ?? []),
+ ...(storyAnnotations?.tags ?? []),
'story'
);
diff --git a/code/lib/source-loader/package.json b/code/lib/source-loader/package.json
index 624a582ef899..5f1ac102b5d6 100644
--- a/code/lib/source-loader/package.json
+++ b/code/lib/source-loader/package.json
@@ -45,7 +45,7 @@
"prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts"
},
"dependencies": {
- "@storybook/csf": "0.1.4--canary.80.aedde7f.0",
+ "@storybook/csf": "0.1.4--canary.80.f469755.0",
"@storybook/types": "workspace:*",
"estraverse": "^5.2.0",
"lodash": "^4.17.21",
diff --git a/code/lib/types/package.json b/code/lib/types/package.json
index fe29111a2043..f9eb319420b7 100644
--- a/code/lib/types/package.json
+++ b/code/lib/types/package.json
@@ -49,7 +49,7 @@
"file-system-cache": "2.3.0"
},
"devDependencies": {
- "@storybook/csf": "0.1.4--canary.80.aedde7f.0",
+ "@storybook/csf": "0.1.4--canary.80.f469755.0",
"@types/fs-extra": "^11.0.1",
"@types/node": "^18.0.0",
"typescript": "^5.3.2"
diff --git a/code/package.json b/code/package.json
index 44ed3a2544ad..454f5b0dd84a 100644
--- a/code/package.json
+++ b/code/package.json
@@ -127,7 +127,7 @@
"@storybook/core-events": "workspace:*",
"@storybook/core-server": "workspace:*",
"@storybook/core-webpack": "workspace:*",
- "@storybook/csf": "0.1.4--canary.80.aedde7f.0",
+ "@storybook/csf": "0.1.4--canary.80.f469755.0",
"@storybook/csf-plugin": "workspace:*",
"@storybook/csf-tools": "workspace:*",
"@storybook/docs-tools": "workspace:*",
diff --git a/code/renderers/server/package.json b/code/renderers/server/package.json
index 70fda2b74aff..54c669fac08a 100644
--- a/code/renderers/server/package.json
+++ b/code/renderers/server/package.json
@@ -46,7 +46,7 @@
"prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts"
},
"dependencies": {
- "@storybook/csf": "0.1.4--canary.80.aedde7f.0",
+ "@storybook/csf": "0.1.4--canary.80.f469755.0",
"@storybook/csf-tools": "workspace:*",
"@storybook/global": "^5.0.0",
"@storybook/preview-api": "workspace:*",
diff --git a/code/ui/blocks/package.json b/code/ui/blocks/package.json
index f93dfeeb03d1..a28714a17228 100644
--- a/code/ui/blocks/package.json
+++ b/code/ui/blocks/package.json
@@ -48,7 +48,7 @@
"@storybook/client-logger": "workspace:*",
"@storybook/components": "workspace:*",
"@storybook/core-events": "workspace:*",
- "@storybook/csf": "0.1.4--canary.80.aedde7f.0",
+ "@storybook/csf": "0.1.4--canary.80.f469755.0",
"@storybook/docs-tools": "workspace:*",
"@storybook/global": "^5.0.0",
"@storybook/icons": "^1.2.5",
diff --git a/code/ui/components/package.json b/code/ui/components/package.json
index 470a71dda15d..6569d692d59a 100644
--- a/code/ui/components/package.json
+++ b/code/ui/components/package.json
@@ -61,7 +61,7 @@
"dependencies": {
"@radix-ui/react-slot": "^1.0.2",
"@storybook/client-logger": "workspace:*",
- "@storybook/csf": "0.1.4--canary.80.aedde7f.0",
+ "@storybook/csf": "0.1.4--canary.80.f469755.0",
"@storybook/global": "^5.0.0",
"@storybook/icons": "^1.2.5",
"@storybook/theming": "workspace:*",
diff --git a/code/yarn.lock b/code/yarn.lock
index 6faa6a67ad53..30fe40584238 100644
--- a/code/yarn.lock
+++ b/code/yarn.lock
@@ -5256,7 +5256,7 @@ __metadata:
dependencies:
"@storybook/client-logger": "workspace:*"
"@storybook/core-events": "workspace:*"
- "@storybook/csf": "npm:0.1.4--canary.80.aedde7f.0"
+ "@storybook/csf": "npm:0.1.4--canary.80.f469755.0"
"@storybook/global": "npm:^5.0.0"
"@storybook/manager-api": "workspace:*"
"@storybook/preview-api": "workspace:*"
@@ -5524,7 +5524,7 @@ __metadata:
"@storybook/client-logger": "workspace:*"
"@storybook/components": "workspace:*"
"@storybook/core-events": "workspace:*"
- "@storybook/csf": "npm:0.1.4--canary.80.aedde7f.0"
+ "@storybook/csf": "npm:0.1.4--canary.80.f469755.0"
"@storybook/docs-tools": "workspace:*"
"@storybook/global": "npm:^5.0.0"
"@storybook/icons": "npm:^1.2.5"
@@ -5755,7 +5755,7 @@ __metadata:
"@babel/core": "npm:^7.23.2"
"@babel/preset-env": "npm:^7.23.2"
"@babel/types": "npm:^7.23.0"
- "@storybook/csf": "npm:0.1.4--canary.80.aedde7f.0"
+ "@storybook/csf": "npm:0.1.4--canary.80.f469755.0"
"@storybook/csf-tools": "workspace:*"
"@storybook/node-logger": "workspace:*"
"@storybook/types": "workspace:*"
@@ -5791,7 +5791,7 @@ __metadata:
"@radix-ui/react-scroll-area": "npm:^1.0.5"
"@radix-ui/react-slot": "npm:^1.0.2"
"@storybook/client-logger": "workspace:*"
- "@storybook/csf": "npm:0.1.4--canary.80.aedde7f.0"
+ "@storybook/csf": "npm:0.1.4--canary.80.f469755.0"
"@storybook/global": "npm:^5.0.0"
"@storybook/icons": "npm:^1.2.5"
"@storybook/test": "workspace:*"
@@ -5884,7 +5884,7 @@ __metadata:
"@storybook/channels": "workspace:*"
"@storybook/core-common": "workspace:*"
"@storybook/core-events": "workspace:*"
- "@storybook/csf": "npm:0.1.4--canary.80.aedde7f.0"
+ "@storybook/csf": "npm:0.1.4--canary.80.f469755.0"
"@storybook/csf-tools": "workspace:*"
"@storybook/docs-mdx": "npm:3.0.0"
"@storybook/global": "npm:^5.0.0"
@@ -5964,7 +5964,7 @@ __metadata:
"@babel/parser": "npm:^7.23.0"
"@babel/traverse": "npm:^7.23.2"
"@babel/types": "npm:^7.23.0"
- "@storybook/csf": "npm:0.1.4--canary.80.aedde7f.0"
+ "@storybook/csf": "npm:0.1.4--canary.80.f469755.0"
"@storybook/types": "workspace:*"
"@types/fs-extra": "npm:^11.0.1"
"@types/js-yaml": "npm:^4.0.5"
@@ -5976,12 +5976,12 @@ __metadata:
languageName: unknown
linkType: soft
-"@storybook/csf@npm:0.1.4--canary.80.aedde7f.0":
- version: 0.1.4--canary.80.aedde7f.0
- resolution: "@storybook/csf@npm:0.1.4--canary.80.aedde7f.0"
+"@storybook/csf@npm:0.1.4--canary.80.f469755.0":
+ version: 0.1.4--canary.80.f469755.0
+ resolution: "@storybook/csf@npm:0.1.4--canary.80.f469755.0"
dependencies:
type-fest: "npm:^2.19.0"
- checksum: 10c0/14bf5d4f7cb44a2534a2ea37e88350a44d43e33218184d57e7cfcf1e029967454fb16118e21ce5ebce21e1d783fe807723d8d8babf7b2cc806207e2345033748
+ checksum: 10c0/a5b2dfe1c2d02b6ab072c7ca0b3918f24ac3093ec740cd79a44f178b583d02ad1f838bfd77cd30ea486c92e7a9b37a921d2e0807e3b1a7cc26f57c62a446ed24
languageName: node
linkType: hard
@@ -6161,7 +6161,7 @@ __metadata:
"@storybook/channels": "workspace:*"
"@storybook/client-logger": "workspace:*"
"@storybook/core-events": "workspace:*"
- "@storybook/csf": "npm:0.1.4--canary.80.aedde7f.0"
+ "@storybook/csf": "npm:0.1.4--canary.80.f469755.0"
"@storybook/global": "npm:^5.0.0"
"@storybook/icons": "npm:^1.2.5"
"@storybook/router": "workspace:*"
@@ -6493,7 +6493,7 @@ __metadata:
"@storybook/client-logger": "workspace:*"
"@storybook/core-common": "workspace:*"
"@storybook/core-events": "workspace:*"
- "@storybook/csf": "npm:0.1.4--canary.80.aedde7f.0"
+ "@storybook/csf": "npm:0.1.4--canary.80.f469755.0"
"@storybook/global": "npm:^5.0.0"
"@storybook/types": "workspace:*"
"@types/qs": "npm:^6.9.5"
@@ -6678,7 +6678,7 @@ __metadata:
"@storybook/core-events": "workspace:*"
"@storybook/core-server": "workspace:*"
"@storybook/core-webpack": "workspace:*"
- "@storybook/csf": "npm:0.1.4--canary.80.aedde7f.0"
+ "@storybook/csf": "npm:0.1.4--canary.80.f469755.0"
"@storybook/csf-plugin": "workspace:*"
"@storybook/csf-tools": "workspace:*"
"@storybook/docs-tools": "workspace:*"
@@ -6833,7 +6833,7 @@ __metadata:
version: 0.0.0-use.local
resolution: "@storybook/server@workspace:renderers/server"
dependencies:
- "@storybook/csf": "npm:0.1.4--canary.80.aedde7f.0"
+ "@storybook/csf": "npm:0.1.4--canary.80.f469755.0"
"@storybook/csf-tools": "workspace:*"
"@storybook/global": "npm:^5.0.0"
"@storybook/preview-api": "workspace:*"
@@ -6850,7 +6850,7 @@ __metadata:
version: 0.0.0-use.local
resolution: "@storybook/source-loader@workspace:lib/source-loader"
dependencies:
- "@storybook/csf": "npm:0.1.4--canary.80.aedde7f.0"
+ "@storybook/csf": "npm:0.1.4--canary.80.f469755.0"
"@storybook/types": "workspace:*"
estraverse: "npm:^5.2.0"
lodash: "npm:^4.17.21"
@@ -7028,7 +7028,7 @@ __metadata:
resolution: "@storybook/types@workspace:lib/types"
dependencies:
"@storybook/channels": "workspace:*"
- "@storybook/csf": "npm:0.1.4--canary.80.aedde7f.0"
+ "@storybook/csf": "npm:0.1.4--canary.80.f469755.0"
"@types/express": "npm:^4.7.0"
"@types/fs-extra": "npm:^11.0.1"
"@types/node": "npm:^18.0.0"
From c52d9438e5654559a8b787300d650f4812ce37a4 Mon Sep 17 00:00:00 2001
From: Michael Shilman
Date: Sat, 30 Mar 2024 00:29:57 +0800
Subject: [PATCH 005/117] Deprecate main.js docs.autodocs in favor of tags
---
.../src/utils/StoryIndexGenerator.ts | 35 ++++++++++++-------
.../src/modules/store/csf/prepareStory.ts | 4 ---
code/lib/types/src/modules/core-common.ts | 2 ++
3 files changed, 24 insertions(+), 17 deletions(-)
diff --git a/code/lib/core-server/src/utils/StoryIndexGenerator.ts b/code/lib/core-server/src/utils/StoryIndexGenerator.ts
index 0aaba41efd67..fae54d3f9372 100644
--- a/code/lib/core-server/src/utils/StoryIndexGenerator.ts
+++ b/code/lib/core-server/src/utils/StoryIndexGenerator.ts
@@ -55,7 +55,6 @@ export type StoryIndexGeneratorOptions = {
};
export const AUTODOCS_TAG = 'autodocs';
-export const NO_AUTODOCS_TAG = `!${AUTODOCS_TAG}`;
export const STORIES_MDX_TAG = 'stories-mdx';
export const PLAY_FN_TAG = 'play-fn';
@@ -254,10 +253,18 @@ export class StoryIndexGenerator {
});
}
+ getExtraTags() {
+ return this.options.docs.autodocs === true ? [AUTODOCS_TAG] : [];
+ }
+
addProjectTags(entry: IndexEntry, projectTags?: Tag[]) {
return {
...entry,
- metaTags: combineTags(...(projectTags || []), ...(entry.metaTags || [])),
+ metaTags: combineTags(
+ ...this.getExtraTags(),
+ ...(projectTags ?? []),
+ ...(entry.metaTags ?? [])
+ ),
};
}
@@ -306,7 +313,13 @@ export class StoryIndexGenerator {
const title = input.title ?? defaultMakeTitle();
// eslint-disable-next-line no-underscore-dangle
const id = input.__id ?? toId(input.metaId ?? title, storyNameFromExport(input.exportName));
- const tags = combineTags(...(input.tags ?? []), ...(input.metaTags ?? []), 'story');
+ const extraTags = this.getExtraTags();
+ const tags = combineTags(
+ ...extraTags,
+ ...(input.metaTags ?? []),
+ ...(input.tags ?? []),
+ 'story'
+ );
return {
type: 'story',
@@ -320,23 +333,18 @@ export class StoryIndexGenerator {
};
});
- const { autodocs } = this.options.docs;
// We need a docs entry attached to the CSF file if either:
- // a) autodocs is globally enabled
- // b) we have autodocs enabled for this file
- // c) it is a stories.mdx transpiled to CSF
+ // a) we have autodocs enabled for this file
+ // b) it is a stories.mdx transpiled to CSF
const hasAutodocsTag = entries.some((entry) => entry.tags.includes(AUTODOCS_TAG));
const isStoriesMdx = entries.some((entry) => entry.tags.includes(STORIES_MDX_TAG));
- const createDocEntry =
- (autodocs === true && !entries.some((entry) => entry.tags.includes(NO_AUTODOCS_TAG))) ||
- (autodocs === 'tag' && hasAutodocsTag) ||
- isStoriesMdx;
+ const createDocEntry = (hasAutodocsTag && this.options.docs.autodocs !== false) || isStoriesMdx;
if (createDocEntry && this.options.build?.test?.disableAutoDocs !== true) {
const name = this.options.docs.defaultName ?? 'Docs';
const { metaId } = indexInputs[0];
const { title } = entries[0];
- const metaTags = indexInputs[0].metaTags || [];
+ const metaTags = indexInputs[0].metaTags ?? [];
const id = toId(metaId ?? title, name);
entries.unshift({
id,
@@ -457,7 +465,8 @@ export class StoryIndexGenerator {
type: 'docs',
// FIXME: update this to use the index entry's metaTags once we update this to run on `IndexInputs`
tags: combineTags(
- ...(result.tags || []),
+ ...this.getExtraTags(),
+ ...(result.tags ?? []),
csfEntry ? 'attached-mdx' : 'unattached-mdx',
'docs'
),
diff --git a/code/lib/preview-api/src/modules/store/csf/prepareStory.ts b/code/lib/preview-api/src/modules/store/csf/prepareStory.ts
index 4b8035f606d3..f7bb302c0d74 100644
--- a/code/lib/preview-api/src/modules/store/csf/prepareStory.ts
+++ b/code/lib/preview-api/src/modules/store/csf/prepareStory.ts
@@ -125,10 +125,6 @@ export function prepareMeta(
projectAnnotations: NormalizedProjectAnnotations,
moduleExport: ModuleExport
): PreparedMeta {
- console.log('prepareMeta', {
- metaTags: componentAnnotations.tags,
- projectAnnotations,
- });
return {
...preparePartialAnnotations(undefined, componentAnnotations, projectAnnotations),
moduleExport,
diff --git a/code/lib/types/src/modules/core-common.ts b/code/lib/types/src/modules/core-common.ts
index c4aeeacbcf1f..0e8dd7f002cd 100644
--- a/code/lib/types/src/modules/core-common.ts
+++ b/code/lib/types/src/modules/core-common.ts
@@ -280,6 +280,8 @@ export type DocsOptions = {
* Should we generate a docs entry per CSF file?
* Set to 'tag' (the default) to generate an entry for every CSF file with the
* 'autodocs' tag.
+ *
+ * @deprecated Use `tags: ['autodocs']` in .storybook/preview.js instead
*/
autodocs?: boolean | 'tag';
/**
From ebce0807d1b1ad53334fef14b8ddfad70ea22511 Mon Sep 17 00:00:00 2001
From: Michael Shilman
Date: Sat, 30 Mar 2024 01:00:51 +0800
Subject: [PATCH 006/117] Add autodocs tag in preview if docs.autodics is true
---
code/lib/preview-api/src/modules/store/csf/prepareStory.ts | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/code/lib/preview-api/src/modules/store/csf/prepareStory.ts b/code/lib/preview-api/src/modules/store/csf/prepareStory.ts
index f7bb302c0d74..3350a32e3fe3 100644
--- a/code/lib/preview-api/src/modules/store/csf/prepareStory.ts
+++ b/code/lib/preview-api/src/modules/store/csf/prepareStory.ts
@@ -21,6 +21,7 @@ import type {
StrictArgTypes,
} from '@storybook/types';
import { includeConditionalArg, combineTags } from '@storybook/csf';
+import { global as globalThis } from '@storybook/global';
import { applyHooks } from '../../addons';
import { combineParameters } from '../parameters';
@@ -140,7 +141,10 @@ function preparePartialAnnotations(
// anything at render time. The assumption is that as we don't load all the stories at once, this
// will have a limited cost. If this proves misguided, we can refactor it.
+ const extraTags = globalThis.DOCS_OPTIONS?.autodocs === true ? ['autodocs'] : [];
+
const tags = combineTags(
+ ...extraTags,
...(projectAnnotations.tags ?? []),
...(componentAnnotations.tags ?? []),
...(storyAnnotations?.tags ?? []),
From 33176192adcbbcc06382a0b6cf4a1122242da6c3 Mon Sep 17 00:00:00 2001
From: Kyle Gach
Date: Tue, 2 Apr 2024 12:38:54 -0600
Subject: [PATCH 007/117] Add docs for story tags
- API reference
- Guide
- CLI options
---
docs/api/cli-options.md | 9 +++-
docs/api/main-config-tags.md | 40 +++++++++++++++
docs/api/main-config.md | 1 +
docs/toc.js | 12 ++++-
docs/writing-stories/tags.md | 99 ++++++++++++++++++++++++++++++++++++
5 files changed, 158 insertions(+), 3 deletions(-)
create mode 100644 docs/api/main-config-tags.md
create mode 100644 docs/writing-stories/tags.md
diff --git a/docs/api/cli-options.md b/docs/api/cli-options.md
index 3aa9f0a3f0a1..39b366429daf 100644
--- a/docs/api/cli-options.md
+++ b/docs/api/cli-options.md
@@ -1,5 +1,6 @@
---
title: 'CLI options'
+hideRendererSelector: true
---
The Storybook command line interface (CLI) is the main tool you use to build and develop Storybook.
@@ -46,9 +47,11 @@ Options include:
| `--no-open` | Do not open Storybook automatically in the browser `storybook dev --no-open` |
| `--quiet` | Suppress verbose build output `storybook dev --quiet` |
| `--debug-webpack` | Display final webpack configurations for debugging purposes `storybook dev --debug-webpack` |
-| `--stats-json` | Write stats JSON to disk `storybook dev ---stats-json /tmp/stats` NOTE: only works for webpack. |
+| `--stats-json` | Write stats JSON to disk `storybook dev ---stats-json /tmp/stats` NOTE: only works for webpack. |
| `--docs` | Starts Storybook in documentation mode. Learn more about it in [here](../writing-docs/build-documentation.md#preview-storybooks-documentation) `storybook dev --docs` |
| `--disable-telemetry` | Disables Storybook's telemetry. Learn more about it [here](../configure/telemetry.md) `storybook dev --disable-telemetry` |
+| `--tags` | Select which [story tags](../writing-stories/tags.md) to include. Accepts a comma-seperated string. `storybook dev --tags "dev,test"` |
+| `--exclude-tags` | Select which [story tags](../writing-stories/tags.md) to exclude. Accepts a comma-seperated string. `storybook dev --tags "docs, test"` |
@@ -75,10 +78,12 @@ Options include:
| `--loglevel [level]` | Controls level of logging during build. Available options: `silly`, `verbose`, `info` (default), `warn`, `error`, `silent` `storybook build --loglevel warn` |
| `--quiet` | Suppress verbose build output `storybook build --quiet` |
| `--debug-webpack` | Display final webpack configurations for debugging purposes `storybook build --debug-webpack` |
-| `---stats-json` | Write stats JSON to disk `storybook build -stats-json /my-storybook/-stats` |
+| `---stats-json` | Write stats JSON to disk `storybook build -stats-json /my-storybook/-stats` |
| `--docs` | Builds Storybook in documentation mode. Learn more about it in [here](../writing-docs/build-documentation.md#publish-storybooks-documentation) `storybook build --docs` |
| `--disable-telemetry` | Disables Storybook's telemetry. Learn more about it [here](../configure/telemetry.md). `storybook build --disable-telemetry` |
| `--test` | Optimize Storybook's production build for performance and tests by removing unnecessary features with the `test` option. Learn more [here](../api/main-config-build.md). `storybook build --test` |
+| `--tags` | Select which [story tags](../writing-stories/tags.md) to include. Accepts a comma-seperated string. `storybook dev --tags "dev,test"` |
+| `--exclude-tags` | Select which [story tags](../writing-stories/tags.md) to exclude. Accepts a comma-seperated string. `storybook dev --tags "docs, test"` |
diff --git a/docs/api/main-config-tags.md b/docs/api/main-config-tags.md
new file mode 100644
index 000000000000..cb593d36afb5
--- /dev/null
+++ b/docs/api/main-config-tags.md
@@ -0,0 +1,40 @@
+---
+title: 'tags'
+hideRendererSelector: true
+---
+
+Parent: [main.js|ts configuration](./main-config.md)
+
+Type: `{ [key: string]: { description?: string } }`
+
+[Tags](../writing-stories/tags.md) allow you to control which stories are included in your Storybook. They are defined in the `tags` property of the Storybook configuration. Then they can be [applied to stories](../writing-stories/tags.md#tagging-stories) at the project, component, and story levels using the `tags` array.
+
+## Default tags
+
+The following tags are available by default in your project:
+
+```js
+{
+ dev: { description: 'Rendered in the Storybook UI, but only in development mode. They do not appear in the sidebar in production mode.' },
+ docs: { description: 'Rendered in the Storybook UI, but only in the docs page. They do not appear in the sidebar.' },
+ test: { description: 'Not rendered in the Storybook UI, in either development or production mode.' },
+}
+```
+
+## Custom tags
+
+You can [define your own tags](../writing-stories/tags.md#custom-tags). When doing so, the key is the tag name, and the value is an object with an optional `description` property.
+
+```ts
+// .storybook/main.ts
+import { StorybookConfig } from '@storybook/';
+
+const config: StorybookConfig = {
+ // ...rest of config
+ tags: {
+ experimental: { description: 'Stories for experimental components or features' },
+ },
+};
+
+export default config;
+```
diff --git a/docs/api/main-config.md b/docs/api/main-config.md
index a9013268e45c..e347bc355b5f 100644
--- a/docs/api/main-config.md
+++ b/docs/api/main-config.md
@@ -44,6 +44,7 @@ An object to configure Storybook containing the following properties:
- [`refs`](./main-config-refs.md)
- [`staticDirs`](./main-config-static-dirs.md)
- [`swc`](./main-config-swc.md)
+- [`tags`](./main-config-tags.md)
- [`typescript`](./main-config-typescript.md)
- [`viteFinal`](./main-config-vite-final.md)
- [`webpackFinal`](./main-config-webpack-final.md)
diff --git a/docs/toc.js b/docs/toc.js
index a4d3e85cf6be..7f3b62cbe9e8 100644
--- a/docs/toc.js
+++ b/docs/toc.js
@@ -136,6 +136,11 @@ module.exports = {
title: 'Loaders',
type: 'link',
},
+ {
+ pathSegment: 'tags',
+ title: 'Tags',
+ type: 'link',
+ },
{
pathSegment: 'naming-components-and-hierarchy',
title: 'Naming components and hierarchy',
@@ -611,11 +616,16 @@ module.exports = {
pathSegment: 'main-config-static-dirs',
type: 'link',
},
- {
+ {
title: 'swc',
pathSegment: 'main-config-swc',
type: 'link',
},
+ {
+ title: 'tags',
+ pathSegment: 'main-config-tags',
+ type: 'link',
+ },
{
title: 'typescript',
pathSegment: 'main-config-typescript',
diff --git a/docs/writing-stories/tags.md b/docs/writing-stories/tags.md
new file mode 100644
index 000000000000..174cfab0183a
--- /dev/null
+++ b/docs/writing-stories/tags.md
@@ -0,0 +1,99 @@
+---
+title: 'Tags'
+---
+
+Tags allow you to control which stories are included in your Storybook.
+
+## Default tags
+
+By default, the following tags are available in your project:
+
+- `dev`: Stories tagged with `dev` are rendered in the Storybook UI, but only in development mode. They do not appear in the sidebar in [production mode](../sharing/publish-storybook.md#build-storybook-as-a-static-web-application).
+- `docs`: Stories tagged with `docs` are rendered in the Storybook UI, but only in the [docs page](../writing-docs/autodocs.md). They do not appear in the sidebar.
+- `test`: Stories tagged with `test` are not rendered in the Storybook UI, in either development or production mode.
+
+## Custom tags
+
+You can define your own tags by adding them to the [`tags` property](../api/main-config-tags.md) in your `main.js|ts` file. For example:
+
+```ts
+// .storybook/main.ts
+import { StorybookConfig } from '@storybook/';
+
+const config: StorybookConfig = {
+ // ...rest of config
+ tags: {
+ experimental: { description: 'Stories for experimental components or features' },
+ },
+};
+
+export default config;
+```
+
+## Tagging stories
+
+You tag stories by defining a `tags` array in the story or meta definitions:
+
+```ts
+// Button.stories.ts
+import { Meta, StoryObj } from '@storybook/';
+
+import { Button } from './Button';
+
+const meta = {
+ component: Button,
+ // 👇 Stories in this file will only be included for development mode, not production
+ tags: ['dev'],
+} satisfies Meta;
+export default meta;
+
+type Story = StoryObj;
+
+export const InternalStory: Story = {
+ /**
+ * 👇 This story inherits the tags from `meta` and defines its own,
+ * making the applied tags `['dev', 'test']`.
+ * It will not be included in the UI, only for testing environments
+ */
+ tags: ['test'],
+};
+```
+
+If you want to add tags to all stories in your project, you can apply them in your `preview.js|ts` file:
+
+```ts
+// .storybook/preview.ts
+import { Preview } from '@storybook/';
+
+const preview: Preview = {
+ // ...rest of preview
+ tags: ['dev'],
+};
+
+export default preview;
+```
+
+### Removing tags
+
+To remove a tag from a story, prefix it with `!`. For example:
+
+```ts
+// Button.stories.ts
+import { Meta, StoryObj } from '@storybook/';
+
+import { Button } from './Button';
+
+const meta = {
+ component: Button,
+ // 👇 Stories in this file will only be included for development mode, not production
+ tags: ['dev'],
+} satisfies Meta;
+export default meta;
+
+type Story = StoryObj;
+
+export const InternalStory: Story = {
+ // 👇 Remove the inherited `dev` tag by using the `!` prefix
+ tags: ['!dev'],
+};
+```
From f54b030b80ef7cc10e0490e66303aa2d94385e26 Mon Sep 17 00:00:00 2001
From: Michael Shilman
Date: Thu, 4 Apr 2024 21:22:40 +0800
Subject: [PATCH 008/117] Always apply dev/docs/test tags
---
code/lib/core-server/package.json | 2 +-
.../src/utils/StoryIndexGenerator.ts | 100 ++++++++----------
.../src/modules/store/csf/prepareStory.ts | 2 +
.../template/stories/tags.stories.ts | 2 +-
code/yarn.lock | 10 +-
5 files changed, 52 insertions(+), 64 deletions(-)
diff --git a/code/lib/core-server/package.json b/code/lib/core-server/package.json
index 6e88034f3591..562e76e0a022 100644
--- a/code/lib/core-server/package.json
+++ b/code/lib/core-server/package.json
@@ -64,7 +64,7 @@
"@storybook/core-events": "workspace:*",
"@storybook/csf": "0.1.4--canary.80.f469755.0",
"@storybook/csf-tools": "workspace:*",
- "@storybook/docs-mdx": "3.0.0",
+ "@storybook/docs-mdx": "3.1.0-next.0",
"@storybook/global": "^5.0.0",
"@storybook/manager": "workspace:*",
"@storybook/manager-api": "workspace:*",
diff --git a/code/lib/core-server/src/utils/StoryIndexGenerator.ts b/code/lib/core-server/src/utils/StoryIndexGenerator.ts
index fae54d3f9372..bd8ebe25d105 100644
--- a/code/lib/core-server/src/utils/StoryIndexGenerator.ts
+++ b/code/lib/core-server/src/utils/StoryIndexGenerator.ts
@@ -9,13 +9,11 @@ import type {
IndexEntry,
StoryIndexEntry,
DocsIndexEntry,
- ComponentTitle,
NormalizedStoriesSpecifier,
DocsOptions,
Path,
Tag,
StoryIndex,
- StoryName,
Indexer,
StorybookConfigRaw,
} from '@storybook/types';
@@ -156,7 +154,7 @@ export class StoryIndexGenerator {
);
const previewCode = await this.getPreviewCode();
- const projectTags = previewCode && this.getProjectTags(previewCode);
+ const projectTags = previewCode ? this.getProjectTags(previewCode) : [];
// Extract stories for each file
await this.ensureExtracted({ projectTags });
@@ -223,11 +221,13 @@ export class StoryIndexGenerator {
// files may use the `` syntax, which requires
// that the story file that contains the meta be processed first.
await this.updateExtracted(async (specifier, absolutePath) =>
- this.isDocsMdx(absolutePath) ? false : this.extractStories(specifier, absolutePath)
+ this.isDocsMdx(absolutePath)
+ ? false
+ : this.extractStories(specifier, absolutePath, projectTags)
);
await this.updateExtracted(async (specifier, absolutePath) =>
- this.extractDocs(specifier, absolutePath)
+ this.extractDocs(specifier, absolutePath, projectTags)
);
return this.specifiers.flatMap((specifier) => {
@@ -240,34 +240,19 @@ export class StoryIndexGenerator {
);
return Object.values(cache).flatMap((entry): (IndexEntry | ErrorEntry)[] => {
if (!entry) return [];
- if (entry.type === 'docs') return [this.addProjectTags(entry, projectTags)];
+ if (entry.type === 'docs') return [entry];
if (entry.type === 'error') return [entry];
return entry.entries.map((item) => {
- if (item.type === 'docs') return this.addProjectTags(item, projectTags);
+ if (item.type === 'docs') return item;
// Drop the meta id as it isn't part of the index, we just used it for record keeping in `extractDocs`
const { metaId, ...existing } = item;
- return this.addProjectTags(existing, projectTags);
+ return existing;
});
});
});
}
- getExtraTags() {
- return this.options.docs.autodocs === true ? [AUTODOCS_TAG] : [];
- }
-
- addProjectTags(entry: IndexEntry, projectTags?: Tag[]) {
- return {
- ...entry,
- metaTags: combineTags(
- ...this.getExtraTags(),
- ...(projectTags ?? []),
- ...(entry.metaTags ?? [])
- ),
- };
- }
-
findDependencies(absoluteImports: Path[]) {
return [...this.specifierToCache.values()].flatMap((cache: SpecifierStoriesCache) =>
Object.entries(cache)
@@ -288,7 +273,8 @@ export class StoryIndexGenerator {
async extractStories(
specifier: NormalizedStoriesSpecifier,
- absolutePath: Path
+ absolutePath: Path,
+ projectTags: Tag[] = []
): Promise {
const relativePath = path.relative(this.options.workingDir, absolutePath);
const importPath = slash(normalizeStoryPath(relativePath));
@@ -313,13 +299,8 @@ export class StoryIndexGenerator {
const title = input.title ?? defaultMakeTitle();
// eslint-disable-next-line no-underscore-dangle
const id = input.__id ?? toId(input.metaId ?? title, storyNameFromExport(input.exportName));
- const extraTags = this.getExtraTags();
- const tags = combineTags(
- ...extraTags,
- ...(input.metaTags ?? []),
- ...(input.tags ?? []),
- 'story'
- );
+ const metaTags = combineTags(...projectTags, ...(input.metaTags ?? []));
+ const tags = combineTags(...metaTags, ...(input.tags ?? []));
return {
type: 'story',
@@ -328,7 +309,7 @@ export class StoryIndexGenerator {
name,
title,
importPath,
- metaTags: input.metaTags ?? [],
+ metaTags,
tags,
};
});
@@ -344,8 +325,10 @@ export class StoryIndexGenerator {
const name = this.options.docs.defaultName ?? 'Docs';
const { metaId } = indexInputs[0];
const { title } = entries[0];
- const metaTags = indexInputs[0].metaTags ?? [];
const id = toId(metaId ?? title, name);
+ const metaTags = combineTags(...projectTags, ...(indexInputs[0].metaTags ?? []));
+ const tags = combineTags(...metaTags, ...(indexInputs[0].tags ?? []));
+
entries.unshift({
id,
title,
@@ -353,11 +336,7 @@ export class StoryIndexGenerator {
importPath,
type: 'docs',
metaTags,
- tags: combineTags(
- ...metaTags,
- 'docs',
- ...(!hasAutodocsTag && !isStoriesMdx ? [AUTODOCS_TAG] : [])
- ),
+ tags,
storiesImports: [],
});
}
@@ -373,7 +352,11 @@ export class StoryIndexGenerator {
};
}
- async extractDocs(specifier: NormalizedStoriesSpecifier, absolutePath: Path) {
+ async extractDocs(
+ specifier: NormalizedStoriesSpecifier,
+ absolutePath: Path,
+ projectTags: Tag[] = []
+ ) {
const relativePath = path.relative(this.options.workingDir, absolutePath);
try {
const normalizedPath = normalizeStoryPath(relativePath);
@@ -381,14 +364,7 @@ export class StoryIndexGenerator {
const content = await fs.readFile(absolutePath, 'utf8');
- const result: {
- title?: ComponentTitle;
- of?: Path;
- name?: StoryName;
- isTemplate?: boolean;
- imports?: Path[];
- tags?: Tag[];
- } = analyze(content);
+ const result = analyze(content);
// Templates are not indexed
if (result.isTemplate) return false;
@@ -454,8 +430,21 @@ export class StoryIndexGenerator {
const name =
result.name ||
(csfEntry ? autoName(importPath, csfEntry.importPath, defaultName) : defaultName);
+
const id = toId(csfEntry?.metaId || title, name);
+ const metaTags = combineTags(
+ ...projectTags,
+ ...(result.metaTags ?? []),
+ ...(csfEntry?.metaTags ?? [])
+ );
+ const tags = combineTags(
+ ...metaTags,
+ ...(csfEntry?.tags ?? []),
+ csfEntry ? 'attached-mdx' : 'unattached-mdx'
+ );
+ console.log({ projectTags, metaTags, tags });
+
const docsEntry: DocsCacheEntry = {
id,
title,
@@ -463,13 +452,8 @@ export class StoryIndexGenerator {
importPath,
storiesImports: sortedDependencies.map((dep) => dep.entries[0].importPath),
type: 'docs',
- // FIXME: update this to use the index entry's metaTags once we update this to run on `IndexInputs`
- tags: combineTags(
- ...this.getExtraTags(),
- ...(result.tags ?? []),
- csfEntry ? 'attached-mdx' : 'unattached-mdx',
- 'docs'
- ),
+ metaTags,
+ tags,
};
return docsEntry;
} catch (err) {
@@ -577,7 +561,7 @@ export class StoryIndexGenerator {
if (this.lastError) throw this.lastError;
const previewCode = await this.getPreviewCode();
- const projectTags = previewCode && this.getProjectTags(previewCode);
+ const projectTags = previewCode ? this.getProjectTags(previewCode) : [];
// Extract any entries that are currently missing
// Pull out each file's stories into a list of stories, to be composed and sorted
@@ -678,8 +662,10 @@ export class StoryIndexGenerator {
getProjectTags(previewCode: string) {
const projectAnnotations = loadConfig(previewCode).parse();
- const projectTags = projectAnnotations.getFieldValue(['tags']);
- return projectTags;
+ const defaultTags = ['dev', 'docs', 'test'];
+ const extraTags = this.options.docs.autodocs === true ? [AUTODOCS_TAG] : [];
+ const projectTags = projectAnnotations.getFieldValue(['tags']) ?? [];
+ return [...defaultTags, ...projectTags, ...extraTags];
}
// Get the story file names in "imported order"
diff --git a/code/lib/preview-api/src/modules/store/csf/prepareStory.ts b/code/lib/preview-api/src/modules/store/csf/prepareStory.ts
index 3350a32e3fe3..bd4a4fc33d1d 100644
--- a/code/lib/preview-api/src/modules/store/csf/prepareStory.ts
+++ b/code/lib/preview-api/src/modules/store/csf/prepareStory.ts
@@ -141,9 +141,11 @@ function preparePartialAnnotations(
// anything at render time. The assumption is that as we don't load all the stories at once, this
// will have a limited cost. If this proves misguided, we can refactor it.
+ const defaultTags = ['dev', 'docs', 'test'];
const extraTags = globalThis.DOCS_OPTIONS?.autodocs === true ? ['autodocs'] : [];
const tags = combineTags(
+ ...defaultTags,
...extraTags,
...(projectAnnotations.tags ?? []),
...(componentAnnotations.tags ?? []),
diff --git a/code/lib/preview-api/template/stories/tags.stories.ts b/code/lib/preview-api/template/stories/tags.stories.ts
index b6b89c37ec6c..f86b36f08491 100644
--- a/code/lib/preview-api/template/stories/tags.stories.ts
+++ b/code/lib/preview-api/template/stories/tags.stories.ts
@@ -4,7 +4,7 @@ import { within, expect } from '@storybook/test';
export default {
component: globalThis.Components.Pre,
- tags: ['component-one', 'component-two', 'autodocs'],
+ tags: ['component-one', 'component-two', '!autodocs'],
decorators: [
(storyFn: PartialStoryFn, context: StoryContext) => {
return storyFn({
diff --git a/code/yarn.lock b/code/yarn.lock
index 30fe40584238..b284385e5ec3 100644
--- a/code/yarn.lock
+++ b/code/yarn.lock
@@ -5886,7 +5886,7 @@ __metadata:
"@storybook/core-events": "workspace:*"
"@storybook/csf": "npm:0.1.4--canary.80.f469755.0"
"@storybook/csf-tools": "workspace:*"
- "@storybook/docs-mdx": "npm:3.0.0"
+ "@storybook/docs-mdx": "npm:3.1.0-next.0"
"@storybook/global": "npm:^5.0.0"
"@storybook/manager": "workspace:*"
"@storybook/manager-api": "workspace:*"
@@ -5994,10 +5994,10 @@ __metadata:
languageName: node
linkType: hard
-"@storybook/docs-mdx@npm:3.0.0":
- version: 3.0.0
- resolution: "@storybook/docs-mdx@npm:3.0.0"
- checksum: 10c0/4f4242fc05b57e8dc239204c71fd0d1481c9abbf20d12dd0f3dace74f77a7ff7cbe0bd07d7d785873b45747be64cad273423d3dc0cf89b52e9f117592a4b054f
+"@storybook/docs-mdx@npm:3.1.0-next.0":
+ version: 3.1.0-next.0
+ resolution: "@storybook/docs-mdx@npm:3.1.0-next.0"
+ checksum: 10c0/7622d7c6318e842c90a71c1836d68531236c31fff7081c885803eddfafb7e3f8998689f612eaa0292209ada8352a36657dcacb5d3ef4632b8e8b8a283c39602e
languageName: node
linkType: hard
From fea9c2e44bba9437d32cb813e4affa34e9f6443a Mon Sep 17 00:00:00 2001
From: Michael Shilman
Date: Fri, 5 Apr 2024 10:04:01 +0800
Subject: [PATCH 009/117] Fix tag precedence in CSF combineTags util
---
code/addons/links/package.json | 2 +-
code/lib/codemod/package.json | 2 +-
code/lib/core-server/package.json | 2 +-
code/lib/csf-tools/package.json | 2 +-
code/lib/manager-api/package.json | 2 +-
code/lib/preview-api/package.json | 2 +-
code/lib/source-loader/package.json | 2 +-
code/lib/types/package.json | 2 +-
code/package.json | 2 +-
code/renderers/server/package.json | 2 +-
code/ui/blocks/package.json | 2 +-
code/ui/components/package.json | 2 +-
code/yarn.lock | 32 ++++++++++++++---------------
13 files changed, 28 insertions(+), 28 deletions(-)
diff --git a/code/addons/links/package.json b/code/addons/links/package.json
index 8a2c600d574c..8d0cb0156fc6 100644
--- a/code/addons/links/package.json
+++ b/code/addons/links/package.json
@@ -67,7 +67,7 @@
"prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/addon-bundle.ts"
},
"dependencies": {
- "@storybook/csf": "0.1.4--canary.80.f469755.0",
+ "@storybook/csf": "0.1.4--canary.80.ba37fd5.0",
"@storybook/global": "^5.0.0",
"ts-dedent": "^2.0.0"
},
diff --git a/code/lib/codemod/package.json b/code/lib/codemod/package.json
index 35e5ebab28c9..494aefdeae80 100644
--- a/code/lib/codemod/package.json
+++ b/code/lib/codemod/package.json
@@ -57,7 +57,7 @@
"@babel/core": "^7.23.2",
"@babel/preset-env": "^7.23.2",
"@babel/types": "^7.23.0",
- "@storybook/csf": "0.1.4--canary.80.f469755.0",
+ "@storybook/csf": "0.1.4--canary.80.ba37fd5.0",
"@storybook/csf-tools": "workspace:*",
"@storybook/node-logger": "workspace:*",
"@storybook/types": "workspace:*",
diff --git a/code/lib/core-server/package.json b/code/lib/core-server/package.json
index 562e76e0a022..3eab0e705596 100644
--- a/code/lib/core-server/package.json
+++ b/code/lib/core-server/package.json
@@ -62,7 +62,7 @@
"@storybook/channels": "workspace:*",
"@storybook/core-common": "workspace:*",
"@storybook/core-events": "workspace:*",
- "@storybook/csf": "0.1.4--canary.80.f469755.0",
+ "@storybook/csf": "0.1.4--canary.80.ba37fd5.0",
"@storybook/csf-tools": "workspace:*",
"@storybook/docs-mdx": "3.1.0-next.0",
"@storybook/global": "^5.0.0",
diff --git a/code/lib/csf-tools/package.json b/code/lib/csf-tools/package.json
index 1f8b41d7b51b..70f557e61717 100644
--- a/code/lib/csf-tools/package.json
+++ b/code/lib/csf-tools/package.json
@@ -46,7 +46,7 @@
"@babel/parser": "^7.23.0",
"@babel/traverse": "^7.23.2",
"@babel/types": "^7.23.0",
- "@storybook/csf": "0.1.4--canary.80.f469755.0",
+ "@storybook/csf": "0.1.4--canary.80.ba37fd5.0",
"@storybook/types": "workspace:*",
"fs-extra": "^11.1.0",
"recast": "^0.23.5",
diff --git a/code/lib/manager-api/package.json b/code/lib/manager-api/package.json
index abced2447faf..1bd4b13a2894 100644
--- a/code/lib/manager-api/package.json
+++ b/code/lib/manager-api/package.json
@@ -47,7 +47,7 @@
"@storybook/channels": "workspace:*",
"@storybook/client-logger": "workspace:*",
"@storybook/core-events": "workspace:*",
- "@storybook/csf": "0.1.4--canary.80.f469755.0",
+ "@storybook/csf": "0.1.4--canary.80.ba37fd5.0",
"@storybook/global": "^5.0.0",
"@storybook/icons": "^1.2.5",
"@storybook/router": "workspace:*",
diff --git a/code/lib/preview-api/package.json b/code/lib/preview-api/package.json
index cb995ab59c37..18d14c1dc6e6 100644
--- a/code/lib/preview-api/package.json
+++ b/code/lib/preview-api/package.json
@@ -47,7 +47,7 @@
"@storybook/channels": "workspace:*",
"@storybook/client-logger": "workspace:*",
"@storybook/core-events": "workspace:*",
- "@storybook/csf": "0.1.4--canary.80.f469755.0",
+ "@storybook/csf": "0.1.4--canary.80.ba37fd5.0",
"@storybook/global": "^5.0.0",
"@storybook/types": "workspace:*",
"@types/qs": "^6.9.5",
diff --git a/code/lib/source-loader/package.json b/code/lib/source-loader/package.json
index 5f1ac102b5d6..d6e881699e63 100644
--- a/code/lib/source-loader/package.json
+++ b/code/lib/source-loader/package.json
@@ -45,7 +45,7 @@
"prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts"
},
"dependencies": {
- "@storybook/csf": "0.1.4--canary.80.f469755.0",
+ "@storybook/csf": "0.1.4--canary.80.ba37fd5.0",
"@storybook/types": "workspace:*",
"estraverse": "^5.2.0",
"lodash": "^4.17.21",
diff --git a/code/lib/types/package.json b/code/lib/types/package.json
index f9eb319420b7..5beadddb167c 100644
--- a/code/lib/types/package.json
+++ b/code/lib/types/package.json
@@ -49,7 +49,7 @@
"file-system-cache": "2.3.0"
},
"devDependencies": {
- "@storybook/csf": "0.1.4--canary.80.f469755.0",
+ "@storybook/csf": "0.1.4--canary.80.ba37fd5.0",
"@types/fs-extra": "^11.0.1",
"@types/node": "^18.0.0",
"typescript": "^5.3.2"
diff --git a/code/package.json b/code/package.json
index 454f5b0dd84a..fa57cc16f825 100644
--- a/code/package.json
+++ b/code/package.json
@@ -127,7 +127,7 @@
"@storybook/core-events": "workspace:*",
"@storybook/core-server": "workspace:*",
"@storybook/core-webpack": "workspace:*",
- "@storybook/csf": "0.1.4--canary.80.f469755.0",
+ "@storybook/csf": "0.1.4--canary.80.ba37fd5.0",
"@storybook/csf-plugin": "workspace:*",
"@storybook/csf-tools": "workspace:*",
"@storybook/docs-tools": "workspace:*",
diff --git a/code/renderers/server/package.json b/code/renderers/server/package.json
index 54c669fac08a..ee1d7500c933 100644
--- a/code/renderers/server/package.json
+++ b/code/renderers/server/package.json
@@ -46,7 +46,7 @@
"prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts"
},
"dependencies": {
- "@storybook/csf": "0.1.4--canary.80.f469755.0",
+ "@storybook/csf": "0.1.4--canary.80.ba37fd5.0",
"@storybook/csf-tools": "workspace:*",
"@storybook/global": "^5.0.0",
"@storybook/preview-api": "workspace:*",
diff --git a/code/ui/blocks/package.json b/code/ui/blocks/package.json
index a28714a17228..6af6b19568c2 100644
--- a/code/ui/blocks/package.json
+++ b/code/ui/blocks/package.json
@@ -48,7 +48,7 @@
"@storybook/client-logger": "workspace:*",
"@storybook/components": "workspace:*",
"@storybook/core-events": "workspace:*",
- "@storybook/csf": "0.1.4--canary.80.f469755.0",
+ "@storybook/csf": "0.1.4--canary.80.ba37fd5.0",
"@storybook/docs-tools": "workspace:*",
"@storybook/global": "^5.0.0",
"@storybook/icons": "^1.2.5",
diff --git a/code/ui/components/package.json b/code/ui/components/package.json
index 6569d692d59a..82dbd87d5f3e 100644
--- a/code/ui/components/package.json
+++ b/code/ui/components/package.json
@@ -61,7 +61,7 @@
"dependencies": {
"@radix-ui/react-slot": "^1.0.2",
"@storybook/client-logger": "workspace:*",
- "@storybook/csf": "0.1.4--canary.80.f469755.0",
+ "@storybook/csf": "0.1.4--canary.80.ba37fd5.0",
"@storybook/global": "^5.0.0",
"@storybook/icons": "^1.2.5",
"@storybook/theming": "workspace:*",
diff --git a/code/yarn.lock b/code/yarn.lock
index b284385e5ec3..a8a50005249a 100644
--- a/code/yarn.lock
+++ b/code/yarn.lock
@@ -5256,7 +5256,7 @@ __metadata:
dependencies:
"@storybook/client-logger": "workspace:*"
"@storybook/core-events": "workspace:*"
- "@storybook/csf": "npm:0.1.4--canary.80.f469755.0"
+ "@storybook/csf": "npm:0.1.4--canary.80.ba37fd5.0"
"@storybook/global": "npm:^5.0.0"
"@storybook/manager-api": "workspace:*"
"@storybook/preview-api": "workspace:*"
@@ -5524,7 +5524,7 @@ __metadata:
"@storybook/client-logger": "workspace:*"
"@storybook/components": "workspace:*"
"@storybook/core-events": "workspace:*"
- "@storybook/csf": "npm:0.1.4--canary.80.f469755.0"
+ "@storybook/csf": "npm:0.1.4--canary.80.ba37fd5.0"
"@storybook/docs-tools": "workspace:*"
"@storybook/global": "npm:^5.0.0"
"@storybook/icons": "npm:^1.2.5"
@@ -5755,7 +5755,7 @@ __metadata:
"@babel/core": "npm:^7.23.2"
"@babel/preset-env": "npm:^7.23.2"
"@babel/types": "npm:^7.23.0"
- "@storybook/csf": "npm:0.1.4--canary.80.f469755.0"
+ "@storybook/csf": "npm:0.1.4--canary.80.ba37fd5.0"
"@storybook/csf-tools": "workspace:*"
"@storybook/node-logger": "workspace:*"
"@storybook/types": "workspace:*"
@@ -5791,7 +5791,7 @@ __metadata:
"@radix-ui/react-scroll-area": "npm:^1.0.5"
"@radix-ui/react-slot": "npm:^1.0.2"
"@storybook/client-logger": "workspace:*"
- "@storybook/csf": "npm:0.1.4--canary.80.f469755.0"
+ "@storybook/csf": "npm:0.1.4--canary.80.ba37fd5.0"
"@storybook/global": "npm:^5.0.0"
"@storybook/icons": "npm:^1.2.5"
"@storybook/test": "workspace:*"
@@ -5884,7 +5884,7 @@ __metadata:
"@storybook/channels": "workspace:*"
"@storybook/core-common": "workspace:*"
"@storybook/core-events": "workspace:*"
- "@storybook/csf": "npm:0.1.4--canary.80.f469755.0"
+ "@storybook/csf": "npm:0.1.4--canary.80.ba37fd5.0"
"@storybook/csf-tools": "workspace:*"
"@storybook/docs-mdx": "npm:3.1.0-next.0"
"@storybook/global": "npm:^5.0.0"
@@ -5964,7 +5964,7 @@ __metadata:
"@babel/parser": "npm:^7.23.0"
"@babel/traverse": "npm:^7.23.2"
"@babel/types": "npm:^7.23.0"
- "@storybook/csf": "npm:0.1.4--canary.80.f469755.0"
+ "@storybook/csf": "npm:0.1.4--canary.80.ba37fd5.0"
"@storybook/types": "workspace:*"
"@types/fs-extra": "npm:^11.0.1"
"@types/js-yaml": "npm:^4.0.5"
@@ -5976,12 +5976,12 @@ __metadata:
languageName: unknown
linkType: soft
-"@storybook/csf@npm:0.1.4--canary.80.f469755.0":
- version: 0.1.4--canary.80.f469755.0
- resolution: "@storybook/csf@npm:0.1.4--canary.80.f469755.0"
+"@storybook/csf@npm:0.1.4--canary.80.ba37fd5.0":
+ version: 0.1.4--canary.80.ba37fd5.0
+ resolution: "@storybook/csf@npm:0.1.4--canary.80.ba37fd5.0"
dependencies:
type-fest: "npm:^2.19.0"
- checksum: 10c0/a5b2dfe1c2d02b6ab072c7ca0b3918f24ac3093ec740cd79a44f178b583d02ad1f838bfd77cd30ea486c92e7a9b37a921d2e0807e3b1a7cc26f57c62a446ed24
+ checksum: 10c0/88518cff77be4b8a714fdfa091686455e3cbe0023116ca4b9bce2b72cc5e0659c6fd6b5a4920a31e51db0050fb622bbcb0f32fffb09c9b84a068df92cb0b0585
languageName: node
linkType: hard
@@ -6161,7 +6161,7 @@ __metadata:
"@storybook/channels": "workspace:*"
"@storybook/client-logger": "workspace:*"
"@storybook/core-events": "workspace:*"
- "@storybook/csf": "npm:0.1.4--canary.80.f469755.0"
+ "@storybook/csf": "npm:0.1.4--canary.80.ba37fd5.0"
"@storybook/global": "npm:^5.0.0"
"@storybook/icons": "npm:^1.2.5"
"@storybook/router": "workspace:*"
@@ -6493,7 +6493,7 @@ __metadata:
"@storybook/client-logger": "workspace:*"
"@storybook/core-common": "workspace:*"
"@storybook/core-events": "workspace:*"
- "@storybook/csf": "npm:0.1.4--canary.80.f469755.0"
+ "@storybook/csf": "npm:0.1.4--canary.80.ba37fd5.0"
"@storybook/global": "npm:^5.0.0"
"@storybook/types": "workspace:*"
"@types/qs": "npm:^6.9.5"
@@ -6678,7 +6678,7 @@ __metadata:
"@storybook/core-events": "workspace:*"
"@storybook/core-server": "workspace:*"
"@storybook/core-webpack": "workspace:*"
- "@storybook/csf": "npm:0.1.4--canary.80.f469755.0"
+ "@storybook/csf": "npm:0.1.4--canary.80.ba37fd5.0"
"@storybook/csf-plugin": "workspace:*"
"@storybook/csf-tools": "workspace:*"
"@storybook/docs-tools": "workspace:*"
@@ -6833,7 +6833,7 @@ __metadata:
version: 0.0.0-use.local
resolution: "@storybook/server@workspace:renderers/server"
dependencies:
- "@storybook/csf": "npm:0.1.4--canary.80.f469755.0"
+ "@storybook/csf": "npm:0.1.4--canary.80.ba37fd5.0"
"@storybook/csf-tools": "workspace:*"
"@storybook/global": "npm:^5.0.0"
"@storybook/preview-api": "workspace:*"
@@ -6850,7 +6850,7 @@ __metadata:
version: 0.0.0-use.local
resolution: "@storybook/source-loader@workspace:lib/source-loader"
dependencies:
- "@storybook/csf": "npm:0.1.4--canary.80.f469755.0"
+ "@storybook/csf": "npm:0.1.4--canary.80.ba37fd5.0"
"@storybook/types": "workspace:*"
estraverse: "npm:^5.2.0"
lodash: "npm:^4.17.21"
@@ -7028,7 +7028,7 @@ __metadata:
resolution: "@storybook/types@workspace:lib/types"
dependencies:
"@storybook/channels": "workspace:*"
- "@storybook/csf": "npm:0.1.4--canary.80.f469755.0"
+ "@storybook/csf": "npm:0.1.4--canary.80.ba37fd5.0"
"@types/express": "npm:^4.7.0"
"@types/fs-extra": "npm:^11.0.1"
"@types/node": "npm:^18.0.0"
From bcd3da875a7e3c716576cdcf25b54965c9366271 Mon Sep 17 00:00:00 2001
From: Michael Shilman
Date: Fri, 5 Apr 2024 11:40:35 +0800
Subject: [PATCH 010/117] Remove metaTags concept
---
.../src/utils/StoryIndexGenerator.test.ts | 225 +++++++++++++-----
.../src/utils/StoryIndexGenerator.ts | 19 +-
.../utils/__tests__/index-extraction.test.ts | 34 +--
.../src/utils/stories-json.test.ts | 52 +++-
code/lib/csf-tools/src/CsfFile.test.ts | 10 -
code/lib/csf-tools/src/CsfFile.ts | 5 +-
.../modules/preview-web/PreviewWeb.test.ts | 12 +
.../src/modules/store/StoryStore.test.ts | 30 +++
code/lib/types/src/modules/indexer.ts | 3 -
9 files changed, 266 insertions(+), 124 deletions(-)
diff --git a/code/lib/core-server/src/utils/StoryIndexGenerator.test.ts b/code/lib/core-server/src/utils/StoryIndexGenerator.test.ts
index ad4aafd4a26e..ed9ed2a20b2c 100644
--- a/code/lib/core-server/src/utils/StoryIndexGenerator.test.ts
+++ b/code/lib/core-server/src/utils/StoryIndexGenerator.test.ts
@@ -79,9 +79,11 @@ describe('StoryIndexGenerator', () => {
"importPath": "./src/A.stories.js",
"name": "Story One",
"tags": [
+ "dev",
+ "docs",
+ "test",
"component-tag",
"story-tag",
- "story",
],
"title": "A",
"type": "story",
@@ -110,8 +112,10 @@ describe('StoryIndexGenerator', () => {
"importPath": "./src/F.story.ts",
"name": "Story One",
"tags": [
+ "dev",
+ "docs",
+ "test",
"autodocs",
- "story",
],
"title": "F",
"type": "story",
@@ -140,8 +144,10 @@ describe('StoryIndexGenerator', () => {
"importPath": "./src/stories.ts",
"name": "Story One",
"tags": [
+ "dev",
+ "docs",
+ "test",
"autodocs",
- "story",
],
"title": "stories",
"type": "story",
@@ -170,8 +176,10 @@ describe('StoryIndexGenerator', () => {
"importPath": "./src/nested/Button.stories.ts",
"name": "Story One",
"tags": [
+ "dev",
+ "docs",
+ "test",
"component-tag",
- "story",
],
"title": "nested/Button",
"type": "story",
@@ -181,7 +189,9 @@ describe('StoryIndexGenerator', () => {
"importPath": "./src/second-nested/G.stories.ts",
"name": "Story One",
"tags": [
- "story",
+ "dev",
+ "docs",
+ "test",
],
"title": "second-nested/G",
"type": "story",
@@ -210,9 +220,11 @@ describe('StoryIndexGenerator', () => {
"importPath": "./src/A.stories.js",
"name": "Story One",
"tags": [
+ "dev",
+ "docs",
+ "test",
"component-tag",
"story-tag",
- "story",
],
"title": "A",
"type": "story",
@@ -222,8 +234,10 @@ describe('StoryIndexGenerator', () => {
"importPath": "./src/B.stories.ts",
"name": "Story One",
"tags": [
+ "dev",
+ "docs",
+ "test",
"autodocs",
- "story",
],
"title": "B",
"type": "story",
@@ -233,8 +247,10 @@ describe('StoryIndexGenerator', () => {
"importPath": "./src/D.stories.jsx",
"name": "Story One",
"tags": [
+ "dev",
+ "docs",
+ "test",
"autodocs",
- "story",
],
"title": "D",
"type": "story",
@@ -244,7 +260,9 @@ describe('StoryIndexGenerator', () => {
"importPath": "./src/first-nested/deeply/F.stories.js",
"name": "Story One",
"tags": [
- "story",
+ "dev",
+ "docs",
+ "test",
],
"title": "first-nested/deeply/F",
"type": "story",
@@ -254,8 +272,10 @@ describe('StoryIndexGenerator', () => {
"importPath": "./src/H.stories.mjs",
"name": "Story One",
"tags": [
+ "dev",
+ "docs",
+ "test",
"autodocs",
- "story",
],
"title": "H",
"type": "story",
@@ -265,8 +285,10 @@ describe('StoryIndexGenerator', () => {
"importPath": "./src/nested/Button.stories.ts",
"name": "Story One",
"tags": [
+ "dev",
+ "docs",
+ "test",
"component-tag",
- "story",
],
"title": "nested/Button",
"type": "story",
@@ -276,7 +298,9 @@ describe('StoryIndexGenerator', () => {
"importPath": "./src/second-nested/G.stories.ts",
"name": "Story One",
"tags": [
- "story",
+ "dev",
+ "docs",
+ "test",
],
"title": "second-nested/G",
"type": "story",
@@ -310,9 +334,11 @@ describe('StoryIndexGenerator', () => {
"importPath": "./src/A.stories.js",
"name": "Story One",
"tags": [
+ "dev",
+ "docs",
+ "test",
"component-tag",
"story-tag",
- "story",
],
"title": "A",
"type": "story",
@@ -323,8 +349,10 @@ describe('StoryIndexGenerator', () => {
"name": "docs",
"storiesImports": [],
"tags": [
- "autodocs",
+ "dev",
"docs",
+ "test",
+ "autodocs",
],
"title": "B",
"type": "docs",
@@ -334,8 +362,10 @@ describe('StoryIndexGenerator', () => {
"importPath": "./src/B.stories.ts",
"name": "Story One",
"tags": [
+ "dev",
+ "docs",
+ "test",
"autodocs",
- "story",
],
"title": "B",
"type": "story",
@@ -346,8 +376,10 @@ describe('StoryIndexGenerator', () => {
"name": "docs",
"storiesImports": [],
"tags": [
- "autodocs",
+ "dev",
"docs",
+ "test",
+ "autodocs",
],
"title": "D",
"type": "docs",
@@ -357,8 +389,10 @@ describe('StoryIndexGenerator', () => {
"importPath": "./src/D.stories.jsx",
"name": "Story One",
"tags": [
+ "dev",
+ "docs",
+ "test",
"autodocs",
- "story",
],
"title": "D",
"type": "story",
@@ -368,7 +402,9 @@ describe('StoryIndexGenerator', () => {
"importPath": "./src/first-nested/deeply/F.stories.js",
"name": "Story One",
"tags": [
- "story",
+ "dev",
+ "docs",
+ "test",
],
"title": "first-nested/deeply/F",
"type": "story",
@@ -379,8 +415,10 @@ describe('StoryIndexGenerator', () => {
"name": "docs",
"storiesImports": [],
"tags": [
- "autodocs",
+ "dev",
"docs",
+ "test",
+ "autodocs",
],
"title": "H",
"type": "docs",
@@ -390,8 +428,10 @@ describe('StoryIndexGenerator', () => {
"importPath": "./src/H.stories.mjs",
"name": "Story One",
"tags": [
+ "dev",
+ "docs",
+ "test",
"autodocs",
- "story",
],
"title": "H",
"type": "story",
@@ -401,8 +441,10 @@ describe('StoryIndexGenerator', () => {
"importPath": "./src/nested/Button.stories.ts",
"name": "Story One",
"tags": [
+ "dev",
+ "docs",
+ "test",
"component-tag",
- "story",
],
"title": "nested/Button",
"type": "story",
@@ -412,7 +454,9 @@ describe('StoryIndexGenerator', () => {
"importPath": "./src/second-nested/G.stories.ts",
"name": "Story One",
"tags": [
- "story",
+ "dev",
+ "docs",
+ "test",
],
"title": "second-nested/G",
"type": "story",
@@ -535,8 +579,10 @@ describe('StoryIndexGenerator', () => {
"name": "docs",
"storiesImports": [],
"tags": [
- "autodocs",
+ "dev",
"docs",
+ "test",
+ "autodocs",
],
"title": "B",
"type": "docs",
@@ -549,8 +595,11 @@ describe('StoryIndexGenerator', () => {
"./src/B.stories.ts",
],
"tags": [
- "attached-mdx",
+ "dev",
"docs",
+ "test",
+ "autodocs",
+ "attached-mdx",
],
"title": "B",
"type": "docs",
@@ -560,8 +609,10 @@ describe('StoryIndexGenerator', () => {
"importPath": "./src/B.stories.ts",
"name": "Story One",
"tags": [
+ "dev",
+ "docs",
+ "test",
"autodocs",
- "story",
],
"title": "B",
"type": "story",
@@ -594,8 +645,10 @@ describe('StoryIndexGenerator', () => {
"name": "docs",
"storiesImports": [],
"tags": [
- "autodocs",
+ "dev",
"docs",
+ "test",
+ "autodocs",
],
"title": "B",
"type": "docs",
@@ -608,8 +661,11 @@ describe('StoryIndexGenerator', () => {
"./src/B.stories.ts",
],
"tags": [
- "attached-mdx",
+ "dev",
"docs",
+ "test",
+ "autodocs",
+ "attached-mdx",
],
"title": "B",
"type": "docs",
@@ -619,8 +675,10 @@ describe('StoryIndexGenerator', () => {
"importPath": "./src/B.stories.ts",
"name": "Story One",
"tags": [
+ "dev",
+ "docs",
+ "test",
"autodocs",
- "story",
],
"title": "B",
"type": "story",
@@ -653,14 +711,19 @@ describe('StoryIndexGenerator', () => {
"entries": {
"a--docs": {
"id": "a--docs",
- "importPath": "./errors/A.mdx",
+ "importPath": "./src/A.stories.js",
"name": "docs",
"storiesImports": [
+ "./errors/A.mdx",
"./src/A.stories.js",
],
"tags": [
- "attached-mdx",
+ "dev",
"docs",
+ "test",
+ "autodocs",
+ "component-tag",
+ "story-tag",
],
"title": "A",
"type": "docs",
@@ -670,9 +733,12 @@ describe('StoryIndexGenerator', () => {
"importPath": "./src/A.stories.js",
"name": "Story One",
"tags": [
+ "dev",
+ "docs",
+ "test",
+ "autodocs",
"component-tag",
"story-tag",
- "story",
],
"title": "A",
"type": "story",
@@ -703,8 +769,10 @@ describe('StoryIndexGenerator', () => {
"./duplicate/SecondA.stories.js",
],
"tags": [
- "autodocs",
+ "dev",
"docs",
+ "test",
+ "autodocs",
],
"title": "duplicate/A",
"type": "docs",
@@ -714,8 +782,10 @@ describe('StoryIndexGenerator', () => {
"importPath": "./duplicate/A.stories.js",
"name": "Story One",
"tags": [
+ "dev",
+ "docs",
+ "test",
"autodocs",
- "story",
],
"title": "duplicate/A",
"type": "story",
@@ -725,8 +795,10 @@ describe('StoryIndexGenerator', () => {
"importPath": "./duplicate/SecondA.stories.js",
"name": "Story Two",
"tags": [
+ "dev",
+ "docs",
+ "test",
"autodocs",
- "story",
],
"title": "duplicate/A",
"type": "story",
@@ -773,8 +845,10 @@ describe('StoryIndexGenerator', () => {
"name": "docs",
"storiesImports": [],
"tags": [
- "autodocs",
+ "dev",
"docs",
+ "test",
+ "autodocs",
],
"title": "A",
"type": "docs",
@@ -784,8 +858,10 @@ describe('StoryIndexGenerator', () => {
"importPath": "./docs-id-generation/A.stories.jsx",
"name": "Story One",
"tags": [
+ "dev",
+ "docs",
+ "test",
"autodocs",
- "story",
],
"title": "A",
"type": "story",
@@ -813,8 +889,12 @@ describe('StoryIndexGenerator', () => {
"./src/A.stories.js",
],
"tags": [
- "attached-mdx",
+ "dev",
"docs",
+ "test",
+ "component-tag",
+ "story-tag",
+ "attached-mdx",
],
"title": "A",
"type": "docs",
@@ -827,8 +907,12 @@ describe('StoryIndexGenerator', () => {
"./src/A.stories.js",
],
"tags": [
- "attached-mdx",
+ "dev",
"docs",
+ "test",
+ "component-tag",
+ "story-tag",
+ "attached-mdx",
],
"title": "A",
"type": "docs",
@@ -838,9 +922,11 @@ describe('StoryIndexGenerator', () => {
"importPath": "./src/A.stories.js",
"name": "Story One",
"tags": [
+ "dev",
+ "docs",
+ "test",
"component-tag",
"story-tag",
- "story",
],
"title": "A",
"type": "story",
@@ -851,8 +937,10 @@ describe('StoryIndexGenerator', () => {
"name": "docs",
"storiesImports": [],
"tags": [
- "unattached-mdx",
+ "dev",
"docs",
+ "test",
+ "unattached-mdx",
],
"title": "ComponentReference",
"type": "docs",
@@ -863,8 +951,10 @@ describe('StoryIndexGenerator', () => {
"name": "docs",
"storiesImports": [],
"tags": [
- "unattached-mdx",
+ "dev",
"docs",
+ "test",
+ "unattached-mdx",
],
"title": "docs2/Yabbadabbadooo",
"type": "docs",
@@ -875,8 +965,10 @@ describe('StoryIndexGenerator', () => {
"name": "docs",
"storiesImports": [],
"tags": [
- "unattached-mdx",
+ "dev",
"docs",
+ "test",
+ "unattached-mdx",
],
"title": "NoTitle",
"type": "docs",
@@ -936,8 +1028,12 @@ describe('StoryIndexGenerator', () => {
"./src/A.stories.js",
],
"tags": [
- "attached-mdx",
+ "dev",
"docs",
+ "test",
+ "component-tag",
+ "story-tag",
+ "attached-mdx",
],
"title": "A",
"type": "docs",
@@ -950,8 +1046,12 @@ describe('StoryIndexGenerator', () => {
"./src/A.stories.js",
],
"tags": [
- "attached-mdx",
+ "dev",
"docs",
+ "test",
+ "component-tag",
+ "story-tag",
+ "attached-mdx",
],
"title": "A",
"type": "docs",
@@ -961,9 +1061,11 @@ describe('StoryIndexGenerator', () => {
"importPath": "./src/A.stories.js",
"name": "Story One",
"tags": [
+ "dev",
+ "docs",
+ "test",
"component-tag",
"story-tag",
- "story",
],
"title": "A",
"type": "story",
@@ -974,8 +1076,10 @@ describe('StoryIndexGenerator', () => {
"name": "Info",
"storiesImports": [],
"tags": [
- "unattached-mdx",
+ "dev",
"docs",
+ "test",
+ "unattached-mdx",
],
"title": "ComponentReference",
"type": "docs",
@@ -986,8 +1090,10 @@ describe('StoryIndexGenerator', () => {
"name": "Info",
"storiesImports": [],
"tags": [
- "unattached-mdx",
+ "dev",
"docs",
+ "test",
+ "unattached-mdx",
],
"title": "docs2/Yabbadabbadooo",
"type": "docs",
@@ -998,8 +1104,10 @@ describe('StoryIndexGenerator', () => {
"name": "Info",
"storiesImports": [],
"tags": [
- "unattached-mdx",
+ "dev",
"docs",
+ "test",
+ "unattached-mdx",
],
"title": "NoTitle",
"type": "docs",
@@ -1028,9 +1136,11 @@ describe('StoryIndexGenerator', () => {
"importPath": "./src/A.stories.js",
"name": "Story One",
"tags": [
+ "dev",
+ "docs",
+ "test",
"component-tag",
"story-tag",
- "story",
],
"title": "A",
"type": "story",
@@ -1040,8 +1150,10 @@ describe('StoryIndexGenerator', () => {
"importPath": "./src/B.stories.ts",
"name": "Story One",
"tags": [
+ "dev",
+ "docs",
+ "test",
"autodocs",
- "story",
],
"title": "B",
"type": "story",
@@ -1055,8 +1167,11 @@ describe('StoryIndexGenerator', () => {
"./src/A.stories.js",
],
"tags": [
- "attached-mdx",
+ "dev",
"docs",
+ "test",
+ "autodocs",
+ "attached-mdx",
],
"title": "B",
"type": "docs",
@@ -1092,8 +1207,10 @@ describe('StoryIndexGenerator', () => {
"./docs-id-generation/B.stories.jsx",
],
"tags": [
- "attached-mdx",
+ "dev",
"docs",
+ "test",
+ "attached-mdx",
],
"title": "B",
"type": "docs",
@@ -1103,7 +1220,9 @@ describe('StoryIndexGenerator', () => {
"importPath": "./docs-id-generation/B.stories.jsx",
"name": "Story One",
"tags": [
- "story",
+ "dev",
+ "docs",
+ "test",
],
"title": "B",
"type": "story",
diff --git a/code/lib/core-server/src/utils/StoryIndexGenerator.ts b/code/lib/core-server/src/utils/StoryIndexGenerator.ts
index bd8ebe25d105..ae97d6acfb11 100644
--- a/code/lib/core-server/src/utils/StoryIndexGenerator.ts
+++ b/code/lib/core-server/src/utils/StoryIndexGenerator.ts
@@ -299,8 +299,7 @@ export class StoryIndexGenerator {
const title = input.title ?? defaultMakeTitle();
// eslint-disable-next-line no-underscore-dangle
const id = input.__id ?? toId(input.metaId ?? title, storyNameFromExport(input.exportName));
- const metaTags = combineTags(...projectTags, ...(input.metaTags ?? []));
- const tags = combineTags(...metaTags, ...(input.tags ?? []));
+ const tags = combineTags(...projectTags, ...(input.tags ?? []));
return {
type: 'story',
@@ -309,7 +308,6 @@ export class StoryIndexGenerator {
name,
title,
importPath,
- metaTags,
tags,
};
});
@@ -326,8 +324,7 @@ export class StoryIndexGenerator {
const { metaId } = indexInputs[0];
const { title } = entries[0];
const id = toId(metaId ?? title, name);
- const metaTags = combineTags(...projectTags, ...(indexInputs[0].metaTags ?? []));
- const tags = combineTags(...metaTags, ...(indexInputs[0].tags ?? []));
+ const tags = combineTags(...projectTags, ...(indexInputs[0].tags ?? []));
entries.unshift({
id,
@@ -335,7 +332,6 @@ export class StoryIndexGenerator {
name,
importPath,
type: 'docs',
- metaTags,
tags,
storiesImports: [],
});
@@ -433,17 +429,13 @@ export class StoryIndexGenerator {
const id = toId(csfEntry?.metaId || title, name);
- const metaTags = combineTags(
- ...projectTags,
- ...(result.metaTags ?? []),
- ...(csfEntry?.metaTags ?? [])
- );
const tags = combineTags(
- ...metaTags,
+ ...projectTags,
...(csfEntry?.tags ?? []),
+ ...(result.metaTags ?? []),
csfEntry ? 'attached-mdx' : 'unattached-mdx'
);
- console.log({ projectTags, metaTags, tags });
+ console.log({ projectTags, tags });
const docsEntry: DocsCacheEntry = {
id,
@@ -452,7 +444,6 @@ export class StoryIndexGenerator {
importPath,
storiesImports: sortedDependencies.map((dep) => dep.entries[0].importPath),
type: 'docs',
- metaTags,
tags,
};
return docsEntry;
diff --git a/code/lib/core-server/src/utils/__tests__/index-extraction.test.ts b/code/lib/core-server/src/utils/__tests__/index-extraction.test.ts
index a1936c6719dd..a7bc19ae8ab9 100644
--- a/code/lib/core-server/src/utils/__tests__/index-extraction.test.ts
+++ b/code/lib/core-server/src/utils/__tests__/index-extraction.test.ts
@@ -70,7 +70,6 @@ describe('story extraction', () => {
"name": "Story One",
"tags": [
"story-tag-from-indexer",
- "story",
],
"title": "A",
"type": "story",
@@ -82,7 +81,6 @@ describe('story extraction', () => {
"name": "Another Story Name",
"tags": [
"story-tag-from-indexer",
- "story",
],
"title": "Custom Title",
"type": "story",
@@ -124,9 +122,7 @@ describe('story extraction', () => {
"importPath": "./src/first-nested/deeply/F.stories.js",
"metaId": undefined,
"name": "Story One",
- "tags": [
- "story",
- ],
+ "tags": [],
"title": "F",
"type": "story",
},
@@ -173,7 +169,6 @@ describe('story extraction', () => {
"name": "Story One",
"tags": [
"story-tag-from-indexer",
- "story",
],
"title": "F",
"type": "story",
@@ -221,7 +216,6 @@ describe('story extraction', () => {
"name": "Story One",
"tags": [
"story-tag-from-indexer",
- "story",
],
"title": "A",
"type": "story",
@@ -287,7 +281,6 @@ describe('story extraction', () => {
"name": "Story One",
"tags": [
"story-tag-from-indexer",
- "story",
],
"title": "A",
"type": "story",
@@ -299,7 +292,6 @@ describe('story extraction', () => {
"name": "Custom Name For Second Story",
"tags": [
"story-tag-from-indexer",
- "story",
],
"title": "Custom Title",
"type": "story",
@@ -311,7 +303,6 @@ describe('story extraction', () => {
"name": "Story Three",
"tags": [
"story-tag-from-indexer",
- "story",
],
"title": "Custom Title",
"type": "story",
@@ -356,7 +347,6 @@ describe('story extraction', () => {
"name": "Story One",
"tags": [
"story-tag-from-indexer",
- "story",
],
"title": "A",
"type": "story",
@@ -399,18 +389,6 @@ describe('docs entries from story extraction', () => {
{
"dependents": [],
"entries": [
- {
- "id": "a--docs",
- "importPath": "./src/A.stories.js",
- "name": "docs",
- "storiesImports": [],
- "tags": [
- "docs",
- "autodocs",
- ],
- "title": "A",
- "type": "docs",
- },
{
"id": "a--story-one",
"importPath": "./src/A.stories.js",
@@ -418,7 +396,6 @@ describe('docs entries from story extraction', () => {
"name": "Story One",
"tags": [
"story-tag-from-indexer",
- "story",
],
"title": "A",
"type": "story",
@@ -465,7 +442,8 @@ describe('docs entries from story extraction', () => {
"name": "docs",
"storiesImports": [],
"tags": [
- "docs",
+ "autodocs",
+ "story-tag-from-indexer",
],
"title": "A",
"type": "docs",
@@ -478,7 +456,6 @@ describe('docs entries from story extraction', () => {
"tags": [
"autodocs",
"story-tag-from-indexer",
- "story",
],
"title": "A",
"type": "story",
@@ -527,7 +504,6 @@ describe('docs entries from story extraction', () => {
"tags": [
"autodocs",
"story-tag-from-indexer",
- "story",
],
"title": "A",
"type": "story",
@@ -574,7 +550,8 @@ describe('docs entries from story extraction', () => {
"name": "docs",
"storiesImports": [],
"tags": [
- "docs",
+ "stories-mdx",
+ "story-tag-from-indexer",
],
"title": "A",
"type": "docs",
@@ -587,7 +564,6 @@ describe('docs entries from story extraction', () => {
"tags": [
"stories-mdx",
"story-tag-from-indexer",
- "story",
],
"title": "A",
"type": "story",
diff --git a/code/lib/core-server/src/utils/stories-json.test.ts b/code/lib/core-server/src/utils/stories-json.test.ts
index a621f5443ce3..81fd7b8bfa3a 100644
--- a/code/lib/core-server/src/utils/stories-json.test.ts
+++ b/code/lib/core-server/src/utils/stories-json.test.ts
@@ -113,8 +113,12 @@ describe('useStoriesJson', () => {
"./src/A.stories.js",
],
"tags": [
- "attached-mdx",
+ "dev",
"docs",
+ "test",
+ "component-tag",
+ "story-tag",
+ "attached-mdx",
],
"title": "A",
"type": "docs",
@@ -127,8 +131,12 @@ describe('useStoriesJson', () => {
"./src/A.stories.js",
],
"tags": [
- "attached-mdx",
+ "dev",
"docs",
+ "test",
+ "component-tag",
+ "story-tag",
+ "attached-mdx",
],
"title": "A",
"type": "docs",
@@ -138,9 +146,11 @@ describe('useStoriesJson', () => {
"importPath": "./src/A.stories.js",
"name": "Story One",
"tags": [
+ "dev",
+ "docs",
+ "test",
"component-tag",
"story-tag",
- "story",
],
"title": "A",
"type": "story",
@@ -150,8 +160,10 @@ describe('useStoriesJson', () => {
"importPath": "./src/B.stories.ts",
"name": "Story One",
"tags": [
+ "dev",
+ "docs",
+ "test",
"autodocs",
- "story",
],
"title": "B",
"type": "story",
@@ -161,8 +173,10 @@ describe('useStoriesJson', () => {
"importPath": "./src/D.stories.jsx",
"name": "Story One",
"tags": [
+ "dev",
+ "docs",
+ "test",
"autodocs",
- "story",
],
"title": "D",
"type": "story",
@@ -173,8 +187,10 @@ describe('useStoriesJson', () => {
"name": "docs",
"storiesImports": [],
"tags": [
- "unattached-mdx",
+ "dev",
"docs",
+ "test",
+ "unattached-mdx",
],
"title": "docs2/ComponentReference",
"type": "docs",
@@ -185,8 +201,10 @@ describe('useStoriesJson', () => {
"name": "docs",
"storiesImports": [],
"tags": [
- "unattached-mdx",
+ "dev",
"docs",
+ "test",
+ "unattached-mdx",
],
"title": "docs2/NoTitle",
"type": "docs",
@@ -197,8 +215,10 @@ describe('useStoriesJson', () => {
"name": "docs",
"storiesImports": [],
"tags": [
- "unattached-mdx",
+ "dev",
"docs",
+ "test",
+ "unattached-mdx",
],
"title": "docs2/Yabbadabbadooo",
"type": "docs",
@@ -208,7 +228,9 @@ describe('useStoriesJson', () => {
"importPath": "./src/first-nested/deeply/F.stories.js",
"name": "Story One",
"tags": [
- "story",
+ "dev",
+ "docs",
+ "test",
],
"title": "first-nested/deeply/F",
"type": "story",
@@ -218,8 +240,10 @@ describe('useStoriesJson', () => {
"importPath": "./src/H.stories.mjs",
"name": "Story One",
"tags": [
+ "dev",
+ "docs",
+ "test",
"autodocs",
- "story",
],
"title": "H",
"type": "story",
@@ -229,8 +253,10 @@ describe('useStoriesJson', () => {
"importPath": "./src/nested/Button.stories.ts",
"name": "Story One",
"tags": [
+ "dev",
+ "docs",
+ "test",
"component-tag",
- "story",
],
"title": "nested/Button",
"type": "story",
@@ -240,7 +266,9 @@ describe('useStoriesJson', () => {
"importPath": "./src/second-nested/G.stories.ts",
"name": "Story One",
"tags": [
- "story",
+ "dev",
+ "docs",
+ "test",
],
"title": "second-nested/G",
"type": "story",
diff --git a/code/lib/csf-tools/src/CsfFile.test.ts b/code/lib/csf-tools/src/CsfFile.test.ts
index 21e902cce52f..cfc650e30e2e 100644
--- a/code/lib/csf-tools/src/CsfFile.test.ts
+++ b/code/lib/csf-tools/src/CsfFile.test.ts
@@ -1104,8 +1104,6 @@ describe('CsfFile', () => {
- component-tag
- story-tag
- play-fn
- metaTags: &ref_0
- - component-tag
__id: component-id--a
- type: story
importPath: foo/bar.stories.js
@@ -1117,7 +1115,6 @@ describe('CsfFile', () => {
- component-tag
- story-tag
- play-fn
- metaTags: *ref_0
__id: component-id--b
`);
});
@@ -1147,8 +1144,6 @@ describe('CsfFile', () => {
metaId: component-id
tags:
- component-tag
- metaTags:
- - component-tag
__id: custom-story-id
`);
});
@@ -1180,11 +1175,6 @@ describe('CsfFile', () => {
- inherit-tag-dup
- story-tag
- story-tag-dup
- metaTags:
- - component-tag
- - component-tag-dup
- - component-tag-dup
- - inherit-tag-dup
__id: custom-foo-title--a
`);
});
diff --git a/code/lib/csf-tools/src/CsfFile.ts b/code/lib/csf-tools/src/CsfFile.ts
index 4516a293b1cc..dc0270b72804 100644
--- a/code/lib/csf-tools/src/CsfFile.ts
+++ b/code/lib/csf-tools/src/CsfFile.ts
@@ -8,7 +8,7 @@ import * as generate from '@babel/generator';
import * as recast from 'recast';
import * as traverse from '@babel/traverse';
-import { toId, isExportStory, storyNameFromExport } from '@storybook/csf';
+import { toId, isExportStory, storyNameFromExport, combineTags } from '@storybook/csf';
import type {
Tag,
StoryAnnotations,
@@ -559,7 +559,7 @@ export class CsfFile {
}
return Object.entries(this._stories).map(([exportName, story]) => {
// combine meta and story tags, removing any duplicates
- const tags = Array.from(new Set([...(this._meta?.tags ?? []), ...(story.tags ?? [])]));
+ const tags = combineTags(...(this._meta?.tags ?? []), ...(story.tags ?? []));
return {
type: 'story',
importPath: this._fileName,
@@ -568,7 +568,6 @@ export class CsfFile {
title: this.meta?.title,
metaId: this.meta?.id,
tags,
- metaTags: this.meta?.tags,
__id: story.id,
};
});
diff --git a/code/lib/preview-api/src/modules/preview-web/PreviewWeb.test.ts b/code/lib/preview-api/src/modules/preview-web/PreviewWeb.test.ts
index e20af13752a6..d0d9ed357704 100644
--- a/code/lib/preview-api/src/modules/preview-web/PreviewWeb.test.ts
+++ b/code/lib/preview-api/src/modules/preview-web/PreviewWeb.test.ts
@@ -3625,7 +3625,10 @@ describe('PreviewWeb', () => {
"story": "A",
"subcomponents": undefined,
"tags": [
+ "dev",
+ "docs",
"story",
+ "test",
],
"title": "Component One",
},
@@ -3672,7 +3675,10 @@ describe('PreviewWeb', () => {
"story": "B",
"subcomponents": undefined,
"tags": [
+ "dev",
+ "docs",
"story",
+ "test",
],
"title": "Component One",
},
@@ -3697,7 +3703,10 @@ describe('PreviewWeb', () => {
"story": "E",
"subcomponents": undefined,
"tags": [
+ "dev",
+ "docs",
"story",
+ "test",
],
"title": "Component One",
},
@@ -3732,7 +3741,10 @@ describe('PreviewWeb', () => {
"story": "C",
"subcomponents": undefined,
"tags": [
+ "dev",
+ "docs",
"story",
+ "test",
],
"title": "Component Two",
},
diff --git a/code/lib/preview-api/src/modules/store/StoryStore.test.ts b/code/lib/preview-api/src/modules/store/StoryStore.test.ts
index 65f225c8ef6e..5bb56dc106d9 100644
--- a/code/lib/preview-api/src/modules/store/StoryStore.test.ts
+++ b/code/lib/preview-api/src/modules/store/StoryStore.test.ts
@@ -311,7 +311,10 @@ describe('StoryStore', () => {
"story": "A",
"subcomponents": undefined,
"tags": [
+ "dev",
+ "docs",
"story",
+ "test",
],
"title": "Component One",
},
@@ -476,7 +479,10 @@ describe('StoryStore', () => {
"story": "A",
"subcomponents": undefined,
"tags": [
+ "dev",
+ "docs",
"story",
+ "test",
],
"title": "Component One",
},
@@ -514,7 +520,10 @@ describe('StoryStore', () => {
"story": "B",
"subcomponents": undefined,
"tags": [
+ "dev",
+ "docs",
"story",
+ "test",
],
"title": "Component One",
},
@@ -552,7 +561,10 @@ describe('StoryStore', () => {
"story": "C",
"subcomponents": undefined,
"tags": [
+ "dev",
+ "docs",
"story",
+ "test",
],
"title": "Component Two",
},
@@ -659,6 +671,9 @@ describe('StoryStore', () => {
"storyFn": [Function],
"subcomponents": undefined,
"tags": [
+ "dev",
+ "docs",
+ "test",
"story",
],
"title": "Component One",
@@ -704,6 +719,9 @@ describe('StoryStore', () => {
"storyFn": [Function],
"subcomponents": undefined,
"tags": [
+ "dev",
+ "docs",
+ "test",
"story",
],
"title": "Component One",
@@ -749,6 +767,9 @@ describe('StoryStore', () => {
"storyFn": [Function],
"subcomponents": undefined,
"tags": [
+ "dev",
+ "docs",
+ "test",
"story",
],
"title": "Component Two",
@@ -810,7 +831,10 @@ describe('StoryStore', () => {
"story": "A",
"subcomponents": undefined,
"tags": [
+ "dev",
+ "docs",
"story",
+ "test",
],
"title": "Component One",
},
@@ -848,7 +872,10 @@ describe('StoryStore', () => {
"story": "B",
"subcomponents": undefined,
"tags": [
+ "dev",
+ "docs",
"story",
+ "test",
],
"title": "Component One",
},
@@ -886,7 +913,10 @@ describe('StoryStore', () => {
"story": "C",
"subcomponents": undefined,
"tags": [
+ "dev",
+ "docs",
"story",
+ "test",
],
"title": "Component Two",
},
diff --git a/code/lib/types/src/modules/indexer.ts b/code/lib/types/src/modules/indexer.ts
index 42f0e21640e8..56d435cdb533 100644
--- a/code/lib/types/src/modules/indexer.ts
+++ b/code/lib/types/src/modules/indexer.ts
@@ -73,7 +73,6 @@ export interface BaseIndexEntry {
id: StoryId;
name: StoryName;
title: ComponentTitle;
- metaTags?: Tag[];
tags?: Tag[];
importPath: Path;
}
@@ -108,8 +107,6 @@ export type BaseIndexInput = {
metaId?: MetaId;
/** Tags for filtering entries in Storybook and its tools. */
tags?: Tag[];
- /** Tags from the meta for filtering entries in Storybook and its tools. */
- metaTags?: Tag[];
/**
* The id of the entry, auto-generated from {@link title}/{@link metaId} and {@link exportName} if unspecified.
* If specified, the story in the CSF file _must_ have a matching id set at `parameters.__id`, to be correctly matched.
From 1efccbe46433eea26e92065b4e198614efba1926 Mon Sep 17 00:00:00 2001
From: Kyle Gach
Date: Mon, 15 Apr 2024 12:24:59 -0600
Subject: [PATCH 011/117] WIP
---
...ch-ts.mdx => document-screen-fetch.ts.mdx} | 0
docs/toc.js | 5 +
docs/writing-stories/mocks.md | 309 ++++++++++++++++++
3 files changed, 314 insertions(+)
rename docs/snippets/react/{document-screen-fetch-ts.mdx => document-screen-fetch.ts.mdx} (100%)
create mode 100644 docs/writing-stories/mocks.md
diff --git a/docs/snippets/react/document-screen-fetch-ts.mdx b/docs/snippets/react/document-screen-fetch.ts.mdx
similarity index 100%
rename from docs/snippets/react/document-screen-fetch-ts.mdx
rename to docs/snippets/react/document-screen-fetch.ts.mdx
diff --git a/docs/toc.js b/docs/toc.js
index a4d3e85cf6be..1a8530bbc6bd 100644
--- a/docs/toc.js
+++ b/docs/toc.js
@@ -141,6 +141,11 @@ module.exports = {
title: 'Naming components and hierarchy',
type: 'link',
},
+ {
+ pathSegment: 'mocks',
+ title: 'Mocking data and modules',
+ type: 'link',
+ },
{
pathSegment: 'build-pages-with-storybook',
title: 'Build pages and screens',
diff --git a/docs/writing-stories/mocks.md b/docs/writing-stories/mocks.md
new file mode 100644
index 000000000000..67b354eff23c
--- /dev/null
+++ b/docs/writing-stories/mocks.md
@@ -0,0 +1,309 @@
+---
+title: 'Mocking data and modules'
+---
+
+1. Intro
+2. Providers
+ 1. Decorators
+3. Network requests
+ 1. MSW
+4. Modules
+ 1. Mock files
+ 1. Naming convention
+ 2. Cannot use subpath imports in a mocked file; must use relative
+ 3. Watch out for side effects
+ 2. Subpath imports
+ 1. Relative imports not supported
+ 2. Fallback to builder aliases
+ 3. Next.js callout
+ 3. Configuring mocks
+ 1. `fn.mock` and friends
+ 2. `beforeEach`
+
+Components that rely on external data or modules can be difficult to use in isolation. Mocking is a way to replace these dependencies with fake data or modules that you control. This allows you to develop and test your components without worrying about the behavior or stability of the external dependencies.
+
+Storybook provides different tools and techniques to help you mock data and modules.
+
+
+
+## Providers
+
+Components can receive data or configuration from context providers. For example, a styled component might access its theme from a ThemeProvider. To mock a provider, you can wrap your component in a [decorator](./decorators.md) that includes the necessary context.
+
+
+
+
+
+
+
+
+
+## Network requests
+
+For components that make network requests (e.g. fetching data from a REST or GraphQL API), you can mock those requests using a tool like [Mock Service Worker (MSW)](https://mswjs.io/). MSW allows you to intercept requests made by your components and respond with fake data.
+
+The MSW addon adds this functionality into Storybook, allowing you to mock API requests in your stories. Below is an overview of how to set up and use the addon.
+
+
+Set up MSW in Storybook
+
+First, run this command to install MSW:
+
+
+
+```sh
+npm install msw --save-dev
+```
+
+Next, install and register the MSW addon:
+
+
+
+```sh
+npx storybook@latest add msw-storybook-addon
+```
+
+Then, generate the service worker file necessary for MSW to work:
+
+
+
+
+
+
+
+
+
+
+
+
+Angular projects will likely need to adjust the command to save the mock service worker file in a different directory (e.g., `src`).
+
+
+
+
+
+Initialize the addon and apply it to all stories with a [project-level loader](./loaders.md#global-loaders):
+
+
+
+
+
+
+
+
+Finally, ensure the [`staticDirs`](../api/main-config-static-dirs.md) property in your Storybook configuration will include the generated service worker file:
+
+
+
+
+
+
+
+
+
+
+Let's use an example of a document screen component that requests data from an API to demonstrate how to mock network requests in Storybook. The following snippets show an example implementation using `fetch` for a REST API and/or GraphQL:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+These examples use the [`fetch` API](https://developer.mozilla.org/en-US/docs/Web/API/fetch) or GraphQL with [Apollo Client](https://www.apollographql.com/docs/) to make network requests. If you're using a different library (e.g. [`axios`](https://axios-http.com/), [URQL](https://formidable.com/open-source/urql/), or [React Query](https://react-query.tanstack.com/)), you can apply the same principles to mock network requests in Storybook.
+
+
+
+### Mocking REST requests
+
+To mock a REST request, you can use the `msw` library to intercept the request and respond with mock data. Here's an example showing two stories for the document screen component. They are each configured to use mock data: one that fetches data successfully and another that fails.
+
+
+
+
+
+
+
+
+Note how each story is configured with `parameters.msw` to define the request handlers for the mock server. Because it uses parameters in this way, it can also be configured at the [component](./parameters.md#component-parameters) or even [project](./parameters.md#global-parameters) level.
+
+### Mocking GraphQL requests
+
+In addition to mocking RESTful requests, the MSW addon can also mock GraphQL requests.
+
+Here's an example showing two stories for the document screen component. They are each configured to use mock data: one that queries data successfully and another that fails.
+
+
+
+
+
+
+
+
+## Modules
+
+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.
+
+There are two primary approaches to mocking modules in Storybook. They both involve creating a mock file to replace the original module. The difference between the two approaches is how you import the mock file into your component.
+
+For either approach, relative imports of the mocked module are not supported.
+
+### Mock files
+
+To mock a module, create a file with the same name as the module you want to mock. For example, if you want to mock a module named `api`, create a file named `api.mock.js|ts` in the same directory as the original module. This file should match the exports of the original module but with fake data or behavior.
+
+Here's an example of a mock file for a module named `api`:
+
+```js
+// api.mock.js
+TK: Add snippet
+```
+
+
+
+When creating a mock file, be careful not to introduce side effects that could affect other tests or components. Mock files should be isolated and only affect the component they are mocking.
+
+Additionally, you must use absolute paths to import any dependencies in the mock file. Relative imports can cause issues when importing the mock file into your component.
+
+
+
+### Subpath imports
+
+The recommended method for mocking modules is to use [subpath imports](https://nodejs.org/api/packages.html#subpath-imports), a feature of Node packages that is supported by both [Vite](../builders/vite.md) and [Webpack](../builders/webpack.md).
+
+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 the `api` and `prisma` internal modules:
+
+TK: External module example?
+
+```json
+// package.json
+{
+ "imports": {
+ "#api": {
+ "storybook": "./api.mock.ts",
+ "default": "./api.ts"
+ },
+ "#prisma/prisma": {
+ "storybook": "./prisma/prisma.mock.ts",
+ "default": "./prisma/prisma.ts"
+ },
+ "#*": ["./*", "./*.ts", "./*.tsx"]
+ }
+}
+```
+
+
+
+Each subpath must begin with `#`, to differentiate it from a regular module path. The `#*` entry is a catch-all that maps all subpaths to the root directory.
+
+
+
+You can then update your component file to use the subpath import:
+
+```ts
+TK: Component snippet
+```
+
+#### Conditional imports
+
+Note the `storybook` and `default` keys in each module's entry. The `storybook` key is used to import the mock file in Storybook, while the `default` key is used to import the original module in your project.
+
+The Storybook environment will match the conditions `storybook` and `test`, so you can apply the same module mapping for both Storybook and your tests.
+
+### Builder aliases
+
+If your project is unable to use [subpath imports](#subpath-imports), you can configure your Storybook builder to alias the module to the mock file.
+
+```js
+// .storybook/main.ts
+
+webpackFinal: async (config) => {
+ if (config.resolve) {
+ config.resolve.alias = {
+ ...config.resolve.alias,
+ 'next/headers': require.resolve('./next-headers'),
+ '@/api/todo$': path.resolve(__dirname, './api/todo.mock.ts'),
+ }
+ }
+
+ return config
+},
+```
From 024237154fc718da4c186273eeb283db5d8ed744 Mon Sep 17 00:00:00 2001
From: Kyle Gach
Date: Mon, 15 Apr 2024 23:28:21 -0600
Subject: [PATCH 012/117] Split into 3 pages
---
docs/toc.js | 21 +-
docs/writing-stories/mocking-modules.md | 93 ++++++
.../mocking-network-requests.md | 178 ++++++++++
docs/writing-stories/mocking-providers.md | 24 ++
docs/writing-stories/mocks.md | 309 ------------------
5 files changed, 314 insertions(+), 311 deletions(-)
create mode 100644 docs/writing-stories/mocking-modules.md
create mode 100644 docs/writing-stories/mocking-network-requests.md
create mode 100644 docs/writing-stories/mocking-providers.md
delete mode 100644 docs/writing-stories/mocks.md
diff --git a/docs/toc.js b/docs/toc.js
index 1a8530bbc6bd..7dbc4bceec4c 100644
--- a/docs/toc.js
+++ b/docs/toc.js
@@ -142,9 +142,26 @@ module.exports = {
type: 'link',
},
{
- pathSegment: 'mocks',
+ pathSegment: '',
title: 'Mocking data and modules',
- type: 'link',
+ type: 'menu',
+ children: [
+ {
+ pathSegment: 'mocking-providers',
+ title: 'Providers',
+ type: 'link',
+ },
+ {
+ pathSegment: 'mocking-network-requests',
+ title: 'Network requests',
+ type: 'link',
+ },
+ {
+ pathSegment: 'mocking-modules',
+ title: 'Modules',
+ type: 'link',
+ },
+ ],
},
{
pathSegment: 'build-pages-with-storybook',
diff --git a/docs/writing-stories/mocking-modules.md b/docs/writing-stories/mocking-modules.md
new file mode 100644
index 000000000000..3c0365382b3a
--- /dev/null
+++ b/docs/writing-stories/mocking-modules.md
@@ -0,0 +1,93 @@
+---
+title: Mocking modules
+---
+
+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.
+
+There are two primary approaches to mocking modules in Storybook. They both involve creating a mock file to replace the original module. The difference between the two approaches is how you import the mock file into your component.
+
+For either approach, relative imports of the mocked module are not supported.
+
+### Mock files
+
+To mock a module, create a file with the same name as the module you want to mock. For example, if you want to mock a module named `api`, create a file named `api.mock.js|ts` in the same directory as the original module. This file should match the exports of the original module but with fake data or behavior.
+
+Here's an example of a mock file for a module named `api`:
+
+```js
+// api.mock.js
+TK: Add snippet
+```
+
+
+
+When creating a mock file, be careful not to introduce side effects that could affect other tests or components. Mock files should be isolated and only affect the component they are mocking.
+
+Additionally, you must use absolute paths to import any dependencies in the mock file. Relative imports can cause issues when importing the mock file into your component.
+
+
+
+### Subpath imports
+
+The recommended method for mocking modules is to use [subpath imports](https://nodejs.org/api/packages.html#subpath-imports), a feature of Node packages that is supported by both [Vite](../builders/vite.md) and [Webpack](../builders/webpack.md).
+
+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 the `api` and `prisma` internal modules:
+
+TK: External module example?
+
+```json
+// package.json
+{
+ "imports": {
+ "#api": {
+ "storybook": "./api.mock.ts",
+ "default": "./api.ts"
+ },
+ "#prisma/prisma": {
+ "storybook": "./prisma/prisma.mock.ts",
+ "default": "./prisma/prisma.ts"
+ },
+ "#*": ["./*", "./*.ts", "./*.tsx"]
+ }
+}
+```
+
+
+
+Each subpath must begin with `#`, to differentiate it from a regular module path. The `#*` entry is a catch-all that maps all subpaths to the root directory.
+
+
+
+You can then update your component file to use the subpath import:
+
+```ts
+TK: Component snippet
+```
+
+#### Conditional imports
+
+Note the `storybook` and `default` keys in each module's entry. The `storybook` key is used to import the mock file in Storybook, while the `default` key is used to import the original module in your project.
+
+The Storybook environment will match the conditions `storybook` and `test`, so you can apply the same module mapping for both Storybook and your tests.
+
+### Builder aliases
+
+If your project is unable to use [subpath imports](#subpath-imports), you can configure your Storybook builder to alias the module to the mock file.
+
+
+
+```js
+// .storybook/main.ts
+
+webpackFinal: async (config) => {
+ if (config.resolve) {
+ config.resolve.alias = {
+ ...config.resolve.alias,
+ 'next/headers': require.resolve('./next-headers'),
+ '@/api/todo$': path.resolve(__dirname, './api/todo.mock.ts'),
+ }
+ }
+
+ return config
+},
+```
diff --git a/docs/writing-stories/mocking-network-requests.md b/docs/writing-stories/mocking-network-requests.md
new file mode 100644
index 000000000000..28afb5e12697
--- /dev/null
+++ b/docs/writing-stories/mocking-network-requests.md
@@ -0,0 +1,178 @@
+---
+title: Mocking network requests
+---
+
+For components that make network requests (e.g. fetching data from a REST or GraphQL API), you can mock those requests using a tool like [Mock Service Worker (MSW)](https://mswjs.io/). MSW allows you to intercept requests made by your components and respond with fake data.
+
+The [MSW addon]https://storybook.js.org/addons/msw-storybook-addon/ brings this functionality into Storybook, allowing you to mock API requests in your stories. Below is an overview of how to set up and use the addon.
+
+## Set up the MSW addon
+
+First, if necessary, run this command to install MSW:
+
+
+
+```sh
+npm install msw --save-dev
+```
+
+Then generate the service worker file necessary for MSW to work:
+
+
+
+
+
+
+
+
+
+
+
+
+Angular projects will likely need to adjust the command to save the mock service worker file in a different directory (e.g., `src`).
+
+
+
+
+
+Next, install and register the MSW addon:
+
+
+
+```sh
+npx storybook@latest add msw-storybook-addon
+```
+
+Then ensure the [`staticDirs`](../api/main-config-static-dirs.md) property in your Storybook configuration will include the generated service worker file:
+
+
+
+
+
+
+
+
+Finally, initialize the addon and apply it to all stories with a [project-level loader](./loaders.md#global-loaders):
+
+
+
+
+
+
+
+
+## Mocking REST requests
+
+If your component fetches data from a REST API, you can use MSW to mock those requests in Storybook. As an example, consider this document screen component:
+
+
+
+
+
+
+
+
+
+This example uses the [`fetch` API](https://developer.mozilla.org/en-US/docs/Web/API/fetch) to make network requests. If you're using a different library (e.g. [`axios`](https://axios-http.com/)), you can apply the same principles to mock network requests in Storybook.
+
+
+
+With the MSW addon, we can write stories that use MSW to mock the REST requests. Here's an example of two stories for the document screen component: one that fetches data successfully and another that fails.
+
+
+
+
+
+
+
+
+## Mocking GraphQL requests
+
+GraphQL is another common way to fetch data in components. You can use MSW to mock GraphQL requests in Storybook. Here's an example of a document screen component that fetches data from a GraphQL API:
+
+
+
+
+
+
+
+
+
+This example uses GraphQL with [Apollo Client](https://www.apollographql.com/docs/) to make network requests. If you're using a different library (e.g. [URQL](https://formidable.com/open-source/urql/) or [React Query](https://react-query.tanstack.com/)), you can apply the same principles to mock network requests in Storybook.
+
+
+
+The MSW addon allows you to write stories that use MSW to mock the GraphQL requests. Here's an example demonstrating two stories for the document screen component. The first story fetches data successfully, while the second story fails.
+
+
+
+
+
+
+
+
+## Configuring MSW for stories
+
+In the examples above, note how each story is configured with `parameters.msw` to define the request handlers for the mock server. Because it uses parameters in this way, it can also be configured at the [component](./parameters.md#component-parameters) or even [project](./parameters.md#global-parameters) level, allowing you to share the same mock server configuration across multiple stories.
diff --git a/docs/writing-stories/mocking-providers.md b/docs/writing-stories/mocking-providers.md
new file mode 100644
index 000000000000..a6000ff59558
--- /dev/null
+++ b/docs/writing-stories/mocking-providers.md
@@ -0,0 +1,24 @@
+---
+title: Mocking providers
+---
+
+
+
+## Providers
+
+Components can receive data or configuration from context providers. For example, a styled component might access its theme from a ThemeProvider. To mock a provider, you can wrap your component in a [decorator](./decorators.md) that includes the necessary context.
+
+
+
+
+
+
+
+TK: Something about the `context` argument?
diff --git a/docs/writing-stories/mocks.md b/docs/writing-stories/mocks.md
deleted file mode 100644
index 67b354eff23c..000000000000
--- a/docs/writing-stories/mocks.md
+++ /dev/null
@@ -1,309 +0,0 @@
----
-title: 'Mocking data and modules'
----
-
-1. Intro
-2. Providers
- 1. Decorators
-3. Network requests
- 1. MSW
-4. Modules
- 1. Mock files
- 1. Naming convention
- 2. Cannot use subpath imports in a mocked file; must use relative
- 3. Watch out for side effects
- 2. Subpath imports
- 1. Relative imports not supported
- 2. Fallback to builder aliases
- 3. Next.js callout
- 3. Configuring mocks
- 1. `fn.mock` and friends
- 2. `beforeEach`
-
-Components that rely on external data or modules can be difficult to use in isolation. Mocking is a way to replace these dependencies with fake data or modules that you control. This allows you to develop and test your components without worrying about the behavior or stability of the external dependencies.
-
-Storybook provides different tools and techniques to help you mock data and modules.
-
-
-
-## Providers
-
-Components can receive data or configuration from context providers. For example, a styled component might access its theme from a ThemeProvider. To mock a provider, you can wrap your component in a [decorator](./decorators.md) that includes the necessary context.
-
-
-
-
-
-
-
-
-
-## Network requests
-
-For components that make network requests (e.g. fetching data from a REST or GraphQL API), you can mock those requests using a tool like [Mock Service Worker (MSW)](https://mswjs.io/). MSW allows you to intercept requests made by your components and respond with fake data.
-
-The MSW addon adds this functionality into Storybook, allowing you to mock API requests in your stories. Below is an overview of how to set up and use the addon.
-
-
-Set up MSW in Storybook
-
-First, run this command to install MSW:
-
-
-
-```sh
-npm install msw --save-dev
-```
-
-Next, install and register the MSW addon:
-
-
-
-```sh
-npx storybook@latest add msw-storybook-addon
-```
-
-Then, generate the service worker file necessary for MSW to work:
-
-
-
-
-
-
-
-
-
-
-
-
-Angular projects will likely need to adjust the command to save the mock service worker file in a different directory (e.g., `src`).
-
-
-
-
-
-Initialize the addon and apply it to all stories with a [project-level loader](./loaders.md#global-loaders):
-
-
-
-
-
-
-
-
-Finally, ensure the [`staticDirs`](../api/main-config-static-dirs.md) property in your Storybook configuration will include the generated service worker file:
-
-
-
-
-
-
-
-
-
-
-Let's use an example of a document screen component that requests data from an API to demonstrate how to mock network requests in Storybook. The following snippets show an example implementation using `fetch` for a REST API and/or GraphQL:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-These examples use the [`fetch` API](https://developer.mozilla.org/en-US/docs/Web/API/fetch) or GraphQL with [Apollo Client](https://www.apollographql.com/docs/) to make network requests. If you're using a different library (e.g. [`axios`](https://axios-http.com/), [URQL](https://formidable.com/open-source/urql/), or [React Query](https://react-query.tanstack.com/)), you can apply the same principles to mock network requests in Storybook.
-
-
-
-### Mocking REST requests
-
-To mock a REST request, you can use the `msw` library to intercept the request and respond with mock data. Here's an example showing two stories for the document screen component. They are each configured to use mock data: one that fetches data successfully and another that fails.
-
-
-
-
-
-
-
-
-Note how each story is configured with `parameters.msw` to define the request handlers for the mock server. Because it uses parameters in this way, it can also be configured at the [component](./parameters.md#component-parameters) or even [project](./parameters.md#global-parameters) level.
-
-### Mocking GraphQL requests
-
-In addition to mocking RESTful requests, the MSW addon can also mock GraphQL requests.
-
-Here's an example showing two stories for the document screen component. They are each configured to use mock data: one that queries data successfully and another that fails.
-
-
-
-
-
-
-
-
-## Modules
-
-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.
-
-There are two primary approaches to mocking modules in Storybook. They both involve creating a mock file to replace the original module. The difference between the two approaches is how you import the mock file into your component.
-
-For either approach, relative imports of the mocked module are not supported.
-
-### Mock files
-
-To mock a module, create a file with the same name as the module you want to mock. For example, if you want to mock a module named `api`, create a file named `api.mock.js|ts` in the same directory as the original module. This file should match the exports of the original module but with fake data or behavior.
-
-Here's an example of a mock file for a module named `api`:
-
-```js
-// api.mock.js
-TK: Add snippet
-```
-
-
-
-When creating a mock file, be careful not to introduce side effects that could affect other tests or components. Mock files should be isolated and only affect the component they are mocking.
-
-Additionally, you must use absolute paths to import any dependencies in the mock file. Relative imports can cause issues when importing the mock file into your component.
-
-
-
-### Subpath imports
-
-The recommended method for mocking modules is to use [subpath imports](https://nodejs.org/api/packages.html#subpath-imports), a feature of Node packages that is supported by both [Vite](../builders/vite.md) and [Webpack](../builders/webpack.md).
-
-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 the `api` and `prisma` internal modules:
-
-TK: External module example?
-
-```json
-// package.json
-{
- "imports": {
- "#api": {
- "storybook": "./api.mock.ts",
- "default": "./api.ts"
- },
- "#prisma/prisma": {
- "storybook": "./prisma/prisma.mock.ts",
- "default": "./prisma/prisma.ts"
- },
- "#*": ["./*", "./*.ts", "./*.tsx"]
- }
-}
-```
-
-
-
-Each subpath must begin with `#`, to differentiate it from a regular module path. The `#*` entry is a catch-all that maps all subpaths to the root directory.
-
-
-
-You can then update your component file to use the subpath import:
-
-```ts
-TK: Component snippet
-```
-
-#### Conditional imports
-
-Note the `storybook` and `default` keys in each module's entry. The `storybook` key is used to import the mock file in Storybook, while the `default` key is used to import the original module in your project.
-
-The Storybook environment will match the conditions `storybook` and `test`, so you can apply the same module mapping for both Storybook and your tests.
-
-### Builder aliases
-
-If your project is unable to use [subpath imports](#subpath-imports), you can configure your Storybook builder to alias the module to the mock file.
-
-```js
-// .storybook/main.ts
-
-webpackFinal: async (config) => {
- if (config.resolve) {
- config.resolve.alias = {
- ...config.resolve.alias,
- 'next/headers': require.resolve('./next-headers'),
- '@/api/todo$': path.resolve(__dirname, './api/todo.mock.ts'),
- }
- }
-
- return config
-},
-```
From ff3f2e9f966f4580d7b8a23552ba03af72646800 Mon Sep 17 00:00:00 2001
From: Jeppe Reinhold
Date: Tue, 16 Apr 2024 15:06:44 +0200
Subject: [PATCH 013/117] Add Modules section to Next.js docs
---
docs/get-started/nextjs.md | 104 +++++++++++++++++++++++++------------
1 file changed, 71 insertions(+), 33 deletions(-)
diff --git a/docs/get-started/nextjs.md b/docs/get-started/nextjs.md
index b17cf19661b3..f1e024ef6049 100644
--- a/docs/get-started/nextjs.md
+++ b/docs/get-started/nextjs.md
@@ -885,54 +885,43 @@ You can refer to the [Install `sharp` to Use Built-In Image Optimization](https:
## API
-### Parameters
+### Modules
-This framework contributes the following [parameters](../writing-stories/parameters.md) to Storybook, under the `nextjs` namespace:
+The `@storybook/nextjs` package exports a number of modules that enables you to [mock](/TODO_LINK_TO_MOCKING) Next.js's internal behavior.
-#### `appDirectory`
+#### `@storybook/nextjs/headers.mock`
-Type: `boolean`
+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
-Default: `false`
+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.
-If your story imports components that use `next/navigation`, you need to set the parameter `nextjs.appDirectory` to `true`. Because this is a parameter, you can apply it to a [single story](../api/parameters.md#story-parameters), [all stories for a component](../api/parameters.md#meta-parameters), or [every story in your Storybook](../api/parameters.md#project-parameters). See [Next.js Navigation](#nextjs-navigation) for more details.
+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:
-#### `navigation`
+- **`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.
-Type:
+For cookies, you can use the existing API to write them, eg. `cookies().set('firstName', 'Jane')`.
-```ts
-{
- asPath?: string;
- pathname?: string;
- query?: Record;
- segments?: (string | [string, string])[];
-}
-```
+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`.
-Default value:
+#### `@storybook/nextjs/navigation.mock`
-```js
-{
- segments: [];
-}
-```
+Type: `typeof import('next/navigation')`
-The router object that is passed to the `next/navigation` context. See [Next.js's navigation docs](https://nextjs.org/docs/app/building-your-application/routing) for more details.
+Exports mocks that replaces the actual implementation of `next/navigation` exports. Use these to mock implementations or assert on mock calls in a story's `play`-function.
-#### `router`
+#### `@storybook/nextjs/router.mock`
-Type:
+Type: `typeof import('next/router')`
-```ts
-{
- asPath?: string;
- pathname?: string;
- query?: Record;
-}
-```
+Exports mocks that replaces the actual implementation of `next/router` exports. Use these to mock implementations or assert on mock calls in a story's `play`-function.
-The router object that is passed to the `next/router` context. See [Next.js's router docs](https://nextjs.org/docs/pages/building-your-application/routing) for more details.
+#### `@storybook/nextjs/export-mocks`
+
+Type: `{ getPackageAliases: ({ useESM?: boolean }) => void }`
+
+`getPackageAliases` returns the aliases needed to set up Portable Stories in a Jest environment. See the [Portable Stories](TODO) section.
### Options
@@ -976,6 +965,55 @@ Type: `string`
The absolute path to the `next.config.js` file. This is necessary if you have a custom `next.config.js` file that is not in the root directory of your project.
+### Parameters
+
+This framework contributes the following [parameters](../writing-stories/parameters.md) to Storybook, under the `nextjs` namespace:
+
+#### `appDirectory`
+
+Type: `boolean`
+
+Default: `false`
+
+If your story imports components that use `next/navigation`, you need to set the parameter `nextjs.appDirectory` to `true`. Because this is a parameter, you can apply it to a [single story](../api/parameters.md#story-parameters), [all stories for a component](../api/parameters.md#meta-parameters), or [every story in your Storybook](../api/parameters.md#project-parameters). See [Next.js Navigation](#nextjs-navigation) for more details.
+
+#### `navigation`
+
+Type:
+
+```ts
+{
+ asPath?: string;
+ pathname?: string;
+ query?: Record;
+ segments?: (string | [string, string])[];
+}
+```
+
+Default value:
+
+```js
+{
+ segments: [];
+}
+```
+
+The router object that is passed to the `next/navigation` context. See [Next.js's navigation docs](https://nextjs.org/docs/app/building-your-application/routing) for more details.
+
+#### `router`
+
+Type:
+
+```ts
+{
+ asPath?: string;
+ pathname?: string;
+ query?: Record;
+}
+```
+
+The router object that is passed to the `next/router` context. See [Next.js's router docs](https://nextjs.org/docs/pages/building-your-application/routing) for more details.
+
From 92e13821355628eb7a383e9058ee9d539b360afa Mon Sep 17 00:00:00 2001
From: Kyle Gach
Date: Tue, 16 Apr 2024 13:17:37 -0600
Subject: [PATCH 014/117] More mocking content
- Providers
- Configuring mocked providers with parameters
- Network requests
- Bad link
- Modules
- Better mock files section
- Using mocked files in stories
---
docs/writing-stories/mocking-modules.md | 148 +++++++++++++++---
.../mocking-network-requests.md | 2 +-
docs/writing-stories/mocking-providers.md | 68 +++++++-
3 files changed, 196 insertions(+), 22 deletions(-)
diff --git a/docs/writing-stories/mocking-modules.md b/docs/writing-stories/mocking-modules.md
index 3c0365382b3a..e59f0f7c2b59 100644
--- a/docs/writing-stories/mocking-modules.md
+++ b/docs/writing-stories/mocking-modules.md
@@ -8,26 +8,27 @@ There are two primary approaches to mocking modules in Storybook. They both invo
For either approach, relative imports of the mocked module are not supported.
-### Mock files
+## Mock files
-To mock a module, create a file with the same name as the module you want to mock. For example, if you want to mock a module named `api`, create a file named `api.mock.js|ts` in the same directory as the original module. This file should match the exports of the original module but with fake data or behavior.
+To mock a module, create a file with the same name and in the same directory as the module you want to mock. For example, to mock a module named `session`, create a file next to it named `session.mock.js|ts`, with a few characteristics:
-Here's an example of a mock file for a module named `api`:
+- It should re-export all exports from the original module.
+- It should use the `fn` utility to mock any necessary functionality from the original module.
+- It cannot use subpath imports or builder aliases for its dependencies.
+- It should not introduce side effects that could affect other tests or components. Mock files should be isolated and only affect the module they are mocking.
-```js
-// api.mock.js
-TK: Add snippet
-```
-
-
-
-When creating a mock file, be careful not to introduce side effects that could affect other tests or components. Mock files should be isolated and only affect the component they are mocking.
+Here's an example of a mock file for a module named `session`:
-Additionally, you must use absolute paths to import any dependencies in the mock file. Relative imports can cause issues when importing the mock file into your component.
+```js
+// session.mock.js
+import { fn } from '@storybook/test';
+import * as actual from './session';
-
+export * from './session';
+export const getUserFromSession = fn(actual.getUserFromSession);
+```
-### Subpath imports
+## Subpath imports
The recommended method for mocking modules is to use [subpath imports](https://nodejs.org/api/packages.html#subpath-imports), a feature of Node packages that is supported by both [Vite](../builders/vite.md) and [Webpack](../builders/webpack.md).
@@ -64,17 +65,17 @@ You can then update your component file to use the subpath import:
TK: Component snippet
```
-#### Conditional imports
+### Conditional imports
Note the `storybook` and `default` keys in each module's entry. The `storybook` key is used to import the mock file in Storybook, while the `default` key is used to import the original module in your project.
The Storybook environment will match the conditions `storybook` and `test`, so you can apply the same module mapping for both Storybook and your tests.
-### Builder aliases
+## Builder aliases
-If your project is unable to use [subpath imports](#subpath-imports), you can configure your Storybook builder to alias the module to the mock file.
+If your project is unable to use [subpath imports](#subpath-imports), you can configure your Storybook builder to alias the module to the mock file. This will instruct the builder to replace the module with the mock file when bundling your Storybook stories.
-
+TK: Vite version, too
```js
// .storybook/main.ts
@@ -91,3 +92,114 @@ webpackFinal: async (config) => {
return config
},
```
+
+## Using mocked modules in stories
+
+When you use the `fn` utility to mock a module, you create full [mock functions](https://vitest.dev/api/mock.html) which have many useful methods. For example, you can use the [`mockReturnValue`](https://vitest.dev/api/mock.html#mockreturnvalue) method to set a return value for the mocked function or [`mockImplementation`](https://vitest.dev/api/mock.html#mockimplementation) to define a custom implementation.
+
+Here, we define `beforeEach` on a story (which will run before the story is rendered) to set a mocked return value for the `getUserFromSession` function used by the Page component:
+
+
+
+```js
+// Page.stories.tsx
+import { Meta, StoryObj } from '@storybook/react';
+import { fn } from '@storybook/test';
+
+import { getUserFromSession } from '#api/session.mock';
+import { Page } from './Page';
+
+const meta: Meta = {
+ component: Page,
+};
+export default meta;
+
+type Story = StoryObj;
+
+export const Default: Story = {
+ async beforeEach() {
+ // 👇 Set the return value for the getUserFromSession function
+ getUserFromSession.mockReturnValue({ id: '1', name: 'Alice' });
+ },
+};
+```
+
+
+
+If you are [writing your stories in TypeScript](./typescript.md), you will need to import your mock modules using the full mocked file name to have the mocked function correctly typed in your stories. You do **not** need do this in your component files, that's what the [subpath import](#subpath-imports) or [builder alias](#builder-aliases) is for.
+
+
+
+### Spying on mocked modules
+
+The `fn` utility also spies on the original module's functions, which you can use to assert their behavior in your tests. For example, you can use [interaction tests](../writing-tests/interaction-testing.md) to verify that a function was called with specific arguments.
+
+For example, this story checks that the `saveNote` function was called when the user clicks the save button:
+
+```ts
+// NoteUI.stories.tsx
+import { Meta, StoryObj } from '@storybook/react';
+import { expect, userEvent, within } from '@storybook/test';
+
+import { saveNote } from '#app/actions.mock';
+import { createNotes } from '#mocks/notes';
+import NoteUI from './note-ui';
+
+const meta = {
+ title: 'Mocked/NoteUI',
+ component: NoteUI,
+} satisfies Meta;
+export default meta;
+
+type Story = StoryObj;
+
+const notes = createNotes();
+
+export const SaveFlow: Story = {
+ name: 'Save Flow ▶',
+ args: {
+ isEditing: true,
+ note: notes[0],
+ },
+ play: async ({ canvasElement, step }) => {
+ const canvas = within(canvasElement);
+
+ const saveButton = canvas.getByRole('menuitem', { name: /done/i });
+ await userEvent.click(saveButton);
+ // 👇 This is the mock function, so you can assert its behavior
+ await expect(saveNote).toHaveBeenCalled();
+ },
+};
+```
+
+### Shared setup and clearing mocks
+
+You can use `beforeEach` at the component level to perform shared setup or clear the mocks between stories. This ensures that each test starts with a clean slate and is not affected by the mocks from previous stories.
+
+
+
+```js
+// Page.stories.tsx
+import { Meta, StoryObj } from '@storybook/react';
+import { fn } from '@storybook/test';
+
+import { getUserFromSession } from '#api/session.mock';
+import { Page } from './Page';
+
+const meta: Meta = {
+ component: Page,
+ async beforeEach() {
+ // 👇 Do this for each story
+ // TK
+ // 👇 Clear the mock between stories
+ getUserFromSession.mockClear();
+ },
+};
+export default meta;
+
+type Story = StoryObj;
+
+export const Default: Story = {
+ // TK
+};
+```
diff --git a/docs/writing-stories/mocking-network-requests.md b/docs/writing-stories/mocking-network-requests.md
index 28afb5e12697..461574f7c856 100644
--- a/docs/writing-stories/mocking-network-requests.md
+++ b/docs/writing-stories/mocking-network-requests.md
@@ -4,7 +4,7 @@ title: Mocking network requests
For components that make network requests (e.g. fetching data from a REST or GraphQL API), you can mock those requests using a tool like [Mock Service Worker (MSW)](https://mswjs.io/). MSW allows you to intercept requests made by your components and respond with fake data.
-The [MSW addon]https://storybook.js.org/addons/msw-storybook-addon/ brings this functionality into Storybook, allowing you to mock API requests in your stories. Below is an overview of how to set up and use the addon.
+The [MSW addon](https://storybook.js.org/addons/msw-storybook-addon/) brings this functionality into Storybook, allowing you to mock API requests in your stories. Below is an overview of how to set up and use the addon.
## Set up the MSW addon
diff --git a/docs/writing-stories/mocking-providers.md b/docs/writing-stories/mocking-providers.md
index a6000ff59558..4c5a8b20a496 100644
--- a/docs/writing-stories/mocking-providers.md
+++ b/docs/writing-stories/mocking-providers.md
@@ -4,8 +4,6 @@ title: Mocking providers
-## Providers
-
Components can receive data or configuration from context providers. For example, a styled component might access its theme from a ThemeProvider. To mock a provider, you can wrap your component in a [decorator](./decorators.md) that includes the necessary context.
@@ -21,4 +19,68 @@ Components can receive data or configuration from context providers. For example
-TK: Something about the `context` argument?
+## Configuring the mock provider
+
+When mocking a provider, it may be necessary to configure the provider to supply a different value for individual stories. For example, you might want to test a component with different themes or user roles.
+
+One way to do this is to define the decorator for each story individually. But if you imagine a scenario where you wish to create stories for each of your components in both light and dark themes, this approach can quickly become cumbersome.
+
+For a better way, with much less repetition, you can use the [decorator function's second "context" argument](./decorators.md#context-for-mocking) to access a story's [`parameters`](./parameters.md) and adjust the provided value. This way, you can define the provider once and adjust its value for each story.
+
+For example, we can adjust the decorator from above to read from `parameters.theme` to determine which theme to provide:
+
+
+
+```ts
+// .storybook/preview.js
+import React from 'react';
+import { Preview } from '@storybook/react';
+import { ThemeProvider } from 'styled-components';
+
+const preview: Preview = {
+ decorators: [
+ // 👇 Defining the decorator in the preview file applies it to all stories
+ (Story, { parameters }) => {
+ // 👇 Make it configurable by reading the theme value from parameters
+ const theme = parameters.theme || 'default';
+ return (
+
+
+
+ );
+ },
+ ],
+};
+
+export default preview;
+```
+
+Now, you can define a `theme` parameter in your stories to adjust the theme provided by the decorator:
+
+
+
+```ts
+// Button.stories.tsx
+import { Meta, StoryObj } from '@storybook/react';
+
+import { Button } from './Button';
+
+const meta: Meta = {
+ component: Button,
+};
+export default meta;
+
+type Story = StoryObj;
+
+// Wrapped in default theme
+export const Default: Story = {};
+
+// Wrapped in dark theme
+export const Dark: Story = {
+ parameters: {
+ theme: 'dark',
+ },
+};
+```
+
+This powerful approach allows you to provide any value (theme, user role, mock data, etc.) to your components in a way that is both flexible and maintainable.
From 75b0820ab13048ef27d47a967d77c745b9e50aeb Mon Sep 17 00:00:00 2001
From: Kyle Gach
Date: Tue, 16 Apr 2024 14:28:54 -0600
Subject: [PATCH 015/117] Updates for Next.js
- Warning for absolute imports & mocking
- Module aliases
- Subpath imports
- Mocking modules
- `@storybook/nextjs/cache.mock` module API
---
docs/get-started/nextjs.md | 122 ++++++++++++++++++++++++++++++++++---
1 file changed, 115 insertions(+), 7 deletions(-)
diff --git a/docs/get-started/nextjs.md b/docs/get-started/nextjs.md
index f1e024ef6049..8f109ff3202c 100644
--- a/docs/get-started/nextjs.md
+++ b/docs/get-started/nextjs.md
@@ -671,6 +671,106 @@ 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.js
+// All good!
+import Button from '@/components/button';
+// Also good!
+import styles from '@/styles/HomePage.module.css';
+
+export default function HomePage() {
+ return (
+ <>
+
Hello World
+
+ >
+ );
+}
+```
+
+## Subpath imports
+
+As an alternative to [module aliases](#module-aliases), you can use [subpath imports](https://nodejs.org/api/packages.html#subpath-imports) to import modules. This follows Node package standards and has benefits when [mocking modules](#mocking-modules).
+
+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 the `components` and `styles` internal modules:
+
+```json
+// package.json
+{
+ "imports": {
+ "#components": "./components",
+ "#styles": "./styles",
+ "#*": ["./*", "./*.ts", "./*.tsx"]
+ }
+}
+```
+
+
+
+Because subpath imports take the place of module aliases, you can remove the path aliases from your TypeScript configuration.
+
+
+
+Which can then be used like this:
+
+```jsx
+// index.js
+import Button from '#components/button';
+import styles from '#styles/HomePage.module.css';
+
+export default function HomePage() {
+ return (
+ <>
+
Hello World
+
+ >
+ );
+}
+```
+
+## Mocking modules
+
+Components often 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](../writing-stories/mocking-modules.md) to control and assert their behavior.
+
+### Built-in mocked modules
+
+This framework provides mocks for many of Next.js' internal modules:
+
+1. [`@storybook/nextjs/cache.mock`](#storybooknextjscachemock)
+2. [`@storybook/nextjs/headers.mock`](#storybooknextjsheadersmock)
+3. [`@storybook/nextjs/navigation.mock`](#storybooknextjsnavigationmock)
+4. [`@storybook/nextjs/router.mock`](#storybooknextjsroutermock)
+
+### Mocking other modules
+
+How you mock other modules in Storybook depends on how you import the module into your component.
+
+The first step, with either approach, is to [create a mock file](../writing-stories/mocking-modules.md#mock-files).
+
+TK: More here?
+
+#### 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.
+
+TK: Add example of mocking modules with subpath imports
+
+#### With module aliases
+
+If you're using [module aliases](#module-aliases), you can add a Webpack alias to your Storybook configuration to point to the mock file.
+
+TK: Add example of mocking modules with module aliases
+
## Runtime config
Next.js allows for [Runtime Configuration](https://nextjs.org/docs/pages/api-reference/next-config-js/runtime-configuration) which lets you import a handy `getConfig` function to get certain configuration defined in your `next.config.js` file at runtime.
@@ -887,7 +987,21 @@ You can refer to the [Install `sharp` to Use Built-In Image Optimization](https:
### Modules
-The `@storybook/nextjs` package exports a number of modules that enables you to [mock](/TODO_LINK_TO_MOCKING) Next.js's internal behavior.
+The `@storybook/nextjs` package exports a number of modules that enables you to [mock](#mocking-modules) Next.js's internal behavior.
+
+#### `@storybook/nextjs/export-mocks`
+
+Type: `{ getPackageAliases: ({ useESM?: boolean }) => void }`
+
+`getPackageAliases` is a helper to generate the aliases needed to set up [portable stories in a Jest environment](../api/portable-stories-jest.md).
+
+TK: Example snippet
+
+#### `@storybook/nextjs/cache.mock`
+
+Type: `typeof import('next/cache')`
+
+TK
#### `@storybook/nextjs/headers.mock`
@@ -917,12 +1031,6 @@ Type: `typeof import('next/router')`
Exports mocks that replaces the actual implementation of `next/router` exports. Use these to mock implementations or assert on mock calls in a story's `play`-function.
-#### `@storybook/nextjs/export-mocks`
-
-Type: `{ getPackageAliases: ({ useESM?: boolean }) => void }`
-
-`getPackageAliases` returns the aliases needed to set up Portable Stories in a Jest environment. See the [Portable Stories](TODO) section.
-
### Options
You can pass an options object for additional configuration if needed:
From 299a2c2a7829cd361a9f938dfa7af17de01e64cf Mon Sep 17 00:00:00 2001
From: Kyle Gach
Date: Tue, 16 Apr 2024 16:46:27 -0600
Subject: [PATCH 016/117] Simplify Building pages with Storybook guide
---
.../build-pages-with-storybook.md | 221 +-----------------
docs/writing-stories/mocking-modules.md | 22 ++
.../mocking-network-requests.md | 2 +-
docs/writing-stories/mocking-providers.md | 8 +-
4 files changed, 37 insertions(+), 216 deletions(-)
diff --git a/docs/writing-stories/build-pages-with-storybook.md b/docs/writing-stories/build-pages-with-storybook.md
index aeb293e09fc8..eb6fefd7842b 100644
--- a/docs/writing-stories/build-pages-with-storybook.md
+++ b/docs/writing-stories/build-pages-with-storybook.md
@@ -73,226 +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 network requests to fetch its data. There are various layers in which you can do that.
+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.
-### Mocking providers
+### [Mocking providers](./mocking-providers.md)
-Suppose you are using a provider that supplies data via the context. In that case, you can wrap your story in a decorator that provides a mocked version of that provider. For example, in the [Screens](https://storybook.js.org/tutorials/intro-to-storybook/react/en/screen/) chapter of the Intro to Storybook tutorial, we mock a Redux provider with mock data.
+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.
-### Mocking API Services
+### [Mocking API Services](./mocking-network-requests.md)
-Connected applications such as Twitter, Instagram, amongst others, are everywhere, consuming data from REST or GraphQL endpoints. Suppose you're working in an application that relies on either of these data providers. In that case, you can add Mock Service Worker (MSW) via [Storybook's MSW addon](https://storybook.js.org/addons/msw-storybook-addon) to mock data alongside your app and stories.
+For components that make network requests (e.g. fetching data from a REST or GraphQL API), you can mock those requests in your stories.
-[Mock Service Worker](https://mswjs.io/) is an API mocking library. It relies on service workers to capture network requests and provides mocked data in response. The MSW addon adds this functionality into Storybook, allowing you to mock API requests in your stories. Below is an overview of how to set up and use the addon.
+### [Mocking imports](./mocking-modules.md)
-Run the following commands to install MSW, the addon, and generate a mock service worker.
-
-
-
-
-
-
-
-
-
-If you're working with Angular, you'll need to adjust the command to save the mock service worker file in a different directory (e.g., `src`).
-
-
-
-Update your `.storybook/preview.js` file and enable the addon via a [global decorator](./decorators.md#global-decorators).
-
-
-
-
-
-
-
-Finally, update your [`.storybook/main.js|ts`](../configure/index.md#using-storybook-api) to allow Storybook to load the generated mock service worker file as follows:
-
-
-
-
-
-
-
-#### Mocking REST requests with MSW addon
-
-If you're working with pure presentational screens, adding stories through [args composition](#args-composition-for-presentational-screens) is recommended. You can easily encode all the data via [args](../writing-stories/args.md), removing the need for handling it with "wrapper components". However, this approach loses its flexibility if the screen's data is retrieved from a RESTful endpoint within the screen itself. For instance, if your screen had a similar implementation to retrieve a list of documents:
-
-
-
-
-
-
-
-To test your screen with the mocked data, you could write a similar set of stories:
-
-
-
-
-
-
-
-
-
-This example details how you can mock the REST request with fetch. Similar HTTP clients such as [`axios`](https://axios-http.com/) can be used as well.
-
-
-
-The mocked data (i.e., `TestData`) will be injected via [parameters](./parameters.md), enabling you to configure it per-story basis.
-
-#### Mocking GraphQL queries with MSW addon
-
-In addition to mocking RESTful requests, the other noteworthy feature of the [MSW addon](https://msw-sb.vercel.app/?path=/story/guides-introduction--page) is the ability to mock incoming data from any of the mainstream [GraphQL](https://www.apollographql.com/docs/react/integrations/integrations/) clients (e.g., [Apollo Client](https://www.apollographql.com/docs/), [URQL](https://formidable.com/open-source/urql/) or [React Query](https://react-query.tanstack.com/)). For instance, if your screen retrieves the user's information and a list of documents based on a query result, you could have a similar implementation:
-
-
-
-
-
-
-
-To test your screen with the GraphQL mocked data, you could write the following stories:
-
-
-
-
-
-
-
-### Mocking imports
-
-It is also possible to mock imports directly, as you might in a unit test, using Webpack’s aliasing. It's advantageous if your component makes network requests directly with third-party libraries.
-
-We'll use [isomorphic-fetch](https://www.npmjs.com/package/isomorphic-fetch) as an example.
-
-Inside a directory called `__mocks__`, create a new file called
-`isomorphic-fetch.js` with the following code:
-
-
-
-
-
-
-
-The code above creates a decorator which reads story-specific data off the story's [parameters](./parameters.md), enabling you to configure the mock on a per-story basis.
-
-To use the mock in place of the real import, we use [Webpack aliasing](https://webpack.js.org/configuration/resolve/#resolvealias):
-
-
-
-
-
-
-
-Add the decorator you've just implemented to your [`storybook/preview.js`](../configure/index.md#configure-story-rendering):
-
-
-
-
-
-
-
-Finally, we can set the mock values in a specific story. Let's borrow an example from this [blog post](https://medium.com/@edogc/visual-unit-testing-with-react-storybook-and-fetch-mock-4594d3a281e6):
-
-
-
-
-
-
-
-### Specific mocks
-
-Another mocking approach is to use libraries that intercept calls at a lower level. For instance, you can use [`fetch-mock`](https://www.npmjs.com/package/fetch-mock) to mock fetch requests specifically.
-
-Like the [import mocking](##mocking-imports) above, once you have a mock, you’ll still want to set the return value of the mock per-story basis. Do this in Storybook with a [decorator](./decorators.md) that reads the story's [parameters](./parameters.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.
diff --git a/docs/writing-stories/mocking-modules.md b/docs/writing-stories/mocking-modules.md
index e59f0f7c2b59..32a123333fff 100644
--- a/docs/writing-stories/mocking-modules.md
+++ b/docs/writing-stories/mocking-modules.md
@@ -93,6 +93,28 @@ webpackFinal: async (config) => {
},
```
+
+
+
+
+
+
+
+
+
+
+
+
+
## Using mocked modules in stories
When you use the `fn` utility to mock a module, you create full [mock functions](https://vitest.dev/api/mock.html) which have many useful methods. For example, you can use the [`mockReturnValue`](https://vitest.dev/api/mock.html#mockreturnvalue) method to set a return value for the mocked function or [`mockImplementation`](https://vitest.dev/api/mock.html#mockimplementation) to define a custom implementation.
diff --git a/docs/writing-stories/mocking-network-requests.md b/docs/writing-stories/mocking-network-requests.md
index 461574f7c856..ec41c806ea9c 100644
--- a/docs/writing-stories/mocking-network-requests.md
+++ b/docs/writing-stories/mocking-network-requests.md
@@ -2,7 +2,7 @@
title: Mocking network requests
---
-For components that make network requests (e.g. fetching data from a REST or GraphQL API), you can mock those requests using a tool like [Mock Service Worker (MSW)](https://mswjs.io/). MSW allows you to intercept requests made by your components and respond with fake data.
+For components that make network requests (e.g. fetching data from a REST or GraphQL API), you can mock those requests using a tool like [Mock Service Worker (MSW)](https://mswjs.io/). MSW is an API mocking library, which relies on service workers to capture network requests and provides mocked data in response.
The [MSW addon](https://storybook.js.org/addons/msw-storybook-addon/) brings this functionality into Storybook, allowing you to mock API requests in your stories. Below is an overview of how to set up and use the addon.
diff --git a/docs/writing-stories/mocking-providers.md b/docs/writing-stories/mocking-providers.md
index 4c5a8b20a496..1b3872371cb8 100644
--- a/docs/writing-stories/mocking-providers.md
+++ b/docs/writing-stories/mocking-providers.md
@@ -4,7 +4,7 @@ title: Mocking providers
-Components can receive data or configuration from context providers. For example, a styled component might access its theme from a ThemeProvider. To mock a provider, you can wrap your component in a [decorator](./decorators.md) that includes the necessary context.
+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. To mock a provider, you can wrap your component in a [decorator](./decorators.md) that includes the necessary context.
@@ -19,6 +19,12 @@ Components can receive data or configuration from context providers. For example
+
+
+For another example, reference the [Screens](https://storybook.js.org/tutorials/intro-to-storybook/react/en/screen/) chapter of the Intro to Storybook tutorial, where we mock a Redux provider with mock data.
+
+
+
## Configuring the mock provider
When mocking a provider, it may be necessary to configure the provider to supply a different value for individual stories. For example, you might want to test a component with different themes or user roles.
From ec6742b1eaaca94b1af87f0259afc0064d98ac4c Mon Sep 17 00:00:00 2001
From: Kyle Gach
Date: Tue, 16 Apr 2024 22:04:42 -0600
Subject: [PATCH 017/117] Update Decorators and Interaction tests pages
- Decorators
- Focus the "context" section on only that argument
- Move irrelevant examples and snippets to the Story rendering page
- Interaction Testing
- Under the Write an interaction test section, add:
- Run code before each test section
- Mocked modules section
---
docs/configure/story-rendering.md | 23 ++++++-
docs/writing-stories/decorators.md | 62 +++++++++---------
docs/writing-stories/mocking-modules.md | 4 +-
docs/writing-tests/interaction-testing.md | 76 +++++++++++++++++++++++
4 files changed, 133 insertions(+), 32 deletions(-)
diff --git a/docs/configure/story-rendering.md b/docs/configure/story-rendering.md
index fff549e8ef4c..1ca2d09c76d8 100644
--- a/docs/configure/story-rendering.md
+++ b/docs/configure/story-rendering.md
@@ -2,7 +2,28 @@
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.
+
+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/writing-stories/decorators.md b/docs/writing-stories/decorators.md
index 8c3160302cf8..da53026077e3 100644
--- a/docs/writing-stories/decorators.md
+++ b/docs/writing-stories/decorators.md
@@ -40,32 +40,7 @@ Some components require a “harness” to render in a useful way. For instance,
## “Context” for mocking
-Framework-specific libraries (e.g., [Styled Components](https://styled-components.com/), [Fontawesome](https://github.com/FortAwesome/vue-fontawesome) for Vue, Angular's [localize](https://angular.io/api/localize)) may require additional configuration to render correctly in Storybook.
-
-For example, if you're working with React's Styled Components and your components use themes, add a single global decorator to [`.storybook/preview.js`](../configure/index.md#configure-story-rendering) to enable them. With Vue, extend Storybook's application and register your library. Or with Angular, add the package into your `polyfills.ts` and import it:
-
-
-
-
-
-
-
-In the example above, the values provided are hardcoded. Still, you may want to vary them, either per-story basis (i.e., if the values you're adding are relevant to a specific story) or in a user-controlled way (e.g., provide a theme switcher or a different set of icons).
-
-The second argument to a decorator function is the **story context** which in particular contains the keys:
+The second argument to a decorator function is the **story context** which contains the properties:
- `args` - the story arguments. You can use some [`args`](./args.md) in your decorators and drop them in the story implementation itself.
- `argTypes`- Storybook's [argTypes](../api/arg-types.md) allow you to customize and fine-tune your stories [`args`](./args.md).
@@ -74,15 +49,42 @@ The second argument to a decorator function is the **story context** which in pa
- `parameters`- the story's static metadata, most commonly used to control Storybook's behavior of features and addons.
- `viewMode`- Storybook's current active window (e.g., canvas, docs).
-
+This context can be used to adjust the behavior of your decorator based on the story's arguments or other metadata. For example, you could create a decorator that wraps the story in a layout, unless the `noLayout` parameter is set to `true`:
+
+
+
+```ts
+// .storybook/preview.js
+import React from 'react';
+import { Preview } from '@storybook/react';
+
+import { Layout } from '../components/Layout';
+
+const preview: Preview = {
+ decorators: [
+ // 👇 Defining the decorator in the preview file applies it to all stories
+ (Story, { parameters }) => {
+ // 👇 Make it configurable by reading from parameters
+ const { noLayout } = parameters;
+ return noLayout ? (
+
+ ) : (
+
+
+
+ );
+ },
+ ],
+};
-This pattern can also be applied to your own stories. Some of Storybook's supported frameworks already use it (e.g., Vue 3).
+export default preview;
+```
-
+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.
### 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.
+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.
## Story decorators
diff --git a/docs/writing-stories/mocking-modules.md b/docs/writing-stories/mocking-modules.md
index 32a123333fff..d4e8007663b5 100644
--- a/docs/writing-stories/mocking-modules.md
+++ b/docs/writing-stories/mocking-modules.md
@@ -117,7 +117,7 @@ webpackFinal: async (config) => {
## Using mocked modules in stories
-When you use the `fn` utility to mock a module, you create full [mock functions](https://vitest.dev/api/mock.html) which have many useful methods. For example, you can use the [`mockReturnValue`](https://vitest.dev/api/mock.html#mockreturnvalue) method to set a return value for the mocked function or [`mockImplementation`](https://vitest.dev/api/mock.html#mockimplementation) to define a custom implementation.
+When you use the `fn` utility to mock a module, you create full [Vitest mock functions](https://vitest.dev/api/mock.html) which have many useful methods. For example, you can use the [`mockReturnValue`](https://vitest.dev/api/mock.html#mockreturnvalue) method to set a return value for the mocked function or [`mockImplementation`](https://vitest.dev/api/mock.html#mockimplementation) to define a custom implementation.
Here, we define `beforeEach` on a story (which will run before the story is rendered) to set a mocked return value for the `getUserFromSession` function used by the Page component:
@@ -158,6 +158,8 @@ The `fn` utility also spies on the original module's functions, which you can us
For example, this story checks that the `saveNote` function was called when the user clicks the save button:
+
+
```ts
// NoteUI.stories.tsx
import { Meta, StoryObj } from '@storybook/react';
diff --git a/docs/writing-tests/interaction-testing.md b/docs/writing-tests/interaction-testing.md
index b949aa284a8a..c235e1122fe7 100644
--- a/docs/writing-tests/interaction-testing.md
+++ b/docs/writing-tests/interaction-testing.md
@@ -90,6 +90,38 @@ Once the story loads in the UI, it simulates the user's behavior and verifies th
/>
+### Run code before each test
+
+It can be helpful to run code before each test to set up the initial state of the component or reset the state of modules. You can do this by adding an `async beforeEach` function to the meta in your stories file. This function will run before each test in the story file.
+
+
+
+```js
+// Page.stories.tsx
+import { Meta, StoryObj } from '@storybook/react';
+import { fn } from '@storybook/test';
+
+import { getUserFromSession } from '#api/session.mock';
+import { Page } from './Page';
+
+const meta: Meta = {
+ component: Page,
+ async beforeEach() {
+ // 👇 Do this for each story
+ // TK
+ // 👇 Clear the mock between stories
+ getUserFromSession.mockClear();
+ },
+};
+export default meta;
+
+type Story = StoryObj;
+
+export const Default: Story = {
+ // TK
+};
+```
+
### API for user-events
Under the hood, Storybook’s `@storybook/test` package provides Testing Library’s [`user-events`](https://testing-library.com/docs/user-event/intro/) APIs. If you’re familiar with [Testing Library](https://testing-library.com/), you should be at home in Storybook.
@@ -152,6 +184,50 @@ This will show your interactions nested in a collapsible group:
![Interaction testing with labeled steps](./storybook-addon-interactions-steps.png)
+### Mocked modules
+
+If your component depends on modules that are imported into the component file, you can mock those modules to control and assert on their behavior. This is detailed in the [mocking modules](./mocking-modules.md) guide.
+
+You can then import the mocked module (which has all of the helpful methods of a [Vitest mocked function](https://vitest.dev/api/mock.html)) into your story and use it to assert on the behavior of your component:
+
+
+
+```ts
+// NoteUI.stories.tsx
+import { Meta, StoryObj } from '@storybook/react';
+import { expect, userEvent, within } from '@storybook/test';
+
+import { saveNote } from '#app/actions.mock';
+import { createNotes } from '#mocks/notes';
+import NoteUI from './note-ui';
+
+const meta = {
+ title: 'Mocked/NoteUI',
+ component: NoteUI,
+} satisfies Meta;
+export default meta;
+
+type Story = StoryObj;
+
+const notes = createNotes();
+
+export const SaveFlow: Story = {
+ name: 'Save Flow ▶',
+ args: {
+ isEditing: true,
+ note: notes[0],
+ },
+ play: async ({ canvasElement, step }) => {
+ const canvas = within(canvasElement);
+
+ const saveButton = canvas.getByRole('menuitem', { name: /done/i });
+ await userEvent.click(saveButton);
+ // 👇 This is the mock function, so you can assert its behavior
+ await expect(saveNote).toHaveBeenCalled();
+ },
+};
+```
+
### Interactive debugger
If you check your interactions panel, you'll see the step-by-step flow. It also offers a handy set of UI controls to pause, resume, rewind, and step through each interaction.
From abc9b7cd8bd4d0a3c972dfd054ba7bd219e3e298 Mon Sep 17 00:00:00 2001
From: Kyle Gach
Date: Tue, 16 Apr 2024 22:06:47 -0600
Subject: [PATCH 018/117] Add missing TODOs
---
docs/get-started/nextjs.md | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/docs/get-started/nextjs.md b/docs/get-started/nextjs.md
index 8f109ff3202c..7f66b0c2486a 100644
--- a/docs/get-started/nextjs.md
+++ b/docs/get-started/nextjs.md
@@ -1001,7 +1001,9 @@ TK: Example snippet
Type: `typeof import('next/cache')`
-TK
+TK: Description
+
+TK: Example snippet
#### `@storybook/nextjs/headers.mock`
@@ -1019,18 +1021,24 @@ For cookies, you can use the existing API to write them, eg. `cookies().set('fir
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`.
+TK: Example snippet
+
#### `@storybook/nextjs/navigation.mock`
Type: `typeof import('next/navigation')`
Exports mocks that replaces the actual implementation of `next/navigation` exports. Use these to mock implementations or assert on mock calls in a story's `play`-function.
+TK: Example snippet
+
#### `@storybook/nextjs/router.mock`
Type: `typeof import('next/router')`
Exports mocks that replaces the actual implementation of `next/router` exports. Use these to mock implementations or assert on mock calls in a story's `play`-function.
+TK: Example snippet
+
### Options
You can pass an options object for additional configuration if needed:
From 194a9bcbc0637a847ea79520a27a7e4bfa7ef039 Mon Sep 17 00:00:00 2001
From: Kyle Gach
Date: Wed, 17 Apr 2024 12:04:16 -0600
Subject: [PATCH 019/117] Add vite example
Co-authored-by: Jeppe Reinhold
---
docs/writing-stories/mocking-modules.md | 17 ++++++++++++++++-
1 file changed, 16 insertions(+), 1 deletion(-)
diff --git a/docs/writing-stories/mocking-modules.md b/docs/writing-stories/mocking-modules.md
index d4e8007663b5..e26ed559c981 100644
--- a/docs/writing-stories/mocking-modules.md
+++ b/docs/writing-stories/mocking-modules.md
@@ -75,7 +75,22 @@ The Storybook environment will match the conditions `storybook` and `test`, so y
If your project is unable to use [subpath imports](#subpath-imports), you can configure your Storybook builder to alias the module to the mock file. This will instruct the builder to replace the module with the mock file when bundling your Storybook stories.
-TK: Vite version, too
+```js
+// .storybook/main.ts
+
+viteFinal: async (config) => {
+ return {
+ ...config,
+ resolve: {
+ ...config.resolve,
+ alias: {
+ ...config.resolve?.alias,
+ 'lodash': require.resolve('./lodash.mock'),
+ '@/api/todo': path.resolve(__dirname, './api/todo.mock.ts')
+ }
+ }
+ }
+},
```js
// .storybook/main.ts
From f2f4d13716a8c0ca266910e5c3dd2217c4df4da8 Mon Sep 17 00:00:00 2001
From: Jeppe Reinhold
Date: Wed, 17 Apr 2024 20:37:40 +0200
Subject: [PATCH 020/117] clarify mock->original import limitations.
---
docs/writing-stories/mocking-modules.md | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/docs/writing-stories/mocking-modules.md b/docs/writing-stories/mocking-modules.md
index e26ed559c981..c4f249f5cc79 100644
--- a/docs/writing-stories/mocking-modules.md
+++ b/docs/writing-stories/mocking-modules.md
@@ -12,9 +12,8 @@ For either approach, relative imports of the mocked module are not supported.
To mock a module, create a file with the same name and in the same directory as the module you want to mock. For example, to mock a module named `session`, create a file next to it named `session.mock.js|ts`, with a few characteristics:
-- It should re-export all exports from the original module.
+- It should re-export all exports from the original module - using relative imports to import the original, as using a subpath or alias import would result in it importing itself.
- It should use the `fn` utility to mock any necessary functionality from the original module.
-- It cannot use subpath imports or builder aliases for its dependencies.
- It should not introduce side effects that could affect other tests or components. Mock files should be isolated and only affect the module they are mocking.
Here's an example of a mock file for a module named `session`:
@@ -75,7 +74,7 @@ The Storybook environment will match the conditions `storybook` and `test`, so y
If your project is unable to use [subpath imports](#subpath-imports), you can configure your Storybook builder to alias the module to the mock file. This will instruct the builder to replace the module with the mock file when bundling your Storybook stories.
-```js
+````js
// .storybook/main.ts
viteFinal: async (config) => {
@@ -106,7 +105,7 @@ webpackFinal: async (config) => {
return config
},
-```
+````
From e7c06768fc90a3ba6bb5b45f7fc1b06e1f271255 Mon Sep 17 00:00:00 2001
From: Jeppe Reinhold
Date: Wed, 17 Apr 2024 21:10:48 +0200
Subject: [PATCH 021/117] add paragraph on mocking external modules.
---
docs/writing-stories/mocking-modules.md | 31 +++++++++++++++++++++----
1 file changed, 27 insertions(+), 4 deletions(-)
diff --git a/docs/writing-stories/mocking-modules.md b/docs/writing-stories/mocking-modules.md
index c4f249f5cc79..61ef7881fe44 100644
--- a/docs/writing-stories/mocking-modules.md
+++ b/docs/writing-stories/mocking-modules.md
@@ -31,7 +31,7 @@ export const getUserFromSession = fn(actual.getUserFromSession);
The recommended method for mocking modules is to use [subpath imports](https://nodejs.org/api/packages.html#subpath-imports), a feature of Node packages that is supported by both [Vite](../builders/vite.md) and [Webpack](../builders/webpack.md).
-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 the `api` and `prisma` internal modules:
+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 four internal modules:
TK: External module example?
@@ -43,15 +43,38 @@ TK: External module example?
"storybook": "./api.mock.ts",
"default": "./api.ts"
},
- "#prisma/prisma": {
- "storybook": "./prisma/prisma.mock.ts",
- "default": "./prisma/prisma.ts"
+ "#app/actions": {
+ "storybook": "./app/actions.mock.ts",
+ "default": "./app/actions.ts"
+ },
+ "#lib/session": {
+ "storybook": "./lib/session.mock.ts",
+ "default": "./lib/session.ts"
+ },
+ "#lib/db": {
+ "storybook": "./lib/db.mock.ts",
+ "default": "./lib/db.ts"
},
"#*": ["./*", "./*.ts", "./*.tsx"]
}
}
```
+You can't directly mock an external module like `uuid` or `node:fs`, so instead of importing them directly in your components you can wrap them in your own modules that you import from instead, and that are mockable like any other internal module. Here's an example of wrapping `uuid` and creating a mock for the wrapper:
+
+```ts
+// lib/uuid.ts
+import { v4 } from 'uuid';
+export const uuidv4 = v4;
+```
+
+```ts
+// lib/uuid.mock.ts
+import { fn } from '@storybook/test';
+import * as actual from './uuid';
+export const uuidv4 = fn(actual.uuidv4);
+```
+
Each subpath must begin with `#`, to differentiate it from a regular module path. The `#*` entry is a catch-all that maps all subpaths to the root directory.
From 5d8ee2820935b5e6c541e6863d2caf5968982f2c Mon Sep 17 00:00:00 2001
From: Jeppe Reinhold
Date: Wed, 17 Apr 2024 22:20:12 +0200
Subject: [PATCH 022/117] add docs on `beforeEach`, `cleanup`,
`parameters.test`
---
docs/api/parameters.md | 45 +++++++++++++++++++++++++
docs/writing-stories/mocking-modules.md | 22 ++++++++----
2 files changed, 60 insertions(+), 7 deletions(-)
diff --git a/docs/api/parameters.md b/docs/api/parameters.md
index 506dc051b3df..7b8ec1446804 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`
+
+TK: Description
+
---
### Essential addons
diff --git a/docs/writing-stories/mocking-modules.md b/docs/writing-stories/mocking-modules.md
index 61ef7881fe44..8063b9f6a718 100644
--- a/docs/writing-stories/mocking-modules.md
+++ b/docs/writing-stories/mocking-modules.md
@@ -233,16 +233,24 @@ export const SaveFlow: Story = {
};
```
-### Shared setup and clearing mocks
+### Setting up and cleaning up
-You can use `beforeEach` at the component level to perform shared setup or clear the mocks between stories. This ensures that each test starts with a clean slate and is not affected by the mocks from previous stories.
+You can use `beforeEach` at the project, component or story level to perform any setup that you need, eg. setting up mock behavior. You can also return a cleanup-function from `beforeEach` which will be called after your story unmounts. This is useful for unsubscribing observers etc.
+
+
+
+It is _not_ necessary to restore `fn()` mocks with the cleanup function, as Storybook will already do that automatically before rendering a story. See the [`parameters.test`](../api/parameters.md#test) API for more information.
+
+
+
+Here's an example of using the [`mockdate`](https://github.com/boblauer/MockDate) package to mock the Date and resetting it when the story unmounts.
```js
// Page.stories.tsx
import { Meta, StoryObj } from '@storybook/react';
-import { fn } from '@storybook/test';
+import MockDate from 'mockdate';
import { getUserFromSession } from '#api/session.mock';
import { Page } from './Page';
@@ -250,10 +258,10 @@ import { Page } from './Page';
const meta: Meta = {
component: Page,
async beforeEach() {
- // 👇 Do this for each story
- // TK
- // 👇 Clear the mock between stories
- getUserFromSession.mockClear();
+ MockDate.set('2024-02-14');
+ return () => {
+ MockDate.reset();
+ };
},
};
export default meta;
From 4faf1aed873a759c294207cccbb33c29a233d659 Mon Sep 17 00:00:00 2001
From: Jeppe Reinhold
Date: Wed, 17 Apr 2024 22:22:00 +0200
Subject: [PATCH 023/117] simplify subpath example in nextjs
---
docs/get-started/nextjs.md | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/docs/get-started/nextjs.md b/docs/get-started/nextjs.md
index 7f66b0c2486a..408e11df3a49 100644
--- a/docs/get-started/nextjs.md
+++ b/docs/get-started/nextjs.md
@@ -702,14 +702,12 @@ export default function HomePage() {
As an alternative to [module aliases](#module-aliases), you can use [subpath imports](https://nodejs.org/api/packages.html#subpath-imports) to import modules. This follows Node package standards and has benefits when [mocking modules](#mocking-modules).
-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 the `components` and `styles` internal modules:
+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
// package.json
{
"imports": {
- "#components": "./components",
- "#styles": "./styles",
"#*": ["./*", "./*.ts", "./*.tsx"]
}
}
From 2ac03c424800b7a0577f18609fe734eec322d85c Mon Sep 17 00:00:00 2001
From: Kyle Gach
Date: Wed, 17 Apr 2024 23:27:34 -0600
Subject: [PATCH 024/117] Address feedback
- Next.js
- Add portable stories section
- Mocking modules
- Clarify requirements of mock files
- Prose and snippet tweaks
- Interaction testing
- Bring over `mockdate` example
- Prose and snippet tweaks
---
docs/api/parameters.md | 2 +-
docs/get-started/nextjs.md | 8 ++-
docs/writing-stories/mocking-modules.md | 65 ++++++++++++++---------
docs/writing-tests/interaction-testing.md | 29 +++++++---
4 files changed, 69 insertions(+), 35 deletions(-)
diff --git a/docs/api/parameters.md b/docs/api/parameters.md
index 7b8ec1446804..e8b0d6529387 100644
--- a/docs/api/parameters.md
+++ b/docs/api/parameters.md
@@ -184,7 +184,7 @@ Type: `boolean`
Default: `false`
-TK: Description
+Setting this to `true` will prevent the [play function](../writing-stories/play-function.md) from failing and showing a warning when an uncaught exception is thrown.
---
diff --git a/docs/get-started/nextjs.md b/docs/get-started/nextjs.md
index 408e11df3a49..678be46c6b2d 100644
--- a/docs/get-started/nextjs.md
+++ b/docs/get-started/nextjs.md
@@ -881,6 +881,12 @@ If your server components access data via the network, we recommend using the [M
In the future we will provide better mocking support in Storybook and support for [Server Actions](https://nextjs.org/docs/app/api-reference/functions/server-actions).
+## Portable stories
+
+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.
+
## Notes for Yarn v2 and v3 users
If you're using [Yarn](https://yarnpkg.com/) v2 or v3, you may run into issues where Storybook can't resolve `style-loader` or `css-loader`. For example, you might get errors like:
@@ -991,7 +997,7 @@ The `@storybook/nextjs` package exports a number of modules that enables you to
Type: `{ getPackageAliases: ({ useESM?: boolean }) => void }`
-`getPackageAliases` is a helper to generate the aliases needed to set up [portable stories in a Jest environment](../api/portable-stories-jest.md).
+`getPackageAliases` is a helper to generate the aliases needed to set up [portable stories](#portable-stories).
TK: Example snippet
diff --git a/docs/writing-stories/mocking-modules.md b/docs/writing-stories/mocking-modules.md
index 8063b9f6a718..a7f2ea886023 100644
--- a/docs/writing-stories/mocking-modules.md
+++ b/docs/writing-stories/mocking-modules.md
@@ -12,7 +12,9 @@ For either approach, relative imports of the mocked module are not supported.
To mock a module, create a file with the same name and in the same directory as the module you want to mock. For example, to mock a module named `session`, create a file next to it named `session.mock.js|ts`, with a few characteristics:
-- It should re-export all exports from the original module - using relative imports to import the original, as using a subpath or alias import would result in it importing itself.
+- It must import the original module using a relative import.
+ - Using a subpath or alias import would result in it importing itself.
+- It should re-export all exports from the original module.
- It should use the `fn` utility to mock any necessary functionality from the original module.
- It should not introduce side effects that could affect other tests or components. Mock files should be isolated and only affect the module they are mocking.
@@ -27,14 +29,36 @@ export * from './session';
export const getUserFromSession = fn(actual.getUserFromSession);
```
+### Mock files for external modules
+
+You can't directly mock an external module like `uuid` or `node:fs`. Instead, you must wrap the module in you own module, which you can then mock like any other internal module. In this example, we wrap `uuid`:
+
+
+
+```ts
+// lib/uuid.ts
+import { v4 } from 'uuid';
+
+export const uuidv4 = v4;
+```
+
+And create a mock for the wrapper:
+
+```ts
+// lib/uuid.mock.ts
+import { fn } from '@storybook/test';
+
+import * as actual from './uuid';
+
+export const uuidv4 = fn(actual.uuidv4);
+```
+
## Subpath imports
The recommended method for mocking modules is to use [subpath imports](https://nodejs.org/api/packages.html#subpath-imports), a feature of Node packages that is supported by both [Vite](../builders/vite.md) and [Webpack](../builders/webpack.md).
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 four internal modules:
-TK: External module example?
-
```json
// package.json
{
@@ -60,21 +84,6 @@ TK: External module example?
}
```
-You can't directly mock an external module like `uuid` or `node:fs`, so instead of importing them directly in your components you can wrap them in your own modules that you import from instead, and that are mockable like any other internal module. Here's an example of wrapping `uuid` and creating a mock for the wrapper:
-
-```ts
-// lib/uuid.ts
-import { v4 } from 'uuid';
-export const uuidv4 = v4;
-```
-
-```ts
-// lib/uuid.mock.ts
-import { fn } from '@storybook/test';
-import * as actual from './uuid';
-export const uuidv4 = fn(actual.uuidv4);
-```
-
Each subpath must begin with `#`, to differentiate it from a regular module path. The `#*` entry is a catch-all that maps all subpaths to the root directory.
@@ -97,7 +106,7 @@ The Storybook environment will match the conditions `storybook` and `test`, so y
If your project is unable to use [subpath imports](#subpath-imports), you can configure your Storybook builder to alias the module to the mock file. This will instruct the builder to replace the module with the mock file when bundling your Storybook stories.
-````js
+```js
// .storybook/main.ts
viteFinal: async (config) => {
@@ -113,6 +122,7 @@ viteFinal: async (config) => {
}
}
},
+```
```js
// .storybook/main.ts
@@ -128,7 +138,7 @@ webpackFinal: async (config) => {
return config
},
-````
+```
@@ -235,15 +245,17 @@ export const SaveFlow: Story = {
### Setting up and cleaning up
-You can use `beforeEach` at the project, component or story level to perform any setup that you need, eg. setting up mock behavior. You can also return a cleanup-function from `beforeEach` which will be called after your story unmounts. This is useful for unsubscribing observers etc.
+You can use the asynchronous `beforeEach` function to perform any setup that you need before the story is rendered, eg. setting up mock behavior. It can be defined at the story, component (which will run for all stories in the file), or project (defined in `.storybook/preview.js|ts`, which will run for all stories in the project) level.
+
+You can also return a cleanup function from `beforeEach` which will be called after your story unmounts. This is useful for tasks like unsubscribing observers, etc.
-It is _not_ necessary to restore `fn()` mocks with the cleanup function, as Storybook will already do that automatically before rendering a story. See the [`parameters.test`](../api/parameters.md#test) API for more information.
+It is _not_ necessary to restore `fn()` mocks with the cleanup function, as Storybook will already do that automatically before rendering a story. See the [`parameters.test.restoreMocks` API](../api/parameters.md#restoremocks) for more information.
-Here's an example of using the [`mockdate`](https://github.com/boblauer/MockDate) package to mock the Date and resetting it when the story unmounts.
+Here's an example of using the [`mockdate`](https://github.com/boblauer/MockDate) package to mock the Date and reset it when the story unmounts.
@@ -257,8 +269,11 @@ import { Page } from './Page';
const meta: Meta = {
component: Page,
+ // 👇 Set the current date for every story in the file
async beforeEach() {
MockDate.set('2024-02-14');
+
+ // 👇 Reset the date after each test
return () => {
MockDate.reset();
};
@@ -268,7 +283,5 @@ export default meta;
type Story = StoryObj;
-export const Default: Story = {
- // TK
-};
+export const Default: Story = {};
```
diff --git a/docs/writing-tests/interaction-testing.md b/docs/writing-tests/interaction-testing.md
index c235e1122fe7..1c9b34509aaf 100644
--- a/docs/writing-tests/interaction-testing.md
+++ b/docs/writing-tests/interaction-testing.md
@@ -92,25 +92,38 @@ Once the story loads in the UI, it simulates the user's behavior and verifies th
### Run code before each test
-It can be helpful to run code before each test to set up the initial state of the component or reset the state of modules. You can do this by adding an `async beforeEach` function to the meta in your stories file. This function will run before each test in the story file.
+It can be helpful to run code before each test to set up the initial state of the component or reset the state of modules. You can do this by adding an asynchronous `beforeEach` function to the story, meta (which will run before each story in the file), or the preview file (`.storybook/preview.js|ts`, which will run before every story in the project).
+
+Additionally, if you return a cleanup function from the `beforeEach` function, it will run **after** each test, when the story is remounted or navigated away from.
+
+
+
+It is _not_ necessary to restore `fn()` mocks with the cleanup function, as Storybook will already do that automatically before rendering a story. See the [`parameters.test.restoreMocks` API](../api/parameters.md#restoremocks) for more information.
+
+
+
+Here's an example of using the [`mockdate`](https://github.com/boblauer/MockDate) package to mock the Date and reset it when the story unmounts.
```js
// Page.stories.tsx
import { Meta, StoryObj } from '@storybook/react';
-import { fn } from '@storybook/test';
+import MockDate from 'mockdate';
import { getUserFromSession } from '#api/session.mock';
import { Page } from './Page';
const meta: Meta = {
component: Page,
+ // 👇 Set the current date for every story in the file
async beforeEach() {
- // 👇 Do this for each story
- // TK
- // 👇 Clear the mock between stories
- getUserFromSession.mockClear();
+ MockDate.set('2024-02-14');
+
+ // 👇 Reset the date after each test
+ return () => {
+ MockDate.reset();
+ };
},
};
export default meta;
@@ -118,7 +131,9 @@ export default meta;
type Story = StoryObj;
export const Default: Story = {
- // TK
+ async play({ canvasElement }) {
+ // ... This will run with the mocked date
+ },
};
```
From b1da9c9168564ac22135f9c39e9b7ec1f67441ec Mon Sep 17 00:00:00 2001
From: Jeppe Reinhold
Date: Thu, 18 Apr 2024 08:58:29 +0200
Subject: [PATCH 025/117] add getPackageAliases example
---
docs/get-started/nextjs.md | 26 +++++++++++++++++++++++++-
1 file changed, 25 insertions(+), 1 deletion(-)
diff --git a/docs/get-started/nextjs.md b/docs/get-started/nextjs.md
index 678be46c6b2d..8cc1b3b219bb 100644
--- a/docs/get-started/nextjs.md
+++ b/docs/get-started/nextjs.md
@@ -999,7 +999,31 @@ Type: `{ getPackageAliases: ({ useESM?: boolean }) => void }`
`getPackageAliases` is a helper to generate the aliases needed to set up [portable stories](#portable-stories).
-TK: Example snippet
+```ts
+import type { Config } from 'jest';
+import nextJest from 'next/jest.js';
+// 👇 import the utility function
+import { getPackageAliases } from '@storybook/nextjs/export-mocks';
+
+const createJestConfig = nextJest({
+ // Provide the path to your Next.js app to load next.config.js and .env files in your test environment
+ dir: './',
+});
+
+// Add any custom config to be passed to Jest
+const config: Config = {
+ coverageProvider: 'v8',
+ testEnvironment: 'jsdom',
+ // Add more setup options before each test is run
+ // setupFilesAfterEnv: ['/jest.setup.ts'],
+ moduleNameMapper: {
+ ...getPackageAliases(), // 👈 add the utility as a moduleNameMapper
+ },
+};
+
+// createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async
+export default createJestConfig(config);
+```
#### `@storybook/nextjs/cache.mock`
From 71a4cea8b88390bec915ccaf4946dc5dddf30d41 Mon Sep 17 00:00:00 2001
From: Jeppe Reinhold
Date: Thu, 18 Apr 2024 12:07:47 +0200
Subject: [PATCH 026/117] add examples to next mock docs
---
docs/get-started/nextjs.md | 143 ++++++++++++++++++++++++++++++++++---
1 file changed, 134 insertions(+), 9 deletions(-)
diff --git a/docs/get-started/nextjs.md b/docs/get-started/nextjs.md
index 8cc1b3b219bb..78197e490e7d 100644
--- a/docs/get-started/nextjs.md
+++ b/docs/get-started/nextjs.md
@@ -1029,9 +1029,34 @@ export default createJestConfig(config);
Type: `typeof import('next/cache')`
-TK: Description
+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.
-TK: Example snippet
+```ts
+import { expect, userEvent, within } from '@storybook/test';
+import { Meta, StoryObj } from '@storybook/react';
+// 👇 import from the Storybook package to get correct mock types
+import { revalidatePath } from '@storybook/nextjs/cache';
+import MyForm from './my-form';
+
+const meta = {
+ component: MyForm,
+} satisfies Meta;
+
+export default meta;
+
+type Story = StoryObj;
+
+export const Submitted: Story = {
+ play: async ({ canvasElement, step }) => {
+ const canvas = within(canvasElement);
+
+ const submitButton = canvas.getByRole('button', { name: /submit/i });
+ await userEvent.click(saveButton);
+ // 👇 use any mock assertions on the function
+ await expect(revalidatePath).toHaveBeenCalledWith('/');
+ },
+};
+```
#### `@storybook/nextjs/headers.mock`
@@ -1049,23 +1074,123 @@ For cookies, you can use the existing API to write them, eg. `cookies().set('fir
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`.
-TK: Example snippet
+```ts
+import { expect, fireEvent, userEvent, within } from '@storybook/test';
+import { Meta, StoryObj } from '@storybook/react';
+// 👇 import from the Storybook package to get correct mock types
+import { cookies, headers } from '@storybook/nextjs/headers';
+import MyForm from './my-form';
+
+const meta = {
+ component: MyForm,
+} satisfies Meta;
+
+export default meta;
+
+type Story = StoryObj;
+
+export const LoggedInEurope: Story = {
+ async beforeEach() {
+ // 👇 set mock cookies and headers ahead of rendering
+ cookies().set('username', 'Sol');
+ headers().set('timezone', 'Central European Summer Time');
+ },
+ play: () => {
+ // 👇 assert that your component called the mocks
+ expect(cookies().get).toHaveBeenCalledOnce();
+ expect(cookies().get).toHaveBeenCalledWith('username');
+ expect(headers().get).toHaveBeenCalledOnce();
+ expect(cookies().get).toHaveBeenCalledWith('timezone');
+ },
+};
+```
#### `@storybook/nextjs/navigation.mock`
-Type: `typeof import('next/navigation')`
+Type: `typeof import('next/navigation') & getRouter: () => ReturnType`
-Exports mocks that replaces the actual implementation of `next/navigation` exports. Use these to mock implementations or assert on mock calls in a story's `play`-function.
+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.
-TK: Example snippet
+```ts
+import { expect, fireEvent, userEvent, within } from '@storybook/test';
+import { Meta, StoryObj } from '@storybook/react';
+// 👇 import from the Storybook package to get correct mock types
+import { redirect, getRouter } from '@storybook/nextjs/navigation';
+import MyForm from './my-form';
+
+const meta = {
+ component: MyForm,
+ parameters: {
+ nextjs: {
+ // 👇 As in the Next.js application, next/navigation only works using App Router
+ appDirectory: true,
+ },
+ },
+} satisfies Meta;
+
+export default meta;
+
+type Story = StoryObj;
+
+export const Unauthenticated: Story = {
+ play: () => {
+ // 👇 assert that your component called redirect()
+ expect(redirect).toHaveBeenCalledWith('/login', 'replace');
+ },
+};
+
+export const GoBack: Story = {
+ play: async ({ canvasElement }) => {
+ const canvas = within(canvasElement);
+ const backBtn = await canvas.findByText('Go back');
+
+ await userEvent.click(backBtn);
+ // 👇 assert that your component called back()
+ await expect(getRouter().back).toHaveBeenCalled();
+ },
+};
+```
#### `@storybook/nextjs/router.mock`
-Type: `typeof import('next/router')`
+Type: `typeof import('next/router') & getRouter: () => ReturnType`
-Exports mocks that replaces the actual implementation of `next/router` exports. Use these to mock implementations or assert on mock calls in a story's `play`-function.
+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.
-TK: Example snippet
+```ts
+import { expect, fireEvent, userEvent, within } from '@storybook/test';
+import { Meta, StoryObj } from '@storybook/react';
+// 👇 import from the Storybook package to get correct mock types
+import { getRouter } from '@storybook/nextjs/router';
+import MyForm from './my-form';
+
+const meta = {
+ component: MyForm,
+ parameters: {
+ nextjs: {
+ // 👇 As in the Next.js application, next/router only works using Pages Router
+ appDirectory: false,
+ },
+ },
+} satisfies Meta;
+
+export default meta;
+
+type Story = StoryObj;
+
+export const GoBack: Story = {
+ play: async ({ canvasElement }) => {
+ const canvas = within(canvasElement);
+ const backBtn = await canvas.findByText('Go back');
+
+ await userEvent.click(backBtn);
+ // 👇 assert that your component called back()
+ await expect(getRouter().back).toHaveBeenCalled();
+ },
+};
+```
### Options
From 5bab73ae83f0213db1925e186b5410d1e08b8bab Mon Sep 17 00:00:00 2001
From: Jeppe Reinhold
Date: Thu, 18 Apr 2024 12:09:05 +0200
Subject: [PATCH 027/117] improve documentation for
parameters.test.dangerouslyIgnoreUnhandledErrors
---
docs/api/parameters.md | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/docs/api/parameters.md b/docs/api/parameters.md
index e8b0d6529387..0e37ebdf06e6 100644
--- a/docs/api/parameters.md
+++ b/docs/api/parameters.md
@@ -184,7 +184,9 @@ Type: `boolean`
Default: `false`
-Setting this to `true` will prevent the [play function](../writing-stories/play-function.md) from failing and showing a warning when an uncaught exception is thrown.
+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 rendering or playing.
+
+Unhandled errors might cause false positive assertions, they can be ignored by setting this parameter to `true`.
---
From 46c31ebbd945c8879ec11c42eb84b7b39b7d4c41 Mon Sep 17 00:00:00 2001
From: Jeppe Reinhold
Date: Thu, 18 Apr 2024 13:14:20 +0200
Subject: [PATCH 028/117] remove docs on default router and navigation contexts
---
docs/get-started/nextjs.md | 83 +------------------
...navigation-push-override-in-preview.js.mdx | 21 -----
...navigation-push-override-in-preview.ts.mdx | 24 ------
...tjs-router-push-override-in-preview.js.mdx | 21 -----
...tjs-router-push-override-in-preview.ts.mdx | 24 ------
5 files changed, 2 insertions(+), 171 deletions(-)
delete mode 100644 docs/snippets/react/nextjs-navigation-push-override-in-preview.js.mdx
delete mode 100644 docs/snippets/react/nextjs-navigation-push-override-in-preview.ts.mdx
delete mode 100644 docs/snippets/react/nextjs-router-push-override-in-preview.js.mdx
delete mode 100644 docs/snippets/react/nextjs-router-push-override-in-preview.ts.mdx
diff --git a/docs/get-started/nextjs.md b/docs/get-started/nextjs.md
index 78197e490e7d..4a9d4cca092a 100644
--- a/docs/get-started/nextjs.md
+++ b/docs/get-started/nextjs.md
@@ -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,7 @@ 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):
-
-
-
-
-
-
+All the functions (such as `back()`, `push()`) are mock functions that can be manipulated and asserted on using [regular mock APIs](https://vitest.dev/api/mock.html).
## Next.js navigation
@@ -493,43 +445,12 @@ 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):
-
-
-
-
-
-
+All the functions (such as `back()`, `push()`) are mock functions that can be manipulated and asserted on using [regular mock APIs](https://vitest.dev/api/mock.html).
## Next.js Head
diff --git a/docs/snippets/react/nextjs-navigation-push-override-in-preview.js.mdx b/docs/snippets/react/nextjs-navigation-push-override-in-preview.js.mdx
deleted file mode 100644
index 03e44ad039a7..000000000000
--- a/docs/snippets/react/nextjs-navigation-push-override-in-preview.js.mdx
+++ /dev/null
@@ -1,21 +0,0 @@
-```js
-// .storybook/preview.js
-
-export default {
- // ...
- parameters: {
- // ...
- nextjs: {
- navigation: {
- push(...args) {
- // Custom logic can go here
- // This logs to the Actions panel
- action('nextNavigation.push')(...args);
- // Return whatever you want here
- return Promise.resolve(true);
- },
- },
- },
- },
-};
-```
diff --git a/docs/snippets/react/nextjs-navigation-push-override-in-preview.ts.mdx b/docs/snippets/react/nextjs-navigation-push-override-in-preview.ts.mdx
deleted file mode 100644
index 9534d5dd227f..000000000000
--- a/docs/snippets/react/nextjs-navigation-push-override-in-preview.ts.mdx
+++ /dev/null
@@ -1,24 +0,0 @@
-```ts
-// .storybook/preview.ts
-import { Preview } from '@storybook/react';
-
-const preview: Preview = {
- // ...
- parameters: {
- // ...
- nextjs: {
- navigation: {
- push(...args) {
- // Custom logic can go here
- // This logs to the Actions panel
- action('nextNavigation.push')(...args);
- // Return whatever you want here
- return Promise.resolve(true);
- },
- },
- },
- },
-};
-
-export default preview;
-```
diff --git a/docs/snippets/react/nextjs-router-push-override-in-preview.js.mdx b/docs/snippets/react/nextjs-router-push-override-in-preview.js.mdx
deleted file mode 100644
index b5e94878eee6..000000000000
--- a/docs/snippets/react/nextjs-router-push-override-in-preview.js.mdx
+++ /dev/null
@@ -1,21 +0,0 @@
-```js
-// .storybook/preview.js
-
-export default {
- // ...
- parameters: {
- // ...
- nextjs: {
- router: {
- push(...args) {
- // Custom logic can go here
- // This logs to the Actions panel
- action('nextRouter.push')(...args);
- // Return whatever you want here
- return Promise.resolve(true);
- },
- },
- },
- },
-};
-```
diff --git a/docs/snippets/react/nextjs-router-push-override-in-preview.ts.mdx b/docs/snippets/react/nextjs-router-push-override-in-preview.ts.mdx
deleted file mode 100644
index a1ae3c70fe6d..000000000000
--- a/docs/snippets/react/nextjs-router-push-override-in-preview.ts.mdx
+++ /dev/null
@@ -1,24 +0,0 @@
-```ts
-// .storybook/preview.ts
-import { Preview } from '@storybook/react';
-
-const preview: Preview = {
- // ...
- parameters: {
- // ...
- nextjs: {
- router: {
- push(...args) {
- // Custom logic can go here
- // This logs to the Actions panel
- action('nextRouter.push')(...args);
- // Return whatever you want here
- return Promise.resolve(true);
- },
- },
- },
- },
-};
-
-export default preview;
-```
From eef32904a4308dd60cecf09cef818a96cf1df514 Mon Sep 17 00:00:00 2001
From: Jeppe Reinhold
Date: Thu, 18 Apr 2024 13:24:53 +0200
Subject: [PATCH 029/117] add reasons for aliases in nextjs+jest
---
docs/get-started/nextjs.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/get-started/nextjs.md b/docs/get-started/nextjs.md
index 4a9d4cca092a..cbc883408964 100644
--- a/docs/get-started/nextjs.md
+++ b/docs/get-started/nextjs.md
@@ -806,7 +806,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.
+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 - as Next.js does - Storybook sets up a set of 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
From f88bea51699bddc6928db97b0a19c2cd5955be6e Mon Sep 17 00:00:00 2001
From: Kyle Gach
Date: Thu, 18 Apr 2024 20:36:02 -0600
Subject: [PATCH 030/117] Add back nextjs router/navigation override
instructions
Other improvements:
- Consistency improvements in snippets throughout
- Prose tweaks
---
docs/api/parameters.md | 2 +-
docs/api/portable-stories-jest.md | 3 +-
docs/get-started/nextjs.md | 217 +++++++++++++++++-----
docs/writing-stories/mocking-modules.md | 91 +++++----
docs/writing-stories/mocking-providers.md | 4 +-
docs/writing-tests/interaction-testing.md | 6 +-
6 files changed, 219 insertions(+), 104 deletions(-)
diff --git a/docs/api/parameters.md b/docs/api/parameters.md
index 0e37ebdf06e6..be7b02cc4b83 100644
--- a/docs/api/parameters.md
+++ b/docs/api/parameters.md
@@ -186,7 +186,7 @@ Default: `false`
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 rendering or playing.
-Unhandled errors might cause false positive assertions, they can be ignored by setting this parameter to `true`.
+Unhandled errors might cause false positive assertions. They can be ignored by setting this parameter to `true`.
---
diff --git a/docs/api/portable-stories-jest.md b/docs/api/portable-stories-jest.md
index 4c4141a53922..cdc92d922e26 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 be able to mock and assert on them.
diff --git a/docs/get-started/nextjs.md b/docs/get-started/nextjs.md
index cbc883408964..3c41ed990db7 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() {
@@ -315,7 +315,33 @@ const defaultRouter = {
};
```
-All the functions (such as `back()`, `push()`) are mock functions that can be manipulated and asserted on using [regular mock APIs](https://vitest.dev/api/mock.html).
+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 = {
+ paramters: {
+ nextjs: {
+ // 👇 Override the default router properties
+ router: {
+ basePath: '/app/',
+ },
+ },
+ },
+ async beforeEach() {
+ // 👇 Manipulate the default router method mocks
+ getRouter().push.mockImplementation(() => {
+ /* ... */
+ });
+ },
+};
+```
## Next.js navigation
@@ -450,7 +476,33 @@ const defaultNavigationContext = {
};
```
-All the functions (such as `back()`, `push()`) are mock functions that can be manipulated and asserted on using [regular mock APIs](https://vitest.dev/api/mock.html).
+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 = {
+ paramters: {
+ nextjs: {
+ // 👇 Override the default navigation properties
+ navigation: {
+ pathname: '/app/',
+ },
+ },
+ },
+ async beforeEach() {
+ // 👇 Manipulate the default navigation method mocks
+ getRouter().push.mockImplementation(() => {
+ /* ... */
+ });
+ },
+};
+```
## Next.js Head
@@ -566,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!
@@ -603,7 +655,7 @@ Absolute imports **cannot** be mocked in stories/tests. See the [Mocking modules
[Module aliases](https://nextjs.org/docs/app/building-your-application/configuring/absolute-imports-and-module-aliases#module-aliases) are also supported.
```jsx
-// index.js
+// index.jsx
// All good!
import Button from '@/components/button';
// Also good!
@@ -643,7 +695,7 @@ Because subpath imports take the place of module aliases, you can remove the pat
Which can then be used like this:
```jsx
-// index.js
+// index.jsx
import Button from '#components/button';
import styles from '#styles/HomePage.module.css';
@@ -674,21 +726,81 @@ This framework provides mocks for many of Next.js' internal modules:
How you mock other modules in Storybook depends on how you import the module into your component.
-The first step, with either approach, is to [create a mock file](../writing-stories/mocking-modules.md#mock-files).
+The first step, with either approach, is to [create a mock file](../writing-stories/mocking-modules.md#mock-files). Here's an example of a mock file for a module named `session`:
+
+
-TK: More here?
+```ts
+// lib/session.mock.ts
+import { fn } from '@storybook/test';
+import * as actual from './session';
+
+export * from './session';
+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.
+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:
-TK: Add example of mocking modules with subpath imports
+
+
+```json
+// package.json
+{
+ "imports": {
+ "#api": {
+ "storybook": "./api.mock.ts",
+ "default": "./api.ts"
+ },
+ "#app/actions": {
+ "storybook": "./app/actions.mock.ts",
+ "default": "./app/actions.ts"
+ },
+ "#lib/session": {
+ "storybook": "./lib/session.mock.ts",
+ "default": "./lib/session.ts"
+ },
+ "#lib/db": {
+ "storybook": "./lib/db.mock.ts",
+ "default": "./lib/db.ts"
+ },
+ "#*": ["./*", "./*.ts", "./*.tsx"]
+ }
+}
+```
+
+
+
+Each subpath must begin with `#`, to differentiate it from a regular module path. The `#*` entry is a catch-all that maps all subpaths to the root directory.
+
+
#### With module aliases
If you're using [module aliases](#module-aliases), you can add a Webpack alias to your Storybook configuration to point to the mock file.
-TK: Add example of mocking modules with module aliases
+
+
+```ts
+// .storybook/main.ts
+webpackFinal: async (config) => {
+ if (config.resolve) {
+ config.resolve.alias = {
+ ...config.resolve.alias,
+ // 👇 External module
+ 'lodash': require.resolve('./lodash.mock'),
+ // 👇 Internal modules
+ '@/api$': path.resolve(__dirname, "./api.mock.ts"),
+ '@/app/actions$': path.resolve(__dirname, "./app/actions.mock.ts"),
+ '@/lib/session$': path.resolve(__dirname, "./lib/session.mock.ts"),
+ '@/lib/db$': path.resolve(__dirname, "./lib/db.mock.ts"),
+ }
+ }
+
+ return config;
+},
+```
## Runtime config
@@ -806,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 - as Next.js does - Storybook sets up a set of 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 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.
## Notes for Yarn v2 and v3 users
@@ -921,9 +1033,10 @@ Type: `{ getPackageAliases: ({ useESM?: boolean }) => void }`
`getPackageAliases` is a helper to generate the aliases needed to set up [portable stories](#portable-stories).
```ts
+// jest.config.ts
import type { Config } from 'jest';
import nextJest from 'next/jest.js';
-// 👇 import the utility function
+// 👇 Import the utility function
import { getPackageAliases } from '@storybook/nextjs/export-mocks';
const createJestConfig = nextJest({
@@ -931,18 +1044,14 @@ const createJestConfig = nextJest({
dir: './',
});
-// Add any custom config to be passed to Jest
const config: Config = {
- coverageProvider: 'v8',
testEnvironment: 'jsdom',
- // Add more setup options before each test is run
- // setupFilesAfterEnv: ['/jest.setup.ts'],
+ // ... rest of Jest config
moduleNameMapper: {
- ...getPackageAliases(), // 👈 add the utility as a moduleNameMapper
+ ...getPackageAliases(), // 👈 Add the utility as mapped module names
},
};
-// createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async
export default createJestConfig(config);
```
@@ -950,13 +1059,16 @@ 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.
+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).
+
+
```ts
+// MyForm.stories.ts
import { expect, userEvent, within } from '@storybook/test';
import { Meta, StoryObj } from '@storybook/react';
-// 👇 import from the Storybook package to get correct mock types
-import { revalidatePath } from '@storybook/nextjs/cache';
+// 👇 Must use this import path to have mocks typed correctly
+import { revalidatePath } from '@storybook/nextjs/cache.mock';
import MyForm from './my-form';
const meta = {
@@ -968,12 +1080,12 @@ export default meta;
type Story = StoryObj;
export const Submitted: Story = {
- play: async ({ canvasElement, step }) => {
+ async play({ canvasElement }) {
const canvas = within(canvasElement);
const submitButton = canvas.getByRole('button', { name: /submit/i });
await userEvent.click(saveButton);
- // 👇 use any mock assertions on the function
+ // 👇 Use any mock assertions on the function
await expect(revalidatePath).toHaveBeenCalledWith('/');
},
};
@@ -995,11 +1107,14 @@ For cookies, you can use the existing API to write them, eg. `cookies().set('fir
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`.
+
+
```ts
+// MyForm.stories.ts
import { expect, fireEvent, userEvent, within } from '@storybook/test';
import { Meta, StoryObj } from '@storybook/react';
-// 👇 import from the Storybook package to get correct mock types
-import { cookies, headers } from '@storybook/nextjs/headers';
+// 👇 Must use this import path to have mocks typed correctly
+import { cookies, headers } from '@storybook/nextjs/headers.mock';
import MyForm from './my-form';
const meta = {
@@ -1012,16 +1127,16 @@ type Story = StoryObj;
export const LoggedInEurope: Story = {
async beforeEach() {
- // 👇 set mock cookies and headers ahead of rendering
+ // 👇 Set mock cookies and headers ahead of rendering
cookies().set('username', 'Sol');
headers().set('timezone', 'Central European Summer Time');
},
- play: () => {
- // 👇 assert that your component called the mocks
- expect(cookies().get).toHaveBeenCalledOnce();
- expect(cookies().get).toHaveBeenCalledWith('username');
- expect(headers().get).toHaveBeenCalledOnce();
- expect(cookies().get).toHaveBeenCalledWith('timezone');
+ async play() {
+ // 👇 Assert that your component called the mocks
+ await expect(cookies().get).toHaveBeenCalledOnce();
+ await expect(cookies().get).toHaveBeenCalledWith('username');
+ await expect(headers().get).toHaveBeenCalledOnce();
+ await expect(cookies().get).toHaveBeenCalledWith('timezone');
},
};
```
@@ -1030,14 +1145,16 @@ export const LoggedInEurope: Story = {
Type: `typeof import('next/navigation') & getRouter: () => ReturnType`
-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.
+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).
+
+
```ts
+// MyForm.stories.ts
import { expect, fireEvent, userEvent, within } from '@storybook/test';
import { Meta, StoryObj } from '@storybook/react';
-// 👇 import from the Storybook package to get correct mock types
-import { redirect, getRouter } from '@storybook/nextjs/navigation';
+// 👇 Must use this import path to have mocks typed correctly
+import { redirect, getRouter } from '@storybook/nextjs/navigation.mock';
import MyForm from './my-form';
const meta = {
@@ -1055,19 +1172,19 @@ export default meta;
type Story = StoryObj;
export const Unauthenticated: Story = {
- play: () => {
- // 👇 assert that your component called redirect()
- expect(redirect).toHaveBeenCalledWith('/login', 'replace');
+ async play() => {
+ // 👇 Assert that your component called redirect()
+ await expect(redirect).toHaveBeenCalledWith('/login', 'replace');
},
};
export const GoBack: Story = {
- play: async ({ canvasElement }) => {
+ async play({ canvasElement }) {
const canvas = within(canvasElement);
const backBtn = await canvas.findByText('Go back');
await userEvent.click(backBtn);
- // 👇 assert that your component called back()
+ // 👇 Assert that your component called back()
await expect(getRouter().back).toHaveBeenCalled();
},
};
@@ -1077,14 +1194,16 @@ export const GoBack: Story = {
Type: `typeof import('next/router') & getRouter: () => ReturnType`
-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.
+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).
+
+
```ts
+// MyForm.stories.ts
import { expect, fireEvent, userEvent, within } from '@storybook/test';
import { Meta, StoryObj } from '@storybook/react';
-// 👇 import from the Storybook package to get correct mock types
-import { getRouter } from '@storybook/nextjs/router';
+// 👇 Must use this import path to have mocks typed correctly
+import { getRouter } from '@storybook/nextjs/router.mock';
import MyForm from './my-form';
const meta = {
@@ -1102,12 +1221,12 @@ export default meta;
type Story = StoryObj;
export const GoBack: Story = {
- play: async ({ canvasElement }) => {
+ async play({ canvasElement }) {
const canvas = within(canvasElement);
const backBtn = await canvas.findByText('Go back');
await userEvent.click(backBtn);
- // 👇 assert that your component called back()
+ // 👇 Assert that your component called back()
await expect(getRouter().back).toHaveBeenCalled();
},
};
diff --git a/docs/writing-stories/mocking-modules.md b/docs/writing-stories/mocking-modules.md
index a7f2ea886023..43db0d9b1f67 100644
--- a/docs/writing-stories/mocking-modules.md
+++ b/docs/writing-stories/mocking-modules.md
@@ -20,8 +20,10 @@ To mock a module, create a file with the same name and in the same directory as
Here's an example of a mock file for a module named `session`:
-```js
-// session.mock.js
+
+
+```ts
+// lib/session.mock.ts
import { fn } from '@storybook/test';
import * as actual from './session';
@@ -33,8 +35,6 @@ export const getUserFromSession = fn(actual.getUserFromSession);
You can't directly mock an external module like `uuid` or `node:fs`. Instead, you must wrap the module in you own module, which you can then mock like any other internal module. In this example, we wrap `uuid`:
-
-
```ts
// lib/uuid.ts
import { v4 } from 'uuid';
@@ -59,6 +59,8 @@ The recommended method for mocking modules is to use [subpath imports](https://n
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 four internal modules:
+
+
```json
// package.json
{
@@ -93,7 +95,14 @@ Each subpath must begin with `#`, to differentiate it from a regular module path
You can then update your component file to use the subpath import:
```ts
-TK: Component snippet
+// AuthButton.ts
+import { getUserFromSession } from '#lib/session';
+
+export const AuthButton = (props) => {
+ const user = getUserFromSession();
+
+ // ...
+};
```
### Conditional imports
@@ -106,62 +115,48 @@ The Storybook environment will match the conditions `storybook` and `test`, so y
If your project is unable to use [subpath imports](#subpath-imports), you can configure your Storybook builder to alias the module to the mock file. This will instruct the builder to replace the module with the mock file when bundling your Storybook stories.
-```js
-// .storybook/main.ts
+
+```ts
+// .storybook/main.ts
viteFinal: async (config) => {
- return {
- ...config,
- resolve: {
- ...config.resolve,
- alias: {
- ...config.resolve?.alias,
- 'lodash': require.resolve('./lodash.mock'),
- '@/api/todo': path.resolve(__dirname, './api/todo.mock.ts')
- }
+ if (config.resolve) {
+ config.resolve.alias = {
+ ...config.resolve?.alias,
+ // 👇 External module
+ 'lodash': require.resolve('./lodash.mock'),
+ // 👇 Internal modules
+ '@/api': path.resolve(__dirname, "./api.mock.ts"),
+ '@/app/actions': path.resolve(__dirname, "./app/actions.mock.ts"),
+ '@/lib/session': path.resolve(__dirname, "./lib/session.mock.ts"),
+ '@/lib/db': path.resolve(__dirname, "./lib/db.mock.ts"),
}
}
+
+ return config;
},
```
-```js
+```ts
// .storybook/main.ts
-
webpackFinal: async (config) => {
if (config.resolve) {
config.resolve.alias = {
...config.resolve.alias,
- 'next/headers': require.resolve('./next-headers'),
- '@/api/todo$': path.resolve(__dirname, './api/todo.mock.ts'),
+ // 👇 External module
+ 'lodash': require.resolve('./lodash.mock'),
+ // 👇 Internal modules
+ '@/api$': path.resolve(__dirname, "./api.mock.ts"),
+ '@/app/actions$': path.resolve(__dirname, "./app/actions.mock.ts"),
+ '@/lib/session$': path.resolve(__dirname, "./lib/session.mock.ts"),
+ '@/lib/db$': path.resolve(__dirname, "./lib/db.mock.ts"),
}
}
- return config
+ return config;
},
```
-
-
-
-
-
-
-
-
-
-
-
-
-
## Using mocked modules in stories
When you use the `fn` utility to mock a module, you create full [Vitest mock functions](https://vitest.dev/api/mock.html) which have many useful methods. For example, you can use the [`mockReturnValue`](https://vitest.dev/api/mock.html#mockreturnvalue) method to set a return value for the mocked function or [`mockImplementation`](https://vitest.dev/api/mock.html#mockimplementation) to define a custom implementation.
@@ -170,8 +165,8 @@ Here, we define `beforeEach` on a story (which will run before the story is rend
-```js
-// Page.stories.tsx
+```ts
+// Page.stories.ts|tsx
import { Meta, StoryObj } from '@storybook/react';
import { fn } from '@storybook/test';
@@ -208,7 +203,7 @@ For example, this story checks that the `saveNote` function was called when the
```ts
-// NoteUI.stories.tsx
+// NoteUI.stories.ts|tsx
import { Meta, StoryObj } from '@storybook/react';
import { expect, userEvent, within } from '@storybook/test';
@@ -259,8 +254,8 @@ Here's an example of using the [`mockdate`](https://github.com/boblauer/MockDate
-```js
-// Page.stories.tsx
+```ts
+// Page.stories.ts|tsx
import { Meta, StoryObj } from '@storybook/react';
import MockDate from 'mockdate';
diff --git a/docs/writing-stories/mocking-providers.md b/docs/writing-stories/mocking-providers.md
index 1b3872371cb8..adeec3072f67 100644
--- a/docs/writing-stories/mocking-providers.md
+++ b/docs/writing-stories/mocking-providers.md
@@ -38,7 +38,7 @@ For example, we can adjust the decorator from above to read from `parameters.the
```ts
-// .storybook/preview.js
+// .storybook/preview.ts
import React from 'react';
import { Preview } from '@storybook/react';
import { ThemeProvider } from 'styled-components';
@@ -66,7 +66,7 @@ Now, you can define a `theme` parameter in your stories to adjust the theme prov
```ts
-// Button.stories.tsx
+// Button.stories.ts|tsx
import { Meta, StoryObj } from '@storybook/react';
import { Button } from './Button';
diff --git a/docs/writing-tests/interaction-testing.md b/docs/writing-tests/interaction-testing.md
index 1c9b34509aaf..4a28b9258abf 100644
--- a/docs/writing-tests/interaction-testing.md
+++ b/docs/writing-tests/interaction-testing.md
@@ -106,8 +106,8 @@ Here's an example of using the [`mockdate`](https://github.com/boblauer/MockDate
-```js
-// Page.stories.tsx
+```ts
+// Page.stories.ts|tsx
import { Meta, StoryObj } from '@storybook/react';
import MockDate from 'mockdate';
@@ -208,7 +208,7 @@ You can then import the mocked module (which has all of the helpful methods of a
```ts
-// NoteUI.stories.tsx
+// NoteUI.stories.ts|tsx
import { Meta, StoryObj } from '@storybook/react';
import { expect, userEvent, within } from '@storybook/test';
From 3f7e44ca6e6bc64d20fdd55339b3f93c55360892 Mon Sep 17 00:00:00 2001
From: Kyle Gach
Date: Thu, 18 Apr 2024 20:37:34 -0600
Subject: [PATCH 031/117] Improve MSW snippets
- Use MSW 2.0 API
- Use loader instead of decorator
- Improve snippet filename format
---
...w-addon-configure-handlers-graphql.ts.mdx} | 44 +++++++------
...dx => msw-addon-configure-handlers.ts.mdx} | 27 +++++---
... msw-addon-configure-handlers-http.js.mdx} | 29 +++++----
...-addon-configure-handlers-http.ts-4-9.mdx} | 27 +++++---
... msw-addon-configure-handlers-http.ts.mdx} | 27 +++++---
.../common/msw-addon-initialize.js.mdx | 16 +++++
...don.ts.mdx => msw-addon-initialize.ts.mdx} | 15 ++---
...=> msw-generate-service-worker.npx.js.mdx} | 0
...> msw-generate-service-worker.pnpm.js.mdx} | 0
docs/snippets/common/msw-install.npm.js.mdx | 3 +
docs/snippets/common/msw-install.pnpm.js.mdx | 3 +
docs/snippets/common/msw-install.yarn.js.mdx | 3 +
...torybook-preview-register-msw-addon.js.mdx | 24 --------
...w-addon-configure-handlers-graphql.js.mdx} | 44 +++++++------
...don-configure-handlers-graphql.ts-4-9.mdx} | 50 ++++++++-------
...w-addon-configure-handlers-graphql.ts.mdx} | 52 ++++++++--------
...w-addon-configure-handlers-graphql.js.mdx} | 53 +++++++++-------
...don-configure-handlers-graphql.ts-4-9.mdx} | 47 ++++++++------
...w-addon-configure-handlers-graphql.ts.mdx} | 47 ++++++++------
...w-addon-configure-handlers-graphql.js.mdx} | 61 ++++++++++---------
...don-configure-handlers-graphql.ts-4-9.mdx} | 61 ++++++++++---------
...w-addon-configure-handlers-graphql.ts.mdx} | 61 ++++++++++---------
... msw-addon-configure-handlers-http.js.mdx} | 27 +++++---
... msw-addon-configure-handlers-http.ts.mdx} | 27 +++++---
.../mocking-network-requests.md | 56 +++++++++--------
25 files changed, 453 insertions(+), 351 deletions(-)
rename docs/snippets/angular/{documentscreen-story-msw-graphql-query.ts.mdx => msw-addon-configure-handlers-graphql.ts.mdx} (73%)
rename docs/snippets/angular/{documentscreen-story-msw-rest-request.ts.mdx => msw-addon-configure-handlers.ts.mdx} (71%)
rename docs/snippets/common/{documentscreen-story-msw-rest-request.js.mdx => msw-addon-configure-handlers-http.js.mdx} (64%)
rename docs/snippets/common/{documentscreen-story-msw-rest-request.ts-4-9.mdx => msw-addon-configure-handlers-http.ts-4-9.mdx} (72%)
rename docs/snippets/common/{documentscreen-story-msw-rest-request.ts.mdx => msw-addon-configure-handlers-http.ts.mdx} (72%)
create mode 100644 docs/snippets/common/msw-addon-initialize.js.mdx
rename docs/snippets/common/{storybook-preview-register-msw-addon.ts.mdx => msw-addon-initialize.ts.mdx} (59%)
rename docs/snippets/common/{storybook-msw-generate.msw.js.mdx => msw-generate-service-worker.npx.js.mdx} (100%)
rename docs/snippets/common/{storybook-msw-generate.msw-pnpm.js.mdx => msw-generate-service-worker.pnpm.js.mdx} (100%)
create mode 100644 docs/snippets/common/msw-install.npm.js.mdx
create mode 100644 docs/snippets/common/msw-install.pnpm.js.mdx
create mode 100644 docs/snippets/common/msw-install.yarn.js.mdx
delete mode 100644 docs/snippets/common/storybook-preview-register-msw-addon.js.mdx
rename docs/snippets/react/{documentscreen-story-msw-graphql-query.js.mdx => msw-addon-configure-handlers-graphql.js.mdx} (70%)
rename docs/snippets/react/{documentscreen-story-msw-graphql-query.ts-4-9.mdx => msw-addon-configure-handlers-graphql.ts-4-9.mdx} (73%)
rename docs/snippets/react/{documentscreen-story-msw-graphql-query.ts.mdx => msw-addon-configure-handlers-graphql.ts.mdx} (70%)
rename docs/snippets/svelte/{documentscreen-story-msw-graphql-query.js.mdx => msw-addon-configure-handlers-graphql.js.mdx} (64%)
rename docs/snippets/svelte/{documentscreen-story-msw-graphql-query.ts-4-9.mdx => msw-addon-configure-handlers-graphql.ts-4-9.mdx} (64%)
rename docs/snippets/svelte/{documentscreen-story-msw-graphql-query.ts.mdx => msw-addon-configure-handlers-graphql.ts.mdx} (64%)
rename docs/snippets/vue/{documentscreen-story-msw-graphql-query.js.mdx => msw-addon-configure-handlers-graphql.js.mdx} (58%)
rename docs/snippets/vue/{documentscreen-story-msw-graphql-query.ts-4-9.mdx => msw-addon-configure-handlers-graphql.ts-4-9.mdx} (62%)
rename docs/snippets/vue/{documentscreen-story-msw-graphql-query.ts.mdx => msw-addon-configure-handlers-graphql.ts.mdx} (62%)
rename docs/snippets/web-components/{documentscreen-story-msw-rest-request.js.mdx => msw-addon-configure-handlers-http.js.mdx} (66%)
rename docs/snippets/web-components/{documentscreen-story-msw-rest-request.ts.mdx => msw-addon-configure-handlers-http.ts.mdx} (69%)
diff --git a/docs/snippets/angular/documentscreen-story-msw-graphql-query.ts.mdx b/docs/snippets/angular/msw-addon-configure-handlers-graphql.ts.mdx
similarity index 73%
rename from docs/snippets/angular/documentscreen-story-msw-graphql-query.ts.mdx
rename to docs/snippets/angular/msw-addon-configure-handlers-graphql.ts.mdx
index 94e0e7ddc942..b70e270726ac 100644
--- a/docs/snippets/angular/documentscreen-story-msw-graphql-query.ts.mdx
+++ b/docs/snippets/angular/msw-addon-configure-handlers-graphql.ts.mdx
@@ -58,28 +58,38 @@ type Story = StoryObj;
export const MockedSuccess: Story = {
parameters: {
- msw: [
- graphql.query('AllInfoQuery', (req, res, ctx) => {
- return res(ctx.data(TestData));
- }),
- ],
+ msw: {
+ handlers: [
+ graphql.query('AllInfoQuery', () => {
+ return new HttpResponse.json({
+ data: {
+ allFilms: {
+ films,
+ },
+ }
+ });
+ }),
+ ],
+ },
},
};
export const MockedError: Story = {
parameters: {
- msw: [
- graphql.query('AllInfoQuery', (req, res, ctx) => {
- return res(
- ctx.delay(800),
- ctx.errors([
- {
- message: 'Access denied',
- },
- ]),
- );
- }),
- ],
+ msw: {
+ handlers: [
+ graphql.query('AllInfoQuery', async () => {
+ delay(800);
+ return new HttpResponse.json({
+ errors: [
+ {
+ message: 'Access denied',
+ },
+ ],
+ });
+ }),
+ ],
+ },
},
};
```
diff --git a/docs/snippets/angular/documentscreen-story-msw-rest-request.ts.mdx b/docs/snippets/angular/msw-addon-configure-handlers.ts.mdx
similarity index 71%
rename from docs/snippets/angular/documentscreen-story-msw-rest-request.ts.mdx
rename to docs/snippets/angular/msw-addon-configure-handlers.ts.mdx
index 271ad00de38f..202051838b1d 100644
--- a/docs/snippets/angular/documentscreen-story-msw-rest-request.ts.mdx
+++ b/docs/snippets/angular/msw-addon-configure-handlers.ts.mdx
@@ -41,21 +41,28 @@ const TestData = {
export const MockedSuccess: Story = {
parameters: {
- msw: [
- rest.get('https://your-restful-endpoint/', (_req, res, ctx) => {
- return res(ctx.json(TestData));
- }),
- ],
+ msw: {
+ handlers: [
+ http.get('https://your-restful-endpoint/', () => {
+ return new HttpResponse.json(TestData);
+ }),
+ ],
+ },
},
};
export const MockedError: Story = {
parameters: {
- msw: [
- rest.get('https://your-restful-endpoint', (_req, res, ctx) => {
- return res(ctx.delay(800), ctx.status(403));
- }),
- ],
+ msw: {
+ handlers: [
+ http.get('https://your-restful-endpoint', async () => {
+ await delay(800);
+ return new HttpResponse(null, {
+ status: 403,
+ });
+ }),
+ ],
+ },
},
};
```
diff --git a/docs/snippets/common/documentscreen-story-msw-rest-request.js.mdx b/docs/snippets/common/msw-addon-configure-handlers-http.js.mdx
similarity index 64%
rename from docs/snippets/common/documentscreen-story-msw-rest-request.js.mdx
rename to docs/snippets/common/msw-addon-configure-handlers-http.js.mdx
index 7eb1e65ba90d..99509f949f4c 100644
--- a/docs/snippets/common/documentscreen-story-msw-rest-request.js.mdx
+++ b/docs/snippets/common/msw-addon-configure-handlers-http.js.mdx
@@ -1,7 +1,7 @@
```js
// YourPage.stories.js|jsx
-import { rest } from 'msw';
+import { http, HttpResponse, delay } from 'msw';
import { DocumentScreen } from './YourPage';
@@ -36,21 +36,28 @@ const TestData = {
export const MockedSuccess = {
parameters: {
- msw: [
- rest.get('https://your-restful-endpoint/', (_req, res, ctx) => {
- return res(ctx.json(TestData));
- }),
- ],
+ msw: {
+ handlers: [
+ http.get('https://your-restful-endpoint/', () => {
+ return new HttpResponse.json(TestData);
+ }),
+ ],
+ },
},
};
export const MockedError = {
parameters: {
- msw: [
- rest.get('https://your-restful-endpoint', (_req, res, ctx) => {
- return res(ctx.delay(800), ctx.status(403));
- }),
- ],
+ msw: {
+ handlers: [
+ http.get('https://your-restful-endpoint', async () => {
+ await delay(800);
+ return new HttpResponse(null, {
+ status: 403,
+ });
+ }),
+ ],
+ },
},
};
```
diff --git a/docs/snippets/common/documentscreen-story-msw-rest-request.ts-4-9.mdx b/docs/snippets/common/msw-addon-configure-handlers-http.ts-4-9.mdx
similarity index 72%
rename from docs/snippets/common/documentscreen-story-msw-rest-request.ts-4-9.mdx
rename to docs/snippets/common/msw-addon-configure-handlers-http.ts-4-9.mdx
index 50dde4300363..717809b8b18e 100644
--- a/docs/snippets/common/documentscreen-story-msw-rest-request.ts-4-9.mdx
+++ b/docs/snippets/common/msw-addon-configure-handlers-http.ts-4-9.mdx
@@ -42,21 +42,28 @@ const TestData = {
export const MockedSuccess: Story = {
parameters: {
- msw: [
- rest.get('https://your-restful-endpoint/', (_req, res, ctx) => {
- return res(ctx.json(TestData));
- }),
- ],
+ msw: {
+ handlers: [
+ http.get('https://your-restful-endpoint/', () => {
+ return new HttpResponse.json(TestData);
+ }),
+ ],
+ },
},
};
export const MockedError: Story = {
parameters: {
- msw: [
- rest.get('https://your-restful-endpoint', (_req, res, ctx) => {
- return res(ctx.delay(800), ctx.status(403));
- }),
- ],
+ msw: {
+ handlers: [
+ http.get('https://your-restful-endpoint', async () => {
+ await delay(800);
+ return new HttpResponse(null, {
+ status: 403,
+ });
+ }),
+ ],
+ },
},
};
```
diff --git a/docs/snippets/common/documentscreen-story-msw-rest-request.ts.mdx b/docs/snippets/common/msw-addon-configure-handlers-http.ts.mdx
similarity index 72%
rename from docs/snippets/common/documentscreen-story-msw-rest-request.ts.mdx
rename to docs/snippets/common/msw-addon-configure-handlers-http.ts.mdx
index a4ae3d1723f7..ab1f4ab92256 100644
--- a/docs/snippets/common/documentscreen-story-msw-rest-request.ts.mdx
+++ b/docs/snippets/common/msw-addon-configure-handlers-http.ts.mdx
@@ -42,21 +42,28 @@ const TestData = {
export const MockedSuccess: Story = {
parameters: {
- msw: [
- rest.get('https://your-restful-endpoint/', (_req, res, ctx) => {
- return res(ctx.json(TestData));
- }),
- ],
+ msw: {
+ handlers: [
+ http.get('https://your-restful-endpoint/', () => {
+ return new HttpResponse.json(TestData);
+ }),
+ ],
+ },
},
};
export const MockedError: Story = {
parameters: {
- msw: [
- rest.get('https://your-restful-endpoint', (_req, res, ctx) => {
- return res(ctx.delay(800), ctx.status(403));
- }),
- ],
+ msw: {
+ handlers: [
+ http.get('https://your-restful-endpoint', async () => {
+ await delay(800);
+ return new HttpResponse(null, {
+ status: 403,
+ });
+ }),
+ ],
+ },
},
};
```
diff --git a/docs/snippets/common/msw-addon-initialize.js.mdx b/docs/snippets/common/msw-addon-initialize.js.mdx
new file mode 100644
index 000000000000..a7bf9aab66d7
--- /dev/null
+++ b/docs/snippets/common/msw-addon-initialize.js.mdx
@@ -0,0 +1,16 @@
+```js
+// .storybook/preview.js
+import { initialize, mswLoader } from 'msw-storybook-addon';
+
+/*
+ * Initializes MSW
+ * See https://github.com/mswjs/msw-storybook-addon#configuring-msw
+ * to learn how to customize it
+ */
+initialize();
+
+export default {
+ // ... rest of preview configuration
+ loaders: [mswLoader], // 👈 Add the MSW loader to all stories
+};
+```
diff --git a/docs/snippets/common/storybook-preview-register-msw-addon.ts.mdx b/docs/snippets/common/msw-addon-initialize.ts.mdx
similarity index 59%
rename from docs/snippets/common/storybook-preview-register-msw-addon.ts.mdx
rename to docs/snippets/common/msw-addon-initialize.ts.mdx
index d7c3ba6e0991..6dcb8080ebc7 100644
--- a/docs/snippets/common/storybook-preview-register-msw-addon.ts.mdx
+++ b/docs/snippets/common/msw-addon-initialize.ts.mdx
@@ -1,10 +1,10 @@
```ts
// .storybook/preview.ts
-// Replace your-framework with the framework you are using (e.g., react, vue3)
+// Replace your-framework with the framework you are using (e.g., nextjs, vue3-vite)
import { Preview } from '@storybook/your-framework';
-import { initialize, mswDecorator } from 'msw-storybook-addon';
+import { initialize, mswLoader } from 'msw-storybook-addon';
/*
* Initializes MSW
@@ -14,15 +14,8 @@ import { initialize, mswDecorator } from 'msw-storybook-addon';
initialize();
const preview: Preview = {
- decorators: [mswDecorator],
- parameters: {
- controls: {
- matchers: {
- color: /(background|color)$/i,
- date: /Date$/,
- },
- },
- },
+ // ... rest of preview configuration
+ loaders: [mswLoader], // 👈 Add the MSW loader to all stories
};
export default preview;
diff --git a/docs/snippets/common/storybook-msw-generate.msw.js.mdx b/docs/snippets/common/msw-generate-service-worker.npx.js.mdx
similarity index 100%
rename from docs/snippets/common/storybook-msw-generate.msw.js.mdx
rename to docs/snippets/common/msw-generate-service-worker.npx.js.mdx
diff --git a/docs/snippets/common/storybook-msw-generate.msw-pnpm.js.mdx b/docs/snippets/common/msw-generate-service-worker.pnpm.js.mdx
similarity index 100%
rename from docs/snippets/common/storybook-msw-generate.msw-pnpm.js.mdx
rename to docs/snippets/common/msw-generate-service-worker.pnpm.js.mdx
diff --git a/docs/snippets/common/msw-install.npm.js.mdx b/docs/snippets/common/msw-install.npm.js.mdx
new file mode 100644
index 000000000000..3bc959fdf516
--- /dev/null
+++ b/docs/snippets/common/msw-install.npm.js.mdx
@@ -0,0 +1,3 @@
+```sh
+npm install msw --save-dev
+```
\ No newline at end of file
diff --git a/docs/snippets/common/msw-install.pnpm.js.mdx b/docs/snippets/common/msw-install.pnpm.js.mdx
new file mode 100644
index 000000000000..88ed6296508e
--- /dev/null
+++ b/docs/snippets/common/msw-install.pnpm.js.mdx
@@ -0,0 +1,3 @@
+```sh
+pnpm add msw --save-dev
+```
\ No newline at end of file
diff --git a/docs/snippets/common/msw-install.yarn.js.mdx b/docs/snippets/common/msw-install.yarn.js.mdx
new file mode 100644
index 000000000000..38ef49b23d8f
--- /dev/null
+++ b/docs/snippets/common/msw-install.yarn.js.mdx
@@ -0,0 +1,3 @@
+```sh
+yarn add msw --save-dev
+```
\ No newline at end of file
diff --git a/docs/snippets/common/storybook-preview-register-msw-addon.js.mdx b/docs/snippets/common/storybook-preview-register-msw-addon.js.mdx
deleted file mode 100644
index 7071b6f180fe..000000000000
--- a/docs/snippets/common/storybook-preview-register-msw-addon.js.mdx
+++ /dev/null
@@ -1,24 +0,0 @@
-```js
-// .storybook/preview.js
-
-import { initialize, mswDecorator } from 'msw-storybook-addon';
-
-/*
- * Initializes MSW
- * See https://github.com/mswjs/msw-storybook-addon#configuring-msw
- * to learn how to customize it
- */
-initialize();
-
-export default {
- decorators: [mswDecorator],
- parameters: {
- controls: {
- matchers: {
- color: /(background|color)$/i,
- date: /Date$/,
- },
- },
- },
-};
-```
diff --git a/docs/snippets/react/documentscreen-story-msw-graphql-query.js.mdx b/docs/snippets/react/msw-addon-configure-handlers-graphql.js.mdx
similarity index 70%
rename from docs/snippets/react/documentscreen-story-msw-graphql-query.js.mdx
rename to docs/snippets/react/msw-addon-configure-handlers-graphql.js.mdx
index 05a784a42e73..02d3494a8701 100644
--- a/docs/snippets/react/documentscreen-story-msw-graphql-query.js.mdx
+++ b/docs/snippets/react/msw-addon-configure-handlers-graphql.js.mdx
@@ -60,28 +60,38 @@ export default {
export const MockedSuccess = {
parameters: {
- msw: [
- graphql.query('AllInfoQuery', (req, res, ctx) => {
- return res(ctx.data(TestData));
- }),
- ],
+ msw: {
+ handlers: [
+ graphql.query('AllInfoQuery', () => {
+ return new HttpResponse.json({
+ data: {
+ allFilms: {
+ films,
+ },
+ }
+ });
+ }),
+ ],
+ },
},
};
export const MockedError = {
parameters: {
- msw: [
- graphql.query('AllInfoQuery', (req, res, ctx) => {
- return res(
- ctx.delay(800),
- ctx.errors([
- {
- message: 'Access denied',
- },
- ]),
- );
- }),
- ],
+ msw: {
+ handlers: [
+ graphql.query('AllInfoQuery', async () => {
+ delay(800);
+ return new HttpResponse.json({
+ errors: [
+ {
+ message: 'Access denied',
+ },
+ ],
+ });
+ }),
+ ],
+ },
},
};
```
diff --git a/docs/snippets/react/documentscreen-story-msw-graphql-query.ts-4-9.mdx b/docs/snippets/react/msw-addon-configure-handlers-graphql.ts-4-9.mdx
similarity index 73%
rename from docs/snippets/react/documentscreen-story-msw-graphql-query.ts-4-9.mdx
rename to docs/snippets/react/msw-addon-configure-handlers-graphql.ts-4-9.mdx
index d70235f57e74..66fc89ef54b2 100644
--- a/docs/snippets/react/documentscreen-story-msw-graphql-query.ts-4-9.mdx
+++ b/docs/snippets/react/msw-addon-configure-handlers-graphql.ts-4-9.mdx
@@ -64,34 +64,38 @@ type Story = StoryObj;
export const MockedSuccess: Story = {
parameters: {
- msw: [
- graphql.query('AllInfoQuery', (req, res, ctx) => {
- return res(
- ctx.data({
- allFilms: {
- films,
- },
- })
- );
- }),
- ],
+ msw: {
+ handlers: [
+ graphql.query('AllInfoQuery', () => {
+ return new HttpResponse.json({
+ data: {
+ allFilms: {
+ films,
+ },
+ }
+ });
+ }),
+ ],
+ },
},
};
export const MockedError: Story = {
parameters: {
- msw: [
- graphql.query('AllInfoQuery', (req, res, ctx) => {
- return res(
- ctx.delay(800),
- ctx.errors([
- {
- message: 'Access denied',
- },
- ])
- );
- }),
- ],
+ msw: {
+ handlers: [
+ graphql.query('AllInfoQuery', async () => {
+ delay(800);
+ return new HttpResponse.json({
+ errors: [
+ {
+ message: 'Access denied',
+ },
+ ],
+ });
+ }),
+ ],
+ },
},
};
```
diff --git a/docs/snippets/react/documentscreen-story-msw-graphql-query.ts.mdx b/docs/snippets/react/msw-addon-configure-handlers-graphql.ts.mdx
similarity index 70%
rename from docs/snippets/react/documentscreen-story-msw-graphql-query.ts.mdx
rename to docs/snippets/react/msw-addon-configure-handlers-graphql.ts.mdx
index d3422a78bc8e..6e38aad3b85b 100644
--- a/docs/snippets/react/documentscreen-story-msw-graphql-query.ts.mdx
+++ b/docs/snippets/react/msw-addon-configure-handlers-graphql.ts.mdx
@@ -5,7 +5,7 @@ import type { Meta, StoryObj } from '@storybook/react';
import { ApolloClient, ApolloProvider, InMemoryCache } from '@apollo/client';
-import { graphql } from 'msw';
+import { graphql, HttpResponse, delay } from 'msw';
import { DocumentScreen } from './YourPage';
@@ -64,34 +64,38 @@ type Story = StoryObj;
export const MockedSuccess: Story = {
parameters: {
- msw: [
- graphql.query('AllInfoQuery', (req, res, ctx) => {
- return res(
- ctx.data({
- allFilms: {
- films,
- },
- })
- );
- }),
- ],
+ msw: {
+ handlers: [
+ graphql.query('AllInfoQuery', () => {
+ return new HttpResponse.json({
+ data: {
+ allFilms: {
+ films,
+ },
+ }
+ });
+ }),
+ ],
+ },
},
};
export const MockedError: Story = {
parameters: {
- msw: [
- graphql.query('AllInfoQuery', (req, res, ctx) => {
- return res(
- ctx.delay(800),
- ctx.errors([
- {
- message: 'Access denied',
- },
- ])
- );
- }),
- ],
+ msw: {
+ handlers: [
+ graphql.query('AllInfoQuery', async () => {
+ delay(800);
+ return new HttpResponse.json({
+ errors: [
+ {
+ message: 'Access denied',
+ },
+ ],
+ });
+ }),
+ ],
+ },
},
};
```
diff --git a/docs/snippets/svelte/documentscreen-story-msw-graphql-query.js.mdx b/docs/snippets/svelte/msw-addon-configure-handlers-graphql.js.mdx
similarity index 64%
rename from docs/snippets/svelte/documentscreen-story-msw-graphql-query.js.mdx
rename to docs/snippets/svelte/msw-addon-configure-handlers-graphql.js.mdx
index 8b0bc550d896..13856ae98299 100644
--- a/docs/snippets/svelte/documentscreen-story-msw-graphql-query.js.mdx
+++ b/docs/snippets/svelte/msw-addon-configure-handlers-graphql.js.mdx
@@ -8,6 +8,9 @@ import MockApolloWrapperClient from './MockApolloWrapperClient.svelte';
export default {
component: DocumentScreen,
+ render: () => ({
+ Component: DocumentScreen,
+ }),
decorators: [() => MockApolloWrapperClient],
};
@@ -37,35 +40,39 @@ const TestData = {
};
export const MockedSuccess = {
- render: () => ({
- Component: DocumentScreen,
- }),
parameters: {
- msw: [
- graphql.query('AllInfoQuery', (req, res, ctx) => {
- return res(ctx.data(TestData));
- }),
- ],
+ msw: {
+ handlers: [
+ graphql.query('AllInfoQuery', () => {
+ return new HttpResponse.json({
+ data: {
+ allFilms: {
+ films,
+ },
+ }
+ });
+ }),
+ ],
+ },
},
};
export const MockedError = {
- render: () => ({
- Component: DocumentScreen,
- }),
parameters: {
- msw: [
- graphql.query('AllInfoQuery', (req, res, ctx) => {
- return res(
- ctx.delay(800),
- ctx.errors([
- {
- message: 'Access denied',
- },
- ]),
- );
- }),
- ],
+ msw: {
+ handlers: [
+ graphql.query('AllInfoQuery', async () => {
+ delay(800);
+ return new HttpResponse.json({
+ errors: [
+ {
+ message: 'Access denied',
+ },
+ ],
+ });
+ }),
+ ],
+ },
},
};
```
diff --git a/docs/snippets/svelte/documentscreen-story-msw-graphql-query.ts-4-9.mdx b/docs/snippets/svelte/msw-addon-configure-handlers-graphql.ts-4-9.mdx
similarity index 64%
rename from docs/snippets/svelte/documentscreen-story-msw-graphql-query.ts-4-9.mdx
rename to docs/snippets/svelte/msw-addon-configure-handlers-graphql.ts-4-9.mdx
index 250c35581100..747bd95e40e1 100644
--- a/docs/snippets/svelte/documentscreen-story-msw-graphql-query.ts-4-9.mdx
+++ b/docs/snippets/svelte/msw-addon-configure-handlers-graphql.ts-4-9.mdx
@@ -10,6 +10,9 @@ import MockApolloWrapperClient from './MockApolloWrapperClient.svelte';
const meta = {
component: DocumentScreen,
+ render: () => ({
+ Component: DocumentScreen,
+ }),
decorators: [() => MockApolloWrapperClient],
} satisfies Meta;
@@ -43,28 +46,38 @@ const TestData = {
export const MockedSuccess: Story = {
parameters: {
- msw: [
- graphql.query('AllInfoQuery', (req, res, ctx) => {
- return res(ctx.data(TestData));
- }),
- ],
+ msw: {
+ handlers: [
+ graphql.query('AllInfoQuery', () => {
+ return new HttpResponse.json({
+ data: {
+ allFilms: {
+ films,
+ },
+ }
+ });
+ }),
+ ],
+ },
},
};
export const MockedError: Story = {
parameters: {
- msw: [
- graphql.query('AllInfoQuery', (req, res, ctx) => {
- return res(
- ctx.delay(800),
- ctx.errors([
- {
- message: 'Access denied',
- },
- ]),
- );
- }),
- ],
+ msw: {
+ handlers: [
+ graphql.query('AllInfoQuery', async () => {
+ delay(800);
+ return new HttpResponse.json({
+ errors: [
+ {
+ message: 'Access denied',
+ },
+ ],
+ });
+ }),
+ ],
+ },
},
};
```
diff --git a/docs/snippets/svelte/documentscreen-story-msw-graphql-query.ts.mdx b/docs/snippets/svelte/msw-addon-configure-handlers-graphql.ts.mdx
similarity index 64%
rename from docs/snippets/svelte/documentscreen-story-msw-graphql-query.ts.mdx
rename to docs/snippets/svelte/msw-addon-configure-handlers-graphql.ts.mdx
index 489e4f17ebe0..16fc69e8726b 100644
--- a/docs/snippets/svelte/documentscreen-story-msw-graphql-query.ts.mdx
+++ b/docs/snippets/svelte/msw-addon-configure-handlers-graphql.ts.mdx
@@ -10,6 +10,9 @@ import MockApolloWrapperClient from './MockApolloWrapperClient.svelte';
const meta: Meta = {
component: DocumentScreen,
+ render: () => ({
+ Component: DocumentScreen,
+ }),
decorators: [() => MockApolloWrapperClient],
};
@@ -43,28 +46,38 @@ const TestData = {
export const MockedSuccess: Story = {
parameters: {
- msw: [
- graphql.query('AllInfoQuery', (req, res, ctx) => {
- return res(ctx.data(TestData));
- }),
- ],
+ msw: {
+ handlers: [
+ graphql.query('AllInfoQuery', () => {
+ return new HttpResponse.json({
+ data: {
+ allFilms: {
+ films,
+ },
+ }
+ });
+ }),
+ ],
+ },
},
};
export const MockedError: Story = {
parameters: {
- msw: [
- graphql.query('AllInfoQuery', (req, res, ctx) => {
- return res(
- ctx.delay(800),
- ctx.errors([
- {
- message: 'Access denied',
- },
- ]),
- );
- }),
- ],
+ msw: {
+ handlers: [
+ graphql.query('AllInfoQuery', async () => {
+ delay(800);
+ return new HttpResponse.json({
+ errors: [
+ {
+ message: 'Access denied',
+ },
+ ],
+ });
+ }),
+ ],
+ },
},
};
```
diff --git a/docs/snippets/vue/documentscreen-story-msw-graphql-query.js.mdx b/docs/snippets/vue/msw-addon-configure-handlers-graphql.js.mdx
similarity index 58%
rename from docs/snippets/vue/documentscreen-story-msw-graphql-query.js.mdx
rename to docs/snippets/vue/msw-addon-configure-handlers-graphql.js.mdx
index d13973f9804d..57169a97bcf9 100644
--- a/docs/snippets/vue/documentscreen-story-msw-graphql-query.js.mdx
+++ b/docs/snippets/vue/msw-addon-configure-handlers-graphql.js.mdx
@@ -9,6 +9,10 @@ import { graphql } from 'msw';
export default {
component: DocumentScreen,
+ render: () => ({
+ components: { DocumentScreen, WrapperComponent },
+ template: '',
+ }),
};
//👇The mocked data that will be used in the story
@@ -36,43 +40,40 @@ const TestData = {
],
};
-/*
- *👇 Render functions are a framework specific feature to allow you control on how the component renders.
- * See https://storybook.js.org/docs/api/csf
- * to learn how to use render functions.
- */
export const MockedSuccess = {
- render: () => ({
- components: { DocumentScreen, WrapperComponent },
- template: '',
- }),
parameters: {
- msw: [
- graphql.query('AllInfoQuery', (req, res, ctx) => {
- return res(ctx.data(TestData));
- }),
- ],
+ msw: {
+ handlers: [
+ graphql.query('AllInfoQuery', () => {
+ return new HttpResponse.json({
+ data: {
+ allFilms: {
+ films,
+ },
+ }
+ });
+ }),
+ ],
+ },
},
};
export const MockedError = {
- render: () => ({
- components: { DocumentScreen, WrapperComponent },
- template: '',
- }),
parameters: {
- msw: [
- graphql.query('AllInfoQuery', (req, res, ctx) => {
- return res(
- ctx.delay(800),
- ctx.errors([
- {
- message: 'Access denied',
- },
- ]),
- );
- }),
- ],
+ msw: {
+ handlers: [
+ graphql.query('AllInfoQuery', async () => {
+ delay(800);
+ return new HttpResponse.json({
+ errors: [
+ {
+ message: 'Access denied',
+ },
+ ],
+ });
+ }),
+ ],
+ },
},
};
```
diff --git a/docs/snippets/vue/documentscreen-story-msw-graphql-query.ts-4-9.mdx b/docs/snippets/vue/msw-addon-configure-handlers-graphql.ts-4-9.mdx
similarity index 62%
rename from docs/snippets/vue/documentscreen-story-msw-graphql-query.ts-4-9.mdx
rename to docs/snippets/vue/msw-addon-configure-handlers-graphql.ts-4-9.mdx
index f1b9f80932f8..62dd097b0d10 100644
--- a/docs/snippets/vue/documentscreen-story-msw-graphql-query.ts-4-9.mdx
+++ b/docs/snippets/vue/msw-addon-configure-handlers-graphql.ts-4-9.mdx
@@ -11,6 +11,10 @@ import WrapperComponent from './ApolloWrapperClient.vue';
const meta = {
component: DocumentScreen,
+ render: () => ({
+ components: { DocumentScreen, WrapperComponent },
+ template: '',
+ }),
} satisfies Meta;
//👇The mocked data that will be used in the story
@@ -41,43 +45,40 @@ const TestData = {
export default meta;
type Story = StoryObj;
-/*
- *👇 Render functions are a framework specific feature to allow you control on how the component renders.
- * See https://storybook.js.org/docs/api/csf
- * to learn how to use render functions.
- */
export const MockedSuccess: Story = {
- render: () => ({
- components: { DocumentScreen, WrapperComponent },
- template: '',
- }),
parameters: {
- msw: [
- graphql.query('AllInfoQuery', (req, res, ctx) => {
- return res(ctx.data(TestData));
- }),
- ],
+ msw: {
+ handlers: [
+ graphql.query('AllInfoQuery', () => {
+ return new HttpResponse.json({
+ data: {
+ allFilms: {
+ films,
+ },
+ }
+ });
+ }),
+ ],
+ },
},
};
export const MockedError: Story = {
- render: () => ({
- components: { DocumentScreen, WrapperComponent },
- template: '',
- }),
parameters: {
- msw: [
- graphql.query('AllInfoQuery', (req, res, ctx) => {
- return res(
- ctx.delay(800),
- ctx.errors([
- {
- message: 'Access denied',
- },
- ]),
- );
- }),
- ],
+ msw: {
+ handlers: [
+ graphql.query('AllInfoQuery', async () => {
+ delay(800);
+ return new HttpResponse.json({
+ errors: [
+ {
+ message: 'Access denied',
+ },
+ ],
+ });
+ }),
+ ],
+ },
},
};
```
diff --git a/docs/snippets/vue/documentscreen-story-msw-graphql-query.ts.mdx b/docs/snippets/vue/msw-addon-configure-handlers-graphql.ts.mdx
similarity index 62%
rename from docs/snippets/vue/documentscreen-story-msw-graphql-query.ts.mdx
rename to docs/snippets/vue/msw-addon-configure-handlers-graphql.ts.mdx
index db042ec13480..fc2cf264d4c3 100644
--- a/docs/snippets/vue/documentscreen-story-msw-graphql-query.ts.mdx
+++ b/docs/snippets/vue/msw-addon-configure-handlers-graphql.ts.mdx
@@ -11,6 +11,10 @@ import WrapperComponent from './ApolloWrapperClient.vue';
const meta: Meta = {
component: DocumentScreen,
+ render: () => ({
+ components: { DocumentScreen, WrapperComponent },
+ template: '',
+ }),
};
//👇The mocked data that will be used in the story
@@ -41,43 +45,40 @@ const TestData = {
export default meta;
type Story = StoryObj;
-/*
- *👇 Render functions are a framework specific feature to allow you control on how the component renders.
- * See https://storybook.js.org/docs/api/csf
- * to learn how to use render functions.
- */
export const MockedSuccess: Story = {
- render: () => ({
- components: { DocumentScreen, WrapperComponent },
- template: '',
- }),
parameters: {
- msw: [
- graphql.query('AllInfoQuery', (req, res, ctx) => {
- return res(ctx.data(TestData));
- }),
- ],
+ msw: {
+ handlers: [
+ graphql.query('AllInfoQuery', () => {
+ return new HttpResponse.json({
+ data: {
+ allFilms: {
+ films,
+ },
+ }
+ });
+ }),
+ ],
+ },
},
};
export const MockedError: Story = {
- render: () => ({
- components: { DocumentScreen, WrapperComponent },
- template: '',
- }),
parameters: {
- msw: [
- graphql.query('AllInfoQuery', (req, res, ctx) => {
- return res(
- ctx.delay(800),
- ctx.errors([
- {
- message: 'Access denied',
- },
- ]),
- );
- }),
- ],
+ msw: {
+ handlers: [
+ graphql.query('AllInfoQuery', async () => {
+ delay(800);
+ return new HttpResponse.json({
+ errors: [
+ {
+ message: 'Access denied',
+ },
+ ],
+ });
+ }),
+ ],
+ },
},
};
```
diff --git a/docs/snippets/web-components/documentscreen-story-msw-rest-request.js.mdx b/docs/snippets/web-components/msw-addon-configure-handlers-http.js.mdx
similarity index 66%
rename from docs/snippets/web-components/documentscreen-story-msw-rest-request.js.mdx
rename to docs/snippets/web-components/msw-addon-configure-handlers-http.js.mdx
index 6a4fce7cc13c..395391e6c154 100644
--- a/docs/snippets/web-components/documentscreen-story-msw-rest-request.js.mdx
+++ b/docs/snippets/web-components/msw-addon-configure-handlers-http.js.mdx
@@ -34,21 +34,28 @@ const TestData = {
export const MockedSuccess = {
parameters: {
- msw: [
- rest.get('https://your-restful-endpoint/', (_req, res, ctx) => {
- return res(ctx.json(TestData));
- }),
- ],
+ msw: {
+ handlers: [
+ http.get('https://your-restful-endpoint/', () => {
+ return new HttpResponse.json(TestData);
+ }),
+ ],
+ },
},
};
export const MockedError = {
parameters: {
- msw: [
- rest.get('https://your-restful-endpoint', (_req, res, ctx) => {
- return res(ctx.delay(800), ctx.status(403));
- }),
- ],
+ msw: {
+ handlers: [
+ http.get('https://your-restful-endpoint', async () => {
+ await delay(800);
+ return new HttpResponse(null, {
+ status: 403,
+ });
+ }),
+ ],
+ },
},
};
```
diff --git a/docs/snippets/web-components/documentscreen-story-msw-rest-request.ts.mdx b/docs/snippets/web-components/msw-addon-configure-handlers-http.ts.mdx
similarity index 69%
rename from docs/snippets/web-components/documentscreen-story-msw-rest-request.ts.mdx
rename to docs/snippets/web-components/msw-addon-configure-handlers-http.ts.mdx
index 9329c3b3f293..f7022c42c624 100644
--- a/docs/snippets/web-components/documentscreen-story-msw-rest-request.ts.mdx
+++ b/docs/snippets/web-components/msw-addon-configure-handlers-http.ts.mdx
@@ -39,21 +39,28 @@ const TestData = {
export const MockedSuccess: Story = {
parameters: {
- msw: [
- rest.get('https://your-restful-endpoint/', (_req, res, ctx) => {
- return res(ctx.json(TestData));
- }),
- ],
+ msw: {
+ handlers: [
+ http.get('https://your-restful-endpoint/', () => {
+ return new HttpResponse.json(TestData);
+ }),
+ ],
+ },
},
};
export const MockedError: Story = {
parameters: {
- msw: [
- rest.get('https://your-restful-endpoint', (_req, res, ctx) => {
- return res(ctx.delay(800), ctx.status(403));
- }),
- ],
+ msw: {
+ handlers: [
+ http.get('https://your-restful-endpoint', async () => {
+ await delay(800);
+ return new HttpResponse(null, {
+ status: 403,
+ });
+ }),
+ ],
+ },
},
};
```
diff --git a/docs/writing-stories/mocking-network-requests.md b/docs/writing-stories/mocking-network-requests.md
index ec41c806ea9c..d27ae6766483 100644
--- a/docs/writing-stories/mocking-network-requests.md
+++ b/docs/writing-stories/mocking-network-requests.md
@@ -10,21 +10,26 @@ The [MSW addon](https://storybook.js.org/addons/msw-storybook-addon/) brings thi
First, if necessary, run this command to install MSW:
-
+
-```sh
-npm install msw --save-dev
-```
+
+
+
Then generate the service worker file necessary for MSW to work:
-
@@ -48,14 +53,14 @@ Next, install and register the MSW addon:
npx storybook@latest add msw-storybook-addon
```
-Then ensure the [`staticDirs`](../api/main-config-static-dirs.md) property in your Storybook configuration will include the generated service worker file:
+Then ensure the [`staticDirs`](../api/main-config-static-dirs.md) property in your Storybook configuration will include the generated service worker file (in `/public`, by default):
-
@@ -65,11 +70,10 @@ Finally, initialize the addon and apply it to all stories with a [project-level
-
@@ -107,14 +111,13 @@ With the MSW addon, we can write stories that use MSW to mock the REST requests.
-
-
Date: Fri, 19 Apr 2024 10:35:42 -0600
Subject: [PATCH 032/117] Address MSW snippets feedback
---
.../msw-addon-configure-handlers-graphql.ts.mdx | 14 +++++---------
...dx => msw-addon-configure-handlers-http.ts.mdx} | 3 +--
.../msw-addon-configure-handlers-http.js.mdx | 1 -
.../msw-addon-configure-handlers-http.ts-4-9.mdx | 5 ++---
.../msw-addon-configure-handlers-http.ts.mdx | 5 ++---
docs/snippets/common/msw-addon-initialize.ts.mdx | 4 ++--
.../msw-addon-configure-handlers-graphql.js.mdx | 6 ++----
...msw-addon-configure-handlers-graphql.ts-4-9.mdx | 6 ++----
.../msw-addon-configure-handlers-graphql.ts.mdx | 4 +---
.../msw-addon-configure-handlers-graphql.js.mdx | 10 +++-------
...msw-addon-configure-handlers-graphql.ts-4-9.mdx | 10 +++-------
.../msw-addon-configure-handlers-graphql.ts.mdx | 10 +++-------
.../msw-addon-configure-handlers-graphql.js.mdx | 8 +++-----
...msw-addon-configure-handlers-graphql.ts-4-9.mdx | 8 +++-----
.../msw-addon-configure-handlers-graphql.ts.mdx | 8 +++-----
.../msw-addon-configure-handlers-http.js.mdx | 3 +--
.../msw-addon-configure-handlers-http.ts.mdx | 3 +--
17 files changed, 37 insertions(+), 71 deletions(-)
rename docs/snippets/angular/{msw-addon-configure-handlers.ts.mdx => msw-addon-configure-handlers-http.ts.mdx} (96%)
diff --git a/docs/snippets/angular/msw-addon-configure-handlers-graphql.ts.mdx b/docs/snippets/angular/msw-addon-configure-handlers-graphql.ts.mdx
index b70e270726ac..0b2fa61c216b 100644
--- a/docs/snippets/angular/msw-addon-configure-handlers-graphql.ts.mdx
+++ b/docs/snippets/angular/msw-addon-configure-handlers-graphql.ts.mdx
@@ -1,20 +1,16 @@
```ts
// YourPage.stories.ts
-
-import { moduleMetadata } from '@storybook/angular';
-
import type { Meta, StoryObj } from '@storybook/angular';
+import { moduleMetadata } from '@storybook/angular';
import { CommonModule } from '@angular/common';
import { HttpClientModule } from '@angular/common/http';
+import { graphql, HttpResponse, delay } from 'msw';
-import { graphql } from 'msw';
-
-import { DocumentScreen } from './YourPage.component';
-import { DocumentList } from './DocumentList.component';
import { DocumentHeader } from './DocumentHeader.component';
+import { DocumentList } from './DocumentList.component';
import { PageLayout } from './PageLayout.component';
-
+import { DocumentScreen } from './YourPage.component';
import { MockGraphQLModule } from './mock-graphql.module';
const meta: Meta = {
@@ -79,7 +75,7 @@ export const MockedError: Story = {
msw: {
handlers: [
graphql.query('AllInfoQuery', async () => {
- delay(800);
+ await delay(800);
return new HttpResponse.json({
errors: [
{
diff --git a/docs/snippets/angular/msw-addon-configure-handlers.ts.mdx b/docs/snippets/angular/msw-addon-configure-handlers-http.ts.mdx
similarity index 96%
rename from docs/snippets/angular/msw-addon-configure-handlers.ts.mdx
rename to docs/snippets/angular/msw-addon-configure-handlers-http.ts.mdx
index 202051838b1d..820857b94b48 100644
--- a/docs/snippets/angular/msw-addon-configure-handlers.ts.mdx
+++ b/docs/snippets/angular/msw-addon-configure-handlers-http.ts.mdx
@@ -1,9 +1,8 @@
```ts
// YourPage.stories.ts
-
import type { Meta, StoryObj } from '@storybook/angular';
-import { rest } from 'msw';
+import { http, HttpResponse, delay } from 'msw';
import { DocumentScreen } from './YourPage.component';
diff --git a/docs/snippets/common/msw-addon-configure-handlers-http.js.mdx b/docs/snippets/common/msw-addon-configure-handlers-http.js.mdx
index 99509f949f4c..cd2138c50a0f 100644
--- a/docs/snippets/common/msw-addon-configure-handlers-http.js.mdx
+++ b/docs/snippets/common/msw-addon-configure-handlers-http.js.mdx
@@ -1,6 +1,5 @@
```js
// YourPage.stories.js|jsx
-
import { http, HttpResponse, delay } from 'msw';
import { DocumentScreen } from './YourPage';
diff --git a/docs/snippets/common/msw-addon-configure-handlers-http.ts-4-9.mdx b/docs/snippets/common/msw-addon-configure-handlers-http.ts-4-9.mdx
index 717809b8b18e..663a9fd0f1f2 100644
--- a/docs/snippets/common/msw-addon-configure-handlers-http.ts-4-9.mdx
+++ b/docs/snippets/common/msw-addon-configure-handlers-http.ts-4-9.mdx
@@ -1,10 +1,9 @@
```ts
// YourPage.stories.ts|tsx
-
-// Replace your-framework with the name of your framework
+// Replace your-framework with the name of your framework (e.g. nextjs, vue3-vite)
import type { Meta, StoryObj } from '@storybook/your-framework';
-import { rest } from 'msw';
+import { http, HttpResponse, delay } from 'msw';
import { MyComponent } from './MyComponent';
diff --git a/docs/snippets/common/msw-addon-configure-handlers-http.ts.mdx b/docs/snippets/common/msw-addon-configure-handlers-http.ts.mdx
index ab1f4ab92256..26d7ffaae623 100644
--- a/docs/snippets/common/msw-addon-configure-handlers-http.ts.mdx
+++ b/docs/snippets/common/msw-addon-configure-handlers-http.ts.mdx
@@ -1,10 +1,9 @@
```ts
// YourPage.stories.ts|tsx
-
-// Replace your-framework with the name of your framework
+// Replace your-framework with the name of your framework (e.g. nextjs, vue3-vite)
import type { Meta, StoryObj } from '@storybook/your-framework';
-import { rest } from 'msw';
+import { http, HttpResponse, delay } from 'msw';
import { MyComponent } from './MyComponent';
diff --git a/docs/snippets/common/msw-addon-initialize.ts.mdx b/docs/snippets/common/msw-addon-initialize.ts.mdx
index 6dcb8080ebc7..e0fa098e8490 100644
--- a/docs/snippets/common/msw-addon-initialize.ts.mdx
+++ b/docs/snippets/common/msw-addon-initialize.ts.mdx
@@ -1,8 +1,8 @@
```ts
// .storybook/preview.ts
-// Replace your-framework with the framework you are using (e.g., nextjs, vue3-vite)
-import { Preview } from '@storybook/your-framework';
+// Replace your-renderer with the renderer you are using (e.g., react, vue, etc.)
+import { Preview } from '@storybook/your-renderer';
import { initialize, mswLoader } from 'msw-storybook-addon';
diff --git a/docs/snippets/react/msw-addon-configure-handlers-graphql.js.mdx b/docs/snippets/react/msw-addon-configure-handlers-graphql.js.mdx
index 02d3494a8701..4b67d992d5dd 100644
--- a/docs/snippets/react/msw-addon-configure-handlers-graphql.js.mdx
+++ b/docs/snippets/react/msw-addon-configure-handlers-graphql.js.mdx
@@ -1,9 +1,7 @@
```js
// YourPage.stories.js|jsx
-
import { ApolloClient, ApolloProvider, InMemoryCache } from '@apollo/client';
-
-import { graphql } from 'msw';
+import { graphql, HttpResponse, delay } from 'msw';
import { DocumentScreen } from './YourPage';
@@ -81,7 +79,7 @@ export const MockedError = {
msw: {
handlers: [
graphql.query('AllInfoQuery', async () => {
- delay(800);
+ await delay(800);
return new HttpResponse.json({
errors: [
{
diff --git a/docs/snippets/react/msw-addon-configure-handlers-graphql.ts-4-9.mdx b/docs/snippets/react/msw-addon-configure-handlers-graphql.ts-4-9.mdx
index 66fc89ef54b2..2fd10eed5dec 100644
--- a/docs/snippets/react/msw-addon-configure-handlers-graphql.ts-4-9.mdx
+++ b/docs/snippets/react/msw-addon-configure-handlers-graphql.ts-4-9.mdx
@@ -1,11 +1,9 @@
```ts
// YourPage.stories.ts|tsx
-
import type { Meta, StoryObj } from '@storybook/react';
import { ApolloClient, ApolloProvider, InMemoryCache } from '@apollo/client';
-
-import { graphql } from 'msw';
+import { graphql, HttpResponse, delay } from 'msw';
import { DocumentScreen } from './YourPage';
@@ -85,7 +83,7 @@ export const MockedError: Story = {
msw: {
handlers: [
graphql.query('AllInfoQuery', async () => {
- delay(800);
+ await delay(800);
return new HttpResponse.json({
errors: [
{
diff --git a/docs/snippets/react/msw-addon-configure-handlers-graphql.ts.mdx b/docs/snippets/react/msw-addon-configure-handlers-graphql.ts.mdx
index 6e38aad3b85b..4367a53a25e9 100644
--- a/docs/snippets/react/msw-addon-configure-handlers-graphql.ts.mdx
+++ b/docs/snippets/react/msw-addon-configure-handlers-graphql.ts.mdx
@@ -1,10 +1,8 @@
```ts
// YourPage.stories.ts|tsx
-
import type { Meta, StoryObj } from '@storybook/react';
import { ApolloClient, ApolloProvider, InMemoryCache } from '@apollo/client';
-
import { graphql, HttpResponse, delay } from 'msw';
import { DocumentScreen } from './YourPage';
@@ -85,7 +83,7 @@ export const MockedError: Story = {
msw: {
handlers: [
graphql.query('AllInfoQuery', async () => {
- delay(800);
+ await delay(800);
return new HttpResponse.json({
errors: [
{
diff --git a/docs/snippets/svelte/msw-addon-configure-handlers-graphql.js.mdx b/docs/snippets/svelte/msw-addon-configure-handlers-graphql.js.mdx
index 13856ae98299..345db9fb4c43 100644
--- a/docs/snippets/svelte/msw-addon-configure-handlers-graphql.js.mdx
+++ b/docs/snippets/svelte/msw-addon-configure-handlers-graphql.js.mdx
@@ -1,16 +1,12 @@
```js
// YourPage.stories.js
+import { graphql, HttpResponse, delay } from 'msw';
-import { graphql } from 'msw';
-
-import DocumentScreen from './YourPage.svelte';
import MockApolloWrapperClient from './MockApolloWrapperClient.svelte';
+import DocumentScreen from './YourPage.svelte';
export default {
component: DocumentScreen,
- render: () => ({
- Component: DocumentScreen,
- }),
decorators: [() => MockApolloWrapperClient],
};
@@ -62,7 +58,7 @@ export const MockedError = {
msw: {
handlers: [
graphql.query('AllInfoQuery', async () => {
- delay(800);
+ await delay(800);
return new HttpResponse.json({
errors: [
{
diff --git a/docs/snippets/svelte/msw-addon-configure-handlers-graphql.ts-4-9.mdx b/docs/snippets/svelte/msw-addon-configure-handlers-graphql.ts-4-9.mdx
index 747bd95e40e1..7f033a7dfb67 100644
--- a/docs/snippets/svelte/msw-addon-configure-handlers-graphql.ts-4-9.mdx
+++ b/docs/snippets/svelte/msw-addon-configure-handlers-graphql.ts-4-9.mdx
@@ -1,18 +1,14 @@
```ts
// YourPage.stories.ts
-
import type { Meta, StoryObj } from '@storybook/svelte';
-import { graphql } from 'msw';
+import { graphql, HttpResponse, delay } from 'msw';
-import DocumentScreen from './YourPage.svelte';
import MockApolloWrapperClient from './MockApolloWrapperClient.svelte';
+import DocumentScreen from './YourPage.svelte';
const meta = {
component: DocumentScreen,
- render: () => ({
- Component: DocumentScreen,
- }),
decorators: [() => MockApolloWrapperClient],
} satisfies Meta;
@@ -67,7 +63,7 @@ export const MockedError: Story = {
msw: {
handlers: [
graphql.query('AllInfoQuery', async () => {
- delay(800);
+ await delay(800);
return new HttpResponse.json({
errors: [
{
diff --git a/docs/snippets/svelte/msw-addon-configure-handlers-graphql.ts.mdx b/docs/snippets/svelte/msw-addon-configure-handlers-graphql.ts.mdx
index 16fc69e8726b..4b7d818e75a2 100644
--- a/docs/snippets/svelte/msw-addon-configure-handlers-graphql.ts.mdx
+++ b/docs/snippets/svelte/msw-addon-configure-handlers-graphql.ts.mdx
@@ -1,18 +1,14 @@
```ts
// YourPage.stories.ts
-
import type { Meta, StoryObj } from '@storybook/svelte';
-import { graphql } from 'msw';
+import { graphql, HttpResponse, delay } from 'msw';
-import DocumentScreen from './YourPage.svelte';
import MockApolloWrapperClient from './MockApolloWrapperClient.svelte';
+import DocumentScreen from './YourPage.svelte';
const meta: Meta = {
component: DocumentScreen,
- render: () => ({
- Component: DocumentScreen,
- }),
decorators: [() => MockApolloWrapperClient],
};
@@ -67,7 +63,7 @@ export const MockedError: Story = {
msw: {
handlers: [
graphql.query('AllInfoQuery', async () => {
- delay(800);
+ await delay(800);
return new HttpResponse.json({
errors: [
{
diff --git a/docs/snippets/vue/msw-addon-configure-handlers-graphql.js.mdx b/docs/snippets/vue/msw-addon-configure-handlers-graphql.js.mdx
index 57169a97bcf9..cfef3b12956b 100644
--- a/docs/snippets/vue/msw-addon-configure-handlers-graphql.js.mdx
+++ b/docs/snippets/vue/msw-addon-configure-handlers-graphql.js.mdx
@@ -1,11 +1,9 @@
```js
// YourPage.stories.js
-
-import DocumentScreen from './YourPage.vue';
+import { graphql, HttpResponse, delay } from 'msw';
import WrapperComponent from './ApolloWrapperClient.vue';
-
-import { graphql } from 'msw';
+import DocumentScreen from './YourPage.vue';
export default {
component: DocumentScreen,
@@ -63,7 +61,7 @@ export const MockedError = {
msw: {
handlers: [
graphql.query('AllInfoQuery', async () => {
- delay(800);
+ await delay(800);
return new HttpResponse.json({
errors: [
{
diff --git a/docs/snippets/vue/msw-addon-configure-handlers-graphql.ts-4-9.mdx b/docs/snippets/vue/msw-addon-configure-handlers-graphql.ts-4-9.mdx
index 62dd097b0d10..b0d12117418f 100644
--- a/docs/snippets/vue/msw-addon-configure-handlers-graphql.ts-4-9.mdx
+++ b/docs/snippets/vue/msw-addon-configure-handlers-graphql.ts-4-9.mdx
@@ -1,13 +1,11 @@
```ts
// YourPage.stories.ts
-
-import { graphql } from 'msw';
-
import type { Meta, StoryObj } from '@storybook/vue3';
-import DocumentScreen from './YourPage.vue';
+import { graphql, HttpResponse, delay } from 'msw';
import WrapperComponent from './ApolloWrapperClient.vue';
+import DocumentScreen from './YourPage.vue';
const meta = {
component: DocumentScreen,
@@ -68,7 +66,7 @@ export const MockedError: Story = {
msw: {
handlers: [
graphql.query('AllInfoQuery', async () => {
- delay(800);
+ await delay(800);
return new HttpResponse.json({
errors: [
{
diff --git a/docs/snippets/vue/msw-addon-configure-handlers-graphql.ts.mdx b/docs/snippets/vue/msw-addon-configure-handlers-graphql.ts.mdx
index fc2cf264d4c3..d98251c2bd5b 100644
--- a/docs/snippets/vue/msw-addon-configure-handlers-graphql.ts.mdx
+++ b/docs/snippets/vue/msw-addon-configure-handlers-graphql.ts.mdx
@@ -1,13 +1,11 @@
```ts
// YourPage.stories.ts
-
-import { graphql } from 'msw';
-
import type { Meta, StoryObj } from '@storybook/vue3';
-import DocumentScreen from './YourPage.vue';
+import { graphql, HttpResponse, delay } from 'msw';
import WrapperComponent from './ApolloWrapperClient.vue';
+import DocumentScreen from './YourPage.vue';
const meta: Meta = {
component: DocumentScreen,
@@ -68,7 +66,7 @@ export const MockedError: Story = {
msw: {
handlers: [
graphql.query('AllInfoQuery', async () => {
- delay(800);
+ await delay(800);
return new HttpResponse.json({
errors: [
{
diff --git a/docs/snippets/web-components/msw-addon-configure-handlers-http.js.mdx b/docs/snippets/web-components/msw-addon-configure-handlers-http.js.mdx
index 395391e6c154..d1c3cda8bc9f 100644
--- a/docs/snippets/web-components/msw-addon-configure-handlers-http.js.mdx
+++ b/docs/snippets/web-components/msw-addon-configure-handlers-http.js.mdx
@@ -1,7 +1,6 @@
```js
// YourPage.stories.js
-
-import { rest } from 'msw';
+import { http, HttpResponse, delay } from 'msw';
export default {
component: 'demo-document-screen',
diff --git a/docs/snippets/web-components/msw-addon-configure-handlers-http.ts.mdx b/docs/snippets/web-components/msw-addon-configure-handlers-http.ts.mdx
index f7022c42c624..b12fd8f93a41 100644
--- a/docs/snippets/web-components/msw-addon-configure-handlers-http.ts.mdx
+++ b/docs/snippets/web-components/msw-addon-configure-handlers-http.ts.mdx
@@ -1,9 +1,8 @@
```ts
// YourPage.stories.ts
-
import type { Meta, StoryObj } from '@storybook/web-components';
-import { rest } from 'msw';
+import { http, HttpResponse, delay } from 'msw';
const meta: Meta = {
component: 'demo-document-screen',
From f4914e3215b3087612881f9065eea12b5a94fbd4 Mon Sep 17 00:00:00 2001
From: Kyle Gach
Date: Fri, 19 Apr 2024 10:44:52 -0600
Subject: [PATCH 033/117] Prettify snippets
---
.../angular/msw-addon-configure-handlers-graphql.ts.mdx | 2 +-
docs/snippets/common/msw-install.npm.js.mdx | 2 +-
docs/snippets/common/msw-install.pnpm.js.mdx | 2 +-
docs/snippets/common/msw-install.yarn.js.mdx | 2 +-
docs/snippets/react/msw-addon-configure-handlers-graphql.js.mdx | 2 +-
.../snippets/svelte/msw-addon-configure-handlers-graphql.js.mdx | 2 +-
.../svelte/msw-addon-configure-handlers-graphql.ts-4-9.mdx | 2 +-
.../snippets/svelte/msw-addon-configure-handlers-graphql.ts.mdx | 2 +-
docs/snippets/vue/msw-addon-configure-handlers-graphql.js.mdx | 2 +-
.../vue/msw-addon-configure-handlers-graphql.ts-4-9.mdx | 2 +-
docs/snippets/vue/msw-addon-configure-handlers-graphql.ts.mdx | 2 +-
11 files changed, 11 insertions(+), 11 deletions(-)
diff --git a/docs/snippets/angular/msw-addon-configure-handlers-graphql.ts.mdx b/docs/snippets/angular/msw-addon-configure-handlers-graphql.ts.mdx
index 0b2fa61c216b..dc3347cfd980 100644
--- a/docs/snippets/angular/msw-addon-configure-handlers-graphql.ts.mdx
+++ b/docs/snippets/angular/msw-addon-configure-handlers-graphql.ts.mdx
@@ -62,7 +62,7 @@ export const MockedSuccess: Story = {
allFilms: {
films,
},
- }
+ },
});
}),
],
diff --git a/docs/snippets/common/msw-install.npm.js.mdx b/docs/snippets/common/msw-install.npm.js.mdx
index 3bc959fdf516..cf6e91f4e0aa 100644
--- a/docs/snippets/common/msw-install.npm.js.mdx
+++ b/docs/snippets/common/msw-install.npm.js.mdx
@@ -1,3 +1,3 @@
```sh
npm install msw --save-dev
-```
\ No newline at end of file
+```
diff --git a/docs/snippets/common/msw-install.pnpm.js.mdx b/docs/snippets/common/msw-install.pnpm.js.mdx
index 88ed6296508e..c4332df90cd0 100644
--- a/docs/snippets/common/msw-install.pnpm.js.mdx
+++ b/docs/snippets/common/msw-install.pnpm.js.mdx
@@ -1,3 +1,3 @@
```sh
pnpm add msw --save-dev
-```
\ No newline at end of file
+```
diff --git a/docs/snippets/common/msw-install.yarn.js.mdx b/docs/snippets/common/msw-install.yarn.js.mdx
index 38ef49b23d8f..6a8d76b5ff15 100644
--- a/docs/snippets/common/msw-install.yarn.js.mdx
+++ b/docs/snippets/common/msw-install.yarn.js.mdx
@@ -1,3 +1,3 @@
```sh
yarn add msw --save-dev
-```
\ No newline at end of file
+```
diff --git a/docs/snippets/react/msw-addon-configure-handlers-graphql.js.mdx b/docs/snippets/react/msw-addon-configure-handlers-graphql.js.mdx
index 4b67d992d5dd..dee1f7976ec4 100644
--- a/docs/snippets/react/msw-addon-configure-handlers-graphql.js.mdx
+++ b/docs/snippets/react/msw-addon-configure-handlers-graphql.js.mdx
@@ -66,7 +66,7 @@ export const MockedSuccess = {
allFilms: {
films,
},
- }
+ },
});
}),
],
diff --git a/docs/snippets/svelte/msw-addon-configure-handlers-graphql.js.mdx b/docs/snippets/svelte/msw-addon-configure-handlers-graphql.js.mdx
index 345db9fb4c43..829ec728386a 100644
--- a/docs/snippets/svelte/msw-addon-configure-handlers-graphql.js.mdx
+++ b/docs/snippets/svelte/msw-addon-configure-handlers-graphql.js.mdx
@@ -45,7 +45,7 @@ export const MockedSuccess = {
allFilms: {
films,
},
- }
+ },
});
}),
],
diff --git a/docs/snippets/svelte/msw-addon-configure-handlers-graphql.ts-4-9.mdx b/docs/snippets/svelte/msw-addon-configure-handlers-graphql.ts-4-9.mdx
index 7f033a7dfb67..866603602f16 100644
--- a/docs/snippets/svelte/msw-addon-configure-handlers-graphql.ts-4-9.mdx
+++ b/docs/snippets/svelte/msw-addon-configure-handlers-graphql.ts-4-9.mdx
@@ -50,7 +50,7 @@ export const MockedSuccess: Story = {
allFilms: {
films,
},
- }
+ },
});
}),
],
diff --git a/docs/snippets/svelte/msw-addon-configure-handlers-graphql.ts.mdx b/docs/snippets/svelte/msw-addon-configure-handlers-graphql.ts.mdx
index 4b7d818e75a2..d78ffb4ce4e8 100644
--- a/docs/snippets/svelte/msw-addon-configure-handlers-graphql.ts.mdx
+++ b/docs/snippets/svelte/msw-addon-configure-handlers-graphql.ts.mdx
@@ -50,7 +50,7 @@ export const MockedSuccess: Story = {
allFilms: {
films,
},
- }
+ },
});
}),
],
diff --git a/docs/snippets/vue/msw-addon-configure-handlers-graphql.js.mdx b/docs/snippets/vue/msw-addon-configure-handlers-graphql.js.mdx
index cfef3b12956b..251b119a6629 100644
--- a/docs/snippets/vue/msw-addon-configure-handlers-graphql.js.mdx
+++ b/docs/snippets/vue/msw-addon-configure-handlers-graphql.js.mdx
@@ -48,7 +48,7 @@ export const MockedSuccess = {
allFilms: {
films,
},
- }
+ },
});
}),
],
diff --git a/docs/snippets/vue/msw-addon-configure-handlers-graphql.ts-4-9.mdx b/docs/snippets/vue/msw-addon-configure-handlers-graphql.ts-4-9.mdx
index b0d12117418f..c4b55f47f6ef 100644
--- a/docs/snippets/vue/msw-addon-configure-handlers-graphql.ts-4-9.mdx
+++ b/docs/snippets/vue/msw-addon-configure-handlers-graphql.ts-4-9.mdx
@@ -53,7 +53,7 @@ export const MockedSuccess: Story = {
allFilms: {
films,
},
- }
+ },
});
}),
],
diff --git a/docs/snippets/vue/msw-addon-configure-handlers-graphql.ts.mdx b/docs/snippets/vue/msw-addon-configure-handlers-graphql.ts.mdx
index d98251c2bd5b..6d1b27cb6946 100644
--- a/docs/snippets/vue/msw-addon-configure-handlers-graphql.ts.mdx
+++ b/docs/snippets/vue/msw-addon-configure-handlers-graphql.ts.mdx
@@ -53,7 +53,7 @@ export const MockedSuccess: Story = {
allFilms: {
films,
},
- }
+ },
});
}),
],
From 9f7ca74a93d2f06294bac11f4f7fcddddaee0ad2 Mon Sep 17 00:00:00 2001
From: Jeppe Reinhold
Date: Fri, 26 Apr 2024 09:36:26 +0200
Subject: [PATCH 034/117] add react prerelease sandbox templates
---
code/lib/cli/src/sandbox-templates.ts | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/code/lib/cli/src/sandbox-templates.ts b/code/lib/cli/src/sandbox-templates.ts
index 9d8a839bfcce..8457b55b43e8 100644
--- a/code/lib/cli/src/sandbox-templates.ts
+++ b/code/lib/cli/src/sandbox-templates.ts
@@ -201,6 +201,17 @@ const baseTemplates = {
},
skipTasks: ['bench'],
},
+ 'react-vite/prerelease-ts': {
+ name: 'React Prerelease (Vite | TypeScript)',
+ script:
+ 'npm create vite --yes {{beforeDir}} -- --template react-ts && cd {{beforeDir}} && yarn add react@next react-dom@next',
+ expected: {
+ framework: '@storybook/react-vite',
+ renderer: '@storybook/react',
+ builder: '@storybook/builder-vite',
+ },
+ skipTasks: ['bench'],
+ },
'react-webpack/18-ts': {
name: 'React Latest (Webpack | TypeScript)',
script: 'yarn create webpack5-react {{beforeDir}}',
@@ -222,6 +233,17 @@ const baseTemplates = {
},
skipTasks: ['e2e-tests-dev', 'bench'],
},
+ 'react-webpack/prerelease-ts': {
+ name: 'React Prerelease (Webpack | TypeScript)',
+ script:
+ 'yarn create webpack5-react {{beforeDir}} --version-react="next" --version-react-dom="next"',
+ 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}}',
From bc44d82de569ac69991ce7741395efe66128bce4 Mon Sep 17 00:00:00 2001
From: Jeppe Reinhold
Date: Fri, 26 Apr 2024 10:03:37 +0200
Subject: [PATCH 035/117] disable snapshots for ResolvedReact stories
---
.../docs/template/stories/docs2/resolved-react.stories.ts | 2 ++
1 file changed, 2 insertions(+)
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..2acdd32b11e4 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
+ chromat: { disable: true },
},
};
From 5d8fd432b77d2b3b4c2f4f94e21ae48a5b1507c7 Mon Sep 17 00:00:00 2001
From: Jeppe Reinhold
Date: Fri, 26 Apr 2024 10:08:31 +0200
Subject: [PATCH 036/117] adjust ci parallelism
---
.circleci/config.yml | 10 +++++-----
code/lib/cli/src/sandbox-templates.ts | 4 +++-
2 files changed, 8 insertions(+), 6 deletions(-)
diff --git a/.circleci/config.yml b/.circleci/config.yml
index ad07b40429a4..59ea2ea028de 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/code/lib/cli/src/sandbox-templates.ts b/code/lib/cli/src/sandbox-templates.ts
index 8457b55b43e8..d1b2c6e15f95 100644
--- a/code/lib/cli/src/sandbox-templates.ts
+++ b/code/lib/cli/src/sandbox-templates.ts
@@ -210,7 +210,7 @@ const baseTemplates = {
renderer: '@storybook/react',
builder: '@storybook/builder-vite',
},
- skipTasks: ['bench'],
+ skipTasks: ['e2e-tests-dev', 'bench'],
},
'react-webpack/18-ts': {
name: 'React Latest (Webpack | TypeScript)',
@@ -650,6 +650,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',
From d6f80324028b873e01b2115055992e74cb44d37a Mon Sep 17 00:00:00 2001
From: Jeppe Reinhold
Date: Fri, 26 Apr 2024 10:38:47 +0200
Subject: [PATCH 037/117] allow react 19 betas in packages' peer dependencies
---
code/addons/links/package.json | 2 +-
code/frameworks/nextjs/package.json | 4 +--
code/frameworks/react-vite/package.json | 4 +--
code/frameworks/react-webpack5/package.json | 4 +--
code/lib/react-dom-shim/package.json | 4 +--
code/lib/theming/package.json | 4 +--
code/presets/react-webpack/package.json | 4 +--
code/renderers/react/package.json | 4 +--
code/ui/blocks/package.json | 4 +--
code/ui/components/package.json | 4 +--
code/yarn.lock | 38 ++++++++++-----------
11 files changed, 38 insertions(+), 38 deletions(-)
diff --git a/code/addons/links/package.json b/code/addons/links/package.json
index cbe50306d3f7..0e5fa4263710 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/frameworks/nextjs/package.json b/code/frameworks/nextjs/package.json
index 65aebaa66c60..d6bf931dee32 100644
--- a/code/frameworks/nextjs/package.json
+++ b/code/frameworks/nextjs/package.json
@@ -133,8 +133,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 02a50462eace..3ed608685398 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 4ef8b479e4f6..14e6a4b5a7f3 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/react-dom-shim/package.json b/code/lib/react-dom-shim/package.json
index 35b3bd868d21..b44f8341c973 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 d0b87b51c0c8..c41b86347437 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 33e1c1abc278..e698647ccd60 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 c71f8628de5e..640dab799161 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 edae82e2f19d..bcafaf40f5ab 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/components/package.json b/code/ui/components/package.json
index 200b81381b85..414b09460a27 100644
--- a/code/ui/components/package.json
+++ b/code/ui/components/package.json
@@ -86,8 +86,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/yarn.lock b/code/yarn.lock
index 9af4e4c2f3fd..c5931988a11e 100644
--- a/code/yarn.lock
+++ b/code/yarn.lock
@@ -5150,7 +5150,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
@@ -5431,8 +5431,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
@@ -5695,8 +5695,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
@@ -6185,8 +6185,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:
@@ -6322,8 +6322,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
@@ -6449,8 +6449,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
@@ -6472,8 +6472,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
@@ -6487,8 +6487,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:
@@ -6529,8 +6529,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:
@@ -6915,8 +6915,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
From e24483045d40366033be23457e85a5ba2c395d64 Mon Sep 17 00:00:00 2001
From: Jeppe Reinhold
Date: Fri, 26 Apr 2024 10:48:43 +0200
Subject: [PATCH 038/117] use beta instead of next react version
---
code/lib/cli/src/sandbox-templates.ts | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/code/lib/cli/src/sandbox-templates.ts b/code/lib/cli/src/sandbox-templates.ts
index d1b2c6e15f95..1d1aeaf8ee03 100644
--- a/code/lib/cli/src/sandbox-templates.ts
+++ b/code/lib/cli/src/sandbox-templates.ts
@@ -204,7 +204,7 @@ const baseTemplates = {
'react-vite/prerelease-ts': {
name: 'React Prerelease (Vite | TypeScript)',
script:
- 'npm create vite --yes {{beforeDir}} -- --template react-ts && cd {{beforeDir}} && yarn add react@next react-dom@next',
+ 'npm create vite --yes {{beforeDir}} -- --template react-ts && cd {{beforeDir}} && yarn add react@beta react-dom@beta',
expected: {
framework: '@storybook/react-vite',
renderer: '@storybook/react',
@@ -236,7 +236,7 @@ const baseTemplates = {
'react-webpack/prerelease-ts': {
name: 'React Prerelease (Webpack | TypeScript)',
script:
- 'yarn create webpack5-react {{beforeDir}} --version-react="next" --version-react-dom="next"',
+ 'yarn create webpack5-react {{beforeDir}} --version-react="beta" --version-react-dom="beta"',
expected: {
framework: '@storybook/react-webpack5',
renderer: '@storybook/react',
From 18df4de9e3fb7216c1eb859c72702ecdab16d5f2 Mon Sep 17 00:00:00 2001
From: Jorge Ji
Date: Sat, 27 Apr 2024 23:31:45 +0100
Subject: [PATCH 039/117] fix: remove state from dependency array
---
code/ui/manager/src/components/layout/Layout.tsx | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/code/ui/manager/src/components/layout/Layout.tsx b/code/ui/manager/src/components/layout/Layout.tsx
index f0c087b2dcad..3df7a5900dfb 100644
--- a/code/ui/manager/src/components/layout/Layout.tsx
+++ b/code/ui/manager/src/components/layout/Layout.tsx
@@ -95,7 +95,8 @@ const useLayoutSyncingState = ({
...nextState,
};
setManagerLayoutState(nextState);
- }, [internalDraggingSizeState, managerLayoutState, setManagerLayoutState]);
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [internalDraggingSizeState, setManagerLayoutState]);
const isPagesShown =
managerLayoutState.viewMode !== 'story' && managerLayoutState.viewMode !== 'docs';
From cf2be736709e1fc86787743a504fb833ccfe693b Mon Sep 17 00:00:00 2001
From: Kasper Peulen
Date: Wed, 17 Apr 2024 11:01:37 +0200
Subject: [PATCH 040/117] Add new actions to the bottom of the panel and auto
scroll
---
.../src/components/ActionLogger/index.tsx | 74 +++++++++++--------
.../src/containers/ActionLogger/index.tsx | 4 +-
.../src/components/ScrollArea/ScrollArea.tsx | 65 ++++++++--------
3 files changed, 76 insertions(+), 67 deletions(-)
diff --git a/code/addons/actions/src/components/ActionLogger/index.tsx b/code/addons/actions/src/components/ActionLogger/index.tsx
index a3e48337107f..4244f75cf449 100644
--- a/code/addons/actions/src/components/ActionLogger/index.tsx
+++ b/code/addons/actions/src/components/ActionLogger/index.tsx
@@ -1,22 +1,23 @@
-import type { FC, PropsWithChildren } from 'react';
-import React, { Fragment } from 'react';
-import { styled, withTheme } from '@storybook/theming';
+import type { ElementRef, ReactNode } from 'react';
+import React, { forwardRef, Fragment, useEffect, useRef } from 'react';
import type { Theme } from '@storybook/theming';
+import { styled, withTheme } from '@storybook/theming';
import { Inspector } from 'react-inspector';
import { ActionBar, ScrollArea } from '@storybook/components';
-import { Action, InspectorContainer, Counter } from './style';
+import { Action, Counter, InspectorContainer } from './style';
import type { ActionDisplay } from '../../models';
-const UnstyledWrapped: FC> = ({
- children,
- className,
-}) => (
-
- {children}
-
+const UnstyledWrapped = forwardRef(
+ ({ children, className }, ref) => (
+
+ {children}
+
+ )
);
+UnstyledWrapped.displayName = 'UnstyledWrapped';
+
export const Wrapper = styled(UnstyledWrapped)({
margin: 0,
padding: '10px 5px 20px',
@@ -39,24 +40,33 @@ interface ActionLoggerProps {
onClear: () => void;
}
-export const ActionLogger = ({ actions, onClear }: ActionLoggerProps) => (
-
-
- {actions.map((action: ActionDisplay) => (
-
- {action.count > 1 && {action.count}}
-
-
-
-
- ))}
-
-
-
-
-);
+export const ActionLogger = ({ actions, onClear }: ActionLoggerProps) => {
+ const wrapperRef = useRef>(null);
+ const wrapper = wrapperRef.current;
+ const wasAtBottom = wrapper && wrapper.scrollHeight - wrapper.scrollTop === wrapper.clientHeight;
+
+ useEffect(() => {
+ if (wasAtBottom) wrapperRef.current.scrollTop = wrapperRef.current.scrollHeight;
+ }, [wasAtBottom, actions.length]);
+
+ return (
+
+
+ {actions.map((action: ActionDisplay) => (
+
+ {action.count > 1 && {action.count}}
+
+
+
+
+ ))}
+
+
+
+ );
+};
diff --git a/code/addons/actions/src/containers/ActionLogger/index.tsx b/code/addons/actions/src/containers/ActionLogger/index.tsx
index bb88fd7f5b2a..6f5d5f3a1366 100644
--- a/code/addons/actions/src/containers/ActionLogger/index.tsx
+++ b/code/addons/actions/src/containers/ActionLogger/index.tsx
@@ -63,12 +63,12 @@ export default class ActionLogger extends Component {
this.setState((prevState: ActionLoggerState) => {
const actions = [...prevState.actions];
- const previous = actions.length && actions[0];
+ const previous = actions.length && actions[actions.length - 1];
if (previous && safeDeepEqual(previous.data, action.data)) {
previous.count++;
} else {
action.count = 1;
- actions.unshift(action);
+ actions.push(action);
}
return { actions: actions.slice(0, action.options.limit) };
});
diff --git a/code/ui/components/src/components/ScrollArea/ScrollArea.tsx b/code/ui/components/src/components/ScrollArea/ScrollArea.tsx
index f3f965af8783..cd697523d44d 100644
--- a/code/ui/components/src/components/ScrollArea/ScrollArea.tsx
+++ b/code/ui/components/src/components/ScrollArea/ScrollArea.tsx
@@ -1,4 +1,4 @@
-import React from 'react';
+import React, { forwardRef } from 'react';
import { styled } from '@storybook/theming';
import * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area';
@@ -75,36 +75,35 @@ const ScrollAreaThumb = styled(ScrollAreaPrimitive.Thumb)(({ theme }) => ({
},
}));
-export const ScrollArea = ({
- children,
- horizontal = false,
- vertical = false,
- offset = 2,
- scrollbarSize = 6,
- className,
-}: ScrollAreaProps) => (
-
- {children}
- {horizontal && (
-
-
-
- )}
- {vertical && (
-
-
-
- )}
- {horizontal && vertical && }
-
+export const ScrollArea = forwardRef(
+ (
+ { children, horizontal = false, vertical = false, offset = 2, scrollbarSize = 6, className },
+ ref
+ ) => (
+
+ {children}
+ {horizontal && (
+
+
+
+ )}
+ {vertical && (
+
+
+
+ )}
+ {horizontal && vertical && }
+
+ )
);
+ScrollArea.displayName = 'ScrollArea';
From 42773e33cabdce84dacf78148fee51708f70b6f2 Mon Sep 17 00:00:00 2001
From: Kasper Peulen
Date: Wed, 17 Apr 2024 15:16:35 +0200
Subject: [PATCH 041/117] Address review
---
code/addons/actions/src/components/ActionLogger/index.tsx | 1 +
1 file changed, 1 insertion(+)
diff --git a/code/addons/actions/src/components/ActionLogger/index.tsx b/code/addons/actions/src/components/ActionLogger/index.tsx
index 4244f75cf449..ec2e6ca2e102 100644
--- a/code/addons/actions/src/components/ActionLogger/index.tsx
+++ b/code/addons/actions/src/components/ActionLogger/index.tsx
@@ -46,6 +46,7 @@ export const ActionLogger = ({ actions, onClear }: ActionLoggerProps) => {
const wasAtBottom = wrapper && wrapper.scrollHeight - wrapper.scrollTop === wrapper.clientHeight;
useEffect(() => {
+ // Scroll to bottom, when the action panel was already scrolled down
if (wasAtBottom) wrapperRef.current.scrollTop = wrapperRef.current.scrollHeight;
}, [wasAtBottom, actions.length]);
From e850d364ffc73cd5d755e956237284de7b58d910 Mon Sep 17 00:00:00 2001
From: Jeppe Reinhold
Date: Wed, 17 Apr 2024 14:37:05 +0200
Subject: [PATCH 042/117] add cache.mock entrypoint to nextjs
---
code/frameworks/nextjs/package.json | 15 ++++++++++-----
1 file changed, 10 insertions(+), 5 deletions(-)
diff --git a/code/frameworks/nextjs/package.json b/code/frameworks/nextjs/package.json
index b09b2f383295..04757586be87 100644
--- a/code/frameworks/nextjs/package.json
+++ b/code/frameworks/nextjs/package.json
@@ -52,21 +52,26 @@
"require": "./dist/export-mocks/index.js",
"import": "./dist/export-mocks/index.mjs"
},
+ "./cache.mock": {
+ "types": "./dist/export-mocks/cache/index.d.ts",
+ "require": "./dist/export-mocks/cache/index.js",
+ "import": "./dist/export-mocks/cache/index.mjs"
+ },
"./headers.mock": {
"types": "./dist/export-mocks/headers/index.d.ts",
"require": "./dist/export-mocks/headers/index.js",
"import": "./dist/export-mocks/headers/index.mjs"
},
- "./router.mock": {
- "types": "./dist/export-mocks/router/index.d.ts",
- "require": "./dist/export-mocks/router/index.js",
- "import": "./dist/export-mocks/router/index.mjs"
- },
"./navigation.mock": {
"types": "./dist/export-mocks/navigation/index.d.ts",
"require": "./dist/export-mocks/navigation/index.js",
"import": "./dist/export-mocks/navigation/index.mjs"
},
+ "./router.mock": {
+ "types": "./dist/export-mocks/router/index.d.ts",
+ "require": "./dist/export-mocks/router/index.js",
+ "import": "./dist/export-mocks/router/index.mjs"
+ },
"./package.json": "./package.json"
},
"main": "dist/index.js",
From 401fd2681b3a48ab407a2b4f9e849530eeb36278 Mon Sep 17 00:00:00 2001
From: Kasper Peulen
Date: Tue, 16 Apr 2024 11:42:57 +0200
Subject: [PATCH 043/117] Try another mock naming convention
---
.../nextjs/src/export-mocks/cache/index.ts | 4 +-
.../src/export-mocks/headers/cookies.ts | 112 ++----------------
.../src/export-mocks/headers/headers.ts | 98 ++-------------
.../src/export-mocks/navigation/index.ts | 59 ++++-----
.../nextjs/src/export-mocks/router/index.ts | 30 +++--
5 files changed, 64 insertions(+), 239 deletions(-)
diff --git a/code/frameworks/nextjs/src/export-mocks/cache/index.ts b/code/frameworks/nextjs/src/export-mocks/cache/index.ts
index 1692979f0159..b9f2fa8dd431 100644
--- a/code/frameworks/nextjs/src/export-mocks/cache/index.ts
+++ b/code/frameworks/nextjs/src/export-mocks/cache/index.ts
@@ -3,8 +3,8 @@ import { unstable_cache } from 'next/dist/server/web/spec-extension/unstable-cac
import { unstable_noStore } from 'next/dist/server/web/spec-extension/unstable-no-store';
// mock utilities/overrides (as of Next v14.2.0)
-const revalidatePath = fn().mockName('revalidatePath');
-const revalidateTag = fn().mockName('revalidateTag');
+const revalidatePath = fn().mockName('next/cache::revalidatePath');
+const revalidateTag = fn().mockName('next/cache::revalidateTag');
const cacheExports = {
unstable_cache,
diff --git a/code/frameworks/nextjs/src/export-mocks/headers/cookies.ts b/code/frameworks/nextjs/src/export-mocks/headers/cookies.ts
index 75022baaea26..c3272dc0e335 100644
--- a/code/frameworks/nextjs/src/export-mocks/headers/cookies.ts
+++ b/code/frameworks/nextjs/src/export-mocks/headers/cookies.ts
@@ -1,116 +1,22 @@
-/* eslint-disable no-underscore-dangle */
import { fn } from '@storybook/test';
-import type { RequestCookies } from 'next/dist/compiled/@edge-runtime/cookies';
-import {
- parseCookie,
- stringifyCookie,
- type RequestCookie,
-} from 'next/dist/compiled/@edge-runtime/cookies';
+import { RequestCookies } from 'next/dist/compiled/@edge-runtime/cookies';
// We need this import to be a singleton, and because it's used in multiple entrypoints
// both in ESM and CJS, importing it via the package name instead of having a local import
// is the only way to achieve it actually being a singleton
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore we must ignore types here as during compilation they are not generated yet
-import { headers, type HeadersStore } from '@storybook/nextjs/headers.mock';
+import { headers } from '@storybook/nextjs/headers.mock';
-const stringifyCookies = (map: Map) => {
- return Array.from(map)
- .map(([_, v]) => stringifyCookie(v).replace(/; /, ''))
- .join('; ');
-};
-
-// Mostly copied from https://github.com/vercel/edge-runtime/blob/c25e2ded39104e2a3be82efc08baf8dc8fb436b3/packages/cookies/src/request-cookies.ts#L7
-class RequestCookiesMock implements RequestCookies {
- /** @internal */
- private readonly _headers: HeadersStore;
-
- _parsed: Map = new Map();
-
- constructor(requestHeaders: HeadersStore) {
- this._headers = requestHeaders;
- const header = requestHeaders?.get('cookie');
- if (header) {
- const parsed = parseCookie(header);
- for (const [name, value] of parsed) {
- this._parsed.set(name, { name, value });
- }
- }
- }
-
- [Symbol.iterator]() {
- return this._parsed[Symbol.iterator]();
- }
-
- get size(): number {
- return this._parsed.size;
- }
+class RequestCookiesMock extends RequestCookies {
+ get = fn(super.get).mockName('next/headers::get');
- get = fn((...args: [name: string] | [RequestCookie]) => {
- const name = typeof args[0] === 'string' ? args[0] : args[0].name;
- return this._parsed.get(name);
- }).mockName('cookies().get');
+ getAll = fn(super.getAll).mockName('next/headers::cookies().getAll');
- getAll = fn((...args: [name: string] | [RequestCookie] | []) => {
- const all = Array.from(this._parsed);
- if (!args.length) {
- return all.map(([_, value]) => value);
- }
+ has = fn(super.has).mockName('next/headers::cookies().has');
- const name = typeof args[0] === 'string' ? args[0] : args[0]?.name;
- return all.filter(([n]) => n === name).map(([_, value]) => value);
- }).mockName('cookies().getAll');
+ set = fn(super.set).mockName('next/headers::cookies().set');
- has = fn((name: string) => {
- return this._parsed.has(name);
- }).mockName('cookies().has');
-
- set = fn((...args: [key: string, value: string] | [options: RequestCookie]): this => {
- const [name, value] = args.length === 1 ? [args[0].name, args[0].value] : args;
-
- const map = this._parsed;
- map.set(name, { name, value });
-
- this._headers.set('cookie', stringifyCookies(map));
- return this;
- }).mockName('cookies().set');
-
- /**
- * Delete the cookies matching the passed name or names in the request.
- */
- delete = fn(
- (
- /** Name or names of the cookies to be deleted */
- names: string | string[]
- ): boolean | boolean[] => {
- const map = this._parsed;
- const result = !Array.isArray(names)
- ? map.delete(names)
- : names.map((name) => map.delete(name));
- this._headers.set('cookie', stringifyCookies(map));
- return result;
- }
- ).mockName('cookies().delete');
-
- /**
- * Delete all the cookies in the cookies in the request.
- */
- clear = fn((): this => {
- this.delete(Array.from(this._parsed.keys()));
- return this;
- }).mockName('cookies().clear');
-
- /**
- * Format the cookies in the request as a string for logging
- */
- [Symbol.for('edge-runtime.inspect.custom')]() {
- return `RequestCookies ${JSON.stringify(Object.fromEntries(this._parsed))}`;
- }
-
- toString() {
- return [...this._parsed.values()]
- .map((v) => `${v.name}=${encodeURIComponent(v.value)}`)
- .join('; ');
- }
+ delete = fn(super.delete).mockName('next/headers::cookies().delete');
}
let requestCookiesMock: RequestCookiesMock;
@@ -120,7 +26,7 @@ export const cookies = fn(() => {
requestCookiesMock = new RequestCookiesMock(headers());
}
return requestCookiesMock;
-});
+}).mockName('next/headers::cookies()');
const originalRestore = cookies.mockRestore.bind(null);
diff --git a/code/frameworks/nextjs/src/export-mocks/headers/headers.ts b/code/frameworks/nextjs/src/export-mocks/headers/headers.ts
index a7511064fa1f..02703265e583 100644
--- a/code/frameworks/nextjs/src/export-mocks/headers/headers.ts
+++ b/code/frameworks/nextjs/src/export-mocks/headers/headers.ts
@@ -1,101 +1,29 @@
import { fn } from '@storybook/test';
-import type { IncomingHttpHeaders } from 'http';
-import type { HeadersAdapter } from 'next/dist/server/web/spec-extension/adapters/headers';
-// Mostly copied from https://github.com/vercel/next.js/blob/763b9a660433ec5278a10e59d7ae89d4010ba212/packages/next/src/server/web/spec-extension/adapters/headers.ts#L20
-// @ts-expect-error unfortunately the headers property is private (and not protected) in HeadersAdapter
-// and we can't access it so we need to redefine it, but that clashes with the type, hence the ts-expect-error comment.
-class HeadersAdapterMock extends Headers implements HeadersAdapter {
- private headers: IncomingHttpHeaders = {};
+import { HeadersAdapter } from 'next/dist/server/web/spec-extension/adapters/headers';
- /**
- * Merges a header value into a string. This stores multiple values as an
- * array, so we need to merge them into a string.
- *
- * @param value a header value
- * @returns a merged header value (a string)
- */
- private merge(value: string | string[]): string {
- if (Array.isArray(value)) return value.join(', ');
-
- return value;
+class HeadersAdapterMock extends HeadersAdapter {
+ constructor() {
+ super({});
}
- public append = fn((name: string, value: string): void => {
- const existing = this.headers[name];
- if (typeof existing === 'string') {
- this.headers[name] = [existing, value];
- } else if (Array.isArray(existing)) {
- existing.push(value);
- } else {
- this.headers[name] = value;
- }
- }).mockName('headers().append');
-
- public delete = fn((name: string) => {
- delete this.headers[name];
- }).mockName('headers().delete');
-
- public get = fn((name: string): string | null => {
- const value = this.headers[name];
- if (typeof value !== 'undefined') return this.merge(value);
+ append = fn(super.append).mockName('next/headers::headers().append');
- return null;
- }).mockName('headers().get');
+ delete = fn(super.delete).mockName('next/headers::headers().delete');
- public has = fn((name: string): boolean => {
- return typeof this.headers[name] !== 'undefined';
- }).mockName('headers().has');
+ get = fn(super.get).mockName('next/headers::headers().get');
- public set = fn((name: string, value: string): void => {
- this.headers[name] = value;
- }).mockName('headers().set');
+ has = fn(super.has).mockName('next/headers::headers().has');
- public forEach = fn(
- (callbackfn: (value: string, name: string, parent: Headers) => void, thisArg?: any): void => {
- for (const [name, value] of this.entries()) {
- callbackfn.call(thisArg, value, name, this);
- }
- }
- ).mockName('headers().forEach');
+ set = fn(super.set).mockName('next/headers::headers().set');
- public entries = fn(
- function* (this: HeadersAdapterMock): IterableIterator<[string, string]> {
- for (const key of Object.keys(this.headers)) {
- const name = key.toLowerCase();
- // We assert here that this is a string because we got it from the
- // Object.keys() call above.
- const value = this.get(name) as string;
+ forEach = fn(super.forEach).mockName('next/headers::headers().forEach');
- yield [name, value];
- }
- }.bind(this)
- ).mockName('headers().entries');
+ entries = fn(super.entries).mockName('next/headers::headers().entries');
- public keys = fn(
- function* (this: HeadersAdapterMock): IterableIterator {
- for (const key of Object.keys(this.headers)) {
- const name = key.toLowerCase();
- yield name;
- }
- }.bind(this)
- ).mockName('headers().keys');
+ keys = fn(super.keys).mockName('next/headers::headers().keys');
- public values = fn(
- function* (this: HeadersAdapterMock): IterableIterator {
- for (const key of Object.keys(this.headers)) {
- // We assert here that this is a string because we got it from the
- // Object.keys() call above.
- const value = this.get(key) as string;
-
- yield value;
- }
- }.bind(this)
- ).mockName('headers().values');
-
- public [Symbol.iterator](): IterableIterator<[string, string]> {
- return this.entries();
- }
+ values = fn(super.values).mockName('next/headers::headers().values');
}
let headersAdapterMock: HeadersAdapterMock;
diff --git a/code/frameworks/nextjs/src/export-mocks/navigation/index.ts b/code/frameworks/nextjs/src/export-mocks/navigation/index.ts
index 47a7e5801b56..fbbeb8ced25c 100644
--- a/code/frameworks/nextjs/src/export-mocks/navigation/index.ts
+++ b/code/frameworks/nextjs/src/export-mocks/navigation/index.ts
@@ -17,14 +17,14 @@ let navigationAPI: {
* @ignore
* @internal
* */
-const createNavigation = (overrides: any) => {
+export const createNavigation = (overrides: any) => {
const navigationActions = {
- push: fn().mockName('useRouter().push'),
- replace: fn().mockName('useRouter().replace'),
- forward: fn().mockName('useRouter().forward'),
- back: fn().mockName('useRouter().back'),
- prefetch: fn().mockName('useRouter().prefetch'),
- refresh: fn().mockName('useRouter().refresh'),
+ push: fn().mockName('next/navigation::useRouter().push'),
+ replace: fn().mockName('next/navigation::useRouter().replace'),
+ forward: fn().mockName('next/navigation::useRouter().forward'),
+ back: fn().mockName('next/navigation::useRouter().back'),
+ prefetch: fn().mockName('next/navigation::useRouter().prefetch'),
+ refresh: fn().mockName('next/navigation::useRouter().refresh'),
};
if (overrides) {
@@ -42,7 +42,7 @@ const createNavigation = (overrides: any) => {
return navigationAPI;
};
-const getRouter = () => {
+export const getRouter = () => {
if (!navigationAPI) {
throw new NextjsRouterMocksNotAvailable({
importType: 'next/navigation',
@@ -56,41 +56,34 @@ const getRouter = () => {
export * from 'next/dist/client/components/navigation';
// mock utilities/overrides (as of Next v14.2.0)
-const redirect = fn().mockName('redirect');
+export const redirect = fn().mockName('next/navigation::redirect');
// passthrough mocks - keep original implementation but allow for spying
-const useSearchParams = fn(originalNavigation.useSearchParams).mockName('useSearchParams');
-const usePathname = fn(originalNavigation.usePathname).mockName('usePathname');
-const useSelectedLayoutSegment = fn(originalNavigation.useSelectedLayoutSegment).mockName(
+export const useSearchParams = fn(originalNavigation.useSearchParams).mockName(
+ 'next/navigation::useSearchParams'
+);
+export const usePathname = fn(originalNavigation.usePathname).mockName(
+ 'next/navigation::usePathname'
+);
+export const useSelectedLayoutSegment = fn(originalNavigation.useSelectedLayoutSegment).mockName(
'useSelectedLayoutSegment'
);
-const useSelectedLayoutSegments = fn(originalNavigation.useSelectedLayoutSegments).mockName(
+export const useSelectedLayoutSegments = fn(originalNavigation.useSelectedLayoutSegments).mockName(
'useSelectedLayoutSegments'
);
-const useRouter = fn(originalNavigation.useRouter).mockName('useRouter');
-const useServerInsertedHTML = fn(originalNavigation.useServerInsertedHTML).mockName(
+export const useRouter = fn(originalNavigation.useRouter).mockName('next/navigation::useRouter');
+export const useServerInsertedHTML = fn(originalNavigation.useServerInsertedHTML).mockName(
'useServerInsertedHTML'
);
-const notFound = fn(originalNavigation.notFound).mockName('notFound');
-const permanentRedirect = fn(originalNavigation.permanentRedirect).mockName('permanentRedirect');
+export const notFound = fn(originalNavigation.notFound).mockName('next/navigation::notFound');
+export const permanentRedirect = fn(originalNavigation.permanentRedirect).mockName(
+ 'permanentRedirect'
+);
// Params, not exported by Next.js, is manually declared to avoid inference issues.
interface Params {
[key: string]: string | string[];
}
-const useParams = fn<[], Params>(originalNavigation.useParams).mockName('useParams');
-
-export {
- createNavigation,
- getRouter,
- redirect,
- useSearchParams,
- usePathname,
- useSelectedLayoutSegment,
- useSelectedLayoutSegments,
- useParams,
- useRouter,
- useServerInsertedHTML,
- notFound,
- permanentRedirect,
-};
+export const useParams = fn<[], Params>(originalNavigation.useParams).mockName(
+ 'next/navigation::useParams'
+);
diff --git a/code/frameworks/nextjs/src/export-mocks/router/index.ts b/code/frameworks/nextjs/src/export-mocks/router/index.ts
index c9f81f3a72be..b3c5e37faa43 100644
--- a/code/frameworks/nextjs/src/export-mocks/router/index.ts
+++ b/code/frameworks/nextjs/src/export-mocks/router/index.ts
@@ -36,27 +36,27 @@ let routerAPI: {
* @ignore
* @internal
* */
-const createRouter = (overrides: Partial) => {
+export const createRouter = (overrides: Partial) => {
const routerActions: Partial = {
push: fn((..._args: any[]) => {
return Promise.resolve(true);
- }).mockName('useRouter().push'),
+ }).mockName('next/router::useRouter().push'),
replace: fn((..._args: any[]) => {
return Promise.resolve(true);
- }).mockName('useRouter().replace'),
- reload: fn((..._args: any[]) => {}).mockName('useRouter().reload'),
- back: fn((..._args: any[]) => {}).mockName('useRouter().back'),
- forward: fn(() => {}).mockName('useRouter().forward'),
+ }).mockName('next/router::useRouter().replace'),
+ reload: fn((..._args: any[]) => {}).mockName('next/router::useRouter().reload'),
+ back: fn((..._args: any[]) => {}).mockName('next/router::useRouter().back'),
+ forward: fn(() => {}).mockName('next/router::useRouter().forward'),
prefetch: fn((..._args: any[]) => {
return Promise.resolve();
- }).mockName('useRouter().prefetch'),
- beforePopState: fn((..._args: any[]) => {}).mockName('useRouter().beforePopState'),
+ }).mockName('next/router::useRouter().prefetch'),
+ beforePopState: fn((..._args: any[]) => {}).mockName('next/router::useRouter().beforePopState'),
};
const routerEvents: NextRouter['events'] = {
- on: fn((..._args: any[]) => {}).mockName('useRouter().events.on'),
- off: fn((..._args: any[]) => {}).mockName('useRouter().events.off'),
- emit: fn((..._args: any[]) => {}).mockName('useRouter().events.emit'),
+ on: fn((..._args: any[]) => {}).mockName('next/router::useRouter().events.on'),
+ off: fn((..._args: any[]) => {}).mockName('next/router::useRouter().events.off'),
+ emit: fn((..._args: any[]) => {}).mockName('next/router::useRouter().events.emit'),
};
if (overrides) {
@@ -95,7 +95,7 @@ const createRouter = (overrides: Partial) => {
return routerAPI as unknown as NextRouter;
};
-const getRouter = () => {
+export const getRouter = () => {
if (!routerAPI) {
throw new NextjsRouterMocksNotAvailable({
importType: 'next/router',
@@ -111,7 +111,5 @@ export default singletonRouter;
// mock utilities/overrides (as of Next v14.2.0)
// passthrough mocks - keep original implementation but allow for spying
-const useRouter = fn(originalRouter.useRouter).mockName('useRouter');
-const withRouter = fn(originalRouter.withRouter).mockName('withRouter');
-
-export { createRouter, getRouter, useRouter, withRouter };
+export const useRouter = fn(originalRouter.useRouter).mockName('next/router::useRouter');
+export const withRouter = fn(originalRouter.withRouter).mockName('next/router::withRouter');
From 9920353e677f7e9eaf73afb7dbc3e069126e71ab Mon Sep 17 00:00:00 2001
From: Kasper Peulen
Date: Tue, 16 Apr 2024 12:11:13 +0200
Subject: [PATCH 044/117] Bind this
---
.../nextjs/src/export-mocks/headers/cookies.ts | 10 +++++-----
.../nextjs/src/export-mocks/headers/headers.ts | 18 +++++++++---------
.../nextjs/yarn.lock | 8 ++++----
3 files changed, 18 insertions(+), 18 deletions(-)
diff --git a/code/frameworks/nextjs/src/export-mocks/headers/cookies.ts b/code/frameworks/nextjs/src/export-mocks/headers/cookies.ts
index c3272dc0e335..3fc9fdc1ae59 100644
--- a/code/frameworks/nextjs/src/export-mocks/headers/cookies.ts
+++ b/code/frameworks/nextjs/src/export-mocks/headers/cookies.ts
@@ -8,15 +8,15 @@ import { RequestCookies } from 'next/dist/compiled/@edge-runtime/cookies';
import { headers } from '@storybook/nextjs/headers.mock';
class RequestCookiesMock extends RequestCookies {
- get = fn(super.get).mockName('next/headers::get');
+ get = fn(super.get.bind(this)).mockName('next/headers::get');
- getAll = fn(super.getAll).mockName('next/headers::cookies().getAll');
+ getAll = fn(super.getAll.bind(this)).mockName('next/headers::cookies().getAll');
- has = fn(super.has).mockName('next/headers::cookies().has');
+ has = fn(super.has.bind(this)).mockName('next/headers::cookies().has');
- set = fn(super.set).mockName('next/headers::cookies().set');
+ set = fn(super.set.bind(this)).mockName('next/headers::cookies().set');
- delete = fn(super.delete).mockName('next/headers::cookies().delete');
+ delete = fn(super.delete.bind(this)).mockName('next/headers::cookies().delete');
}
let requestCookiesMock: RequestCookiesMock;
diff --git a/code/frameworks/nextjs/src/export-mocks/headers/headers.ts b/code/frameworks/nextjs/src/export-mocks/headers/headers.ts
index 02703265e583..d9eb5177b447 100644
--- a/code/frameworks/nextjs/src/export-mocks/headers/headers.ts
+++ b/code/frameworks/nextjs/src/export-mocks/headers/headers.ts
@@ -7,23 +7,23 @@ class HeadersAdapterMock extends HeadersAdapter {
super({});
}
- append = fn(super.append).mockName('next/headers::headers().append');
+ append = fn(super.append.bind(this)).mockName('next/headers::headers().append');
- delete = fn(super.delete).mockName('next/headers::headers().delete');
+ delete = fn(super.delete.bind(this)).mockName('next/headers::headers().delete');
- get = fn(super.get).mockName('next/headers::headers().get');
+ get = fn(super.get.bind(this)).mockName('next/headers::headers().get');
- has = fn(super.has).mockName('next/headers::headers().has');
+ has = fn(super.has.bind(this)).mockName('next/headers::headers().has');
- set = fn(super.set).mockName('next/headers::headers().set');
+ set = fn(super.set.bind(this)).mockName('next/headers::headers().set');
- forEach = fn(super.forEach).mockName('next/headers::headers().forEach');
+ forEach = fn(super.forEach.bind(this)).mockName('next/headers::headers().forEach');
- entries = fn(super.entries).mockName('next/headers::headers().entries');
+ entries = fn(super.entries.bind(this)).mockName('next/headers::headers().entries');
- keys = fn(super.keys).mockName('next/headers::headers().keys');
+ keys = fn(super.keys.bind(this)).mockName('next/headers::headers().keys');
- values = fn(super.values).mockName('next/headers::headers().values');
+ values = fn(super.values.bind(this)).mockName('next/headers::headers().values');
}
let headersAdapterMock: HeadersAdapterMock;
diff --git a/test-storybooks/portable-stories-kitchen-sink/nextjs/yarn.lock b/test-storybooks/portable-stories-kitchen-sink/nextjs/yarn.lock
index e0efcfd9d6d9..71f8b86f9807 100644
--- a/test-storybooks/portable-stories-kitchen-sink/nextjs/yarn.lock
+++ b/test-storybooks/portable-stories-kitchen-sink/nextjs/yarn.lock
@@ -3167,7 +3167,7 @@ __metadata:
"@storybook/nextjs@file:../../../code/frameworks/nextjs::locator=portable-stories-nextjs%40workspace%3A.":
version: 8.1.0-alpha.7
- resolution: "@storybook/nextjs@file:../../../code/frameworks/nextjs#../../../code/frameworks/nextjs::hash=246816&locator=portable-stories-nextjs%40workspace%3A."
+ resolution: "@storybook/nextjs@file:../../../code/frameworks/nextjs#../../../code/frameworks/nextjs::hash=28a407&locator=portable-stories-nextjs%40workspace%3A."
dependencies:
"@babel/core": "npm:^7.23.2"
"@babel/plugin-syntax-bigint": "npm:^7.8.3"
@@ -3227,7 +3227,7 @@ __metadata:
optional: true
webpack:
optional: true
- checksum: 10/71732cc220e381872106dab3824883f4a77dcbb60901c939f58599bf92f156564a34fe646ce1159c307702be03cf532fbffaa1a515e271a6623f340d19000283
+ checksum: 10/fd9a469074e0b7aba065a0b0f81f272e75e708370362ff047a653d9a19c5b753fcfab8b2f30b3f9ae226a12739b7b11054358afe4707e8d7185b9dca11c75a7c
languageName: node
linkType: hard
@@ -3326,7 +3326,7 @@ __metadata:
"@storybook/react@file:../../../code/renderers/react::locator=portable-stories-nextjs%40workspace%3A.":
version: 8.1.0-alpha.7
- resolution: "@storybook/react@file:../../../code/renderers/react#../../../code/renderers/react::hash=cd73fa&locator=portable-stories-nextjs%40workspace%3A."
+ resolution: "@storybook/react@file:../../../code/renderers/react#../../../code/renderers/react::hash=faf3f4&locator=portable-stories-nextjs%40workspace%3A."
dependencies:
"@storybook/client-logger": "workspace:*"
"@storybook/docs-tools": "workspace:*"
@@ -3356,7 +3356,7 @@ __metadata:
peerDependenciesMeta:
typescript:
optional: true
- checksum: 10/834084c0092a8b179f7048c1111308b6a2381cf3ee119372ed2940833b02e13577b5bc334c3f14c3224bd2d8b786939a74fbaf4dea6e8423ff610b34047412b4
+ checksum: 10/f89fb42e87e17377af48b57b337dd4708053ae83d8558a0ae35af479b1faa6d7206b07934e18113707febed5ebcf94510caa89b8c92bc87cf7b5391296bbfef2
languageName: node
linkType: hard
From b55673f1d8067a2770c69b98b91f6856bade4137 Mon Sep 17 00:00:00 2001
From: Kasper Peulen
Date: Tue, 16 Apr 2024 13:02:34 +0200
Subject: [PATCH 045/117] Update snapshot
---
.../nextjs/stories/__snapshots__/portable-stories.test.tsx.snap | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/test-storybooks/portable-stories-kitchen-sink/nextjs/stories/__snapshots__/portable-stories.test.tsx.snap b/test-storybooks/portable-stories-kitchen-sink/nextjs/stories/__snapshots__/portable-stories.test.tsx.snap
index 2b8af2b9cd59..a22fa06a8ab3 100644
--- a/test-storybooks/portable-stories-kitchen-sink/nextjs/stories/__snapshots__/portable-stories.test.tsx.snap
+++ b/test-storybooks/portable-stories-kitchen-sink/nextjs/stories/__snapshots__/portable-stories.test.tsx.snap
@@ -794,7 +794,7 @@ exports[`renders nextHeaderStories stories renders Default 1`] = `
- firstName=Jane; lastName=Doe
+ firstName=Jane; ; lastName=Doe;
Date: Tue, 16 Apr 2024 13:11:55 +0200
Subject: [PATCH 046/117] Fix name
---
code/frameworks/nextjs/src/export-mocks/headers/cookies.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/code/frameworks/nextjs/src/export-mocks/headers/cookies.ts b/code/frameworks/nextjs/src/export-mocks/headers/cookies.ts
index 3fc9fdc1ae59..3d84ecba3885 100644
--- a/code/frameworks/nextjs/src/export-mocks/headers/cookies.ts
+++ b/code/frameworks/nextjs/src/export-mocks/headers/cookies.ts
@@ -8,7 +8,7 @@ import { RequestCookies } from 'next/dist/compiled/@edge-runtime/cookies';
import { headers } from '@storybook/nextjs/headers.mock';
class RequestCookiesMock extends RequestCookies {
- get = fn(super.get.bind(this)).mockName('next/headers::get');
+ get = fn(super.get.bind(this)).mockName('next/headers::cookies().get');
getAll = fn(super.getAll.bind(this)).mockName('next/headers::cookies().getAll');
From 537fbb22a01d14137fa12492c8bfd88655387e0e Mon Sep 17 00:00:00 2001
From: Kasper Peulen
Date: Tue, 16 Apr 2024 14:08:31 +0200
Subject: [PATCH 047/117] Hide some junk of next for nwo
---
code/addons/actions/src/loaders.ts | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)
diff --git a/code/addons/actions/src/loaders.ts b/code/addons/actions/src/loaders.ts
index d49a048da231..a1fff870b7be 100644
--- a/code/addons/actions/src/loaders.ts
+++ b/code/addons/actions/src/loaders.ts
@@ -18,7 +18,19 @@ const logActionsWhenMockCalled: LoaderFunction = (context) => {
typeof global.__STORYBOOK_TEST_ON_MOCK_CALL__ === 'function'
) {
const onMockCall = global.__STORYBOOK_TEST_ON_MOCK_CALL__ as typeof onMockCallType;
- onMockCall((mock, args) => action(mock.getMockName())(args));
+ onMockCall((mock, args) => {
+ const name = mock.getMockName();
+
+ // TODO: Make this a configurable API in 8.2
+ if (
+ !/^next\/.*::/.test(name) ||
+ ((name.startsWith('next/headers::cookies()') ||
+ name.startsWith('next/headers::headers()')) &&
+ (name.endsWith('set') || name.endsWith('delete')))
+ ) {
+ action(name)(args);
+ }
+ });
subscribed = true;
}
};
From 7cacdfab2aea2c479e9a3a93c18f4f0ff7a007b0 Mon Sep 17 00:00:00 2001
From: Kasper Peulen
Date: Tue, 16 Apr 2024 14:13:47 +0200
Subject: [PATCH 048/117] Update
code/frameworks/nextjs/src/export-mocks/navigation/index.ts
Co-authored-by: Yann Braga
---
code/frameworks/nextjs/src/export-mocks/navigation/index.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/code/frameworks/nextjs/src/export-mocks/navigation/index.ts b/code/frameworks/nextjs/src/export-mocks/navigation/index.ts
index fbbeb8ced25c..61344102c242 100644
--- a/code/frameworks/nextjs/src/export-mocks/navigation/index.ts
+++ b/code/frameworks/nextjs/src/export-mocks/navigation/index.ts
@@ -77,7 +77,7 @@ export const useServerInsertedHTML = fn(originalNavigation.useServerInsertedHTML
);
export const notFound = fn(originalNavigation.notFound).mockName('next/navigation::notFound');
export const permanentRedirect = fn(originalNavigation.permanentRedirect).mockName(
- 'permanentRedirect'
+ 'next/navigation::permanentRedirect'
);
// Params, not exported by Next.js, is manually declared to avoid inference issues.
From af31a1fd822c56f5279e2c3c4b956758be1dd199 Mon Sep 17 00:00:00 2001
From: Kasper Peulen
Date: Tue, 16 Apr 2024 14:13:53 +0200
Subject: [PATCH 049/117] Update
code/frameworks/nextjs/src/export-mocks/navigation/index.ts
Co-authored-by: Yann Braga
---
code/frameworks/nextjs/src/export-mocks/navigation/index.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/code/frameworks/nextjs/src/export-mocks/navigation/index.ts b/code/frameworks/nextjs/src/export-mocks/navigation/index.ts
index 61344102c242..2973c65120ed 100644
--- a/code/frameworks/nextjs/src/export-mocks/navigation/index.ts
+++ b/code/frameworks/nextjs/src/export-mocks/navigation/index.ts
@@ -66,7 +66,7 @@ export const usePathname = fn(originalNavigation.usePathname).mockName(
'next/navigation::usePathname'
);
export const useSelectedLayoutSegment = fn(originalNavigation.useSelectedLayoutSegment).mockName(
- 'useSelectedLayoutSegment'
+ 'next/navigation::useSelectedLayoutSegment'
);
export const useSelectedLayoutSegments = fn(originalNavigation.useSelectedLayoutSegments).mockName(
'useSelectedLayoutSegments'
From 789991fefed476e17f08caf198cd4bed42a713f0 Mon Sep 17 00:00:00 2001
From: Kasper Peulen
Date: Tue, 16 Apr 2024 14:14:02 +0200
Subject: [PATCH 050/117] Update
code/frameworks/nextjs/src/export-mocks/navigation/index.ts
Co-authored-by: Yann Braga
---
code/frameworks/nextjs/src/export-mocks/navigation/index.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/code/frameworks/nextjs/src/export-mocks/navigation/index.ts b/code/frameworks/nextjs/src/export-mocks/navigation/index.ts
index 2973c65120ed..63458b0fc927 100644
--- a/code/frameworks/nextjs/src/export-mocks/navigation/index.ts
+++ b/code/frameworks/nextjs/src/export-mocks/navigation/index.ts
@@ -73,7 +73,7 @@ export const useSelectedLayoutSegments = fn(originalNavigation.useSelectedLayout
);
export const useRouter = fn(originalNavigation.useRouter).mockName('next/navigation::useRouter');
export const useServerInsertedHTML = fn(originalNavigation.useServerInsertedHTML).mockName(
- 'useServerInsertedHTML'
+ 'next/navigation::useServerInsertedHTML'
);
export const notFound = fn(originalNavigation.notFound).mockName('next/navigation::notFound');
export const permanentRedirect = fn(originalNavigation.permanentRedirect).mockName(
From 17c5152d5d43b4f7e94b3566245fa0013c708682 Mon Sep 17 00:00:00 2001
From: Kasper Peulen
Date: Tue, 16 Apr 2024 14:14:07 +0200
Subject: [PATCH 051/117] Update
code/frameworks/nextjs/src/export-mocks/navigation/index.ts
Co-authored-by: Yann Braga
---
code/frameworks/nextjs/src/export-mocks/navigation/index.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/code/frameworks/nextjs/src/export-mocks/navigation/index.ts b/code/frameworks/nextjs/src/export-mocks/navigation/index.ts
index 63458b0fc927..dd9e9a692e6f 100644
--- a/code/frameworks/nextjs/src/export-mocks/navigation/index.ts
+++ b/code/frameworks/nextjs/src/export-mocks/navigation/index.ts
@@ -69,7 +69,7 @@ export const useSelectedLayoutSegment = fn(originalNavigation.useSelectedLayoutS
'next/navigation::useSelectedLayoutSegment'
);
export const useSelectedLayoutSegments = fn(originalNavigation.useSelectedLayoutSegments).mockName(
- 'useSelectedLayoutSegments'
+ 'next/navigation::useSelectedLayoutSegments'
);
export const useRouter = fn(originalNavigation.useRouter).mockName('next/navigation::useRouter');
export const useServerInsertedHTML = fn(originalNavigation.useServerInsertedHTML).mockName(
From fc7e66439d9adffb29e62e76b04ee7c1738e86e3 Mon Sep 17 00:00:00 2001
From: Kasper Peulen
Date: Tue, 16 Apr 2024 14:19:52 +0200
Subject: [PATCH 052/117] Hide some junk of next for now
---
code/addons/actions/src/loaders.ts | 2 ++
1 file changed, 2 insertions(+)
diff --git a/code/addons/actions/src/loaders.ts b/code/addons/actions/src/loaders.ts
index a1fff870b7be..7f279bba5775 100644
--- a/code/addons/actions/src/loaders.ts
+++ b/code/addons/actions/src/loaders.ts
@@ -24,6 +24,8 @@ const logActionsWhenMockCalled: LoaderFunction = (context) => {
// TODO: Make this a configurable API in 8.2
if (
!/^next\/.*::/.test(name) ||
+ name.startsWith('next/router::useRouter()') ||
+ name.startsWith('next/navigation::useRouter()') ||
((name.startsWith('next/headers::cookies()') ||
name.startsWith('next/headers::headers()')) &&
(name.endsWith('set') || name.endsWith('delete')))
From 5fafb468807e041e93d1bbb9ed9245058723a82b Mon Sep 17 00:00:00 2001
From: Kasper Peulen
Date: Tue, 16 Apr 2024 15:09:15 +0200
Subject: [PATCH 053/117] Show redirect and next/cache spies
---
code/addons/actions/src/loaders.ts | 15 ++++++++++-----
1 file changed, 10 insertions(+), 5 deletions(-)
diff --git a/code/addons/actions/src/loaders.ts b/code/addons/actions/src/loaders.ts
index 7f279bba5775..eebb09acb71b 100644
--- a/code/addons/actions/src/loaders.ts
+++ b/code/addons/actions/src/loaders.ts
@@ -24,11 +24,16 @@ const logActionsWhenMockCalled: LoaderFunction = (context) => {
// TODO: Make this a configurable API in 8.2
if (
!/^next\/.*::/.test(name) ||
- name.startsWith('next/router::useRouter()') ||
- name.startsWith('next/navigation::useRouter()') ||
- ((name.startsWith('next/headers::cookies()') ||
- name.startsWith('next/headers::headers()')) &&
- (name.endsWith('set') || name.endsWith('delete')))
+ [
+ 'next/router::useRouter()',
+ 'next/navigation::useRouter()',
+ 'next/navigation::redirect',
+ 'next/cache::',
+ 'next/headers::cookies().set',
+ 'next/headers::cookies().delete',
+ 'next/headers::headers().set',
+ 'next/headers::headers().delete',
+ ].some((prefix) => name.startsWith(prefix))
) {
action(name)(args);
}
From a9ce1d4e2d926be30b6e945b233caf5c621417fb Mon Sep 17 00:00:00 2001
From: Kyle Gach
Date: Tue, 30 Apr 2024 11:45:15 -0600
Subject: [PATCH 054/117] Apply suggestions from code review
Co-authored-by: jonniebigodes
---
docs/get-started/nextjs.md | 8 ++++----
docs/writing-stories/build-pages-with-storybook.md | 2 +-
docs/writing-stories/mocking-modules.md | 4 ++--
3 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/docs/get-started/nextjs.md b/docs/get-started/nextjs.md
index 3c41ed990db7..99314381ae23 100644
--- a/docs/get-started/nextjs.md
+++ b/docs/get-started/nextjs.md
@@ -688,7 +688,7 @@ To configure subpath imports, you define the `imports` property in your project'
-Because subpath imports take the place of module aliases, you can remove the path aliases from your TypeScript configuration.
+Because subpath imports replace module aliases, you can remove the path aliases from your TypeScript configuration.
@@ -726,7 +726,7 @@ This framework provides mocks for many of Next.js' internal modules:
How you mock other modules in Storybook depends on how you import the module into your component.
-The first step, with either approach, is to [create a mock file](../writing-stories/mocking-modules.md#mock-files). Here's an example of a mock file for a module named `session`:
+With either approach, the first step is to [create a mock file](../writing-stories/mocking-modules.md#mock-files). Here's an example of a mock file for a module named `session`:
@@ -1024,13 +1024,13 @@ You can refer to the [Install `sharp` to Use Built-In Image Optimization](https:
### Modules
-The `@storybook/nextjs` package exports a number of modules that enables you to [mock](#mocking-modules) Next.js's internal behavior.
+The `@storybook/nextjs` package exports several modules that enable you to [mock](#mocking-modules) Next.js's internal behavior.
#### `@storybook/nextjs/export-mocks`
Type: `{ getPackageAliases: ({ useESM?: boolean }) => void }`
-`getPackageAliases` is a helper to generate the aliases needed to set up [portable stories](#portable-stories).
+`getPackageAliases` is a helper for generating the aliases needed to set up [portable stories](#portable-stories).
```ts
// jest.config.ts
diff --git a/docs/writing-stories/build-pages-with-storybook.md b/docs/writing-stories/build-pages-with-storybook.md
index eb6fefd7842b..dc7b34bd8a44 100644
--- a/docs/writing-stories/build-pages-with-storybook.md
+++ b/docs/writing-stories/build-pages-with-storybook.md
@@ -81,7 +81,7 @@ Components can receive data or configuration from context providers. For example
### [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.
+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)
diff --git a/docs/writing-stories/mocking-modules.md b/docs/writing-stories/mocking-modules.md
index 43db0d9b1f67..b5d9e670f1af 100644
--- a/docs/writing-stories/mocking-modules.md
+++ b/docs/writing-stories/mocking-modules.md
@@ -33,7 +33,7 @@ export const getUserFromSession = fn(actual.getUserFromSession);
### Mock files for external modules
-You can't directly mock an external module like `uuid` or `node:fs`. Instead, you must wrap the module in you own module, which you can then mock like any other internal module. In this example, we wrap `uuid`:
+You can't directly mock an external module like [`uuid`](https://github.com/uuidjs/uuid) or `node:fs`. Instead, you must wrap it in your own module, which you can mock like any other internal one. For example, with `uuid`, you could do the following:
```ts
// lib/uuid.ts
@@ -190,7 +190,7 @@ export const Default: Story = {
-If you are [writing your stories in TypeScript](./typescript.md), you will need to import your mock modules using the full mocked file name to have the mocked function correctly typed in your stories. You do **not** need do this in your component files, that's what the [subpath import](#subpath-imports) or [builder alias](#builder-aliases) is for.
+If you are [writing your stories in TypeScript](./typescript.md), you must import your mock modules using the full mocked file name to have the functions correctly typed in your stories. You do **not** need to do this in your component files. That's what the [subpath import](#subpath-imports) or [builder alias](#builder-aliases) is for.
From 01c65098a82f68b8b9311ea4b8c0e7bc241ed014 Mon Sep 17 00:00:00 2001
From: Kyle Gach
Date: Tue, 30 Apr 2024 13:53:17 -0600
Subject: [PATCH 055/117] Address feedback
- 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
---
docs/configure/story-rendering.md | 26 ++++++++++++++++
docs/get-started/nextjs.md | 30 +++++++++----------
...sw-addon-configure-handlers-graphql.ts.mdx | 4 +--
...sw-addon-configure-handlers-graphql.js.mdx | 4 +--
...ddon-configure-handlers-graphql.ts-4-9.mdx | 4 +--
...sw-addon-configure-handlers-graphql.ts.mdx | 4 +--
...sw-addon-configure-handlers-graphql.js.mdx | 4 +--
...ddon-configure-handlers-graphql.ts-4-9.mdx | 4 +--
...sw-addon-configure-handlers-graphql.ts.mdx | 4 +--
...sw-addon-configure-handlers-graphql.js.mdx | 4 +--
...ddon-configure-handlers-graphql.ts-4-9.mdx | 4 +--
...sw-addon-configure-handlers-graphql.ts.mdx | 4 +--
docs/toc.js | 8 ++---
.../build-pages-with-storybook.md | 10 +++----
docs/writing-stories/decorators.md | 4 +++
docs/writing-stories/mocking-modules.md | 27 +++++++----------
docs/writing-stories/mocking-providers.md | 18 ++++++++++-
17 files changed, 101 insertions(+), 62 deletions(-)
diff --git a/docs/configure/story-rendering.md b/docs/configure/story-rendering.md
index 1ca2d09c76d8..87f8bc8c8d9b 100644
--- a/docs/configure/story-rendering.md
+++ b/docs/configure/story-rendering.md
@@ -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.
+
+
+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:
@@ -25,6 +49,8 @@ For example, with Vue, you can extend Storybook's application and register your
+
+
## Adding to <head>
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:
diff --git a/docs/get-started/nextjs.md b/docs/get-started/nextjs.md
index 99314381ae23..36140f9def16 100644
--- a/docs/get-started/nextjs.md
+++ b/docs/get-started/nextjs.md
@@ -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: {
@@ -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: {
@@ -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": [
@@ -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": {
@@ -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:
-```json
+```jsonc
// package.json
{
"imports": {
@@ -827,7 +827,7 @@ module.exports = {
Calls to `getConfig` would return the following object when called within Storybook:
-```json
+```jsonc
// Runtime config
{
"serverRuntimeConfig": {},
@@ -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": {
@@ -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
@@ -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",
@@ -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).
@@ -1095,7 +1095,7 @@ 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:
@@ -1103,7 +1103,7 @@ Next.js's default [`headers()`](https://nextjs.org/docs/app/api-reference/functi
- **`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`.
@@ -1145,7 +1145,7 @@ export const LoggedInEurope: Story = {
Type: `typeof import('next/navigation') & getRouter: () => ReturnType`
-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).
@@ -1194,7 +1194,7 @@ export const GoBack: Story = {
Type: `typeof import('next/router') & getRouter: () => ReturnType`
-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).
diff --git a/docs/snippets/angular/msw-addon-configure-handlers-graphql.ts.mdx b/docs/snippets/angular/msw-addon-configure-handlers-graphql.ts.mdx
index dc3347cfd980..f35bcd16f033 100644
--- a/docs/snippets/angular/msw-addon-configure-handlers-graphql.ts.mdx
+++ b/docs/snippets/angular/msw-addon-configure-handlers-graphql.ts.mdx
@@ -59,8 +59,8 @@ export const MockedSuccess: Story = {
graphql.query('AllInfoQuery', () => {
return new HttpResponse.json({
data: {
- allFilms: {
- films,
+ AllInfo: {
+ ...TestData,
},
},
});
diff --git a/docs/snippets/react/msw-addon-configure-handlers-graphql.js.mdx b/docs/snippets/react/msw-addon-configure-handlers-graphql.js.mdx
index dee1f7976ec4..0138bd74920c 100644
--- a/docs/snippets/react/msw-addon-configure-handlers-graphql.js.mdx
+++ b/docs/snippets/react/msw-addon-configure-handlers-graphql.js.mdx
@@ -63,8 +63,8 @@ export const MockedSuccess = {
graphql.query('AllInfoQuery', () => {
return new HttpResponse.json({
data: {
- allFilms: {
- films,
+ AllInfo: {
+ ...TestData,
},
},
});
diff --git a/docs/snippets/react/msw-addon-configure-handlers-graphql.ts-4-9.mdx b/docs/snippets/react/msw-addon-configure-handlers-graphql.ts-4-9.mdx
index 2fd10eed5dec..468c1866b98c 100644
--- a/docs/snippets/react/msw-addon-configure-handlers-graphql.ts-4-9.mdx
+++ b/docs/snippets/react/msw-addon-configure-handlers-graphql.ts-4-9.mdx
@@ -67,8 +67,8 @@ export const MockedSuccess: Story = {
graphql.query('AllInfoQuery', () => {
return new HttpResponse.json({
data: {
- allFilms: {
- films,
+ AllInfo: {
+ ...TestData,
},
}
});
diff --git a/docs/snippets/react/msw-addon-configure-handlers-graphql.ts.mdx b/docs/snippets/react/msw-addon-configure-handlers-graphql.ts.mdx
index 4367a53a25e9..281441a95612 100644
--- a/docs/snippets/react/msw-addon-configure-handlers-graphql.ts.mdx
+++ b/docs/snippets/react/msw-addon-configure-handlers-graphql.ts.mdx
@@ -67,8 +67,8 @@ export const MockedSuccess: Story = {
graphql.query('AllInfoQuery', () => {
return new HttpResponse.json({
data: {
- allFilms: {
- films,
+ AllInfo: {
+ ...TestData,
},
}
});
diff --git a/docs/snippets/svelte/msw-addon-configure-handlers-graphql.js.mdx b/docs/snippets/svelte/msw-addon-configure-handlers-graphql.js.mdx
index 829ec728386a..bbe3531e64b0 100644
--- a/docs/snippets/svelte/msw-addon-configure-handlers-graphql.js.mdx
+++ b/docs/snippets/svelte/msw-addon-configure-handlers-graphql.js.mdx
@@ -42,8 +42,8 @@ export const MockedSuccess = {
graphql.query('AllInfoQuery', () => {
return new HttpResponse.json({
data: {
- allFilms: {
- films,
+ AllInfo: {
+ ...TestData,
},
},
});
diff --git a/docs/snippets/svelte/msw-addon-configure-handlers-graphql.ts-4-9.mdx b/docs/snippets/svelte/msw-addon-configure-handlers-graphql.ts-4-9.mdx
index 866603602f16..91e56485b7a5 100644
--- a/docs/snippets/svelte/msw-addon-configure-handlers-graphql.ts-4-9.mdx
+++ b/docs/snippets/svelte/msw-addon-configure-handlers-graphql.ts-4-9.mdx
@@ -47,8 +47,8 @@ export const MockedSuccess: Story = {
graphql.query('AllInfoQuery', () => {
return new HttpResponse.json({
data: {
- allFilms: {
- films,
+ AllInfo: {
+ ...TestData,
},
},
});
diff --git a/docs/snippets/svelte/msw-addon-configure-handlers-graphql.ts.mdx b/docs/snippets/svelte/msw-addon-configure-handlers-graphql.ts.mdx
index d78ffb4ce4e8..cacc4af7674a 100644
--- a/docs/snippets/svelte/msw-addon-configure-handlers-graphql.ts.mdx
+++ b/docs/snippets/svelte/msw-addon-configure-handlers-graphql.ts.mdx
@@ -47,8 +47,8 @@ export const MockedSuccess: Story = {
graphql.query('AllInfoQuery', () => {
return new HttpResponse.json({
data: {
- allFilms: {
- films,
+ AllInfo: {
+ ...TestData,
},
},
});
diff --git a/docs/snippets/vue/msw-addon-configure-handlers-graphql.js.mdx b/docs/snippets/vue/msw-addon-configure-handlers-graphql.js.mdx
index 251b119a6629..3e13c859cc70 100644
--- a/docs/snippets/vue/msw-addon-configure-handlers-graphql.js.mdx
+++ b/docs/snippets/vue/msw-addon-configure-handlers-graphql.js.mdx
@@ -45,8 +45,8 @@ export const MockedSuccess = {
graphql.query('AllInfoQuery', () => {
return new HttpResponse.json({
data: {
- allFilms: {
- films,
+ AllInfo: {
+ ...TestData,
},
},
});
diff --git a/docs/snippets/vue/msw-addon-configure-handlers-graphql.ts-4-9.mdx b/docs/snippets/vue/msw-addon-configure-handlers-graphql.ts-4-9.mdx
index c4b55f47f6ef..0efceab56d6a 100644
--- a/docs/snippets/vue/msw-addon-configure-handlers-graphql.ts-4-9.mdx
+++ b/docs/snippets/vue/msw-addon-configure-handlers-graphql.ts-4-9.mdx
@@ -50,8 +50,8 @@ export const MockedSuccess: Story = {
graphql.query('AllInfoQuery', () => {
return new HttpResponse.json({
data: {
- allFilms: {
- films,
+ AllInfo: {
+ ...TestData,
},
},
});
diff --git a/docs/snippets/vue/msw-addon-configure-handlers-graphql.ts.mdx b/docs/snippets/vue/msw-addon-configure-handlers-graphql.ts.mdx
index 6d1b27cb6946..fd23daacece7 100644
--- a/docs/snippets/vue/msw-addon-configure-handlers-graphql.ts.mdx
+++ b/docs/snippets/vue/msw-addon-configure-handlers-graphql.ts.mdx
@@ -50,8 +50,8 @@ export const MockedSuccess: Story = {
graphql.query('AllInfoQuery', () => {
return new HttpResponse.json({
data: {
- allFilms: {
- films,
+ AllInfo: {
+ ...TestData,
},
},
});
diff --git a/docs/toc.js b/docs/toc.js
index 7dbc4bceec4c..c872f84d2aa4 100644
--- a/docs/toc.js
+++ b/docs/toc.js
@@ -147,8 +147,8 @@ module.exports = {
type: 'menu',
children: [
{
- pathSegment: 'mocking-providers',
- title: 'Providers',
+ pathSegment: 'mocking-modules',
+ title: 'Modules',
type: 'link',
},
{
@@ -157,8 +157,8 @@ module.exports = {
type: 'link',
},
{
- pathSegment: 'mocking-modules',
- title: 'Modules',
+ pathSegment: 'mocking-providers',
+ title: 'Providers',
type: 'link',
},
],
diff --git a/docs/writing-stories/build-pages-with-storybook.md b/docs/writing-stories/build-pages-with-storybook.md
index dc7b34bd8a44..0563733e0dac 100644
--- a/docs/writing-stories/build-pages-with-storybook.md
+++ b/docs/writing-stories/build-pages-with-storybook.md
@@ -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.
diff --git a/docs/writing-stories/decorators.md b/docs/writing-stories/decorators.md
index da53026077e3..f673ed393c04 100644
--- a/docs/writing-stories/decorators.md
+++ b/docs/writing-stories/decorators.md
@@ -80,8 +80,12 @@ const preview: Preview = {
export default preview;
```
+
+
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.
+
+
### 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.
diff --git a/docs/writing-stories/mocking-modules.md b/docs/writing-stories/mocking-modules.md
index b5d9e670f1af..fa1452b2222d 100644
--- a/docs/writing-stories/mocking-modules.md
+++ b/docs/writing-stories/mocking-modules.md
@@ -86,31 +86,24 @@ To configure subpath imports, you define the `imports` property in your project'
}
```
-
+There are two aspects to this configuration worth noting:
-Each subpath must begin with `#`, to differentiate it from a regular module path. The `#*` entry is a catch-all that maps all subpaths to the root directory.
+First, **each subpath must begin with `#`**, to differentiate it from a regular module path. The `#*` entry is a catch-all that maps all subpaths to the root directory.
-
+Second, note the **`storybook` and `default` keys** in each module's entry. The `storybook` value is used to import the mock file when loaded in Storybook, while the `default` value is used to import the original module in your project. The Storybook environment will match the conditions `storybook` and `test`, so you can apply the same module mapping for both Storybook and your tests.
-You can then update your component file to use the subpath import:
+With the package configuration in place, you can then update your component file to use the subpath import:
```ts
// AuthButton.ts
+// ➖ Remove this line
+// import { getUserFromSession } from '../../lib/session';
+// ➕ Add this line
import { getUserFromSession } from '#lib/session';
-export const AuthButton = (props) => {
- const user = getUserFromSession();
-
- // ...
-};
+// ... rest of the file
```
-### Conditional imports
-
-Note the `storybook` and `default` keys in each module's entry. The `storybook` key is used to import the mock file in Storybook, while the `default` key is used to import the original module in your project.
-
-The Storybook environment will match the conditions `storybook` and `test`, so you can apply the same module mapping for both Storybook and your tests.
-
## Builder aliases
If your project is unable to use [subpath imports](#subpath-imports), you can configure your Storybook builder to alias the module to the mock file. This will instruct the builder to replace the module with the mock file when bundling your Storybook stories.
@@ -240,7 +233,7 @@ export const SaveFlow: Story = {
### Setting up and cleaning up
-You can use the asynchronous `beforeEach` function to perform any setup that you need before the story is rendered, eg. setting up mock behavior. It can be defined at the story, component (which will run for all stories in the file), or project (defined in `.storybook/preview.js|ts`, which will run for all stories in the project) level.
+Before the story renders, you can use the asynchronous `beforeEach` function to perform any setup you need (e.g., configure the mock behavior). This function can be defined at the story, component (which will run for all stories in the file), or project (defined in `.storybook/preview.js|ts`, which will run for all stories in the project).
You can also return a cleanup function from `beforeEach` which will be called after your story unmounts. This is useful for tasks like unsubscribing observers, etc.
@@ -250,7 +243,7 @@ It is _not_ necessary to restore `fn()` mocks with the cleanup function, as Stor
-Here's an example of using the [`mockdate`](https://github.com/boblauer/MockDate) package to mock the Date and reset it when the story unmounts.
+Here's an example of using the [`mockdate`](https://github.com/boblauer/MockDate) package to mock the [`Date`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date) and reset it when the story unmounts.
diff --git a/docs/writing-stories/mocking-providers.md b/docs/writing-stories/mocking-providers.md
index adeec3072f67..0671ff37532e 100644
--- a/docs/writing-stories/mocking-providers.md
+++ b/docs/writing-stories/mocking-providers.md
@@ -2,7 +2,21 @@
title: Mocking providers
---
-
+export const SUPPORTED_RENDERERS = ['react', 'solid'];
+
+
+
+
+
+The [context provider pattern](https://react.dev/learn/passing-data-deeply-with-context) and how to mock it only applies to renderers that use JSX, like [React](?renderer=react) or [Solid](?renderer=solid).
+
+
+
+
+
+
+
+
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. To mock a provider, you can wrap your component in a [decorator](./decorators.md) that includes the necessary context.
@@ -90,3 +104,5 @@ export const Dark: Story = {
```
This powerful approach allows you to provide any value (theme, user role, mock data, etc.) to your components in a way that is both flexible and maintainable.
+
+
From 1dca308bf46991a0b48c88604628b8865b11b285 Mon Sep 17 00:00:00 2001
From: Kyle Gach
Date: Tue, 30 Apr 2024 22:21:31 -0600
Subject: [PATCH 056/117] Snippetize examples
---
docs/get-started/nextjs.md | 238 ++++--------------
.../before-each-in-meta-mock-date.ts.mdx | 31 +++
.../angular/storybook-test-fn-mock-spy.ts.mdx | 36 +++
.../storybook-test-mock-return-value.ts.mdx | 22 ++
.../before-each-in-meta-mock-date.js.mdx | 26 ++
.../before-each-in-meta-mock-date.ts-4-9.mdx | 32 +++
.../before-each-in-meta-mock-date.ts.mdx | 32 +++
.../common/module-aliases-config.vite.js.mdx | 25 ++
.../common/module-aliases-config.vite.ts.mdx | 29 +++
.../module-aliases-config.webpack.js.mdx | 25 ++
.../module-aliases-config.webpack.ts.mdx | 29 +++
.../common/msw-addon-install.npm.js.mdx | 3 +
.../common/msw-addon-install.pnpm.js.mdx | 3 +
.../common/msw-addon-install.yarn.js.mdx | 3 +
docs/snippets/common/msw-install.npm.js.mdx | 3 -
docs/snippets/common/msw-install.pnpm.js.mdx | 3 -
docs/snippets/common/msw-install.yarn.js.mdx | 3 -
.../common/storybook-test-fn-mock-spy.js.mdx | 31 +++
.../storybook-test-fn-mock-spy.ts-4-9.mdx | 37 +++
.../common/storybook-test-fn-mock-spy.ts.mdx | 37 +++
.../storybook-test-mock-file-example.ts.mdx | 8 +
.../storybook-test-mock-return-value.js.mdx | 18 ++
...torybook-test-mock-return-value.ts-4-9.mdx | 25 ++
.../storybook-test-mock-return-value.ts.mdx | 24 ++
.../common/subpath-imports-config.json.mdx | 24 ++
...-mock-provider-with-story-parameter.js.mdx | 18 ++
...k-provider-with-story-parameter.ts-4-9.mdx | 23 ++
...-mock-provider-with-story-parameter.ts.mdx | 23 ++
.../decorator-parameterized-in-preview.js.mdx | 28 +++
.../decorator-parameterized-in-preview.ts.mdx | 31 +++
.../react/mock-provider-in-preview.js.mdx | 23 ++
.../react/mock-provider-in-preview.ts.mdx | 26 ++
docs/snippets/react/nextjs-cache-mock.js.mdx | 22 ++
.../react/nextjs-cache-mock.ts-4-9.mdx | 28 +++
docs/snippets/react/nextjs-cache-mock.ts.mdx | 28 +++
.../snippets/react/nextjs-headers-mock.js.mdx | 26 ++
.../react/nextjs-headers-mock.ts-4-9.mdx | 32 +++
.../snippets/react/nextjs-headers-mock.ts.mdx | 32 +++
.../react/nextjs-navigation-mock.js.mdx | 35 +++
.../react/nextjs-navigation-mock.ts-4-9.mdx | 41 +++
.../react/nextjs-navigation-mock.ts.mdx | 41 +++
docs/snippets/react/nextjs-router-mock.js.mdx | 23 ++
.../react/nextjs-router-mock.ts-4-9.mdx | 28 +++
docs/snippets/react/nextjs-router-mock.ts.mdx | 28 +++
.../before-each-in-meta-mock-date.js.mdx | 25 ++
.../before-each-in-meta-mock-date.ts.mdx | 30 +++
.../storybook-test-fn-mock-spy.js.mdx | 30 +++
.../storybook-test-fn-mock-spy.ts.mdx | 35 +++
.../storybook-test-mock-return-value.js.mdx | 15 ++
.../storybook-test-mock-return-value.ts.mdx | 21 ++
docs/writing-stories/decorators.md | 43 +---
docs/writing-stories/mocking-modules.md | 219 +++++-----------
.../mocking-network-requests.md | 18 +-
docs/writing-stories/mocking-providers.md | 74 ++----
docs/writing-tests/interaction-testing.md | 95 ++-----
55 files changed, 1379 insertions(+), 509 deletions(-)
create mode 100644 docs/snippets/angular/before-each-in-meta-mock-date.ts.mdx
create mode 100644 docs/snippets/angular/storybook-test-fn-mock-spy.ts.mdx
create mode 100644 docs/snippets/angular/storybook-test-mock-return-value.ts.mdx
create mode 100644 docs/snippets/common/before-each-in-meta-mock-date.js.mdx
create mode 100644 docs/snippets/common/before-each-in-meta-mock-date.ts-4-9.mdx
create mode 100644 docs/snippets/common/before-each-in-meta-mock-date.ts.mdx
create mode 100644 docs/snippets/common/module-aliases-config.vite.js.mdx
create mode 100644 docs/snippets/common/module-aliases-config.vite.ts.mdx
create mode 100644 docs/snippets/common/module-aliases-config.webpack.js.mdx
create mode 100644 docs/snippets/common/module-aliases-config.webpack.ts.mdx
create mode 100644 docs/snippets/common/msw-addon-install.npm.js.mdx
create mode 100644 docs/snippets/common/msw-addon-install.pnpm.js.mdx
create mode 100644 docs/snippets/common/msw-addon-install.yarn.js.mdx
delete mode 100644 docs/snippets/common/msw-install.npm.js.mdx
delete mode 100644 docs/snippets/common/msw-install.pnpm.js.mdx
delete mode 100644 docs/snippets/common/msw-install.yarn.js.mdx
create mode 100644 docs/snippets/common/storybook-test-fn-mock-spy.js.mdx
create mode 100644 docs/snippets/common/storybook-test-fn-mock-spy.ts-4-9.mdx
create mode 100644 docs/snippets/common/storybook-test-fn-mock-spy.ts.mdx
create mode 100644 docs/snippets/common/storybook-test-mock-file-example.ts.mdx
create mode 100644 docs/snippets/common/storybook-test-mock-return-value.js.mdx
create mode 100644 docs/snippets/common/storybook-test-mock-return-value.ts-4-9.mdx
create mode 100644 docs/snippets/common/storybook-test-mock-return-value.ts.mdx
create mode 100644 docs/snippets/common/subpath-imports-config.json.mdx
create mode 100644 docs/snippets/react/configure-mock-provider-with-story-parameter.js.mdx
create mode 100644 docs/snippets/react/configure-mock-provider-with-story-parameter.ts-4-9.mdx
create mode 100644 docs/snippets/react/configure-mock-provider-with-story-parameter.ts.mdx
create mode 100644 docs/snippets/react/decorator-parameterized-in-preview.js.mdx
create mode 100644 docs/snippets/react/decorator-parameterized-in-preview.ts.mdx
create mode 100644 docs/snippets/react/mock-provider-in-preview.js.mdx
create mode 100644 docs/snippets/react/mock-provider-in-preview.ts.mdx
create mode 100644 docs/snippets/react/nextjs-cache-mock.js.mdx
create mode 100644 docs/snippets/react/nextjs-cache-mock.ts-4-9.mdx
create mode 100644 docs/snippets/react/nextjs-cache-mock.ts.mdx
create mode 100644 docs/snippets/react/nextjs-headers-mock.js.mdx
create mode 100644 docs/snippets/react/nextjs-headers-mock.ts-4-9.mdx
create mode 100644 docs/snippets/react/nextjs-headers-mock.ts.mdx
create mode 100644 docs/snippets/react/nextjs-navigation-mock.js.mdx
create mode 100644 docs/snippets/react/nextjs-navigation-mock.ts-4-9.mdx
create mode 100644 docs/snippets/react/nextjs-navigation-mock.ts.mdx
create mode 100644 docs/snippets/react/nextjs-router-mock.js.mdx
create mode 100644 docs/snippets/react/nextjs-router-mock.ts-4-9.mdx
create mode 100644 docs/snippets/react/nextjs-router-mock.ts.mdx
create mode 100644 docs/snippets/web-components/before-each-in-meta-mock-date.js.mdx
create mode 100644 docs/snippets/web-components/before-each-in-meta-mock-date.ts.mdx
create mode 100644 docs/snippets/web-components/storybook-test-fn-mock-spy.js.mdx
create mode 100644 docs/snippets/web-components/storybook-test-fn-mock-spy.ts.mdx
create mode 100644 docs/snippets/web-components/storybook-test-mock-return-value.js.mdx
create mode 100644 docs/snippets/web-components/storybook-test-mock-return-value.ts.mdx
diff --git a/docs/get-started/nextjs.md b/docs/get-started/nextjs.md
index 36140f9def16..957f58f8bb31 100644
--- a/docs/get-started/nextjs.md
+++ b/docs/get-started/nextjs.md
@@ -728,47 +728,29 @@ How you mock other modules in Storybook depends on how you import the module int
With either approach, the first step is to [create a mock file](../writing-stories/mocking-modules.md#mock-files). Here's an example of a mock file for a module named `session`:
-
+
-```ts
-// lib/session.mock.ts
-import { fn } from '@storybook/test';
-import * as actual from './session';
+
-export * from './session';
-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#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:
-
+
-```jsonc
-// package.json
-{
- "imports": {
- "#api": {
- "storybook": "./api.mock.ts",
- "default": "./api.ts"
- },
- "#app/actions": {
- "storybook": "./app/actions.mock.ts",
- "default": "./app/actions.ts"
- },
- "#lib/session": {
- "storybook": "./lib/session.mock.ts",
- "default": "./lib/session.ts"
- },
- "#lib/db": {
- "storybook": "./lib/db.mock.ts",
- "default": "./lib/db.ts"
- },
- "#*": ["./*", "./*.ts", "./*.tsx"]
- }
-}
-```
+
+
+
@@ -780,27 +762,16 @@ Each subpath must begin with `#`, to differentiate it from a regular module path
If you're using [module aliases](#module-aliases), you can add a Webpack alias to your Storybook configuration to point to the mock file.
-
+
-```ts
-// .storybook/main.ts
-webpackFinal: async (config) => {
- if (config.resolve) {
- config.resolve.alias = {
- ...config.resolve.alias,
- // 👇 External module
- 'lodash': require.resolve('./lodash.mock'),
- // 👇 Internal modules
- '@/api$': path.resolve(__dirname, "./api.mock.ts"),
- '@/app/actions$': path.resolve(__dirname, "./app/actions.mock.ts"),
- '@/lib/session$': path.resolve(__dirname, "./lib/session.mock.ts"),
- '@/lib/db$': path.resolve(__dirname, "./lib/db.mock.ts"),
- }
- }
+
- return config;
-},
-```
+
## Runtime config
@@ -1061,35 +1032,16 @@ Type: `typeof import('next/cache')`
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).
-
-
-```ts
-// MyForm.stories.ts
-import { expect, userEvent, within } from '@storybook/test';
-import { Meta, StoryObj } from '@storybook/react';
-// 👇 Must use this import path to have mocks typed correctly
-import { revalidatePath } from '@storybook/nextjs/cache.mock';
-import MyForm from './my-form';
-
-const meta = {
- component: MyForm,
-} satisfies Meta;
-
-export default meta;
-
-type Story = StoryObj;
+
-export const Submitted: Story = {
- async play({ canvasElement }) {
- const canvas = within(canvasElement);
+
- const submitButton = canvas.getByRole('button', { name: /submit/i });
- await userEvent.click(saveButton);
- // 👇 Use any mock assertions on the function
- await expect(revalidatePath).toHaveBeenCalledWith('/');
- },
-};
-```
+
#### `@storybook/nextjs/headers.mock`
@@ -1107,39 +1059,16 @@ For cookies, you can use the existing API to write them. E.g., `cookies().set('f
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`.
-
-
-```ts
-// MyForm.stories.ts
-import { expect, fireEvent, userEvent, within } from '@storybook/test';
-import { Meta, StoryObj } from '@storybook/react';
-// 👇 Must use this import path to have mocks typed correctly
-import { cookies, headers } from '@storybook/nextjs/headers.mock';
-import MyForm from './my-form';
-
-const meta = {
- component: MyForm,
-} satisfies Meta;
-
-export default meta;
+
-type Story = StoryObj;
+
-export const LoggedInEurope: Story = {
- async beforeEach() {
- // 👇 Set mock cookies and headers ahead of rendering
- cookies().set('username', 'Sol');
- headers().set('timezone', 'Central European Summer Time');
- },
- async play() {
- // 👇 Assert that your component called the mocks
- await expect(cookies().get).toHaveBeenCalledOnce();
- await expect(cookies().get).toHaveBeenCalledWith('username');
- await expect(headers().get).toHaveBeenCalledOnce();
- await expect(cookies().get).toHaveBeenCalledWith('timezone');
- },
-};
-```
+
#### `@storybook/nextjs/navigation.mock`
@@ -1147,48 +1076,16 @@ Type: `typeof import('next/navigation') & getRouter: () => ReturnType
-
-```ts
-// MyForm.stories.ts
-import { expect, fireEvent, userEvent, within } from '@storybook/test';
-import { Meta, StoryObj } from '@storybook/react';
-// 👇 Must use this import path to have mocks typed correctly
-import { redirect, getRouter } from '@storybook/nextjs/navigation.mock';
-import MyForm from './my-form';
-
-const meta = {
- component: MyForm,
- parameters: {
- nextjs: {
- // 👇 As in the Next.js application, next/navigation only works using App Router
- appDirectory: true,
- },
- },
-} satisfies Meta;
-
-export default meta;
-
-type Story = StoryObj;
-
-export const Unauthenticated: Story = {
- async play() => {
- // 👇 Assert that your component called redirect()
- await expect(redirect).toHaveBeenCalledWith('/login', 'replace');
- },
-};
+
-export const GoBack: Story = {
- async play({ canvasElement }) {
- const canvas = within(canvasElement);
- const backBtn = await canvas.findByText('Go back');
+
- await userEvent.click(backBtn);
- // 👇 Assert that your component called back()
- await expect(getRouter().back).toHaveBeenCalled();
- },
-};
-```
+
#### `@storybook/nextjs/router.mock`
@@ -1196,41 +1093,16 @@ Type: `typeof import('next/router') & getRouter: () => ReturnType
-
-```ts
-// MyForm.stories.ts
-import { expect, fireEvent, userEvent, within } from '@storybook/test';
-import { Meta, StoryObj } from '@storybook/react';
-// 👇 Must use this import path to have mocks typed correctly
-import { getRouter } from '@storybook/nextjs/router.mock';
-import MyForm from './my-form';
-
-const meta = {
- component: MyForm,
- parameters: {
- nextjs: {
- // 👇 As in the Next.js application, next/router only works using Pages Router
- appDirectory: false,
- },
- },
-} satisfies Meta;
-
-export default meta;
-
-type Story = StoryObj;
+
-export const GoBack: Story = {
- async play({ canvasElement }) {
- const canvas = within(canvasElement);
- const backBtn = await canvas.findByText('Go back');
+
- await userEvent.click(backBtn);
- // 👇 Assert that your component called back()
- await expect(getRouter().back).toHaveBeenCalled();
- },
-};
-```
+
### Options
diff --git a/docs/snippets/angular/before-each-in-meta-mock-date.ts.mdx b/docs/snippets/angular/before-each-in-meta-mock-date.ts.mdx
new file mode 100644
index 000000000000..4d79269e028d
--- /dev/null
+++ b/docs/snippets/angular/before-each-in-meta-mock-date.ts.mdx
@@ -0,0 +1,31 @@
+```ts
+// Page.stories.ts
+import { Meta, StoryObj } from '@storybook/angular';
+import MockDate from 'mockdate';
+
+// 👇 Must use this import path to have mocks typed correctly
+import { getUserFromSession } from '#api/session.mock';
+import { Page } from './Page';
+
+const meta: Meta = {
+ component: Page,
+ // 👇 Set the value of Date for every story in the file
+ async beforeEach() {
+ MockDate.set('2024-02-14');
+
+ // 👇 Reset the Date after each story
+ return () => {
+ MockDate.reset();
+ };
+ },
+};
+export default meta;
+
+type Story = StoryObj;
+
+export const Default: Story = {
+ async play({ canvasElement }) {
+ // ... This will run with the mocked Date
+ },
+};
+```
\ No newline at end of file
diff --git a/docs/snippets/angular/storybook-test-fn-mock-spy.ts.mdx b/docs/snippets/angular/storybook-test-fn-mock-spy.ts.mdx
new file mode 100644
index 000000000000..b0aac94d7473
--- /dev/null
+++ b/docs/snippets/angular/storybook-test-fn-mock-spy.ts.mdx
@@ -0,0 +1,36 @@
+```ts
+// NoteUI.stories.ts
+import { Meta, StoryObj } from '@storybook/angular';
+import { expect, userEvent, within } from '@storybook/test';
+
+// 👇 Must use this import path to have mocks typed correctly
+import { saveNote } from '#app/actions.mock';
+import { createNotes } from '#mocks/notes';
+import NoteUI from './note-ui';
+
+const meta: Meta = {
+ title: 'Mocked/NoteUI',
+ component: NoteUI,
+};
+export default meta;
+
+type Story = StoryObj;
+
+const notes = createNotes();
+
+export const SaveFlow: Story = {
+ name: 'Save Flow ▶',
+ args: {
+ isEditing: true,
+ note: notes[0],
+ },
+ play: async ({ canvasElement, step }) => {
+ const canvas = within(canvasElement);
+
+ const saveButton = canvas.getByRole('menuitem', { name: /done/i });
+ await userEvent.click(saveButton);
+ // 👇 This is the mock function, so you can assert its behavior
+ await expect(saveNote).toHaveBeenCalled();
+ },
+};
+```
\ No newline at end of file
diff --git a/docs/snippets/angular/storybook-test-mock-return-value.ts.mdx b/docs/snippets/angular/storybook-test-mock-return-value.ts.mdx
new file mode 100644
index 000000000000..693843191081
--- /dev/null
+++ b/docs/snippets/angular/storybook-test-mock-return-value.ts.mdx
@@ -0,0 +1,22 @@
+```ts
+// Page.stories.ts
+import type { Meta, StoryObj } from '@storybook/angular';
+
+// 👇 Must use this import path to have mocks typed correctly
+import { getUserFromSession } from '#api/session.mock';
+import { Page } from './Page';
+
+const meta: Meta = {
+ component: Page,
+};
+export default meta;
+
+type Story = StoryObj;
+
+export const Default: Story = {
+ async beforeEach() {
+ // 👇 Set the return value for the getUserFromSession function
+ getUserFromSession.mockReturnValue({ id: '1', name: 'Alice' });
+ },
+};
+```
diff --git a/docs/snippets/common/before-each-in-meta-mock-date.js.mdx b/docs/snippets/common/before-each-in-meta-mock-date.js.mdx
new file mode 100644
index 000000000000..61151b7a4850
--- /dev/null
+++ b/docs/snippets/common/before-each-in-meta-mock-date.js.mdx
@@ -0,0 +1,26 @@
+```ts
+// Page.stories.ts
+import MockDate from 'mockdate';
+
+import { getUserFromSession } from '#api/session.mock';
+import { Page } from './Page';
+
+export default {
+ component: Page,
+ // 👇 Set the value of Date for every story in the file
+ async beforeEach() {
+ MockDate.set('2024-02-14');
+
+ // 👇 Reset the Date after each story
+ return () => {
+ MockDate.reset();
+ };
+ },
+};
+
+export const Default: Story = {
+ async play({ canvasElement }) {
+ // ... This will run with the mocked Date
+ },
+};
+```
diff --git a/docs/snippets/common/before-each-in-meta-mock-date.ts-4-9.mdx b/docs/snippets/common/before-each-in-meta-mock-date.ts-4-9.mdx
new file mode 100644
index 000000000000..521d5ac7a1d3
--- /dev/null
+++ b/docs/snippets/common/before-each-in-meta-mock-date.ts-4-9.mdx
@@ -0,0 +1,32 @@
+```ts
+// Page.stories.ts
+// Replace your-renderer with the name of your renderer (e.g. react, vue3)
+import type { Meta, StoryObj } from '@storybook/your-renderer';
+import MockDate from 'mockdate';
+
+// 👇 Must use this import path to have mocks typed correctly
+import { getUserFromSession } from '#api/session.mock';
+import { Page } from './Page';
+
+const meta = {
+ component: Page,
+ // 👇 Set the value of Date for every story in the file
+ async beforeEach() {
+ MockDate.set('2024-02-14');
+
+ // 👇 Reset the Date after each story
+ return () => {
+ MockDate.reset();
+ };
+ },
+} satisfies Meta;
+export default meta;
+
+type Story = StoryObj;
+
+export const Default: Story = {
+ async play({ canvasElement }) {
+ // ... This will run with the mocked Date
+ },
+};
+```
diff --git a/docs/snippets/common/before-each-in-meta-mock-date.ts.mdx b/docs/snippets/common/before-each-in-meta-mock-date.ts.mdx
new file mode 100644
index 000000000000..35da639d0cdc
--- /dev/null
+++ b/docs/snippets/common/before-each-in-meta-mock-date.ts.mdx
@@ -0,0 +1,32 @@
+```ts
+// Page.stories.ts
+// Replace your-renderer with the name of your renderer (e.g. react, vue3)
+import type { Meta, StoryObj } from '@storybook/your-renderer';
+import MockDate from 'mockdate';
+
+// 👇 Must use this import path to have mocks typed correctly
+import { getUserFromSession } from '#api/session.mock';
+import { Page } from './Page';
+
+const meta: Meta = {
+ component: Page,
+ // 👇 Set the value of Date for every story in the file
+ async beforeEach() {
+ MockDate.set('2024-02-14');
+
+ // 👇 Reset the Date after each story
+ return () => {
+ MockDate.reset();
+ };
+ },
+};
+export default meta;
+
+type Story = StoryObj;
+
+export const Default: Story = {
+ async play({ canvasElement }) {
+ // ... This will run with the mocked Date
+ },
+};
+```
diff --git a/docs/snippets/common/module-aliases-config.vite.js.mdx b/docs/snippets/common/module-aliases-config.vite.js.mdx
new file mode 100644
index 000000000000..f87d528689d3
--- /dev/null
+++ b/docs/snippets/common/module-aliases-config.vite.js.mdx
@@ -0,0 +1,25 @@
+```js
+// .storybook/main.js
+
+export default {
+ // Replace your-framework with the framework you are using (e.g., react-webpack5, vue3-vite)
+ framework: '@storybook/your-framework',
+ stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
+ viteFinal: async (config) => {
+ if (config.resolve) {
+ config.resolve.alias = {
+ ...config.resolve?.alias,
+ // 👇 External module
+ 'lodash': require.resolve('./lodash.mock'),
+ // 👇 Internal modules
+ '@/api': path.resolve(__dirname, "./api.mock.ts"),
+ '@/app/actions': path.resolve(__dirname, "./app/actions.mock.ts"),
+ '@/lib/session': path.resolve(__dirname, "./lib/session.mock.ts"),
+ '@/lib/db': path.resolve(__dirname, "./lib/db.mock.ts"),
+ }
+ }
+
+ return config;
+ },
+};
+```
\ No newline at end of file
diff --git a/docs/snippets/common/module-aliases-config.vite.ts.mdx b/docs/snippets/common/module-aliases-config.vite.ts.mdx
new file mode 100644
index 000000000000..57f3e02b20ce
--- /dev/null
+++ b/docs/snippets/common/module-aliases-config.vite.ts.mdx
@@ -0,0 +1,29 @@
+```ts
+// .storybook/main.ts
+
+// Replace your-framework with the framework you are using (e.g., react-webpack5, vue3-vite)
+import type { StorybookConfig } from '@storybook/your-framework';
+
+const config: StorybookConfig = {
+ framework: '@storybook/your-framework',
+ stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
+ viteFinal: async (config) => {
+ if (config.resolve) {
+ config.resolve.alias = {
+ ...config.resolve?.alias,
+ // 👇 External module
+ 'lodash': require.resolve('./lodash.mock'),
+ // 👇 Internal modules
+ '@/api': path.resolve(__dirname, "./api.mock.ts"),
+ '@/app/actions': path.resolve(__dirname, "./app/actions.mock.ts"),
+ '@/lib/session': path.resolve(__dirname, "./lib/session.mock.ts"),
+ '@/lib/db': path.resolve(__dirname, "./lib/db.mock.ts"),
+ }
+ }
+
+ return config;
+ },
+};
+
+export default config;
+```
\ No newline at end of file
diff --git a/docs/snippets/common/module-aliases-config.webpack.js.mdx b/docs/snippets/common/module-aliases-config.webpack.js.mdx
new file mode 100644
index 000000000000..b3b0b44c05e9
--- /dev/null
+++ b/docs/snippets/common/module-aliases-config.webpack.js.mdx
@@ -0,0 +1,25 @@
+```js
+// .storybook/main.js
+
+export default {
+ // Replace your-framework with the framework you are using (e.g., nextjs, vue3-vite)
+ framework: '@storybook/your-framework',
+ stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
+ webpackFinal: async (config) => {
+ if (config.resolve) {
+ config.resolve.alias = {
+ ...config.resolve.alias,
+ // 👇 External module
+ 'lodash': require.resolve('./lodash.mock'),
+ // 👇 Internal modules
+ '@/api$': path.resolve(__dirname, "./api.mock.ts"),
+ '@/app/actions$': path.resolve(__dirname, "./app/actions.mock.ts"),
+ '@/lib/session$': path.resolve(__dirname, "./lib/session.mock.ts"),
+ '@/lib/db$': path.resolve(__dirname, "./lib/db.mock.ts"),
+ }
+ }
+
+ return config;
+ },
+};
+```
\ No newline at end of file
diff --git a/docs/snippets/common/module-aliases-config.webpack.ts.mdx b/docs/snippets/common/module-aliases-config.webpack.ts.mdx
new file mode 100644
index 000000000000..327295007df6
--- /dev/null
+++ b/docs/snippets/common/module-aliases-config.webpack.ts.mdx
@@ -0,0 +1,29 @@
+```ts
+// .storybook/main.ts
+
+// Replace your-framework with the framework you are using (e.g., nextjs, vue3-vite)
+import type { StorybookConfig } from '@storybook/your-framework';
+
+const config: StorybookConfig = {
+ framework: '@storybook/your-framework',
+ stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
+ webpackFinal: async (config) => {
+ if (config.resolve) {
+ config.resolve.alias = {
+ ...config.resolve.alias,
+ // 👇 External module
+ 'lodash': require.resolve('./lodash.mock'),
+ // 👇 Internal modules
+ '@/api$': path.resolve(__dirname, "./api.mock.ts"),
+ '@/app/actions$': path.resolve(__dirname, "./app/actions.mock.ts"),
+ '@/lib/session$': path.resolve(__dirname, "./lib/session.mock.ts"),
+ '@/lib/db$': path.resolve(__dirname, "./lib/db.mock.ts"),
+ }
+ }
+
+ return config;
+ },
+};
+
+export default config;
+```
\ No newline at end of file
diff --git a/docs/snippets/common/msw-addon-install.npm.js.mdx b/docs/snippets/common/msw-addon-install.npm.js.mdx
new file mode 100644
index 000000000000..99de2511c94e
--- /dev/null
+++ b/docs/snippets/common/msw-addon-install.npm.js.mdx
@@ -0,0 +1,3 @@
+```sh
+npm install msw msw-storybook-addon --save-dev
+```
diff --git a/docs/snippets/common/msw-addon-install.pnpm.js.mdx b/docs/snippets/common/msw-addon-install.pnpm.js.mdx
new file mode 100644
index 000000000000..19ac40a7f0be
--- /dev/null
+++ b/docs/snippets/common/msw-addon-install.pnpm.js.mdx
@@ -0,0 +1,3 @@
+```sh
+pnpm add msw msw-storybook-addon --save-dev
+```
diff --git a/docs/snippets/common/msw-addon-install.yarn.js.mdx b/docs/snippets/common/msw-addon-install.yarn.js.mdx
new file mode 100644
index 000000000000..ac4eacd25629
--- /dev/null
+++ b/docs/snippets/common/msw-addon-install.yarn.js.mdx
@@ -0,0 +1,3 @@
+```sh
+yarn add msw msw-storybook-addon --save-dev
+```
diff --git a/docs/snippets/common/msw-install.npm.js.mdx b/docs/snippets/common/msw-install.npm.js.mdx
deleted file mode 100644
index cf6e91f4e0aa..000000000000
--- a/docs/snippets/common/msw-install.npm.js.mdx
+++ /dev/null
@@ -1,3 +0,0 @@
-```sh
-npm install msw --save-dev
-```
diff --git a/docs/snippets/common/msw-install.pnpm.js.mdx b/docs/snippets/common/msw-install.pnpm.js.mdx
deleted file mode 100644
index c4332df90cd0..000000000000
--- a/docs/snippets/common/msw-install.pnpm.js.mdx
+++ /dev/null
@@ -1,3 +0,0 @@
-```sh
-pnpm add msw --save-dev
-```
diff --git a/docs/snippets/common/msw-install.yarn.js.mdx b/docs/snippets/common/msw-install.yarn.js.mdx
deleted file mode 100644
index 6a8d76b5ff15..000000000000
--- a/docs/snippets/common/msw-install.yarn.js.mdx
+++ /dev/null
@@ -1,3 +0,0 @@
-```sh
-yarn add msw --save-dev
-```
diff --git a/docs/snippets/common/storybook-test-fn-mock-spy.js.mdx b/docs/snippets/common/storybook-test-fn-mock-spy.js.mdx
new file mode 100644
index 000000000000..e9d26c1b36c4
--- /dev/null
+++ b/docs/snippets/common/storybook-test-fn-mock-spy.js.mdx
@@ -0,0 +1,31 @@
+```js
+// NoteUI.stories.js
+import { expect, userEvent, within } from '@storybook/test';
+
+import { saveNote } from '#app/actions.mock';
+import { createNotes } from '#mocks/notes';
+import NoteUI from './note-ui';
+
+export default {
+ title: 'Mocked/NoteUI',
+ component: NoteUI,
+};
+
+const notes = createNotes();
+
+export const SaveFlow = {
+ name: 'Save Flow ▶',
+ args: {
+ isEditing: true,
+ note: notes[0],
+ },
+ play: async ({ canvasElement, step }) => {
+ const canvas = within(canvasElement);
+
+ const saveButton = canvas.getByRole('menuitem', { name: /done/i });
+ await userEvent.click(saveButton);
+ // 👇 This is the mock function, so you can assert its behavior
+ await expect(saveNote).toHaveBeenCalled();
+ },
+};
+```
diff --git a/docs/snippets/common/storybook-test-fn-mock-spy.ts-4-9.mdx b/docs/snippets/common/storybook-test-fn-mock-spy.ts-4-9.mdx
new file mode 100644
index 000000000000..11e9829b6ff1
--- /dev/null
+++ b/docs/snippets/common/storybook-test-fn-mock-spy.ts-4-9.mdx
@@ -0,0 +1,37 @@
+```ts
+// NoteUI.stories.ts
+// Replace your-renderer with the name of your renderer (e.g. react, vue3)
+import type { Meta, StoryObj } from '@storybook/your-renderer';
+import { expect, userEvent, within } from '@storybook/test';
+
+// 👇 Must use this import path to have mocks typed correctly
+import { saveNote } from '#app/actions.mock';
+import { createNotes } from '#mocks/notes';
+import NoteUI from './note-ui';
+
+const meta = {
+ title: 'Mocked/NoteUI',
+ component: NoteUI,
+} satisfies Meta;
+export default meta;
+
+type Story = StoryObj;
+
+const notes = createNotes();
+
+export const SaveFlow: Story = {
+ name: 'Save Flow ▶',
+ args: {
+ isEditing: true,
+ note: notes[0],
+ },
+ play: async ({ canvasElement, step }) => {
+ const canvas = within(canvasElement);
+
+ const saveButton = canvas.getByRole('menuitem', { name: /done/i });
+ await userEvent.click(saveButton);
+ // 👇 This is the mock function, so you can assert its behavior
+ await expect(saveNote).toHaveBeenCalled();
+ },
+};
+```
diff --git a/docs/snippets/common/storybook-test-fn-mock-spy.ts.mdx b/docs/snippets/common/storybook-test-fn-mock-spy.ts.mdx
new file mode 100644
index 000000000000..3462e5a2cb7b
--- /dev/null
+++ b/docs/snippets/common/storybook-test-fn-mock-spy.ts.mdx
@@ -0,0 +1,37 @@
+```ts
+// NoteUI.stories.ts
+// Replace your-renderer with the name of your renderer (e.g. react, vue3)
+import type { Meta, StoryObj } from '@storybook/your-renderer';
+import { expect, userEvent, within } from '@storybook/test';
+
+// 👇 Must use this import path to have mocks typed correctly
+import { saveNote } from '#app/actions.mock';
+import { createNotes } from '#mocks/notes';
+import NoteUI from './note-ui';
+
+const meta: Meta = {
+ title: 'Mocked/NoteUI',
+ component: NoteUI,
+};
+export default meta;
+
+type Story = StoryObj;
+
+const notes = createNotes();
+
+export const SaveFlow: Story = {
+ name: 'Save Flow ▶',
+ args: {
+ isEditing: true,
+ note: notes[0],
+ },
+ play: async ({ canvasElement, step }) => {
+ const canvas = within(canvasElement);
+
+ const saveButton = canvas.getByRole('menuitem', { name: /done/i });
+ await userEvent.click(saveButton);
+ // 👇 This is the mock function, so you can assert its behavior
+ await expect(saveNote).toHaveBeenCalled();
+ },
+};
+```
diff --git a/docs/snippets/common/storybook-test-mock-file-example.ts.mdx b/docs/snippets/common/storybook-test-mock-file-example.ts.mdx
new file mode 100644
index 000000000000..de9c01a2b4cc
--- /dev/null
+++ b/docs/snippets/common/storybook-test-mock-file-example.ts.mdx
@@ -0,0 +1,8 @@
+```ts
+// lib/session.mock.ts
+import { fn } from '@storybook/test';
+import * as actual from './session';
+
+export * from './session';
+export const getUserFromSession = fn(actual.getUserFromSession);
+```
diff --git a/docs/snippets/common/storybook-test-mock-return-value.js.mdx b/docs/snippets/common/storybook-test-mock-return-value.js.mdx
new file mode 100644
index 000000000000..4ace89df1aef
--- /dev/null
+++ b/docs/snippets/common/storybook-test-mock-return-value.js.mdx
@@ -0,0 +1,18 @@
+```js
+// Page.stories.js
+import { fn } from '@storybook/test';
+
+import { getUserFromSession } from '#api/session.mock';
+import { Page } from './Page';
+
+export default {
+ component: Page,
+};
+
+export const Default = {
+ async beforeEach() {
+ // 👇 Set the return value for the getUserFromSession function
+ getUserFromSession.mockReturnValue({ id: '1', name: 'Alice' });
+ },
+};
+```
diff --git a/docs/snippets/common/storybook-test-mock-return-value.ts-4-9.mdx b/docs/snippets/common/storybook-test-mock-return-value.ts-4-9.mdx
new file mode 100644
index 000000000000..3d2e8543d052
--- /dev/null
+++ b/docs/snippets/common/storybook-test-mock-return-value.ts-4-9.mdx
@@ -0,0 +1,25 @@
+```ts
+// Page.stories.ts
+// Replace your-renderer with the name of your renderer (e.g. react, vue3)
+import type { Meta, StoryObj } from '@storybook/your-renderer';
+import { fn } from '@storybook/test';
+
+// 👇 Must use this import path to have mocks typed correctly
+import { getUserFromSession } from '#api/session.mock';
+import { Page } from './Page';
+
+const meta = {
+ component: Page,
+} satisfies Meta;
+
+export default meta;
+
+type Story = StoryObj;
+
+export const Default: Story = {
+ async beforeEach() {
+ // 👇 Set the return value for the getUserFromSession function
+ getUserFromSession.mockReturnValue({ id: '1', name: 'Alice' });
+ },
+};
+```
diff --git a/docs/snippets/common/storybook-test-mock-return-value.ts.mdx b/docs/snippets/common/storybook-test-mock-return-value.ts.mdx
new file mode 100644
index 000000000000..be9a8e343194
--- /dev/null
+++ b/docs/snippets/common/storybook-test-mock-return-value.ts.mdx
@@ -0,0 +1,24 @@
+```ts
+// Page.stories.ts
+// Replace your-renderer with the name of your renderer (e.g. react, vue3)
+import type { Meta, StoryObj } from '@storybook/your-renderer';
+import { fn } from '@storybook/test';
+
+// 👇 Must use this import path to have mocks typed correctly
+import { getUserFromSession } from '#api/session.mock';
+import { Page } from './Page';
+
+const meta: Meta = {
+ component: Page,
+};
+export default meta;
+
+type Story = StoryObj;
+
+export const Default: Story = {
+ async beforeEach() {
+ // 👇 Set the return value for the getUserFromSession function
+ getUserFromSession.mockReturnValue({ id: '1', name: 'Alice' });
+ },
+};
+```
diff --git a/docs/snippets/common/subpath-imports-config.json.mdx b/docs/snippets/common/subpath-imports-config.json.mdx
new file mode 100644
index 000000000000..63615b528eb4
--- /dev/null
+++ b/docs/snippets/common/subpath-imports-config.json.mdx
@@ -0,0 +1,24 @@
+```jsonc
+// package.json
+{
+ "imports": {
+ "#api": {
+ "storybook": "./api.mock.ts",
+ "default": "./api.ts"
+ },
+ "#app/actions": {
+ "storybook": "./app/actions.mock.ts",
+ "default": "./app/actions.ts"
+ },
+ "#lib/session": {
+ "storybook": "./lib/session.mock.ts",
+ "default": "./lib/session.ts"
+ },
+ "#lib/db": {
+ "storybook": "./lib/db.mock.ts",
+ "default": "./lib/db.ts"
+ },
+ "#*": ["./*", "./*.ts", "./*.tsx"]
+ }
+}
+```
\ No newline at end of file
diff --git a/docs/snippets/react/configure-mock-provider-with-story-parameter.js.mdx b/docs/snippets/react/configure-mock-provider-with-story-parameter.js.mdx
new file mode 100644
index 000000000000..edb87b826e1d
--- /dev/null
+++ b/docs/snippets/react/configure-mock-provider-with-story-parameter.js.mdx
@@ -0,0 +1,18 @@
+```js
+// Button.stories.js
+import { Button } from './Button';
+
+export default {
+ component: Button,
+};
+
+// Wrapped in light theme
+export const Default = {};
+
+// Wrapped in dark theme
+export const Dark = {
+ parameters: {
+ theme: 'dark',
+ },
+};
+```
\ No newline at end of file
diff --git a/docs/snippets/react/configure-mock-provider-with-story-parameter.ts-4-9.mdx b/docs/snippets/react/configure-mock-provider-with-story-parameter.ts-4-9.mdx
new file mode 100644
index 000000000000..f866555fc6dd
--- /dev/null
+++ b/docs/snippets/react/configure-mock-provider-with-story-parameter.ts-4-9.mdx
@@ -0,0 +1,23 @@
+```ts
+// Button.stories.ts
+import { Meta, StoryObj } from '@storybook/react';
+
+import { Button } from './Button';
+
+const meta = {
+ component: Button,
+} satisfies Meta;
+export default meta;
+
+type Story = StoryObj;
+
+// Wrapped in light theme
+export const Default: Story = {};
+
+// Wrapped in dark theme
+export const Dark: Story = {
+ parameters: {
+ theme: 'dark',
+ },
+};
+```
\ No newline at end of file
diff --git a/docs/snippets/react/configure-mock-provider-with-story-parameter.ts.mdx b/docs/snippets/react/configure-mock-provider-with-story-parameter.ts.mdx
new file mode 100644
index 000000000000..e59575a2e767
--- /dev/null
+++ b/docs/snippets/react/configure-mock-provider-with-story-parameter.ts.mdx
@@ -0,0 +1,23 @@
+```ts
+// Button.stories.ts
+import { Meta, StoryObj } from '@storybook/react';
+
+import { Button } from './Button';
+
+const meta: Meta = {
+ component: Button,
+};
+export default meta;
+
+type Story = StoryObj;
+
+// Wrapped in light theme
+export const Default: Story = {};
+
+// Wrapped in dark theme
+export const Dark: Story = {
+ parameters: {
+ theme: 'dark',
+ },
+};
+```
\ No newline at end of file
diff --git a/docs/snippets/react/decorator-parameterized-in-preview.js.mdx b/docs/snippets/react/decorator-parameterized-in-preview.js.mdx
new file mode 100644
index 000000000000..b4c07fce70f2
--- /dev/null
+++ b/docs/snippets/react/decorator-parameterized-in-preview.js.mdx
@@ -0,0 +1,28 @@
+```ts
+// .storybook/preview.tsx
+import React from 'react';
+
+export default {
+ decorators: [
+ // 👇 Defining the decorator in the preview file applies it to all stories
+ (Story, { parameters }) => {
+ // 👇 Make it configurable by reading from parameters
+ const { pageLayout } = parameters;
+ switch (pageLayout) {
+ case 'page':
+ return (
+ // Your page layout is probably a little more complex than this ;)
+
+ );
+ case 'page-mobile':
+ return (
+
+ );
+ case default:
+ // In the default case, don't apply a layout
+ return ;
+ }
+ },
+ ],
+};
+```
diff --git a/docs/snippets/react/decorator-parameterized-in-preview.ts.mdx b/docs/snippets/react/decorator-parameterized-in-preview.ts.mdx
new file mode 100644
index 000000000000..c46809be6b7f
--- /dev/null
+++ b/docs/snippets/react/decorator-parameterized-in-preview.ts.mdx
@@ -0,0 +1,31 @@
+```ts
+// .storybook/preview.tsx
+import React from 'react';
+import { Preview } from '@storybook/react';
+
+const preview: Preview = {
+ decorators: [
+ // 👇 Defining the decorator in the preview file applies it to all stories
+ (Story, { parameters }) => {
+ // 👇 Make it configurable by reading from parameters
+ const { pageLayout } = parameters;
+ switch (pageLayout) {
+ case 'page':
+ return (
+ // Your page layout is probably a little more complex than this ;)
+
+ );
+ case 'page-mobile':
+ return (
+
+ );
+ case default:
+ // In the default case, don't apply a layout
+ return ;
+ }
+ },
+ ],
+};
+
+export default preview;
+```
diff --git a/docs/snippets/react/mock-provider-in-preview.js.mdx b/docs/snippets/react/mock-provider-in-preview.js.mdx
new file mode 100644
index 000000000000..95bd0fa2d455
--- /dev/null
+++ b/docs/snippets/react/mock-provider-in-preview.js.mdx
@@ -0,0 +1,23 @@
+```jsx
+// .storybook/preview.jsx
+import React from 'react';
+import { ThemeProvider } from 'styled-components';
+
+// themes = { light, dark }
+import * as themes from '../src/themes';
+
+export default {
+ decorators: [
+ // 👇 Defining the decorator in the preview file applies it to all stories
+ (Story, { parameters }) => {
+ // 👇 Make it configurable by reading the theme value from parameters
+ const { theme = 'light' } = parameters;
+ return (
+
+
+
+ );
+ },
+ ],
+};
+```
diff --git a/docs/snippets/react/mock-provider-in-preview.ts.mdx b/docs/snippets/react/mock-provider-in-preview.ts.mdx
new file mode 100644
index 000000000000..60f8635927db
--- /dev/null
+++ b/docs/snippets/react/mock-provider-in-preview.ts.mdx
@@ -0,0 +1,26 @@
+```tsx
+// .storybook/preview.tsx
+import React from 'react';
+import { Preview } from '@storybook/react';
+import { ThemeProvider } from 'styled-components';
+
+// themes = { light, dark }
+import * as themes from '../src/themes';
+
+const preview: Preview = {
+ decorators: [
+ // 👇 Defining the decorator in the preview file applies it to all stories
+ (Story, { parameters }) => {
+ // 👇 Make it configurable by reading the theme value from parameters
+ const { theme = 'light' } = parameters;
+ return (
+
+
+
+ );
+ },
+ ],
+};
+
+export default preview;
+```
diff --git a/docs/snippets/react/nextjs-cache-mock.js.mdx b/docs/snippets/react/nextjs-cache-mock.js.mdx
new file mode 100644
index 000000000000..b8e00ac1b414
--- /dev/null
+++ b/docs/snippets/react/nextjs-cache-mock.js.mdx
@@ -0,0 +1,22 @@
+```js
+// MyForm.stories.js
+import { expect, userEvent, within } from '@storybook/test';
+import { revalidatePath } from '@storybook/nextjs/cache.mock';
+
+import MyForm from './my-form';
+
+export default {
+ component: MyForm,
+};
+
+export const Submitted = {
+ async play({ canvasElement }) {
+ const canvas = within(canvasElement);
+
+ const submitButton = canvas.getByRole('button', { name: /submit/i });
+ await userEvent.click(saveButton);
+ // 👇 Use any mock assertions on the function
+ await expect(revalidatePath).toHaveBeenCalledWith('/');
+ },
+};
+```
diff --git a/docs/snippets/react/nextjs-cache-mock.ts-4-9.mdx b/docs/snippets/react/nextjs-cache-mock.ts-4-9.mdx
new file mode 100644
index 000000000000..23877ecdef4d
--- /dev/null
+++ b/docs/snippets/react/nextjs-cache-mock.ts-4-9.mdx
@@ -0,0 +1,28 @@
+```ts
+// MyForm.stories.ts
+import { expect, userEvent, within } from '@storybook/test';
+import { Meta, StoryObj } from '@storybook/react';
+// 👇 Must use this import path to have mocks typed correctly
+import { revalidatePath } from '@storybook/nextjs/cache.mock';
+
+import MyForm from './my-form';
+
+const meta = {
+ component: MyForm,
+} satisfies Meta;
+
+export default meta;
+
+type Story = StoryObj;
+
+export const Submitted: Story = {
+ async play({ canvasElement }) {
+ const canvas = within(canvasElement);
+
+ const submitButton = canvas.getByRole('button', { name: /submit/i });
+ await userEvent.click(saveButton);
+ // 👇 Use any mock assertions on the function
+ await expect(revalidatePath).toHaveBeenCalledWith('/');
+ },
+};
+```
diff --git a/docs/snippets/react/nextjs-cache-mock.ts.mdx b/docs/snippets/react/nextjs-cache-mock.ts.mdx
new file mode 100644
index 000000000000..6f5872224ee5
--- /dev/null
+++ b/docs/snippets/react/nextjs-cache-mock.ts.mdx
@@ -0,0 +1,28 @@
+```ts
+// MyForm.stories.ts
+import { expect, userEvent, within } from '@storybook/test';
+import { Meta, StoryObj } from '@storybook/react';
+// 👇 Must use this import path to have mocks typed correctly
+import { revalidatePath } from '@storybook/nextjs/cache.mock';
+
+import MyForm from './my-form';
+
+const meta: Meta = {
+ component: MyForm,
+};
+
+export default meta;
+
+type Story = StoryObj;
+
+export const Submitted: Story = {
+ async play({ canvasElement }) {
+ const canvas = within(canvasElement);
+
+ const submitButton = canvas.getByRole('button', { name: /submit/i });
+ await userEvent.click(saveButton);
+ // 👇 Use any mock assertions on the function
+ await expect(revalidatePath).toHaveBeenCalledWith('/');
+ },
+};
+```
diff --git a/docs/snippets/react/nextjs-headers-mock.js.mdx b/docs/snippets/react/nextjs-headers-mock.js.mdx
new file mode 100644
index 000000000000..1fed8238ceeb
--- /dev/null
+++ b/docs/snippets/react/nextjs-headers-mock.js.mdx
@@ -0,0 +1,26 @@
+```js
+// MyForm.stories.js
+import { expect, userEvent, within } from '@storybook/test';
+import { cookies, headers } from '@storybook/nextjs/headers.mock';
+
+import MyForm from './my-form';
+
+export default {
+ component: MyForm,
+};
+
+export const LoggedInEurope = {
+ async beforeEach() {
+ // 👇 Set mock cookies and headers ahead of rendering
+ cookies().set('username', 'Sol');
+ headers().set('timezone', 'Central European Summer Time');
+ },
+ async play() {
+ // 👇 Assert that your component called the mocks
+ await expect(cookies().get).toHaveBeenCalledOnce();
+ await expect(cookies().get).toHaveBeenCalledWith('username');
+ await expect(headers().get).toHaveBeenCalledOnce();
+ await expect(cookies().get).toHaveBeenCalledWith('timezone');
+ },
+};
+```
diff --git a/docs/snippets/react/nextjs-headers-mock.ts-4-9.mdx b/docs/snippets/react/nextjs-headers-mock.ts-4-9.mdx
new file mode 100644
index 000000000000..d82d73152165
--- /dev/null
+++ b/docs/snippets/react/nextjs-headers-mock.ts-4-9.mdx
@@ -0,0 +1,32 @@
+```ts
+// MyForm.stories.ts
+import { expect, fireEvent, userEvent, within } from '@storybook/test';
+import { Meta, StoryObj } from '@storybook/react';
+// 👇 Must use this import path to have mocks typed correctly
+import { cookies, headers } from '@storybook/nextjs/headers.mock';
+
+import MyForm from './my-form';
+
+const meta = {
+ component: MyForm,
+} satisfies Meta;
+
+export default meta;
+
+type Story = StoryObj;
+
+export const LoggedInEurope: Story = {
+ async beforeEach() {
+ // 👇 Set mock cookies and headers ahead of rendering
+ cookies().set('username', 'Sol');
+ headers().set('timezone', 'Central European Summer Time');
+ },
+ async play() {
+ // 👇 Assert that your component called the mocks
+ await expect(cookies().get).toHaveBeenCalledOnce();
+ await expect(cookies().get).toHaveBeenCalledWith('username');
+ await expect(headers().get).toHaveBeenCalledOnce();
+ await expect(cookies().get).toHaveBeenCalledWith('timezone');
+ },
+};
+```
\ No newline at end of file
diff --git a/docs/snippets/react/nextjs-headers-mock.ts.mdx b/docs/snippets/react/nextjs-headers-mock.ts.mdx
new file mode 100644
index 000000000000..eb6fa6aebed3
--- /dev/null
+++ b/docs/snippets/react/nextjs-headers-mock.ts.mdx
@@ -0,0 +1,32 @@
+```ts
+// MyForm.stories.ts
+import { expect, fireEvent, userEvent, within } from '@storybook/test';
+import { Meta, StoryObj } from '@storybook/react';
+// 👇 Must use this import path to have mocks typed correctly
+import { cookies, headers } from '@storybook/nextjs/headers.mock';
+
+import MyForm from './my-form';
+
+const meta: Meta = {
+ component: MyForm,
+};
+
+export default meta;
+
+type Story = StoryObj;
+
+export const LoggedInEurope: Story = {
+ async beforeEach() {
+ // 👇 Set mock cookies and headers ahead of rendering
+ cookies().set('username', 'Sol');
+ headers().set('timezone', 'Central European Summer Time');
+ },
+ async play() {
+ // 👇 Assert that your component called the mocks
+ await expect(cookies().get).toHaveBeenCalledOnce();
+ await expect(cookies().get).toHaveBeenCalledWith('username');
+ await expect(headers().get).toHaveBeenCalledOnce();
+ await expect(cookies().get).toHaveBeenCalledWith('timezone');
+ },
+};
+```
\ No newline at end of file
diff --git a/docs/snippets/react/nextjs-navigation-mock.js.mdx b/docs/snippets/react/nextjs-navigation-mock.js.mdx
new file mode 100644
index 000000000000..ed5bf26a99f5
--- /dev/null
+++ b/docs/snippets/react/nextjs-navigation-mock.js.mdx
@@ -0,0 +1,35 @@
+```js
+// MyForm.stories.js
+import { expect, fireEvent, userEvent, within } from '@storybook/test';
+import { redirect, getRouter } from '@storybook/nextjs/navigation.mock';
+
+import MyForm from './my-form';
+
+export default {
+ component: MyForm,
+ parameters: {
+ nextjs: {
+ // 👇 As in the Next.js application, next/navigation only works using App Router
+ appDirectory: true,
+ },
+ },
+};
+
+export const Unauthenticated = {
+ async play() => {
+ // 👇 Assert that your component called redirect()
+ await expect(redirect).toHaveBeenCalledWith('/login', 'replace');
+ },
+};
+
+export const GoBack = {
+ async play({ canvasElement }) {
+ const canvas = within(canvasElement);
+ const backBtn = await canvas.findByText('Go back');
+
+ await userEvent.click(backBtn);
+ // 👇 Assert that your component called back()
+ await expect(getRouter().back).toHaveBeenCalled();
+ },
+};
+```
diff --git a/docs/snippets/react/nextjs-navigation-mock.ts-4-9.mdx b/docs/snippets/react/nextjs-navigation-mock.ts-4-9.mdx
new file mode 100644
index 000000000000..d2fcddf10693
--- /dev/null
+++ b/docs/snippets/react/nextjs-navigation-mock.ts-4-9.mdx
@@ -0,0 +1,41 @@
+```ts
+// MyForm.stories.ts
+import { expect, fireEvent, userEvent, within } from '@storybook/test';
+import { Meta, StoryObj } from '@storybook/react';
+// 👇 Must use this import path to have mocks typed correctly
+import { redirect, getRouter } from '@storybook/nextjs/navigation.mock';
+
+import MyForm from './my-form';
+
+const meta = {
+ component: MyForm,
+ parameters: {
+ nextjs: {
+ // 👇 As in the Next.js application, next/navigation only works using App Router
+ appDirectory: true,
+ },
+ },
+} satisfies Meta;
+
+export default meta;
+
+type Story = StoryObj;
+
+export const Unauthenticated: Story = {
+ async play() => {
+ // 👇 Assert that your component called redirect()
+ await expect(redirect).toHaveBeenCalledWith('/login', 'replace');
+ },
+};
+
+export const GoBack: Story = {
+ async play({ canvasElement }) {
+ const canvas = within(canvasElement);
+ const backBtn = await canvas.findByText('Go back');
+
+ await userEvent.click(backBtn);
+ // 👇 Assert that your component called back()
+ await expect(getRouter().back).toHaveBeenCalled();
+ },
+};
+```
diff --git a/docs/snippets/react/nextjs-navigation-mock.ts.mdx b/docs/snippets/react/nextjs-navigation-mock.ts.mdx
new file mode 100644
index 000000000000..0a549357c831
--- /dev/null
+++ b/docs/snippets/react/nextjs-navigation-mock.ts.mdx
@@ -0,0 +1,41 @@
+```ts
+// MyForm.stories.ts
+import { expect, fireEvent, userEvent, within } from '@storybook/test';
+import { Meta, StoryObj } from '@storybook/react';
+// 👇 Must use this import path to have mocks typed correctly
+import { redirect, getRouter } from '@storybook/nextjs/navigation.mock';
+
+import MyForm from './my-form';
+
+const meta: Meta = {
+ component: MyForm,
+ parameters: {
+ nextjs: {
+ // 👇 As in the Next.js application, next/navigation only works using App Router
+ appDirectory: true,
+ },
+ },
+};
+
+export default meta;
+
+type Story = StoryObj;
+
+export const Unauthenticated: Story = {
+ async play() => {
+ // 👇 Assert that your component called redirect()
+ await expect(redirect).toHaveBeenCalledWith('/login', 'replace');
+ },
+};
+
+export const GoBack: Story = {
+ async play({ canvasElement }) {
+ const canvas = within(canvasElement);
+ const backBtn = await canvas.findByText('Go back');
+
+ await userEvent.click(backBtn);
+ // 👇 Assert that your component called back()
+ await expect(getRouter().back).toHaveBeenCalled();
+ },
+};
+```
diff --git a/docs/snippets/react/nextjs-router-mock.js.mdx b/docs/snippets/react/nextjs-router-mock.js.mdx
new file mode 100644
index 000000000000..7b23deb663ea
--- /dev/null
+++ b/docs/snippets/react/nextjs-router-mock.js.mdx
@@ -0,0 +1,23 @@
+```ts
+// MyForm.stories.ts
+import { expect, fireEvent, userEvent, within } from '@storybook/test';
+// 👇 Must use this import path to have mocks typed correctly
+import { getRouter } from '@storybook/nextjs/router.mock';
+
+import MyForm from './my-form';
+
+export default {
+ component: MyForm,
+};
+
+export const GoBack = {
+ async play({ canvasElement }) {
+ const canvas = within(canvasElement);
+ const backBtn = await canvas.findByText('Go back');
+
+ await userEvent.click(backBtn);
+ // 👇 Assert that your component called back()
+ await expect(getRouter().back).toHaveBeenCalled();
+ },
+};
+```
diff --git a/docs/snippets/react/nextjs-router-mock.ts-4-9.mdx b/docs/snippets/react/nextjs-router-mock.ts-4-9.mdx
new file mode 100644
index 000000000000..c04067374c85
--- /dev/null
+++ b/docs/snippets/react/nextjs-router-mock.ts-4-9.mdx
@@ -0,0 +1,28 @@
+```ts
+// MyForm.stories.ts
+import { expect, fireEvent, userEvent, within } from '@storybook/test';
+import { Meta, StoryObj } from '@storybook/react';
+// 👇 Must use this import path to have mocks typed correctly
+import { getRouter } from '@storybook/nextjs/router.mock';
+
+import MyForm from './my-form';
+
+const meta = {
+ component: MyForm,
+} satisfies Meta;
+
+export default meta;
+
+type Story = StoryObj;
+
+export const GoBack: Story = {
+ async play({ canvasElement }) {
+ const canvas = within(canvasElement);
+ const backBtn = await canvas.findByText('Go back');
+
+ await userEvent.click(backBtn);
+ // 👇 Assert that your component called back()
+ await expect(getRouter().back).toHaveBeenCalled();
+ },
+};
+```
diff --git a/docs/snippets/react/nextjs-router-mock.ts.mdx b/docs/snippets/react/nextjs-router-mock.ts.mdx
new file mode 100644
index 000000000000..0f55cee0394d
--- /dev/null
+++ b/docs/snippets/react/nextjs-router-mock.ts.mdx
@@ -0,0 +1,28 @@
+```ts
+// MyForm.stories.ts
+import { expect, fireEvent, userEvent, within } from '@storybook/test';
+import { Meta, StoryObj } from '@storybook/react';
+// 👇 Must use this import path to have mocks typed correctly
+import { getRouter } from '@storybook/nextjs/router.mock';
+
+import MyForm from './my-form';
+
+const meta: Meta = {
+ component: MyForm,
+};
+
+export default meta;
+
+type Story = StoryObj;
+
+export const GoBack: Story = {
+ async play({ canvasElement }) {
+ const canvas = within(canvasElement);
+ const backBtn = await canvas.findByText('Go back');
+
+ await userEvent.click(backBtn);
+ // 👇 Assert that your component called back()
+ await expect(getRouter().back).toHaveBeenCalled();
+ },
+};
+```
diff --git a/docs/snippets/web-components/before-each-in-meta-mock-date.js.mdx b/docs/snippets/web-components/before-each-in-meta-mock-date.js.mdx
new file mode 100644
index 000000000000..a53bd72eacfd
--- /dev/null
+++ b/docs/snippets/web-components/before-each-in-meta-mock-date.js.mdx
@@ -0,0 +1,25 @@
+```ts
+// Page.stories.ts
+import MockDate from 'mockdate';
+
+import { getUserFromSession } from '#api/session.mock';
+
+export default {
+ component: 'my-page',
+ // 👇 Set the value of Date for every story in the file
+ async beforeEach() {
+ MockDate.set('2024-02-14');
+
+ // 👇 Reset the Date after each story
+ return () => {
+ MockDate.reset();
+ };
+ },
+};
+
+export const Default = {
+ async play({ canvasElement }) {
+ // ... This will run with the mocked Date
+ },
+};
+```
diff --git a/docs/snippets/web-components/before-each-in-meta-mock-date.ts.mdx b/docs/snippets/web-components/before-each-in-meta-mock-date.ts.mdx
new file mode 100644
index 000000000000..e422865271d5
--- /dev/null
+++ b/docs/snippets/web-components/before-each-in-meta-mock-date.ts.mdx
@@ -0,0 +1,30 @@
+```ts
+// Page.stories.ts
+import { Meta, StoryObj } from '@storybook/web-components';
+import MockDate from 'mockdate';
+
+// 👇 Must use this import path to have mocks typed correctly
+import { getUserFromSession } from '#api/session.mock';
+
+const meta: Meta = {
+ component: 'my-page',
+ // 👇 Set the value of Date for every story in the file
+ async beforeEach() {
+ MockDate.set('2024-02-14');
+
+ // 👇 Reset the Date after each story
+ return () => {
+ MockDate.reset();
+ };
+ },
+};
+export default meta;
+
+type Story = StoryObj;
+
+export const Default: Story = {
+ async play({ canvasElement }) {
+ // ... This will run with the mocked Date
+ },
+};
+```
diff --git a/docs/snippets/web-components/storybook-test-fn-mock-spy.js.mdx b/docs/snippets/web-components/storybook-test-fn-mock-spy.js.mdx
new file mode 100644
index 000000000000..94bf53639ea6
--- /dev/null
+++ b/docs/snippets/web-components/storybook-test-fn-mock-spy.js.mdx
@@ -0,0 +1,30 @@
+```ts
+// NoteUI.stories.ts
+import { expect, userEvent, within } from '@storybook/test';
+
+import { saveNote } from '#app/actions.mock';
+import { createNotes } from '#mocks/notes';
+
+export default {
+ title: 'Mocked/NoteUI',
+ component: 'note-ui',
+};
+
+const notes = createNotes();
+
+export const SaveFlow = {
+ name: 'Save Flow ▶',
+ args: {
+ isEditing: true,
+ note: notes[0],
+ },
+ play: async ({ canvasElement, step }) => {
+ const canvas = within(canvasElement);
+
+ const saveButton = canvas.getByRole('menuitem', { name: /done/i });
+ await userEvent.click(saveButton);
+ // 👇 This is the mock function, so you can assert its behavior
+ await expect(saveNote).toHaveBeenCalled();
+ },
+};
+```
diff --git a/docs/snippets/web-components/storybook-test-fn-mock-spy.ts.mdx b/docs/snippets/web-components/storybook-test-fn-mock-spy.ts.mdx
new file mode 100644
index 000000000000..7f58a6acdcd3
--- /dev/null
+++ b/docs/snippets/web-components/storybook-test-fn-mock-spy.ts.mdx
@@ -0,0 +1,35 @@
+```ts
+// NoteUI.stories.ts
+import { Meta, StoryObj } from '@storybook/react';
+import { expect, userEvent, within } from '@storybook/test';
+
+// 👇 Must use this import path to have mocks typed correctly
+import { saveNote } from '#app/actions.mock';
+import { createNotes } from '#mocks/notes';
+
+const meta: Meta = {
+ title: 'Mocked/NoteUI',
+ component: 'note-ui',
+};
+export default meta;
+
+type Story = StoryObj;
+
+const notes = createNotes();
+
+export const SaveFlow: Story = {
+ name: 'Save Flow ▶',
+ args: {
+ isEditing: true,
+ note: notes[0],
+ },
+ play: async ({ canvasElement, step }) => {
+ const canvas = within(canvasElement);
+
+ const saveButton = canvas.getByRole('menuitem', { name: /done/i });
+ await userEvent.click(saveButton);
+ // 👇 This is the mock function, so you can assert its behavior
+ await expect(saveNote).toHaveBeenCalled();
+ },
+};
+```
diff --git a/docs/snippets/web-components/storybook-test-mock-return-value.js.mdx b/docs/snippets/web-components/storybook-test-mock-return-value.js.mdx
new file mode 100644
index 000000000000..84131094426b
--- /dev/null
+++ b/docs/snippets/web-components/storybook-test-mock-return-value.js.mdx
@@ -0,0 +1,15 @@
+```ts
+// Page.stories.ts
+import { getUserFromSession } from '#api/session.mock';
+
+export default {
+ component: 'my-page',
+};
+
+export const Default = {
+ async beforeEach() {
+ // 👇 Set the return value for the getUserFromSession function
+ getUserFromSession.mockReturnValue({ id: '1', name: 'Alice' });
+ },
+};
+```
diff --git a/docs/snippets/web-components/storybook-test-mock-return-value.ts.mdx b/docs/snippets/web-components/storybook-test-mock-return-value.ts.mdx
new file mode 100644
index 000000000000..70566e35f583
--- /dev/null
+++ b/docs/snippets/web-components/storybook-test-mock-return-value.ts.mdx
@@ -0,0 +1,21 @@
+```ts
+// Page.stories.ts
+import type { Meta, StoryObj } from '@storybook/web-components';
+
+// 👇 Must use this import path to have mocks typed correctly
+import { getUserFromSession } from '#api/session.mock';
+
+const meta: Meta = {
+ component: 'my-page',
+};
+export default meta;
+
+type Story = StoryObj;
+
+export const Default: Story = {
+ async beforeEach() {
+ // 👇 Set the return value for the getUserFromSession function
+ getUserFromSession.mockReturnValue({ id: '1', name: 'Alice' });
+ },
+};
+```
diff --git a/docs/writing-stories/decorators.md b/docs/writing-stories/decorators.md
index f673ed393c04..b7a0870201cf 100644
--- a/docs/writing-stories/decorators.md
+++ b/docs/writing-stories/decorators.md
@@ -49,36 +49,19 @@ The second argument to a decorator function is the **story context** which conta
- `parameters`- the story's static metadata, most commonly used to control Storybook's behavior of features and addons.
- `viewMode`- Storybook's current active window (e.g., canvas, docs).
-This context can be used to adjust the behavior of your decorator based on the story's arguments or other metadata. For example, you could create a decorator that wraps the story in a layout, unless the `noLayout` parameter is set to `true`:
-
-
-
-```ts
-// .storybook/preview.js
-import React from 'react';
-import { Preview } from '@storybook/react';
-
-import { Layout } from '../components/Layout';
-
-const preview: Preview = {
- decorators: [
- // 👇 Defining the decorator in the preview file applies it to all stories
- (Story, { parameters }) => {
- // 👇 Make it configurable by reading from parameters
- const { noLayout } = parameters;
- return noLayout ? (
-
- ) : (
-
-
-
- );
- },
- ],
-};
-
-export default preview;
-```
+This context can be used to adjust the behavior of your decorator based on the story's arguments or other metadata. For example, you could create a decorator that allows you to optionally apply a layout to the story, by defining `parameters.pageLayout = 'page'` (or `'page-mobile'`):
+:
+
+
+
+
+
+
diff --git a/docs/writing-stories/mocking-modules.md b/docs/writing-stories/mocking-modules.md
index fa1452b2222d..d310feaa6e0d 100644
--- a/docs/writing-stories/mocking-modules.md
+++ b/docs/writing-stories/mocking-modules.md
@@ -20,16 +20,15 @@ To mock a module, create a file with the same name and in the same directory as
Here's an example of a mock file for a module named `session`:
-
+
-```ts
-// lib/session.mock.ts
-import { fn } from '@storybook/test';
-import * as actual from './session';
+
-export * from './session';
-export const getUserFromSession = fn(actual.getUserFromSession);
-```
+
### Mock files for external modules
@@ -59,32 +58,15 @@ The recommended method for mocking modules is to use [subpath imports](https://n
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 four internal modules:
-
-
-```json
-// package.json
-{
- "imports": {
- "#api": {
- "storybook": "./api.mock.ts",
- "default": "./api.ts"
- },
- "#app/actions": {
- "storybook": "./app/actions.mock.ts",
- "default": "./app/actions.ts"
- },
- "#lib/session": {
- "storybook": "./lib/session.mock.ts",
- "default": "./lib/session.ts"
- },
- "#lib/db": {
- "storybook": "./lib/db.mock.ts",
- "default": "./lib/db.ts"
- },
- "#*": ["./*", "./*.ts", "./*.tsx"]
- }
-}
-```
+
+
+
+
+
There are two aspects to this configuration worth noting:
@@ -108,47 +90,18 @@ import { getUserFromSession } from '#lib/session';
If your project is unable to use [subpath imports](#subpath-imports), you can configure your Storybook builder to alias the module to the mock file. This will instruct the builder to replace the module with the mock file when bundling your Storybook stories.
-
+
-```ts
-// .storybook/main.ts
-viteFinal: async (config) => {
- if (config.resolve) {
- config.resolve.alias = {
- ...config.resolve?.alias,
- // 👇 External module
- 'lodash': require.resolve('./lodash.mock'),
- // 👇 Internal modules
- '@/api': path.resolve(__dirname, "./api.mock.ts"),
- '@/app/actions': path.resolve(__dirname, "./app/actions.mock.ts"),
- '@/lib/session': path.resolve(__dirname, "./lib/session.mock.ts"),
- '@/lib/db': path.resolve(__dirname, "./lib/db.mock.ts"),
- }
- }
-
- return config;
-},
-```
+
-```ts
-// .storybook/main.ts
-webpackFinal: async (config) => {
- if (config.resolve) {
- config.resolve.alias = {
- ...config.resolve.alias,
- // 👇 External module
- 'lodash': require.resolve('./lodash.mock'),
- // 👇 Internal modules
- '@/api$': path.resolve(__dirname, "./api.mock.ts"),
- '@/app/actions$': path.resolve(__dirname, "./app/actions.mock.ts"),
- '@/lib/session$': path.resolve(__dirname, "./lib/session.mock.ts"),
- '@/lib/db$': path.resolve(__dirname, "./lib/db.mock.ts"),
- }
- }
-
- return config;
-},
-```
+
## Using mocked modules in stories
@@ -156,30 +109,19 @@ When you use the `fn` utility to mock a module, you create full [Vitest mock fun
Here, we define `beforeEach` on a story (which will run before the story is rendered) to set a mocked return value for the `getUserFromSession` function used by the Page component:
-
-
-```ts
-// Page.stories.ts|tsx
-import { Meta, StoryObj } from '@storybook/react';
-import { fn } from '@storybook/test';
-
-import { getUserFromSession } from '#api/session.mock';
-import { Page } from './Page';
+
-const meta: Meta = {
- component: Page,
-};
-export default meta;
+
-type Story = StoryObj;
-
-export const Default: Story = {
- async beforeEach() {
- // 👇 Set the return value for the getUserFromSession function
- getUserFromSession.mockReturnValue({ id: '1', name: 'Alice' });
- },
-};
-```
+
@@ -193,43 +135,19 @@ The `fn` utility also spies on the original module's functions, which you can us
For example, this story checks that the `saveNote` function was called when the user clicks the save button:
-
+
-```ts
-// NoteUI.stories.ts|tsx
-import { Meta, StoryObj } from '@storybook/react';
-import { expect, userEvent, within } from '@storybook/test';
-
-import { saveNote } from '#app/actions.mock';
-import { createNotes } from '#mocks/notes';
-import NoteUI from './note-ui';
-
-const meta = {
- title: 'Mocked/NoteUI',
- component: NoteUI,
-} satisfies Meta;
-export default meta;
-
-type Story = StoryObj;
-
-const notes = createNotes();
-
-export const SaveFlow: Story = {
- name: 'Save Flow ▶',
- args: {
- isEditing: true,
- note: notes[0],
- },
- play: async ({ canvasElement, step }) => {
- const canvas = within(canvasElement);
-
- const saveButton = canvas.getByRole('menuitem', { name: /done/i });
- await userEvent.click(saveButton);
- // 👇 This is the mock function, so you can assert its behavior
- await expect(saveNote).toHaveBeenCalled();
- },
-};
-```
+
+
+
### Setting up and cleaning up
@@ -245,31 +163,16 @@ It is _not_ necessary to restore `fn()` mocks with the cleanup function, as Stor
Here's an example of using the [`mockdate`](https://github.com/boblauer/MockDate) package to mock the [`Date`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date) and reset it when the story unmounts.
-
+
-```ts
-// Page.stories.ts|tsx
-import { Meta, StoryObj } from '@storybook/react';
-import MockDate from 'mockdate';
-
-import { getUserFromSession } from '#api/session.mock';
-import { Page } from './Page';
-
-const meta: Meta = {
- component: Page,
- // 👇 Set the current date for every story in the file
- async beforeEach() {
- MockDate.set('2024-02-14');
-
- // 👇 Reset the date after each test
- return () => {
- MockDate.reset();
- };
- },
-};
-export default meta;
-
-type Story = StoryObj;
-
-export const Default: Story = {};
-```
+
+
+
diff --git a/docs/writing-stories/mocking-network-requests.md b/docs/writing-stories/mocking-network-requests.md
index d27ae6766483..fc71ce849eae 100644
--- a/docs/writing-stories/mocking-network-requests.md
+++ b/docs/writing-stories/mocking-network-requests.md
@@ -8,21 +8,21 @@ The [MSW addon](https://storybook.js.org/addons/msw-storybook-addon/) brings thi
## Set up the MSW addon
-First, if necessary, run this command to install MSW:
+First, if necessary, run this command to install MSW and the MSW addon:
-Then generate the service worker file necessary for MSW to work:
+If you're not already using MSW, generate the service worker file necessary for MSW to work:
@@ -45,14 +45,6 @@ Angular projects will likely need to adjust the command to save the mock service
-Next, install and register the MSW addon:
-
-
-
-```sh
-npx storybook@latest add msw-storybook-addon
-```
-
Then ensure the [`staticDirs`](../api/main-config-static-dirs.md) property in your Storybook configuration will include the generated service worker file (in `/public`, by default):
diff --git a/docs/writing-stories/mocking-providers.md b/docs/writing-stories/mocking-providers.md
index 0671ff37532e..a20ea662373f 100644
--- a/docs/writing-stories/mocking-providers.md
+++ b/docs/writing-stories/mocking-providers.md
@@ -33,10 +33,20 @@ Components can receive data or configuration from context providers. For example
+
+
+Note the file extension above (`.tsx` or `.jsx`). You may need to adjust your preview file's extension to allow use of JSX, depending on your project's settings.
+
+
+
+
+
For another example, reference the [Screens](https://storybook.js.org/tutorials/intro-to-storybook/react/en/screen/) chapter of the Intro to Storybook tutorial, where we mock a Redux provider with mock data.
+
+
## Configuring the mock provider
@@ -49,59 +59,29 @@ For a better way, with much less repetition, you can use the [decorator function
For example, we can adjust the decorator from above to read from `parameters.theme` to determine which theme to provide:
-
-
-```ts
-// .storybook/preview.ts
-import React from 'react';
-import { Preview } from '@storybook/react';
-import { ThemeProvider } from 'styled-components';
-
-const preview: Preview = {
- decorators: [
- // 👇 Defining the decorator in the preview file applies it to all stories
- (Story, { parameters }) => {
- // 👇 Make it configurable by reading the theme value from parameters
- const theme = parameters.theme || 'default';
- return (
-
-
-
- );
- },
- ],
-};
-
-export default preview;
-```
-
-Now, you can define a `theme` parameter in your stories to adjust the theme provided by the decorator:
-
-
+
-```ts
-// Button.stories.ts|tsx
-import { Meta, StoryObj } from '@storybook/react';
+
-import { Button } from './Button';
+
-const meta: Meta = {
- component: Button,
-};
-export default meta;
+Now, you can define a `theme` parameter in your stories to adjust the theme provided by the decorator:
-type Story = StoryObj;
+
-// Wrapped in default theme
-export const Default: Story = {};
+
-// Wrapped in dark theme
-export const Dark: Story = {
- parameters: {
- theme: 'dark',
- },
-};
-```
+
This powerful approach allows you to provide any value (theme, user role, mock data, etc.) to your components in a way that is both flexible and maintainable.
diff --git a/docs/writing-tests/interaction-testing.md b/docs/writing-tests/interaction-testing.md
index 4a28b9258abf..cb1b3eea6918 100644
--- a/docs/writing-tests/interaction-testing.md
+++ b/docs/writing-tests/interaction-testing.md
@@ -104,38 +104,19 @@ It is _not_ necessary to restore `fn()` mocks with the cleanup function, as Stor
Here's an example of using the [`mockdate`](https://github.com/boblauer/MockDate) package to mock the Date and reset it when the story unmounts.
-
-
-```ts
-// Page.stories.ts|tsx
-import { Meta, StoryObj } from '@storybook/react';
-import MockDate from 'mockdate';
-
-import { getUserFromSession } from '#api/session.mock';
-import { Page } from './Page';
-
-const meta: Meta = {
- component: Page,
- // 👇 Set the current date for every story in the file
- async beforeEach() {
- MockDate.set('2024-02-14');
-
- // 👇 Reset the date after each test
- return () => {
- MockDate.reset();
- };
- },
-};
-export default meta;
-
-type Story = StoryObj;
-
-export const Default: Story = {
- async play({ canvasElement }) {
- // ... This will run with the mocked date
- },
-};
-```
+
+
+
+
+
### API for user-events
@@ -205,43 +186,19 @@ If your component depends on modules that are imported into the component file,
You can then import the mocked module (which has all of the helpful methods of a [Vitest mocked function](https://vitest.dev/api/mock.html)) into your story and use it to assert on the behavior of your component:
-
-
-```ts
-// NoteUI.stories.ts|tsx
-import { Meta, StoryObj } from '@storybook/react';
-import { expect, userEvent, within } from '@storybook/test';
-
-import { saveNote } from '#app/actions.mock';
-import { createNotes } from '#mocks/notes';
-import NoteUI from './note-ui';
-
-const meta = {
- title: 'Mocked/NoteUI',
- component: NoteUI,
-} satisfies Meta;
-export default meta;
-
-type Story = StoryObj;
-
-const notes = createNotes();
-
-export const SaveFlow: Story = {
- name: 'Save Flow ▶',
- args: {
- isEditing: true,
- note: notes[0],
- },
- play: async ({ canvasElement, step }) => {
- const canvas = within(canvasElement);
-
- const saveButton = canvas.getByRole('menuitem', { name: /done/i });
- await userEvent.click(saveButton);
- // 👇 This is the mock function, so you can assert its behavior
- await expect(saveNote).toHaveBeenCalled();
- },
-};
-```
+
+
+
+
+
### Interactive debugger
From 6bf05a1707c802db2a92518b34ad67ecb9cd84ba Mon Sep 17 00:00:00 2001
From: Kyle Gach
Date: Tue, 30 Apr 2024 22:31:12 -0600
Subject: [PATCH 057/117] Remove unnecessary snippets
---
docs/configure/images-and-assets.md | 4 +--
.../angular/app-story-with-mock.ts.mdx | 33 -----------------
.../common/app-story-with-mock.js.mdx | 28 ---------------
.../common/app-story-with-mock.ts-4-9.mdx | 35 -------------------
.../common/app-story-with-mock.ts.mdx | 34 ------------------
.../common/isomorphic-fetch-mock.js.mdx | 25 -------------
.../storybook-main-with-mock-decorator.js.mdx | 12 -------
...orybook-main-with-single-static-dir.js.mdx | 10 ------
...orybook-main-with-single-static-dir.ts.mdx | 14 --------
.../common/storybook-msw-install.npm.js.mdx | 3 --
.../common/storybook-msw-install.pnpm.js.mdx | 3 --
.../common/storybook-msw-install.yarn.js.mdx | 3 --
...orybook-preview-with-mock-decorator.js.mdx | 8 -----
...orybook-preview-with-mock-decorator.ts.mdx | 14 --------
.../web-components/app-story-with-mock.js.mdx | 26 --------------
.../web-components/app-story-with-mock.ts.mdx | 31 ----------------
.../portable-stories.test.tsx.snap | 2 +-
17 files changed, 3 insertions(+), 282 deletions(-)
delete mode 100644 docs/snippets/angular/app-story-with-mock.ts.mdx
delete mode 100644 docs/snippets/common/app-story-with-mock.js.mdx
delete mode 100644 docs/snippets/common/app-story-with-mock.ts-4-9.mdx
delete mode 100644 docs/snippets/common/app-story-with-mock.ts.mdx
delete mode 100644 docs/snippets/common/isomorphic-fetch-mock.js.mdx
delete mode 100644 docs/snippets/common/storybook-main-with-mock-decorator.js.mdx
delete mode 100644 docs/snippets/common/storybook-main-with-single-static-dir.js.mdx
delete mode 100644 docs/snippets/common/storybook-main-with-single-static-dir.ts.mdx
delete mode 100644 docs/snippets/common/storybook-msw-install.npm.js.mdx
delete mode 100644 docs/snippets/common/storybook-msw-install.pnpm.js.mdx
delete mode 100644 docs/snippets/common/storybook-msw-install.yarn.js.mdx
delete mode 100644 docs/snippets/common/storybook-preview-with-mock-decorator.js.mdx
delete mode 100644 docs/snippets/common/storybook-preview-with-mock-decorator.ts.mdx
delete mode 100644 docs/snippets/web-components/app-story-with-mock.js.mdx
delete mode 100644 docs/snippets/web-components/app-story-with-mock.ts.mdx
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/snippets/angular/app-story-with-mock.ts.mdx b/docs/snippets/angular/app-story-with-mock.ts.mdx
deleted file mode 100644
index 79fd261a6ef5..000000000000
--- a/docs/snippets/angular/app-story-with-mock.ts.mdx
+++ /dev/null
@@ -1,33 +0,0 @@
-```ts
-// Button.stories.ts
-
-import type { Meta, StoryObj } from '@storybook/angular';
-
-import { App } from './app.component';
-
-const meta: Meta = {
- component: App,
-};
-
-export default meta;
-type Story = StoryObj;
-
-export const Success: Story = {
- parameters: {
- fetch: {
- json: {
- JavaScript: 3390991,
- 'C++': 44974,
- TypeScript: 15530,
- CoffeeScript: 12253,
- Python: 9383,
- C: 5341,
- Shell: 5115,
- HTML: 3420,
- CSS: 3171,
- Makefile: 189,
- },
- },
- },
-};
-```
diff --git a/docs/snippets/common/app-story-with-mock.js.mdx b/docs/snippets/common/app-story-with-mock.js.mdx
deleted file mode 100644
index 5e9815db826f..000000000000
--- a/docs/snippets/common/app-story-with-mock.js.mdx
+++ /dev/null
@@ -1,28 +0,0 @@
-```js
-// App.stories.js|jsx
-
-import App from './App';
-
-export default {
- component: App,
-};
-
-export const Success = {
- parameters: {
- fetch: {
- json: {
- JavaScript: 3390991,
- 'C++': 44974,
- TypeScript: 15530,
- CoffeeScript: 12253,
- Python: 9383,
- C: 5341,
- Shell: 5115,
- HTML: 3420,
- CSS: 3171,
- Makefile: 189,
- },
- },
- },
-};
-```
diff --git a/docs/snippets/common/app-story-with-mock.ts-4-9.mdx b/docs/snippets/common/app-story-with-mock.ts-4-9.mdx
deleted file mode 100644
index 14d0a19c7719..000000000000
--- a/docs/snippets/common/app-story-with-mock.ts-4-9.mdx
+++ /dev/null
@@ -1,35 +0,0 @@
-```ts
-// App.stories.ts|tsx
-
-// Replace your-framework with the name of your framework
-import type { Meta, StoryObj } from '@storybook/your-framework';
-
-import App from './App';
-
-const meta = {
- component: App,
-} satisfies Meta;
-
-export default meta;
-
-type Story = StoryObj;
-
-export const Success: Story = {
- parameters: {
- fetch: {
- json: {
- JavaScript: 3390991,
- 'C++': 44974,
- TypeScript: 15530,
- CoffeeScript: 12253,
- Python: 9383,
- C: 5341,
- Shell: 5115,
- HTML: 3420,
- CSS: 3171,
- Makefile: 189,
- },
- },
- },
-};
-```
diff --git a/docs/snippets/common/app-story-with-mock.ts.mdx b/docs/snippets/common/app-story-with-mock.ts.mdx
deleted file mode 100644
index dfaa74bf679b..000000000000
--- a/docs/snippets/common/app-story-with-mock.ts.mdx
+++ /dev/null
@@ -1,34 +0,0 @@
-```ts
-// App.stories.ts|tsx
-
-// Replace your-framework with the name of your framework
-import type { Meta, StoryObj } from '@storybook/your-framework';
-
-import App from './App';
-
-const meta: Meta = {
- component: App,
-};
-
-export default meta;
-type Story = StoryObj;
-
-export const Success: Story = {
- parameters: {
- fetch: {
- json: {
- JavaScript: 3390991,
- 'C++': 44974,
- TypeScript: 15530,
- CoffeeScript: 12253,
- Python: 9383,
- C: 5341,
- Shell: 5115,
- HTML: 3420,
- CSS: 3171,
- Makefile: 189,
- },
- },
- },
-};
-```
diff --git a/docs/snippets/common/isomorphic-fetch-mock.js.mdx b/docs/snippets/common/isomorphic-fetch-mock.js.mdx
deleted file mode 100644
index 7ad937afc843..000000000000
--- a/docs/snippets/common/isomorphic-fetch-mock.js.mdx
+++ /dev/null
@@ -1,25 +0,0 @@
-```js
-// __mocks__/isomorphic-fetch.js
-
-// Your fetch implementation to be added to ./storybook/main.js.
-// In your webpackFinal configuration object.
-
-let nextJson;
-export default async function fetch() {
- if (nextJson) {
- return {
- json: () => nextJson,
- };
- }
- nextJson = null;
-}
-
-// The decorator to be used in ./storybook/preview to apply the mock to all stories
-
-export function decorator(story, { parameters }) {
- if (parameters && parameters.fetch) {
- nextJson = parameters.fetch.json;
- }
- return story();
-}
-```
diff --git a/docs/snippets/common/storybook-main-with-mock-decorator.js.mdx b/docs/snippets/common/storybook-main-with-mock-decorator.js.mdx
deleted file mode 100644
index 0e8418371b6d..000000000000
--- a/docs/snippets/common/storybook-main-with-mock-decorator.js.mdx
+++ /dev/null
@@ -1,12 +0,0 @@
-```js
-// .storybook/main.js
-
-export default {
- // Your Storybook configuration
-
- webpackFinal: async (config) => {
- config.resolve.alias['isomorphic-fetch'] = require.resolve('../__mocks__/isomorphic-fetch.js');
- return config;
- },
-};
-```
diff --git a/docs/snippets/common/storybook-main-with-single-static-dir.js.mdx b/docs/snippets/common/storybook-main-with-single-static-dir.js.mdx
deleted file mode 100644
index 50898439ff1c..000000000000
--- a/docs/snippets/common/storybook-main-with-single-static-dir.js.mdx
+++ /dev/null
@@ -1,10 +0,0 @@
-```js
-// .storybook/main.js
-
-export default {
- // Replace your-framework with the framework you are using (e.g., react-webpack5, vue3-vite)
- framework: '@storybook/your-framework',
- stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
- staticDirs: ['../public'], //👈 Configures the static asset folder in Storybook
-};
-```
diff --git a/docs/snippets/common/storybook-main-with-single-static-dir.ts.mdx b/docs/snippets/common/storybook-main-with-single-static-dir.ts.mdx
deleted file mode 100644
index 779ebee771f3..000000000000
--- a/docs/snippets/common/storybook-main-with-single-static-dir.ts.mdx
+++ /dev/null
@@ -1,14 +0,0 @@
-```ts
-// .storybook/main.ts
-
-// Replace your-framework with the framework you are using (e.g., react-webpack5, vue3-vite)
-import type { StorybookConfig } from '@storybook/your-framework';
-
-const config: StorybookConfig = {
- framework: '@storybook/your-framework',
- stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
- staticDirs: ['../public'], //👈 Configures the static asset folder in Storybook
-};
-
-export default config;
-```
diff --git a/docs/snippets/common/storybook-msw-install.npm.js.mdx b/docs/snippets/common/storybook-msw-install.npm.js.mdx
deleted file mode 100644
index 9f0ed4aeed6f..000000000000
--- a/docs/snippets/common/storybook-msw-install.npm.js.mdx
+++ /dev/null
@@ -1,3 +0,0 @@
-```shell
-npm install msw msw-storybook-addon --save-dev
-```
diff --git a/docs/snippets/common/storybook-msw-install.pnpm.js.mdx b/docs/snippets/common/storybook-msw-install.pnpm.js.mdx
deleted file mode 100644
index d18a7d9d344f..000000000000
--- a/docs/snippets/common/storybook-msw-install.pnpm.js.mdx
+++ /dev/null
@@ -1,3 +0,0 @@
-```shell
-pnpm add --save-dev msw msw-storybook-addon
-```
diff --git a/docs/snippets/common/storybook-msw-install.yarn.js.mdx b/docs/snippets/common/storybook-msw-install.yarn.js.mdx
deleted file mode 100644
index 95e75837af80..000000000000
--- a/docs/snippets/common/storybook-msw-install.yarn.js.mdx
+++ /dev/null
@@ -1,3 +0,0 @@
-```shell
-yarn add --dev msw msw-storybook-addon
-```
diff --git a/docs/snippets/common/storybook-preview-with-mock-decorator.js.mdx b/docs/snippets/common/storybook-preview-with-mock-decorator.js.mdx
deleted file mode 100644
index 54d5c621f5b1..000000000000
--- a/docs/snippets/common/storybook-preview-with-mock-decorator.js.mdx
+++ /dev/null
@@ -1,8 +0,0 @@
-```js
-// .storybook/preview.js
-
-import { decorator } from '../__mocks__/isomorphic-fetch';
-
-// Add the decorator to all stories
-export default { decorators: [decorator] };
-```
diff --git a/docs/snippets/common/storybook-preview-with-mock-decorator.ts.mdx b/docs/snippets/common/storybook-preview-with-mock-decorator.ts.mdx
deleted file mode 100644
index fb6cdb2c68f7..000000000000
--- a/docs/snippets/common/storybook-preview-with-mock-decorator.ts.mdx
+++ /dev/null
@@ -1,14 +0,0 @@
-```ts
-// .storybook/preview.ts
-
-// Replace your-framework with the framework you are using (e.g., react, vue3)
-import { Preview } from '@storybook/your-framework';
-
-import { decorator } from '../__mocks__/isomorphic-fetch';
-
-const preview: Preview = {
- decorators: [decorator],
-};
-
-export default preview;
-```
diff --git a/docs/snippets/web-components/app-story-with-mock.js.mdx b/docs/snippets/web-components/app-story-with-mock.js.mdx
deleted file mode 100644
index 2d5b9a38f63a..000000000000
--- a/docs/snippets/web-components/app-story-with-mock.js.mdx
+++ /dev/null
@@ -1,26 +0,0 @@
-```js
-// App.stories.js
-
-export default {
- component: 'demo-app',
-};
-
-export const Success = {
- parameters: {
- fetch: {
- json: {
- JavaScript: 3390991,
- 'C++': 44974,
- TypeScript: 15530,
- CoffeeScript: 12253,
- Python: 9383,
- C: 5341,
- Shell: 5115,
- HTML: 3420,
- CSS: 3171,
- Makefile: 189,
- },
- },
- },
-};
-```
diff --git a/docs/snippets/web-components/app-story-with-mock.ts.mdx b/docs/snippets/web-components/app-story-with-mock.ts.mdx
deleted file mode 100644
index e8de25628bd7..000000000000
--- a/docs/snippets/web-components/app-story-with-mock.ts.mdx
+++ /dev/null
@@ -1,31 +0,0 @@
-```ts
-// App.stories.ts
-
-import type { Meta, StoryObj } from '@storybook/web-components';
-
-const meta: Meta = {
- component: 'demo-app',
-};
-
-export default meta;
-type Story = StoryObj;
-
-export const Success: Story = {
- parameters: {
- fetch: {
- json: {
- JavaScript: 3390991,
- 'C++': 44974,
- TypeScript: 15530,
- CoffeeScript: 12253,
- Python: 9383,
- C: 5341,
- Shell: 5115,
- HTML: 3420,
- CSS: 3171,
- Makefile: 189,
- },
- },
- },
-};
-```
diff --git a/test-storybooks/portable-stories-kitchen-sink/nextjs/stories/__snapshots__/portable-stories.test.tsx.snap b/test-storybooks/portable-stories-kitchen-sink/nextjs/stories/__snapshots__/portable-stories.test.tsx.snap
index 2f6747019333..5e046e9b7a0b 100644
--- a/test-storybooks/portable-stories-kitchen-sink/nextjs/stories/__snapshots__/portable-stories.test.tsx.snap
+++ b/test-storybooks/portable-stories-kitchen-sink/nextjs/stories/__snapshots__/portable-stories.test.tsx.snap
@@ -1010,7 +1010,7 @@ exports[`renders nextHeaderStories stories renders Default 1`] = `
- firstName=Jane; ; lastName=Doe;
+ firstName=Jane; lastName=Doe
Date: Tue, 30 Apr 2024 22:32:01 -0600
Subject: [PATCH 058/117] Prettify snippets
---
.../angular/before-each-in-meta-mock-date.ts.mdx | 2 +-
.../angular/storybook-test-fn-mock-spy.ts.mdx | 2 +-
.../common/module-aliases-config.vite.js.mdx | 14 +++++++-------
.../common/module-aliases-config.vite.ts.mdx | 14 +++++++-------
.../common/module-aliases-config.webpack.js.mdx | 14 +++++++-------
.../common/module-aliases-config.webpack.ts.mdx | 14 +++++++-------
.../common/subpath-imports-config.json.mdx | 2 +-
...igure-mock-provider-with-story-parameter.js.mdx | 2 +-
...e-mock-provider-with-story-parameter.ts-4-9.mdx | 2 +-
...igure-mock-provider-with-story-parameter.ts.mdx | 2 +-
docs/snippets/react/nextjs-headers-mock.ts-4-9.mdx | 2 +-
docs/snippets/react/nextjs-headers-mock.ts.mdx | 2 +-
12 files changed, 36 insertions(+), 36 deletions(-)
diff --git a/docs/snippets/angular/before-each-in-meta-mock-date.ts.mdx b/docs/snippets/angular/before-each-in-meta-mock-date.ts.mdx
index 4d79269e028d..96a21706b8a8 100644
--- a/docs/snippets/angular/before-each-in-meta-mock-date.ts.mdx
+++ b/docs/snippets/angular/before-each-in-meta-mock-date.ts.mdx
@@ -28,4 +28,4 @@ export const Default: Story = {
// ... This will run with the mocked Date
},
};
-```
\ No newline at end of file
+```
diff --git a/docs/snippets/angular/storybook-test-fn-mock-spy.ts.mdx b/docs/snippets/angular/storybook-test-fn-mock-spy.ts.mdx
index b0aac94d7473..3a106e1eb1bf 100644
--- a/docs/snippets/angular/storybook-test-fn-mock-spy.ts.mdx
+++ b/docs/snippets/angular/storybook-test-fn-mock-spy.ts.mdx
@@ -33,4 +33,4 @@ export const SaveFlow: Story = {
await expect(saveNote).toHaveBeenCalled();
},
};
-```
\ No newline at end of file
+```
diff --git a/docs/snippets/common/module-aliases-config.vite.js.mdx b/docs/snippets/common/module-aliases-config.vite.js.mdx
index f87d528689d3..925cee51558a 100644
--- a/docs/snippets/common/module-aliases-config.vite.js.mdx
+++ b/docs/snippets/common/module-aliases-config.vite.js.mdx
@@ -10,16 +10,16 @@ export default {
config.resolve.alias = {
...config.resolve?.alias,
// 👇 External module
- 'lodash': require.resolve('./lodash.mock'),
+ lodash: require.resolve('./lodash.mock'),
// 👇 Internal modules
- '@/api': path.resolve(__dirname, "./api.mock.ts"),
- '@/app/actions': path.resolve(__dirname, "./app/actions.mock.ts"),
- '@/lib/session': path.resolve(__dirname, "./lib/session.mock.ts"),
- '@/lib/db': path.resolve(__dirname, "./lib/db.mock.ts"),
- }
+ '@/api': path.resolve(__dirname, './api.mock.ts'),
+ '@/app/actions': path.resolve(__dirname, './app/actions.mock.ts'),
+ '@/lib/session': path.resolve(__dirname, './lib/session.mock.ts'),
+ '@/lib/db': path.resolve(__dirname, './lib/db.mock.ts'),
+ };
}
return config;
},
};
-```
\ No newline at end of file
+```
diff --git a/docs/snippets/common/module-aliases-config.vite.ts.mdx b/docs/snippets/common/module-aliases-config.vite.ts.mdx
index 57f3e02b20ce..08fd48c2535d 100644
--- a/docs/snippets/common/module-aliases-config.vite.ts.mdx
+++ b/docs/snippets/common/module-aliases-config.vite.ts.mdx
@@ -12,13 +12,13 @@ const config: StorybookConfig = {
config.resolve.alias = {
...config.resolve?.alias,
// 👇 External module
- 'lodash': require.resolve('./lodash.mock'),
+ lodash: require.resolve('./lodash.mock'),
// 👇 Internal modules
- '@/api': path.resolve(__dirname, "./api.mock.ts"),
- '@/app/actions': path.resolve(__dirname, "./app/actions.mock.ts"),
- '@/lib/session': path.resolve(__dirname, "./lib/session.mock.ts"),
- '@/lib/db': path.resolve(__dirname, "./lib/db.mock.ts"),
- }
+ '@/api': path.resolve(__dirname, './api.mock.ts'),
+ '@/app/actions': path.resolve(__dirname, './app/actions.mock.ts'),
+ '@/lib/session': path.resolve(__dirname, './lib/session.mock.ts'),
+ '@/lib/db': path.resolve(__dirname, './lib/db.mock.ts'),
+ };
}
return config;
@@ -26,4 +26,4 @@ const config: StorybookConfig = {
};
export default config;
-```
\ No newline at end of file
+```
diff --git a/docs/snippets/common/module-aliases-config.webpack.js.mdx b/docs/snippets/common/module-aliases-config.webpack.js.mdx
index b3b0b44c05e9..6dd020023cce 100644
--- a/docs/snippets/common/module-aliases-config.webpack.js.mdx
+++ b/docs/snippets/common/module-aliases-config.webpack.js.mdx
@@ -10,16 +10,16 @@ export default {
config.resolve.alias = {
...config.resolve.alias,
// 👇 External module
- 'lodash': require.resolve('./lodash.mock'),
+ lodash: require.resolve('./lodash.mock'),
// 👇 Internal modules
- '@/api$': path.resolve(__dirname, "./api.mock.ts"),
- '@/app/actions$': path.resolve(__dirname, "./app/actions.mock.ts"),
- '@/lib/session$': path.resolve(__dirname, "./lib/session.mock.ts"),
- '@/lib/db$': path.resolve(__dirname, "./lib/db.mock.ts"),
- }
+ '@/api$': path.resolve(__dirname, './api.mock.ts'),
+ '@/app/actions$': path.resolve(__dirname, './app/actions.mock.ts'),
+ '@/lib/session$': path.resolve(__dirname, './lib/session.mock.ts'),
+ '@/lib/db$': path.resolve(__dirname, './lib/db.mock.ts'),
+ };
}
return config;
},
};
-```
\ No newline at end of file
+```
diff --git a/docs/snippets/common/module-aliases-config.webpack.ts.mdx b/docs/snippets/common/module-aliases-config.webpack.ts.mdx
index 327295007df6..3dcbe14be825 100644
--- a/docs/snippets/common/module-aliases-config.webpack.ts.mdx
+++ b/docs/snippets/common/module-aliases-config.webpack.ts.mdx
@@ -12,13 +12,13 @@ const config: StorybookConfig = {
config.resolve.alias = {
...config.resolve.alias,
// 👇 External module
- 'lodash': require.resolve('./lodash.mock'),
+ lodash: require.resolve('./lodash.mock'),
// 👇 Internal modules
- '@/api$': path.resolve(__dirname, "./api.mock.ts"),
- '@/app/actions$': path.resolve(__dirname, "./app/actions.mock.ts"),
- '@/lib/session$': path.resolve(__dirname, "./lib/session.mock.ts"),
- '@/lib/db$': path.resolve(__dirname, "./lib/db.mock.ts"),
- }
+ '@/api$': path.resolve(__dirname, './api.mock.ts'),
+ '@/app/actions$': path.resolve(__dirname, './app/actions.mock.ts'),
+ '@/lib/session$': path.resolve(__dirname, './lib/session.mock.ts'),
+ '@/lib/db$': path.resolve(__dirname, './lib/db.mock.ts'),
+ };
}
return config;
@@ -26,4 +26,4 @@ const config: StorybookConfig = {
};
export default config;
-```
\ No newline at end of file
+```
diff --git a/docs/snippets/common/subpath-imports-config.json.mdx b/docs/snippets/common/subpath-imports-config.json.mdx
index 63615b528eb4..02dca8840caf 100644
--- a/docs/snippets/common/subpath-imports-config.json.mdx
+++ b/docs/snippets/common/subpath-imports-config.json.mdx
@@ -21,4 +21,4 @@
"#*": ["./*", "./*.ts", "./*.tsx"]
}
}
-```
\ No newline at end of file
+```
diff --git a/docs/snippets/react/configure-mock-provider-with-story-parameter.js.mdx b/docs/snippets/react/configure-mock-provider-with-story-parameter.js.mdx
index edb87b826e1d..b0d07bb65756 100644
--- a/docs/snippets/react/configure-mock-provider-with-story-parameter.js.mdx
+++ b/docs/snippets/react/configure-mock-provider-with-story-parameter.js.mdx
@@ -15,4 +15,4 @@ export const Dark = {
theme: 'dark',
},
};
-```
\ No newline at end of file
+```
diff --git a/docs/snippets/react/configure-mock-provider-with-story-parameter.ts-4-9.mdx b/docs/snippets/react/configure-mock-provider-with-story-parameter.ts-4-9.mdx
index f866555fc6dd..85b212f60d46 100644
--- a/docs/snippets/react/configure-mock-provider-with-story-parameter.ts-4-9.mdx
+++ b/docs/snippets/react/configure-mock-provider-with-story-parameter.ts-4-9.mdx
@@ -20,4 +20,4 @@ export const Dark: Story = {
theme: 'dark',
},
};
-```
\ No newline at end of file
+```
diff --git a/docs/snippets/react/configure-mock-provider-with-story-parameter.ts.mdx b/docs/snippets/react/configure-mock-provider-with-story-parameter.ts.mdx
index e59575a2e767..aa76044dfcf5 100644
--- a/docs/snippets/react/configure-mock-provider-with-story-parameter.ts.mdx
+++ b/docs/snippets/react/configure-mock-provider-with-story-parameter.ts.mdx
@@ -20,4 +20,4 @@ export const Dark: Story = {
theme: 'dark',
},
};
-```
\ No newline at end of file
+```
diff --git a/docs/snippets/react/nextjs-headers-mock.ts-4-9.mdx b/docs/snippets/react/nextjs-headers-mock.ts-4-9.mdx
index d82d73152165..f05ba295d6d1 100644
--- a/docs/snippets/react/nextjs-headers-mock.ts-4-9.mdx
+++ b/docs/snippets/react/nextjs-headers-mock.ts-4-9.mdx
@@ -29,4 +29,4 @@ export const LoggedInEurope: Story = {
await expect(cookies().get).toHaveBeenCalledWith('timezone');
},
};
-```
\ No newline at end of file
+```
diff --git a/docs/snippets/react/nextjs-headers-mock.ts.mdx b/docs/snippets/react/nextjs-headers-mock.ts.mdx
index eb6fa6aebed3..1d9d94eda281 100644
--- a/docs/snippets/react/nextjs-headers-mock.ts.mdx
+++ b/docs/snippets/react/nextjs-headers-mock.ts.mdx
@@ -29,4 +29,4 @@ export const LoggedInEurope: Story = {
await expect(cookies().get).toHaveBeenCalledWith('timezone');
},
};
-```
\ No newline at end of file
+```
From 27d7ed9ccba9e1389e8cae410b145f6b2b251df7 Mon Sep 17 00:00:00 2001
From: Josh Wooding <12938082+joshwooding@users.noreply.github.com>
Date: Thu, 28 Mar 2024 01:35:31 +0000
Subject: [PATCH 059/117] Upgrade
@joshwooding/vite-plugin-react-docgen-typescript to 0.3.1
---
code/frameworks/react-vite/package.json | 2 +-
code/yarn.lock | 10 +++++-----
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/code/frameworks/react-vite/package.json b/code/frameworks/react-vite/package.json
index 02a50462eace..827e9479764b 100644
--- a/code/frameworks/react-vite/package.json
+++ b/code/frameworks/react-vite/package.json
@@ -47,7 +47,7 @@
"prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts"
},
"dependencies": {
- "@joshwooding/vite-plugin-react-docgen-typescript": "0.3.0",
+ "@joshwooding/vite-plugin-react-docgen-typescript": "0.3.1",
"@rollup/pluginutils": "^5.0.2",
"@storybook/builder-vite": "workspace:*",
"@storybook/node-logger": "workspace:*",
diff --git a/code/yarn.lock b/code/yarn.lock
index 562da21be2e4..c67ff1b9b72f 100644
--- a/code/yarn.lock
+++ b/code/yarn.lock
@@ -3392,9 +3392,9 @@ __metadata:
languageName: node
linkType: hard
-"@joshwooding/vite-plugin-react-docgen-typescript@npm:0.3.0":
- version: 0.3.0
- resolution: "@joshwooding/vite-plugin-react-docgen-typescript@npm:0.3.0"
+"@joshwooding/vite-plugin-react-docgen-typescript@npm:0.3.1":
+ version: 0.3.1
+ resolution: "@joshwooding/vite-plugin-react-docgen-typescript@npm:0.3.1"
dependencies:
glob: "npm:^7.2.0"
glob-promise: "npm:^4.2.0"
@@ -3406,7 +3406,7 @@ __metadata:
peerDependenciesMeta:
typescript:
optional: true
- checksum: 10c0/31098ad8fcc2440437534599c111d9f2951dd74821e8ba46c521b969bae4c918d830b7bb0484efbad29a51711bb62d3bc623d5a1ed5b1695b5b5594ea9dd4ca0
+ checksum: 10c0/a9c7a03d7d1daf5bd64949255516ba64c88d5600366c8c74dcdb6f37c2a6099daaec02860b7587d2220e61afa47a0b2de17ef70d723c2db02f24e0890edfd9f3
languageName: node
linkType: hard
@@ -6458,7 +6458,7 @@ __metadata:
version: 0.0.0-use.local
resolution: "@storybook/react-vite@workspace:frameworks/react-vite"
dependencies:
- "@joshwooding/vite-plugin-react-docgen-typescript": "npm:0.3.0"
+ "@joshwooding/vite-plugin-react-docgen-typescript": "npm:0.3.1"
"@rollup/pluginutils": "npm:^5.0.2"
"@storybook/builder-vite": "workspace:*"
"@storybook/node-logger": "workspace:*"
From 7be08e32cfa8e64a754cb70baaac8a107415aa3b Mon Sep 17 00:00:00 2001
From: Kyle Gach
Date: Wed, 1 May 2024 09:44:47 -0600
Subject: [PATCH 060/117] Address feedback
- Make nextjs module alias usage more clear
- Update msw graphql snippets
- Add preact support for mocking providers
- Prose improvements
---
docs/api/portable-stories-jest.md | 2 +-
.../angular/msw-addon-configure-handlers-graphql.ts.mdx | 2 +-
docs/snippets/react/document-screen-with-graphql.ts.mdx | 2 +-
docs/snippets/react/msw-addon-configure-handlers-graphql.js.mdx | 2 +-
.../react/msw-addon-configure-handlers-graphql.ts-4-9.mdx | 2 +-
docs/snippets/react/msw-addon-configure-handlers-graphql.ts.mdx | 2 +-
docs/snippets/solid/document-screen-with-graphql.ts.mdx | 2 +-
.../snippets/svelte/msw-addon-configure-handlers-graphql.js.mdx | 2 +-
.../svelte/msw-addon-configure-handlers-graphql.ts-4-9.mdx | 2 +-
.../snippets/svelte/msw-addon-configure-handlers-graphql.ts.mdx | 2 +-
docs/snippets/vue/msw-addon-configure-handlers-graphql.js.mdx | 2 +-
.../vue/msw-addon-configure-handlers-graphql.ts-4-9.mdx | 2 +-
docs/snippets/vue/msw-addon-configure-handlers-graphql.ts.mdx | 2 +-
docs/writing-stories/mocking-modules.md | 2 +-
docs/writing-stories/mocking-providers.md | 2 +-
15 files changed, 15 insertions(+), 15 deletions(-)
diff --git a/docs/api/portable-stories-jest.md b/docs/api/portable-stories-jest.md
index cdc92d922e26..8c016e28f1cd 100644
--- a/docs/api/portable-stories-jest.md
+++ b/docs/api/portable-stories-jest.md
@@ -30,7 +30,7 @@ Normally, Storybok composes a story and its [annotations](#annotations) automati
- 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 be able to mock and assert on them.
+- 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/snippets/angular/msw-addon-configure-handlers-graphql.ts.mdx b/docs/snippets/angular/msw-addon-configure-handlers-graphql.ts.mdx
index f35bcd16f033..23599c6494de 100644
--- a/docs/snippets/angular/msw-addon-configure-handlers-graphql.ts.mdx
+++ b/docs/snippets/angular/msw-addon-configure-handlers-graphql.ts.mdx
@@ -59,7 +59,7 @@ export const MockedSuccess: Story = {
graphql.query('AllInfoQuery', () => {
return new HttpResponse.json({
data: {
- AllInfo: {
+ allInfo: {
...TestData,
},
},
diff --git a/docs/snippets/react/document-screen-with-graphql.ts.mdx b/docs/snippets/react/document-screen-with-graphql.ts.mdx
index 61a6cc29365e..56f5a24cdcc0 100644
--- a/docs/snippets/react/document-screen-with-graphql.ts.mdx
+++ b/docs/snippets/react/document-screen-with-graphql.ts.mdx
@@ -31,7 +31,7 @@ const AllInfoQuery = gql`
`;
interface Data {
- AllInfo: {
+ allInfo: {
user: {
userID: number;
name: string;
diff --git a/docs/snippets/react/msw-addon-configure-handlers-graphql.js.mdx b/docs/snippets/react/msw-addon-configure-handlers-graphql.js.mdx
index 0138bd74920c..b7964e2d4925 100644
--- a/docs/snippets/react/msw-addon-configure-handlers-graphql.js.mdx
+++ b/docs/snippets/react/msw-addon-configure-handlers-graphql.js.mdx
@@ -63,7 +63,7 @@ export const MockedSuccess = {
graphql.query('AllInfoQuery', () => {
return new HttpResponse.json({
data: {
- AllInfo: {
+ allInfo: {
...TestData,
},
},
diff --git a/docs/snippets/react/msw-addon-configure-handlers-graphql.ts-4-9.mdx b/docs/snippets/react/msw-addon-configure-handlers-graphql.ts-4-9.mdx
index 468c1866b98c..ab5786361bba 100644
--- a/docs/snippets/react/msw-addon-configure-handlers-graphql.ts-4-9.mdx
+++ b/docs/snippets/react/msw-addon-configure-handlers-graphql.ts-4-9.mdx
@@ -67,7 +67,7 @@ export const MockedSuccess: Story = {
graphql.query('AllInfoQuery', () => {
return new HttpResponse.json({
data: {
- AllInfo: {
+ allInfo: {
...TestData,
},
}
diff --git a/docs/snippets/react/msw-addon-configure-handlers-graphql.ts.mdx b/docs/snippets/react/msw-addon-configure-handlers-graphql.ts.mdx
index 281441a95612..4ffb616b0c66 100644
--- a/docs/snippets/react/msw-addon-configure-handlers-graphql.ts.mdx
+++ b/docs/snippets/react/msw-addon-configure-handlers-graphql.ts.mdx
@@ -67,7 +67,7 @@ export const MockedSuccess: Story = {
graphql.query('AllInfoQuery', () => {
return new HttpResponse.json({
data: {
- AllInfo: {
+ allInfo: {
...TestData,
},
}
diff --git a/docs/snippets/solid/document-screen-with-graphql.ts.mdx b/docs/snippets/solid/document-screen-with-graphql.ts.mdx
index 039bb7c64424..7b7f7f234c3d 100644
--- a/docs/snippets/solid/document-screen-with-graphql.ts.mdx
+++ b/docs/snippets/solid/document-screen-with-graphql.ts.mdx
@@ -32,7 +32,7 @@ const AllInfoQuery = gql`
`;
interface Data {
- AllInfo: {
+ allInfo: {
user: {
userID: number;
name: string;
diff --git a/docs/snippets/svelte/msw-addon-configure-handlers-graphql.js.mdx b/docs/snippets/svelte/msw-addon-configure-handlers-graphql.js.mdx
index bbe3531e64b0..6c9974f74e53 100644
--- a/docs/snippets/svelte/msw-addon-configure-handlers-graphql.js.mdx
+++ b/docs/snippets/svelte/msw-addon-configure-handlers-graphql.js.mdx
@@ -42,7 +42,7 @@ export const MockedSuccess = {
graphql.query('AllInfoQuery', () => {
return new HttpResponse.json({
data: {
- AllInfo: {
+ allInfo: {
...TestData,
},
},
diff --git a/docs/snippets/svelte/msw-addon-configure-handlers-graphql.ts-4-9.mdx b/docs/snippets/svelte/msw-addon-configure-handlers-graphql.ts-4-9.mdx
index 91e56485b7a5..a099095c28f8 100644
--- a/docs/snippets/svelte/msw-addon-configure-handlers-graphql.ts-4-9.mdx
+++ b/docs/snippets/svelte/msw-addon-configure-handlers-graphql.ts-4-9.mdx
@@ -47,7 +47,7 @@ export const MockedSuccess: Story = {
graphql.query('AllInfoQuery', () => {
return new HttpResponse.json({
data: {
- AllInfo: {
+ allInfo: {
...TestData,
},
},
diff --git a/docs/snippets/svelte/msw-addon-configure-handlers-graphql.ts.mdx b/docs/snippets/svelte/msw-addon-configure-handlers-graphql.ts.mdx
index cacc4af7674a..cc9fef5e7cb9 100644
--- a/docs/snippets/svelte/msw-addon-configure-handlers-graphql.ts.mdx
+++ b/docs/snippets/svelte/msw-addon-configure-handlers-graphql.ts.mdx
@@ -47,7 +47,7 @@ export const MockedSuccess: Story = {
graphql.query('AllInfoQuery', () => {
return new HttpResponse.json({
data: {
- AllInfo: {
+ allInfo: {
...TestData,
},
},
diff --git a/docs/snippets/vue/msw-addon-configure-handlers-graphql.js.mdx b/docs/snippets/vue/msw-addon-configure-handlers-graphql.js.mdx
index 3e13c859cc70..137d1b9a37c4 100644
--- a/docs/snippets/vue/msw-addon-configure-handlers-graphql.js.mdx
+++ b/docs/snippets/vue/msw-addon-configure-handlers-graphql.js.mdx
@@ -45,7 +45,7 @@ export const MockedSuccess = {
graphql.query('AllInfoQuery', () => {
return new HttpResponse.json({
data: {
- AllInfo: {
+ allInfo: {
...TestData,
},
},
diff --git a/docs/snippets/vue/msw-addon-configure-handlers-graphql.ts-4-9.mdx b/docs/snippets/vue/msw-addon-configure-handlers-graphql.ts-4-9.mdx
index 0efceab56d6a..701fc8dddd30 100644
--- a/docs/snippets/vue/msw-addon-configure-handlers-graphql.ts-4-9.mdx
+++ b/docs/snippets/vue/msw-addon-configure-handlers-graphql.ts-4-9.mdx
@@ -50,7 +50,7 @@ export const MockedSuccess: Story = {
graphql.query('AllInfoQuery', () => {
return new HttpResponse.json({
data: {
- AllInfo: {
+ allInfo: {
...TestData,
},
},
diff --git a/docs/snippets/vue/msw-addon-configure-handlers-graphql.ts.mdx b/docs/snippets/vue/msw-addon-configure-handlers-graphql.ts.mdx
index fd23daacece7..da7551e9474c 100644
--- a/docs/snippets/vue/msw-addon-configure-handlers-graphql.ts.mdx
+++ b/docs/snippets/vue/msw-addon-configure-handlers-graphql.ts.mdx
@@ -50,7 +50,7 @@ export const MockedSuccess: Story = {
graphql.query('AllInfoQuery', () => {
return new HttpResponse.json({
data: {
- AllInfo: {
+ allInfo: {
...TestData,
},
},
diff --git a/docs/writing-stories/mocking-modules.md b/docs/writing-stories/mocking-modules.md
index d310feaa6e0d..5e3964578783 100644
--- a/docs/writing-stories/mocking-modules.md
+++ b/docs/writing-stories/mocking-modules.md
@@ -72,7 +72,7 @@ There are two aspects to this configuration worth noting:
First, **each subpath must begin with `#`**, to differentiate it from a regular module path. The `#*` entry is a catch-all that maps all subpaths to the root directory.
-Second, note the **`storybook` and `default` keys** in each module's entry. The `storybook` value is used to import the mock file when loaded in Storybook, while the `default` value is used to import the original module in your project. The Storybook environment will match the conditions `storybook` and `test`, so you can apply the same module mapping for both Storybook and your tests.
+Second, note the **`storybook` and `default` keys** in each module's entry. The `storybook` value is used to import the mock file when loaded in Storybook, while the `default` value is used to import the original module when loaded in your project. The Storybook environment will match the conditions `storybook` and `test`, so you can apply the same module mapping for both Storybook and your tests.
With the package configuration in place, you can then update your component file to use the subpath import:
diff --git a/docs/writing-stories/mocking-providers.md b/docs/writing-stories/mocking-providers.md
index a20ea662373f..f44d148d0d55 100644
--- a/docs/writing-stories/mocking-providers.md
+++ b/docs/writing-stories/mocking-providers.md
@@ -2,7 +2,7 @@
title: Mocking providers
---
-export const SUPPORTED_RENDERERS = ['react', 'solid'];
+export const SUPPORTED_RENDERERS = ['preact', 'react', 'solid'];
From ef6710f5432dbcc0039c9d31a35152979f6a04bc Mon Sep 17 00:00:00 2001
From: Shrey Sudhir
Date: Thu, 2 May 2024 11:06:56 +1000
Subject: [PATCH 061/117] Fix broken migration notes for @storybook/addon-knobs
---
MIGRATION.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/MIGRATION.md b/MIGRATION.md
index 8b431bd37e90..4f4ffad6ff31 100644
--- a/MIGRATION.md
+++ b/MIGRATION.md
@@ -3859,7 +3859,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
From a9d9bfa464b667a7d96f798dc322958b57290c40 Mon Sep 17 00:00:00 2001
From: Kyle Gach
Date: Thu, 2 May 2024 22:44:18 -0600
Subject: [PATCH 062/117] Update decorator snippets
---
.../decorator-parameterized-in-preview.ts.mdx | 27 +++++++++++++++++
.../decorator-parameterized-in-preview.js.mdx | 4 +--
.../decorator-parameterized-in-preview.ts.mdx | 2 +-
.../decorator-parameterized-in-preview.js.mdx | 26 ++++++++++++++++
.../decorator-parameterized-in-preview.ts.mdx | 30 +++++++++++++++++++
.../decorator-parameterized-in-preview.js.mdx | 22 ++++++++++++++
.../decorator-parameterized-in-preview.ts.mdx | 26 ++++++++++++++++
docs/writing-stories/decorators.md | 5 ++++
8 files changed, 139 insertions(+), 3 deletions(-)
create mode 100644 docs/snippets/angular/decorator-parameterized-in-preview.ts.mdx
create mode 100644 docs/snippets/solid/decorator-parameterized-in-preview.js.mdx
create mode 100644 docs/snippets/solid/decorator-parameterized-in-preview.ts.mdx
create mode 100644 docs/snippets/vue/decorator-parameterized-in-preview.js.mdx
create mode 100644 docs/snippets/vue/decorator-parameterized-in-preview.ts.mdx
diff --git a/docs/snippets/angular/decorator-parameterized-in-preview.ts.mdx b/docs/snippets/angular/decorator-parameterized-in-preview.ts.mdx
new file mode 100644
index 000000000000..9de5b88b4bef
--- /dev/null
+++ b/docs/snippets/angular/decorator-parameterized-in-preview.ts.mdx
@@ -0,0 +1,27 @@
+```tsx
+// .storybook/preview.tsx
+import type { Preview } from '@storybook/angular';
+import { componentWrapperDecorator } from '@storybook/angular';
+
+const preview: Preview = {
+ decorators: [
+ // 👇 Defining the decorator in the preview file applies it to all stories
+ componentWrapperDecorator((story, { parameters }) => {
+ // 👇 Make it configurable by reading from parameters
+ const { pageLayout } = parameters;
+ switch (pageLayout) {
+ case 'page':
+ // Your page layout is probably a little more complex than this ;)
+ return `
${story}
`;
+ case 'page-mobile':
+ return `
${story}
`;
+ case default:
+ // In the default case, don't apply a layout
+ return story;
+ }
+ }),
+ ],
+};
+
+export default preview;
+```
diff --git a/docs/snippets/react/decorator-parameterized-in-preview.js.mdx b/docs/snippets/react/decorator-parameterized-in-preview.js.mdx
index b4c07fce70f2..123175807b18 100644
--- a/docs/snippets/react/decorator-parameterized-in-preview.js.mdx
+++ b/docs/snippets/react/decorator-parameterized-in-preview.js.mdx
@@ -1,5 +1,5 @@
-```ts
-// .storybook/preview.tsx
+```js
+// .storybook/preview.jsx
import React from 'react';
export default {
diff --git a/docs/snippets/react/decorator-parameterized-in-preview.ts.mdx b/docs/snippets/react/decorator-parameterized-in-preview.ts.mdx
index c46809be6b7f..010256148114 100644
--- a/docs/snippets/react/decorator-parameterized-in-preview.ts.mdx
+++ b/docs/snippets/react/decorator-parameterized-in-preview.ts.mdx
@@ -1,4 +1,4 @@
-```ts
+```tsx
// .storybook/preview.tsx
import React from 'react';
import { Preview } from '@storybook/react';
diff --git a/docs/snippets/solid/decorator-parameterized-in-preview.js.mdx b/docs/snippets/solid/decorator-parameterized-in-preview.js.mdx
new file mode 100644
index 000000000000..c2dfdbf375bf
--- /dev/null
+++ b/docs/snippets/solid/decorator-parameterized-in-preview.js.mdx
@@ -0,0 +1,26 @@
+```jsx
+// .storybook/preview.jsx
+export default {
+ decorators: [
+ // 👇 Defining the decorator in the preview file applies it to all stories
+ (Story, { parameters }) => {
+ // 👇 Make it configurable by reading from parameters
+ const { pageLayout } = parameters;
+ switch (pageLayout) {
+ case 'page':
+ return (
+ // Your page layout is probably a little more complex than this ;)
+
+ );
+ case 'page-mobile':
+ return (
+
+ );
+ case default:
+ // In the default case, don't apply a layout
+ return ;
+ }
+ },
+ ],
+};
+```
diff --git a/docs/snippets/solid/decorator-parameterized-in-preview.ts.mdx b/docs/snippets/solid/decorator-parameterized-in-preview.ts.mdx
new file mode 100644
index 000000000000..763ad428596d
--- /dev/null
+++ b/docs/snippets/solid/decorator-parameterized-in-preview.ts.mdx
@@ -0,0 +1,30 @@
+```tsx
+// .storybook/preview.tsx
+import { Preview } from 'storybook-solidjs';
+
+const preview: Preview = {
+ decorators: [
+ // 👇 Defining the decorator in the preview file applies it to all stories
+ (Story, { parameters }) => {
+ // 👇 Make it configurable by reading from parameters
+ const { pageLayout } = parameters;
+ switch (pageLayout) {
+ case 'page':
+ return (
+ // Your page layout is probably a little more complex than this ;)
+
+ );
+ case 'page-mobile':
+ return (
+
+ );
+ case default:
+ // In the default case, don't apply a layout
+ return ;
+ }
+ },
+ ],
+};
+
+export default preview;
+```
diff --git a/docs/snippets/vue/decorator-parameterized-in-preview.js.mdx b/docs/snippets/vue/decorator-parameterized-in-preview.js.mdx
new file mode 100644
index 000000000000..be2c87827ef0
--- /dev/null
+++ b/docs/snippets/vue/decorator-parameterized-in-preview.js.mdx
@@ -0,0 +1,22 @@
+```js
+// .storybook/preview.js
+export default {
+ decorators: [
+ // 👇 Defining the decorator in the preview file applies it to all stories
+ (_, { parameters }) => {
+ // 👇 Make it configurable by reading from parameters
+ const { pageLayout } = parameters;
+ switch (pageLayout) {
+ case 'page':
+ // Your page layout is probably a little more complex than this ;)
+ return { template: '
' };
+ case 'page-mobile':
+ return { template: '
' };
+ case default:
+ // In the default case, don't apply a layout
+ return { template: '' };
+ }
+ },
+ ],
+};
+```
diff --git a/docs/snippets/vue/decorator-parameterized-in-preview.ts.mdx b/docs/snippets/vue/decorator-parameterized-in-preview.ts.mdx
new file mode 100644
index 000000000000..da802b69a66f
--- /dev/null
+++ b/docs/snippets/vue/decorator-parameterized-in-preview.ts.mdx
@@ -0,0 +1,26 @@
+```ts
+// .storybook/preview.ts
+import { Preview } from '@storybook/vue3';
+
+const preview: Preview = {
+ decorators: [
+ // 👇 Defining the decorator in the preview file applies it to all stories
+ (_, { parameters }) => {
+ // 👇 Make it configurable by reading from parameters
+ const { pageLayout } = parameters;
+ switch (pageLayout) {
+ case 'page':
+ // Your page layout is probably a little more complex than this ;)
+ return { template: '
' };
+ case 'page-mobile':
+ return { template: '
' };
+ case default:
+ // In the default case, don't apply a layout
+ return { template: '' };
+ }
+ },
+ ],
+};
+
+export default preview;
+```
diff --git a/docs/writing-stories/decorators.md b/docs/writing-stories/decorators.md
index b7a0870201cf..7ada612fe8b8 100644
--- a/docs/writing-stories/decorators.md
+++ b/docs/writing-stories/decorators.md
@@ -58,6 +58,11 @@ This context can be used to adjust the behavior of your decorator based on the s
paths={[
'react/decorator-parameterized-in-preview.js.mdx',
'react/decorator-parameterized-in-preview.ts.mdx',
+ 'vue/decorator-parameterized-in-preview.js.mdx',
+ 'vue/decorator-parameterized-in-preview.ts.mdx',
+ 'angular/decorator-parameterized-in-preview.ts.mdx',
+ 'solid/decorator-parameterized-in-preview.js.mdx',
+ 'solid/decorator-parameterized-in-preview.ts.mdx',
]}
/>
From 370cff61301c33cefc530c06da8b5868cb64fa64 Mon Sep 17 00:00:00 2001
From: Laurens
Date: Fri, 3 May 2024 17:01:50 +0200
Subject: [PATCH 063/117] Update index.md
add space between text and link
---
docs/api/index.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/api/index.md b/docs/api/index.md
index 60edd614c32c..f719ab3291b3 100644
--- a/docs/api/index.md
+++ b/docs/api/index.md
@@ -66,7 +66,7 @@ An overview of all available API references for Storybook.
- Component Story Format (CSF) is the API for writing stories. It's an
+ Component Story Format (CSF) is the API for writing stories. It's an
open standard based on ES6 modules that
is portable beyond Storybook.
From d2f7fe8448db742b6a058af20aa1822852cdf34d Mon Sep 17 00:00:00 2001
From: Kyle Gach
Date: Fri, 3 May 2024 11:13:53 -0600
Subject: [PATCH 064/117] Address feedback
- Snippet consistency
- Prose tweaks
---
docs/api/parameters.md | 4 +---
docs/snippets/angular/before-each-in-meta-mock-date.ts.mdx | 2 +-
.../angular/decorator-parameterized-in-preview.ts.mdx | 4 ++--
docs/snippets/angular/storybook-test-fn-mock-spy.ts.mdx | 2 +-
docs/snippets/common/subpath-imports-config.json.mdx | 4 +++-
.../configure-mock-provider-with-story-parameter.ts-4-9.mdx | 2 +-
.../react/configure-mock-provider-with-story-parameter.ts.mdx | 2 +-
docs/snippets/react/decorator-parameterized-in-preview.ts.mdx | 3 ++-
docs/snippets/react/mock-provider-in-preview.js.mdx | 1 +
docs/snippets/react/mock-provider-in-preview.ts.mdx | 3 ++-
docs/snippets/react/nextjs-cache-mock.ts-4-9.mdx | 2 +-
docs/snippets/react/nextjs-cache-mock.ts.mdx | 2 +-
docs/snippets/react/nextjs-headers-mock.ts-4-9.mdx | 2 +-
docs/snippets/react/nextjs-headers-mock.ts.mdx | 2 +-
docs/snippets/react/nextjs-navigation-mock.ts-4-9.mdx | 2 +-
docs/snippets/react/nextjs-navigation-mock.ts.mdx | 2 +-
docs/snippets/react/nextjs-router-mock.js.mdx | 4 ++--
docs/snippets/react/nextjs-router-mock.ts-4-9.mdx | 2 +-
docs/snippets/react/nextjs-router-mock.ts.mdx | 2 +-
docs/snippets/vue/decorator-parameterized-in-preview.ts.mdx | 2 +-
.../web-components/before-each-in-meta-mock-date.js.mdx | 4 ++--
.../web-components/before-each-in-meta-mock-date.ts.mdx | 2 +-
.../snippets/web-components/storybook-test-fn-mock-spy.js.mdx | 4 ++--
.../snippets/web-components/storybook-test-fn-mock-spy.ts.mdx | 2 +-
.../web-components/storybook-test-mock-return-value.js.mdx | 4 ++--
docs/writing-stories/mocking-modules.md | 2 +-
docs/writing-tests/interaction-testing.md | 2 +-
27 files changed, 36 insertions(+), 33 deletions(-)
diff --git a/docs/api/parameters.md b/docs/api/parameters.md
index be7b02cc4b83..f70fa3395962 100644
--- a/docs/api/parameters.md
+++ b/docs/api/parameters.md
@@ -184,9 +184,7 @@ Type: `boolean`
Default: `false`
-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 rendering or playing.
-
-Unhandled errors might cause false positive assertions. They can be ignored by setting this parameter to `true`.
+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.
---
diff --git a/docs/snippets/angular/before-each-in-meta-mock-date.ts.mdx b/docs/snippets/angular/before-each-in-meta-mock-date.ts.mdx
index 96a21706b8a8..9e3dcefaf5cb 100644
--- a/docs/snippets/angular/before-each-in-meta-mock-date.ts.mdx
+++ b/docs/snippets/angular/before-each-in-meta-mock-date.ts.mdx
@@ -1,6 +1,6 @@
```ts
// Page.stories.ts
-import { Meta, StoryObj } from '@storybook/angular';
+import type { Meta, StoryObj } from '@storybook/angular';
import MockDate from 'mockdate';
// 👇 Must use this import path to have mocks typed correctly
diff --git a/docs/snippets/angular/decorator-parameterized-in-preview.ts.mdx b/docs/snippets/angular/decorator-parameterized-in-preview.ts.mdx
index 9de5b88b4bef..2200726832fe 100644
--- a/docs/snippets/angular/decorator-parameterized-in-preview.ts.mdx
+++ b/docs/snippets/angular/decorator-parameterized-in-preview.ts.mdx
@@ -1,5 +1,5 @@
-```tsx
-// .storybook/preview.tsx
+```ts
+// .storybook/preview.ts
import type { Preview } from '@storybook/angular';
import { componentWrapperDecorator } from '@storybook/angular';
diff --git a/docs/snippets/angular/storybook-test-fn-mock-spy.ts.mdx b/docs/snippets/angular/storybook-test-fn-mock-spy.ts.mdx
index 3a106e1eb1bf..d11b6fa261cb 100644
--- a/docs/snippets/angular/storybook-test-fn-mock-spy.ts.mdx
+++ b/docs/snippets/angular/storybook-test-fn-mock-spy.ts.mdx
@@ -1,6 +1,6 @@
```ts
// NoteUI.stories.ts
-import { Meta, StoryObj } from '@storybook/angular';
+import type { Meta, StoryObj } from '@storybook/angular';
import { expect, userEvent, within } from '@storybook/test';
// 👇 Must use this import path to have mocks typed correctly
diff --git a/docs/snippets/common/subpath-imports-config.json.mdx b/docs/snippets/common/subpath-imports-config.json.mdx
index 02dca8840caf..5450344f1178 100644
--- a/docs/snippets/common/subpath-imports-config.json.mdx
+++ b/docs/snippets/common/subpath-imports-config.json.mdx
@@ -3,6 +3,7 @@
{
"imports": {
"#api": {
+ // storybook condition applies to Storybook
"storybook": "./api.mock.ts",
"default": "./api.ts"
},
@@ -15,7 +16,8 @@
"default": "./lib/session.ts"
},
"#lib/db": {
- "storybook": "./lib/db.mock.ts",
+ // test condition applies to test environments *and* Storybook
+ "test": "./lib/db.mock.ts",
"default": "./lib/db.ts"
},
"#*": ["./*", "./*.ts", "./*.tsx"]
diff --git a/docs/snippets/react/configure-mock-provider-with-story-parameter.ts-4-9.mdx b/docs/snippets/react/configure-mock-provider-with-story-parameter.ts-4-9.mdx
index 85b212f60d46..d164216f0994 100644
--- a/docs/snippets/react/configure-mock-provider-with-story-parameter.ts-4-9.mdx
+++ b/docs/snippets/react/configure-mock-provider-with-story-parameter.ts-4-9.mdx
@@ -1,6 +1,6 @@
```ts
// Button.stories.ts
-import { Meta, StoryObj } from '@storybook/react';
+import type { Meta, StoryObj } from '@storybook/react';
import { Button } from './Button';
diff --git a/docs/snippets/react/configure-mock-provider-with-story-parameter.ts.mdx b/docs/snippets/react/configure-mock-provider-with-story-parameter.ts.mdx
index aa76044dfcf5..0668941b5fdd 100644
--- a/docs/snippets/react/configure-mock-provider-with-story-parameter.ts.mdx
+++ b/docs/snippets/react/configure-mock-provider-with-story-parameter.ts.mdx
@@ -1,6 +1,6 @@
```ts
// Button.stories.ts
-import { Meta, StoryObj } from '@storybook/react';
+import type { Meta, StoryObj } from '@storybook/react';
import { Button } from './Button';
diff --git a/docs/snippets/react/decorator-parameterized-in-preview.ts.mdx b/docs/snippets/react/decorator-parameterized-in-preview.ts.mdx
index 010256148114..9475198340d6 100644
--- a/docs/snippets/react/decorator-parameterized-in-preview.ts.mdx
+++ b/docs/snippets/react/decorator-parameterized-in-preview.ts.mdx
@@ -1,7 +1,8 @@
```tsx
// .storybook/preview.tsx
import React from 'react';
-import { Preview } from '@storybook/react';
+
+import type { Preview } from '@storybook/react';
const preview: Preview = {
decorators: [
diff --git a/docs/snippets/react/mock-provider-in-preview.js.mdx b/docs/snippets/react/mock-provider-in-preview.js.mdx
index 95bd0fa2d455..e16bdcef8bcf 100644
--- a/docs/snippets/react/mock-provider-in-preview.js.mdx
+++ b/docs/snippets/react/mock-provider-in-preview.js.mdx
@@ -1,6 +1,7 @@
```jsx
// .storybook/preview.jsx
import React from 'react';
+
import { ThemeProvider } from 'styled-components';
// themes = { light, dark }
diff --git a/docs/snippets/react/mock-provider-in-preview.ts.mdx b/docs/snippets/react/mock-provider-in-preview.ts.mdx
index 60f8635927db..1dbdcdac969f 100644
--- a/docs/snippets/react/mock-provider-in-preview.ts.mdx
+++ b/docs/snippets/react/mock-provider-in-preview.ts.mdx
@@ -1,7 +1,8 @@
```tsx
// .storybook/preview.tsx
import React from 'react';
-import { Preview } from '@storybook/react';
+
+import type { Preview } from '@storybook/react';
import { ThemeProvider } from 'styled-components';
// themes = { light, dark }
diff --git a/docs/snippets/react/nextjs-cache-mock.ts-4-9.mdx b/docs/snippets/react/nextjs-cache-mock.ts-4-9.mdx
index 23877ecdef4d..67a416a6840f 100644
--- a/docs/snippets/react/nextjs-cache-mock.ts-4-9.mdx
+++ b/docs/snippets/react/nextjs-cache-mock.ts-4-9.mdx
@@ -1,7 +1,7 @@
```ts
// MyForm.stories.ts
+import type { Meta, StoryObj } from '@storybook/react';
import { expect, userEvent, within } from '@storybook/test';
-import { Meta, StoryObj } from '@storybook/react';
// 👇 Must use this import path to have mocks typed correctly
import { revalidatePath } from '@storybook/nextjs/cache.mock';
diff --git a/docs/snippets/react/nextjs-cache-mock.ts.mdx b/docs/snippets/react/nextjs-cache-mock.ts.mdx
index 6f5872224ee5..14a2e439f0d5 100644
--- a/docs/snippets/react/nextjs-cache-mock.ts.mdx
+++ b/docs/snippets/react/nextjs-cache-mock.ts.mdx
@@ -1,7 +1,7 @@
```ts
// MyForm.stories.ts
+import type { Meta, StoryObj } from '@storybook/react';
import { expect, userEvent, within } from '@storybook/test';
-import { Meta, StoryObj } from '@storybook/react';
// 👇 Must use this import path to have mocks typed correctly
import { revalidatePath } from '@storybook/nextjs/cache.mock';
diff --git a/docs/snippets/react/nextjs-headers-mock.ts-4-9.mdx b/docs/snippets/react/nextjs-headers-mock.ts-4-9.mdx
index f05ba295d6d1..681d2efe0607 100644
--- a/docs/snippets/react/nextjs-headers-mock.ts-4-9.mdx
+++ b/docs/snippets/react/nextjs-headers-mock.ts-4-9.mdx
@@ -1,7 +1,7 @@
```ts
// MyForm.stories.ts
+import type { Meta, StoryObj } from '@storybook/react';
import { expect, fireEvent, userEvent, within } from '@storybook/test';
-import { Meta, StoryObj } from '@storybook/react';
// 👇 Must use this import path to have mocks typed correctly
import { cookies, headers } from '@storybook/nextjs/headers.mock';
diff --git a/docs/snippets/react/nextjs-headers-mock.ts.mdx b/docs/snippets/react/nextjs-headers-mock.ts.mdx
index 1d9d94eda281..10a25b7ead42 100644
--- a/docs/snippets/react/nextjs-headers-mock.ts.mdx
+++ b/docs/snippets/react/nextjs-headers-mock.ts.mdx
@@ -1,7 +1,7 @@
```ts
// MyForm.stories.ts
+import type { Meta, StoryObj } from '@storybook/react';
import { expect, fireEvent, userEvent, within } from '@storybook/test';
-import { Meta, StoryObj } from '@storybook/react';
// 👇 Must use this import path to have mocks typed correctly
import { cookies, headers } from '@storybook/nextjs/headers.mock';
diff --git a/docs/snippets/react/nextjs-navigation-mock.ts-4-9.mdx b/docs/snippets/react/nextjs-navigation-mock.ts-4-9.mdx
index d2fcddf10693..40ca4dce86b8 100644
--- a/docs/snippets/react/nextjs-navigation-mock.ts-4-9.mdx
+++ b/docs/snippets/react/nextjs-navigation-mock.ts-4-9.mdx
@@ -1,7 +1,7 @@
```ts
// MyForm.stories.ts
+import type { Meta, StoryObj } from '@storybook/react';
import { expect, fireEvent, userEvent, within } from '@storybook/test';
-import { Meta, StoryObj } from '@storybook/react';
// 👇 Must use this import path to have mocks typed correctly
import { redirect, getRouter } from '@storybook/nextjs/navigation.mock';
diff --git a/docs/snippets/react/nextjs-navigation-mock.ts.mdx b/docs/snippets/react/nextjs-navigation-mock.ts.mdx
index 0a549357c831..11ca27070d6c 100644
--- a/docs/snippets/react/nextjs-navigation-mock.ts.mdx
+++ b/docs/snippets/react/nextjs-navigation-mock.ts.mdx
@@ -1,7 +1,7 @@
```ts
// MyForm.stories.ts
+import type { Meta, StoryObj } from '@storybook/react';
import { expect, fireEvent, userEvent, within } from '@storybook/test';
-import { Meta, StoryObj } from '@storybook/react';
// 👇 Must use this import path to have mocks typed correctly
import { redirect, getRouter } from '@storybook/nextjs/navigation.mock';
diff --git a/docs/snippets/react/nextjs-router-mock.js.mdx b/docs/snippets/react/nextjs-router-mock.js.mdx
index 7b23deb663ea..a9a4c1253968 100644
--- a/docs/snippets/react/nextjs-router-mock.js.mdx
+++ b/docs/snippets/react/nextjs-router-mock.js.mdx
@@ -1,5 +1,5 @@
-```ts
-// MyForm.stories.ts
+```js
+// MyForm.stories.js
import { expect, fireEvent, userEvent, within } from '@storybook/test';
// 👇 Must use this import path to have mocks typed correctly
import { getRouter } from '@storybook/nextjs/router.mock';
diff --git a/docs/snippets/react/nextjs-router-mock.ts-4-9.mdx b/docs/snippets/react/nextjs-router-mock.ts-4-9.mdx
index c04067374c85..39e05f2ab502 100644
--- a/docs/snippets/react/nextjs-router-mock.ts-4-9.mdx
+++ b/docs/snippets/react/nextjs-router-mock.ts-4-9.mdx
@@ -1,7 +1,7 @@
```ts
// MyForm.stories.ts
+import type { Meta, StoryObj } from '@storybook/react';
import { expect, fireEvent, userEvent, within } from '@storybook/test';
-import { Meta, StoryObj } from '@storybook/react';
// 👇 Must use this import path to have mocks typed correctly
import { getRouter } from '@storybook/nextjs/router.mock';
diff --git a/docs/snippets/react/nextjs-router-mock.ts.mdx b/docs/snippets/react/nextjs-router-mock.ts.mdx
index 0f55cee0394d..8783e346f751 100644
--- a/docs/snippets/react/nextjs-router-mock.ts.mdx
+++ b/docs/snippets/react/nextjs-router-mock.ts.mdx
@@ -1,7 +1,7 @@
```ts
// MyForm.stories.ts
+import type { Meta, StoryObj } from '@storybook/react';
import { expect, fireEvent, userEvent, within } from '@storybook/test';
-import { Meta, StoryObj } from '@storybook/react';
// 👇 Must use this import path to have mocks typed correctly
import { getRouter } from '@storybook/nextjs/router.mock';
diff --git a/docs/snippets/vue/decorator-parameterized-in-preview.ts.mdx b/docs/snippets/vue/decorator-parameterized-in-preview.ts.mdx
index da802b69a66f..c65f8be884e7 100644
--- a/docs/snippets/vue/decorator-parameterized-in-preview.ts.mdx
+++ b/docs/snippets/vue/decorator-parameterized-in-preview.ts.mdx
@@ -1,6 +1,6 @@
```ts
// .storybook/preview.ts
-import { Preview } from '@storybook/vue3';
+import type { Preview } from '@storybook/vue3';
const preview: Preview = {
decorators: [
diff --git a/docs/snippets/web-components/before-each-in-meta-mock-date.js.mdx b/docs/snippets/web-components/before-each-in-meta-mock-date.js.mdx
index a53bd72eacfd..f6607ecc5c45 100644
--- a/docs/snippets/web-components/before-each-in-meta-mock-date.js.mdx
+++ b/docs/snippets/web-components/before-each-in-meta-mock-date.js.mdx
@@ -1,5 +1,5 @@
-```ts
-// Page.stories.ts
+```js
+// Page.stories.js
import MockDate from 'mockdate';
import { getUserFromSession } from '#api/session.mock';
diff --git a/docs/snippets/web-components/before-each-in-meta-mock-date.ts.mdx b/docs/snippets/web-components/before-each-in-meta-mock-date.ts.mdx
index e422865271d5..cee04d87e602 100644
--- a/docs/snippets/web-components/before-each-in-meta-mock-date.ts.mdx
+++ b/docs/snippets/web-components/before-each-in-meta-mock-date.ts.mdx
@@ -1,6 +1,6 @@
```ts
// Page.stories.ts
-import { Meta, StoryObj } from '@storybook/web-components';
+import type { Meta, StoryObj } from '@storybook/web-components';
import MockDate from 'mockdate';
// 👇 Must use this import path to have mocks typed correctly
diff --git a/docs/snippets/web-components/storybook-test-fn-mock-spy.js.mdx b/docs/snippets/web-components/storybook-test-fn-mock-spy.js.mdx
index 94bf53639ea6..c93f3ba6b3ef 100644
--- a/docs/snippets/web-components/storybook-test-fn-mock-spy.js.mdx
+++ b/docs/snippets/web-components/storybook-test-fn-mock-spy.js.mdx
@@ -1,5 +1,5 @@
-```ts
-// NoteUI.stories.ts
+```js
+// NoteUI.stories.js
import { expect, userEvent, within } from '@storybook/test';
import { saveNote } from '#app/actions.mock';
diff --git a/docs/snippets/web-components/storybook-test-fn-mock-spy.ts.mdx b/docs/snippets/web-components/storybook-test-fn-mock-spy.ts.mdx
index 7f58a6acdcd3..79ebde78c83a 100644
--- a/docs/snippets/web-components/storybook-test-fn-mock-spy.ts.mdx
+++ b/docs/snippets/web-components/storybook-test-fn-mock-spy.ts.mdx
@@ -1,6 +1,6 @@
```ts
// NoteUI.stories.ts
-import { Meta, StoryObj } from '@storybook/react';
+import type { Meta, StoryObj } from '@storybook/react';
import { expect, userEvent, within } from '@storybook/test';
// 👇 Must use this import path to have mocks typed correctly
diff --git a/docs/snippets/web-components/storybook-test-mock-return-value.js.mdx b/docs/snippets/web-components/storybook-test-mock-return-value.js.mdx
index 84131094426b..04c8c9858980 100644
--- a/docs/snippets/web-components/storybook-test-mock-return-value.js.mdx
+++ b/docs/snippets/web-components/storybook-test-mock-return-value.js.mdx
@@ -1,5 +1,5 @@
-```ts
-// Page.stories.ts
+```js
+// Page.stories.js
import { getUserFromSession } from '#api/session.mock';
export default {
diff --git a/docs/writing-stories/mocking-modules.md b/docs/writing-stories/mocking-modules.md
index 5e3964578783..a0df47cae700 100644
--- a/docs/writing-stories/mocking-modules.md
+++ b/docs/writing-stories/mocking-modules.md
@@ -72,7 +72,7 @@ There are two aspects to this configuration worth noting:
First, **each subpath must begin with `#`**, to differentiate it from a regular module path. The `#*` entry is a catch-all that maps all subpaths to the root directory.
-Second, note the **`storybook` and `default` keys** in each module's entry. The `storybook` value is used to import the mock file when loaded in Storybook, while the `default` value is used to import the original module when loaded in your project. The Storybook environment will match the conditions `storybook` and `test`, so you can apply the same module mapping for both Storybook and your tests.
+Second, note the **`storybook`, `test`, and `default` keys** in each module's entry. The `storybook` value is used to import the mock file when loaded in Storybook, while the `default` value is used to import the original module when loaded in your project. The `test` condition is also used within Storybook, which allows you to use the same configuration in Storybook and your other tests.
With the package configuration in place, you can then update your component file to use the subpath import:
diff --git a/docs/writing-tests/interaction-testing.md b/docs/writing-tests/interaction-testing.md
index cb1b3eea6918..9c2774d10b3c 100644
--- a/docs/writing-tests/interaction-testing.md
+++ b/docs/writing-tests/interaction-testing.md
@@ -102,7 +102,7 @@ It is _not_ necessary to restore `fn()` mocks with the cleanup function, as Stor
-Here's an example of using the [`mockdate`](https://github.com/boblauer/MockDate) package to mock the Date and reset it when the story unmounts.
+Here's an example of using the [`mockdate`](https://github.com/boblauer/MockDate) package to mock the [`Date`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date) and reset it when the story unmounts.
From 88253e458bdcfb7cb895d9d76a2f36842e2531bd Mon Sep 17 00:00:00 2001
From: Jeppe Reinhold
Date: Fri, 3 May 2024 23:49:36 +0200
Subject: [PATCH 065/117] add react 19 types resolutions, allow peer dep
problems in prereleases.
---
.../stories/docs2/resolved-react.stories.ts | 2 +-
code/lib/cli/src/sandbox-templates.ts | 15 +++++++++++++--
scripts/sandbox/generate.ts | 2 +-
scripts/utils/yarn.ts | 3 ++-
4 files changed, 17 insertions(+), 5 deletions(-)
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 2acdd32b11e4..20095ec6164b 100644
--- a/code/addons/docs/template/stories/docs2/resolved-react.stories.ts
+++ b/code/addons/docs/template/stories/docs2/resolved-react.stories.ts
@@ -45,7 +45,7 @@ export default {
name: 'ResolvedReact',
},
// the version string changes with every release of React/Next.js/Preact, not worth snapshotting
- chromat: { disable: true },
+ chromatic: { disable: true },
},
};
diff --git a/code/lib/cli/src/sandbox-templates.ts b/code/lib/cli/src/sandbox-templates.ts
index 1d1aeaf8ee03..5f2ff4634c02 100644
--- a/code/lib/cli/src/sandbox-templates.ts
+++ b/code/lib/cli/src/sandbox-templates.ts
@@ -203,8 +203,19 @@ const baseTemplates = {
},
'react-vite/prerelease-ts': {
name: 'React Prerelease (Vite | TypeScript)',
- script:
- 'npm create vite --yes {{beforeDir}} -- --template react-ts && cd {{beforeDir}} && yarn add react@beta react-dom@beta',
+ /**
+ * 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',
diff --git a/scripts/sandbox/generate.ts b/scripts/sandbox/generate.ts
index bc71a1433441..8f11b3a7a4db 100755
--- a/scripts/sandbox/generate.ts
+++ b/scripts/sandbox/generate.ts
@@ -163,7 +163,7 @@ const runGenerators = async (
else if (expected.renderer === '@storybook/server') flags = ['--type server'];
const time = process.hrtime();
- console.log(`🧬 Generating ${name} (${{ dirName }})`);
+ console.log(`🧬 Generating ${name} (${dirName})`);
await emptyDir(baseDir);
// We do the creation inside a temp dir to avoid yarn container problems
diff --git a/scripts/utils/yarn.ts b/scripts/utils/yarn.ts
index a2fb82e1fa6a..03b05ff3bf40 100644
--- a/scripts/utils/yarn.ts
+++ b/scripts/utils/yarn.ts
@@ -22,6 +22,7 @@ export const addPackageResolutions = async ({ cwd, dryRun }: YarnOptions) => {
const packageJsonPath = path.join(cwd, 'package.json');
const packageJson = await readJSON(packageJsonPath);
packageJson.resolutions = {
+ ...packageJson.resolutions,
...storybookVersions,
'enhanced-resolve': '~5.10.0', // TODO, remove this
// this is for our CI test, ensure we use the same version as docker image, it should match version specified in `./code/package.json` and `.circleci/config.yml`
@@ -97,7 +98,7 @@ export const configureYarn2ForVerdaccio = async ({
`yarn config set enableImmutableInstalls false`,
];
- if (key.includes('svelte-kit')) {
+ if (key.includes('svelte-kit') || key.includes('prerelease')) {
// Don't error with INCOMPATIBLE_PEER_DEPENDENCY for SvelteKit sandboxes, it is expected to happen with @sveltejs/vite-plugin-svelte
command.push(
`yarn config set logFilters --json '[ { "code": "YN0013", "level": "discard" } ]'`
From f6771152c702b75fb6e61a4478f8cb22feb5cf67 Mon Sep 17 00:00:00 2001
From: Michael Shilman
Date: Sun, 5 May 2024 05:29:14 +0800
Subject: [PATCH 066/117] Add preview.js watcher for project tags
---
.../src/utils/StoryIndexGenerator.ts | 15 ++++++++--
.../src/utils/getStoryIndexGenerator.ts | 4 ++-
.../lib/core-server/src/utils/stories-json.ts | 13 +++++++++
code/lib/core-server/src/utils/watchConfig.ts | 28 +++++++++++++++++++
.../src/modules/store/csf/prepareStory.ts | 5 ++--
5 files changed, 58 insertions(+), 7 deletions(-)
create mode 100644 code/lib/core-server/src/utils/watchConfig.ts
diff --git a/code/lib/core-server/src/utils/StoryIndexGenerator.ts b/code/lib/core-server/src/utils/StoryIndexGenerator.ts
index 5ad352d84cba..46b5fc296930 100644
--- a/code/lib/core-server/src/utils/StoryIndexGenerator.ts
+++ b/code/lib/core-server/src/utils/StoryIndexGenerator.ts
@@ -602,6 +602,16 @@ export class StoryIndexGenerator {
}
}
+ invalidateAll() {
+ this.specifierToCache.forEach((cache) => {
+ Object.keys(cache).forEach((key) => {
+ cache[key] = false;
+ });
+ });
+ this.lastIndex = null;
+ this.lastError = null;
+ }
+
invalidate(specifier: NormalizedStoriesSpecifier, importPath: Path, removed: boolean) {
const absolutePath = slash(path.resolve(this.options.workingDir, importPath));
const cache = this.specifierToCache.get(specifier);
@@ -656,10 +666,9 @@ export class StoryIndexGenerator {
getProjectTags(previewCode: string) {
const projectAnnotations = loadConfig(previewCode).parse();
- const defaultTags = ['dev', 'docs', 'test'];
const extraTags = this.options.docs.autodocs === true ? [AUTODOCS_TAG] : [];
- const projectTags = projectAnnotations.getFieldValue(['tags']) ?? [];
- return [...defaultTags, ...projectTags, ...extraTags];
+ const projectTags = projectAnnotations.getFieldValue(['tags']) ?? ['dev', 'docs', 'test'];
+ return [...projectTags, ...extraTags];
}
// Get the story file names in "imported order"
diff --git a/code/lib/core-server/src/utils/getStoryIndexGenerator.ts b/code/lib/core-server/src/utils/getStoryIndexGenerator.ts
index f8c1bc97f615..9c2a32385db9 100644
--- a/code/lib/core-server/src/utils/getStoryIndexGenerator.ts
+++ b/code/lib/core-server/src/utils/getStoryIndexGenerator.ts
@@ -13,8 +13,9 @@ export async function getStoryIndexGenerator(
serverChannel: ServerChannel
): Promise {
const workingDir = process.cwd();
+ const configDir = options.configDir;
const directories = {
- configDir: options.configDir,
+ configDir,
workingDir,
};
const stories = options.presets.apply('stories');
@@ -37,6 +38,7 @@ export async function getStoryIndexGenerator(
normalizedStories,
serverChannel,
workingDir,
+ configDir,
});
return initializedStoryIndexGenerator;
diff --git a/code/lib/core-server/src/utils/stories-json.ts b/code/lib/core-server/src/utils/stories-json.ts
index 074c0248cb0c..71b29a54a30b 100644
--- a/code/lib/core-server/src/utils/stories-json.ts
+++ b/code/lib/core-server/src/utils/stories-json.ts
@@ -1,3 +1,4 @@
+import { basename } from 'path';
import type { Router, Request, Response } from 'express';
import { writeJSON } from 'fs-extra';
@@ -7,6 +8,7 @@ import debounce from 'lodash/debounce.js';
import { STORY_INDEX_INVALIDATED } from '@storybook/core-events';
import type { StoryIndexGenerator } from './StoryIndexGenerator';
import { watchStorySpecifiers } from './watch-story-specifiers';
+import { watchConfig } from './watchConfig';
import type { ServerChannel } from './get-server-channel';
export const DEBOUNCE = 100;
@@ -25,6 +27,7 @@ export function useStoriesJson({
router,
initializedStoryIndexGenerator,
workingDir = process.cwd(),
+ configDir,
serverChannel,
normalizedStories,
}: {
@@ -32,6 +35,7 @@ export function useStoriesJson({
initializedStoryIndexGenerator: Promise;
serverChannel: ServerChannel;
workingDir?: string;
+ configDir?: string;
normalizedStories: NormalizedStoriesSpecifier[];
}) {
const maybeInvalidate = debounce(() => serverChannel.emit(STORY_INDEX_INVALIDATED), DEBOUNCE, {
@@ -42,6 +46,15 @@ export function useStoriesJson({
generator.invalidate(specifier, path, removed);
maybeInvalidate();
});
+ if (configDir) {
+ watchConfig(configDir, async (filePath) => {
+ if (basename(filePath).startsWith('preview')) {
+ const generator = await initializedStoryIndexGenerator;
+ generator.invalidateAll();
+ maybeInvalidate();
+ }
+ });
+ }
router.use('/index.json', async (req: Request, res: Response) => {
try {
diff --git a/code/lib/core-server/src/utils/watchConfig.ts b/code/lib/core-server/src/utils/watchConfig.ts
new file mode 100644
index 000000000000..14b925f1c1d8
--- /dev/null
+++ b/code/lib/core-server/src/utils/watchConfig.ts
@@ -0,0 +1,28 @@
+import Watchpack from 'watchpack';
+import type { Path } from '@storybook/types';
+
+// copied from './watch-story-specifiers.ts'
+/** Watch the .storybook dir for changes */
+export function watchConfig(
+ configDir: Path,
+ onInvalidate: (path: Path, removed: boolean) => Promise
+) {
+ const wp = new Watchpack({
+ followSymlinks: false,
+ ignored: ['**/.git', '**/node_modules'],
+ });
+
+ console.log('watching previewjs', configDir);
+ wp.watch({
+ directories: [configDir],
+ });
+ wp.on('change', async (filePath: Path, mtime: Date, explanation: string) => {
+ const removed = !mtime;
+ await onInvalidate(filePath, removed);
+ });
+ wp.on('remove', async (filePath: Path, explanation: string) => {
+ await onInvalidate(filePath, true);
+ });
+
+ return () => wp.close();
+}
diff --git a/code/lib/preview-api/src/modules/store/csf/prepareStory.ts b/code/lib/preview-api/src/modules/store/csf/prepareStory.ts
index 3c477a22180f..e13adec15e00 100644
--- a/code/lib/preview-api/src/modules/store/csf/prepareStory.ts
+++ b/code/lib/preview-api/src/modules/store/csf/prepareStory.ts
@@ -141,13 +141,12 @@ function preparePartialAnnotations(
// anything at render time. The assumption is that as we don't load all the stories at once, this
// will have a limited cost. If this proves misguided, we can refactor it.
- const defaultTags = ['dev', 'docs', 'test'];
+ const projectTags = projectAnnotations.tags ?? ['dev', 'docs', 'test'];
const extraTags = globalThis.DOCS_OPTIONS?.autodocs === true ? ['autodocs'] : [];
const tags = combineTags(
- ...defaultTags,
+ ...projectTags,
...extraTags,
- ...(projectAnnotations.tags ?? []),
...(componentAnnotations.tags ?? []),
...(storyAnnotations?.tags ?? []),
'story'
From 5dfa56b05621c3c5e3d32b828d5f0e5712e60f96 Mon Sep 17 00:00:00 2001
From: Michael Shilman
Date: Sun, 5 May 2024 07:49:08 +0800
Subject: [PATCH 067/117] Restore dev/docs/test default tags
---
code/lib/core-server/src/utils/StoryIndexGenerator.ts | 5 +++--
code/lib/preview-api/src/modules/store/csf/prepareStory.ts | 5 +++--
2 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/code/lib/core-server/src/utils/StoryIndexGenerator.ts b/code/lib/core-server/src/utils/StoryIndexGenerator.ts
index 46b5fc296930..476e0cf4795a 100644
--- a/code/lib/core-server/src/utils/StoryIndexGenerator.ts
+++ b/code/lib/core-server/src/utils/StoryIndexGenerator.ts
@@ -666,9 +666,10 @@ export class StoryIndexGenerator {
getProjectTags(previewCode: string) {
const projectAnnotations = loadConfig(previewCode).parse();
+ const defaultTags = ['dev', 'docs', 'test'];
const extraTags = this.options.docs.autodocs === true ? [AUTODOCS_TAG] : [];
- const projectTags = projectAnnotations.getFieldValue(['tags']) ?? ['dev', 'docs', 'test'];
- return [...projectTags, ...extraTags];
+ const projectTags = projectAnnotations.getFieldValue(['tags']) ?? [];
+ return [...defaultTags, ...projectTags, ...extraTags];
}
// Get the story file names in "imported order"
diff --git a/code/lib/preview-api/src/modules/store/csf/prepareStory.ts b/code/lib/preview-api/src/modules/store/csf/prepareStory.ts
index e13adec15e00..3c477a22180f 100644
--- a/code/lib/preview-api/src/modules/store/csf/prepareStory.ts
+++ b/code/lib/preview-api/src/modules/store/csf/prepareStory.ts
@@ -141,12 +141,13 @@ function preparePartialAnnotations(
// anything at render time. The assumption is that as we don't load all the stories at once, this
// will have a limited cost. If this proves misguided, we can refactor it.
- const projectTags = projectAnnotations.tags ?? ['dev', 'docs', 'test'];
+ const defaultTags = ['dev', 'docs', 'test'];
const extraTags = globalThis.DOCS_OPTIONS?.autodocs === true ? ['autodocs'] : [];
const tags = combineTags(
- ...projectTags,
+ ...defaultTags,
...extraTags,
+ ...(projectAnnotations.tags ?? []),
...(componentAnnotations.tags ?? []),
...(storyAnnotations?.tags ?? []),
'story'
From 31603542c82d945353491dc35225e22959501a56 Mon Sep 17 00:00:00 2001
From: Michael Shilman
Date: Sun, 5 May 2024 08:41:02 +0800
Subject: [PATCH 068/117] Preserver negated tags for index
---
code/lib/csf-tools/src/CsfFile.test.ts | 3 +++
code/lib/csf-tools/src/CsfFile.ts | 7 ++++---
2 files changed, 7 insertions(+), 3 deletions(-)
diff --git a/code/lib/csf-tools/src/CsfFile.test.ts b/code/lib/csf-tools/src/CsfFile.test.ts
index cfc650e30e2e..669109d268ac 100644
--- a/code/lib/csf-tools/src/CsfFile.test.ts
+++ b/code/lib/csf-tools/src/CsfFile.test.ts
@@ -1172,9 +1172,12 @@ describe('CsfFile', () => {
tags:
- component-tag
- component-tag-dup
+ - component-tag-dup
- inherit-tag-dup
- story-tag
- story-tag-dup
+ - story-tag-dup
+ - inherit-tag-dup
__id: custom-foo-title--a
`);
});
diff --git a/code/lib/csf-tools/src/CsfFile.ts b/code/lib/csf-tools/src/CsfFile.ts
index dc0270b72804..cac931501eec 100644
--- a/code/lib/csf-tools/src/CsfFile.ts
+++ b/code/lib/csf-tools/src/CsfFile.ts
@@ -8,7 +8,7 @@ import * as generate from '@babel/generator';
import * as recast from 'recast';
import * as traverse from '@babel/traverse';
-import { toId, isExportStory, storyNameFromExport, combineTags } from '@storybook/csf';
+import { toId, isExportStory, storyNameFromExport } from '@storybook/csf';
import type {
Tag,
StoryAnnotations,
@@ -557,9 +557,10 @@ export class CsfFile {
Either add the fileName option when creating the CsfFile instance, or create the index inputs manually.`
);
}
+
return Object.entries(this._stories).map(([exportName, story]) => {
- // combine meta and story tags, removing any duplicates
- const tags = combineTags(...(this._meta?.tags ?? []), ...(story.tags ?? []));
+ // don't remove any duplicates or negations -- tags will be combined in the index
+ const tags = [...(this._meta?.tags ?? []), ...(story.tags ?? [])];
return {
type: 'story',
importPath: this._fileName,
From a9c0730f55124f6cce7bc40b2cab7916f10fd555 Mon Sep 17 00:00:00 2001
From: Michael Shilman
Date: Sun, 5 May 2024 08:41:42 +0800
Subject: [PATCH 069/117] Remove auto-generated story tag, update tests
---
.../src/modules/store/csf/prepareStory.test.ts | 14 +++++++++++---
.../src/modules/store/csf/prepareStory.ts | 3 +--
2 files changed, 12 insertions(+), 5 deletions(-)
diff --git a/code/lib/preview-api/src/modules/store/csf/prepareStory.test.ts b/code/lib/preview-api/src/modules/store/csf/prepareStory.test.ts
index b05485e5a506..893fc2133eb3 100644
--- a/code/lib/preview-api/src/modules/store/csf/prepareStory.test.ts
+++ b/code/lib/preview-api/src/modules/store/csf/prepareStory.test.ts
@@ -44,7 +44,15 @@ describe('prepareStory', () => {
{ render }
);
- expect(tags).toEqual(['story-1', 'story-2', 'story']);
+ expect(tags).toEqual([
+ 'dev',
+ 'docs',
+ 'test',
+ 'component-1',
+ 'component-2',
+ 'story-1',
+ 'story-2',
+ ]);
});
it('component tags work if story are unset', () => {
@@ -58,13 +66,13 @@ describe('prepareStory', () => {
{ render }
);
- expect(tags).toEqual(['component-1', 'component-2', 'story']);
+ expect(tags).toEqual(['dev', 'docs', 'test', 'component-1', 'component-2']);
});
it('sets a value even if annotations do not have tags', () => {
const { tags } = prepareStory({ id, name, moduleExport }, { id, title }, { render });
- expect(tags).toEqual(['story']);
+ expect(tags).toEqual(['dev', 'docs', 'test']);
});
});
diff --git a/code/lib/preview-api/src/modules/store/csf/prepareStory.ts b/code/lib/preview-api/src/modules/store/csf/prepareStory.ts
index 3c477a22180f..769147ecd5ac 100644
--- a/code/lib/preview-api/src/modules/store/csf/prepareStory.ts
+++ b/code/lib/preview-api/src/modules/store/csf/prepareStory.ts
@@ -149,8 +149,7 @@ function preparePartialAnnotations(
...extraTags,
...(projectAnnotations.tags ?? []),
...(componentAnnotations.tags ?? []),
- ...(storyAnnotations?.tags ?? []),
- 'story'
+ ...(storyAnnotations?.tags ?? [])
);
const parameters: Parameters = combineParameters(
From ca88bcace2cafb61a9e3e21885fd8a2153ce566f Mon Sep 17 00:00:00 2001
From: Michael Shilman
Date: Sun, 5 May 2024 09:36:02 +0800
Subject: [PATCH 070/117] Add dev/docs tag behaviors
---
code/addons/docs/src/preview.ts | 4 +++-
code/lib/core-server/src/presets/common-manager.ts | 4 ++--
2 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/code/addons/docs/src/preview.ts b/code/addons/docs/src/preview.ts
index 991a7811b472..0e6dcbfed2fd 100644
--- a/code/addons/docs/src/preview.ts
+++ b/code/addons/docs/src/preview.ts
@@ -22,7 +22,9 @@ export const parameters: any = {
filter: (story: PreparedStory) => {
const tags = story.tags || [];
return (
- tags.filter((tag) => excludeTags[tag]).length === 0 && !story.parameters.docs?.disable
+ tags.includes('docs') &&
+ tags.filter((tag) => excludeTags[tag]).length === 0 &&
+ !story.parameters.docs?.disable
);
},
},
diff --git a/code/lib/core-server/src/presets/common-manager.ts b/code/lib/core-server/src/presets/common-manager.ts
index 0564f8e00b92..c3dfbd8d6fb8 100644
--- a/code/lib/core-server/src/presets/common-manager.ts
+++ b/code/lib/core-server/src/presets/common-manager.ts
@@ -18,7 +18,7 @@ addons.register(STATIC_FILTER, (api) => {
);
api.experimental_setFilter(STATIC_FILTER, (item) => {
- const tags = item.tags || [];
- return tags.filter((tag) => excludeTags[tag]).length === 0;
+ const tags = item.tags ?? [];
+ return tags.includes('dev') && tags.filter((tag) => excludeTags[tag]).length === 0;
});
});
From d89ff2e0643e9ead04541877d368681e6199571d Mon Sep 17 00:00:00 2001
From: Michael Shilman
Date: Sun, 5 May 2024 10:45:31 +0800
Subject: [PATCH 071/117] Deprecate main.js `docs.autodocs` and remove from
CLI template
---
MIGRATION.md | 40 ++++++++++++++++++-
code/lib/cli/src/generators/baseGenerator.ts | 1 -
.../src/modules/store/csf/prepareStory.ts | 11 ++++-
code/lib/types/src/modules/core-common.ts | 2 +-
4 files changed, 49 insertions(+), 5 deletions(-)
diff --git a/MIGRATION.md b/MIGRATION.md
index 34c0ca87c685..d8b631e557b2 100644
--- a/MIGRATION.md
+++ b/MIGRATION.md
@@ -1,8 +1,9 @@
Migration
- [From version 8.0 to 8.1.0](#from-version-80-to-810)
+ - [main.js `docs.autodocs` is deprecated](#mainjs-docsautodocs-is-deprecated)
- [Subtitle block and `parameters.componentSubtitle`](#subtitle-block-and-parameterscomponentsubtitle)
- - [Title block](#title-block)
+ - [Title block `of` prop](#title-block-of-prop)
- [From version 7.x to 8.0.0](#from-version-7x-to-800)
- [Portable stories](#portable-stories)
- [Project annotations are now merged instead of overwritten in composeStory](#project-annotations-are-now-merged-instead-of-overwritten-in-composestory)
@@ -408,13 +409,48 @@
## From version 8.0 to 8.1.0
+### main.js `docs.autodocs` is deprecated
+
+The `docs.autodocs` setting in `main.js` is deprecated in 8.1 and will be removed in 9.0.
+
+It has been replaced with a tags-based system which is more flexible than before.
+
+`docs.autodocs` takes three values:
+- `true`: generate autodocs for every component
+- `false`: don't generate autodocs at all
+- `tag`: generate autodocs for components that have been tagged `'autodocs'`.
+
+Starting in 8.1, to generate autodocs for every component (`docs.autodocs = true`), add the following code to `.storybook/preview.js`:
+
+```js
+// .storybook/preview.js
+export default {
+ tags: ['autodocs'],
+}
+```
+
+Tags cascade, so setting `'autodocs'` at the project level automatically propagates to every component and story. If you set autodocs globally and want to opt-out for a particular component, you can remove the `'autodocs'` tag for a component like this:
+
+```js
+// Button.stories.ts
+export default {
+ component: Button,
+ tags: ['!autodocs'],
+}
+```
+
+If you had set `docs.autodocs = 'tag'`, the default setting, you can remove the setting from `.storybook/main.js`. That is now the default behavior.
+
+If you had set `docs.autodocs = false`, this still works in 8.x, but will go away in 9.0 as a breaking change. If you don't want autodocs at all, simply remove the `'autodocs'` tag throughout your Storybook and autodocs will not be created.
+
+
### Subtitle block and `parameters.componentSubtitle`
The `Subtitle` block now accepts an `of` prop, which can be a reference to a CSF file or a default export (meta).
`parameters.componentSubtitle` has been deprecated to be consistent with other parameters related to autodocs, instead use `parameters.docs.subtitle`.
-##### Title block
+### Title block `of` prop
The `Title` block now accepts an `of` prop, which can be a reference to a CSF file or a default export (meta).
diff --git a/code/lib/cli/src/generators/baseGenerator.ts b/code/lib/cli/src/generators/baseGenerator.ts
index 2f85d0af4549..44f1214c0aa6 100644
--- a/code/lib/cli/src/generators/baseGenerator.ts
+++ b/code/lib/cli/src/generators/baseGenerator.ts
@@ -354,7 +354,6 @@ export async function baseGenerator(
},
prefixes,
storybookConfigFolder,
- docs: { autodocs: 'tag' },
addons: shouldApplyRequireWrapperOnPackageNames
? addons.map((addon) => applyRequireWrapper(addon))
: addons,
diff --git a/code/lib/preview-api/src/modules/store/csf/prepareStory.ts b/code/lib/preview-api/src/modules/store/csf/prepareStory.ts
index 769147ecd5ac..78b6319c4abc 100644
--- a/code/lib/preview-api/src/modules/store/csf/prepareStory.ts
+++ b/code/lib/preview-api/src/modules/store/csf/prepareStory.ts
@@ -1,6 +1,7 @@
/* eslint-disable @typescript-eslint/no-loop-func,no-underscore-dangle */
-import { global } from '@storybook/global';
+import { dedent } from 'ts-dedent';
+import { global } from '@storybook/global';
import type {
Args,
ArgsStoryFn,
@@ -22,6 +23,7 @@ import type {
} from '@storybook/types';
import { includeConditionalArg, combineTags } from '@storybook/csf';
import { global as globalThis } from '@storybook/global';
+import { once } from '@storybook/client-logger';
import { applyHooks } from '../../addons';
import { combineParameters } from '../parameters';
@@ -142,6 +144,13 @@ function preparePartialAnnotations(
// will have a limited cost. If this proves misguided, we can refactor it.
const defaultTags = ['dev', 'docs', 'test'];
+ if (typeof globalThis.DOCS_OPTIONS?.autodocs !== 'undefined') {
+ once.warn(dedent`
+ The \`docs.autodocs\` setting in '.storybook/main.js' is deprecated. Use \`tags: ['autodocs']\` in \`.storybook/preview.js\` instead.
+
+ For more info see: https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#mainjs-docsautodocs-is-deprecated
+ `);
+ }
const extraTags = globalThis.DOCS_OPTIONS?.autodocs === true ? ['autodocs'] : [];
const tags = combineTags(
diff --git a/code/lib/types/src/modules/core-common.ts b/code/lib/types/src/modules/core-common.ts
index 0e8dd7f002cd..9a3106931053 100644
--- a/code/lib/types/src/modules/core-common.ts
+++ b/code/lib/types/src/modules/core-common.ts
@@ -281,7 +281,7 @@ export type DocsOptions = {
* Set to 'tag' (the default) to generate an entry for every CSF file with the
* 'autodocs' tag.
*
- * @deprecated Use `tags: ['autodocs']` in .storybook/preview.js instead
+ * @deprecated Use `tags: ['autodocs']` in `.storybook/preview.js` instead
*/
autodocs?: boolean | 'tag';
/**
From 1e0e8220bcdc8d0988732d2f8889cdc42284d48b Mon Sep 17 00:00:00 2001
From: storybook-bot <32066757+storybook-bot@users.noreply.github.com>
Date: Sun, 5 May 2024 11:40:16 +0000
Subject: [PATCH 072/117] Update CHANGELOG.md for v8.0.10 [skip ci]
---
CHANGELOG.md | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d93c3b996486..f8bdc7027469 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,9 @@
+## 8.0.10
+
+- MDX: Don't transform `http://` links - [#26488](https://github.com/storybookjs/storybook/pull/26488), thanks @JReinhold!
+- React: Support v19 in `react-dom-shim` - [#26898](https://github.com/storybookjs/storybook/pull/26898), thanks @Tobbe!
+- Vite: Merge assetsInclude property with Storybook default values - [#26860](https://github.com/storybookjs/storybook/pull/26860), thanks @yuemori!
+
## 8.0.9
- Addon-docs: Fix MDX compilation when using `@vitejs/plugin-react-swc` with plugins - [#26837](https://github.com/storybookjs/storybook/pull/26837), thanks @JReinhold!
From bd1f0cbc6bd0ae8e32cd561d0ef39a0c93f1297d Mon Sep 17 00:00:00 2001
From: Jeppe Reinhold
Date: Sun, 5 May 2024 15:30:13 +0200
Subject: [PATCH 073/117] expect React 19 in react prerelease sandboxes
---
code/e2e-tests/addon-docs.spec.ts | 2 ++
1 file changed, 2 insertions(+)
diff --git a/code/e2e-tests/addon-docs.spec.ts b/code/e2e-tests/addon-docs.spec.ts
index 2713892fd042..5fd470dc81b2 100644
--- a/code/e2e-tests/addon-docs.spec.ts
+++ b/code/e2e-tests/addon-docs.spec.ts
@@ -200,6 +200,8 @@ test.describe('addon-docs', () => {
expectedReactVersionRange = /^17/;
} else if (templateName.includes('react16')) {
expectedReactVersionRange = /^16/;
+ } else if (templateName.includes('prerelease')) {
+ expectedReactVersionRange = /^19/;
}
// Arrange - Get the actual versions
From 206e6ec62cdfdc059661fd7bf6865f2d1f0d8138 Mon Sep 17 00:00:00 2001
From: Jeppe Reinhold
Date: Sun, 5 May 2024 17:21:51 +0200
Subject: [PATCH 074/117] dont be stupid now
---
code/e2e-tests/addon-docs.spec.ts | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/code/e2e-tests/addon-docs.spec.ts b/code/e2e-tests/addon-docs.spec.ts
index 5fd470dc81b2..b8b20bb46455 100644
--- a/code/e2e-tests/addon-docs.spec.ts
+++ b/code/e2e-tests/addon-docs.spec.ts
@@ -200,7 +200,10 @@ test.describe('addon-docs', () => {
expectedReactVersionRange = /^17/;
} else if (templateName.includes('react16')) {
expectedReactVersionRange = /^16/;
- } else if (templateName.includes('prerelease')) {
+ } else if (
+ templateName.includes('react-vite/prerelease') ||
+ templateName.includes('react-webpack/prerelease')
+ ) {
expectedReactVersionRange = /^19/;
}
From 6b2023618845bada7099b5baf1abb7196ec4fa9d Mon Sep 17 00:00:00 2001
From: Jeppe Reinhold
Date: Sun, 5 May 2024 21:15:35 +0200
Subject: [PATCH 075/117] convert defaultProps to default assignments in
functional components.
---
code/ui/blocks/src/components/Source.tsx | 5 +-
.../src/components/form/field/field.tsx | 4 --
.../components/src/components/tabs/tabs.tsx | 21 ++-----
.../src/components/tooltip/ListItem.tsx | 33 ++++------
.../src/components/tooltip/Tooltip.tsx | 18 +++---
.../components/tooltip/TooltipLinkList.tsx | 6 +-
.../src/components/tooltip/TooltipMessage.tsx | 6 --
.../src/components/tooltip/WithTooltip.tsx | 62 ++++++++-----------
8 files changed, 55 insertions(+), 100 deletions(-)
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/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;
From 4f2bec7a48e72cd7a1d5224a92677e214b720ced Mon Sep 17 00:00:00 2001
From: rai
Date: Mon, 6 May 2024 11:06:35 +0900
Subject: [PATCH 076/117] Fix incorrect TypeScript syntax
---
.../common/storybook-coverage-addon-config-options.ts.mdx | 1 -
1 file changed, 1 deletion(-)
diff --git a/docs/snippets/common/storybook-coverage-addon-config-options.ts.mdx b/docs/snippets/common/storybook-coverage-addon-config-options.ts.mdx
index 71f5946e1da0..cf933c89f06a 100644
--- a/docs/snippets/common/storybook-coverage-addon-config-options.ts.mdx
+++ b/docs/snippets/common/storybook-coverage-addon-config-options.ts.mdx
@@ -23,7 +23,6 @@ const config: StorybookConfig = {
{
name: '@storybook/addon-coverage',
options: coverageConfig,
- },
},
],
};
From 2f7978c87277b1a2e4326897c04efc436b380afb Mon Sep 17 00:00:00 2001
From: Kyle Gach
Date: Sun, 5 May 2024 22:22:24 -0600
Subject: [PATCH 077/117] Unpublish portable stories jest/vitest for now
---
docs/api/portable-stories-jest.md | 2 ++
docs/api/portable-stories-vitest.md | 2 ++
docs/toc.js | 22 ++++++++++++----------
3 files changed, 16 insertions(+), 10 deletions(-)
diff --git a/docs/api/portable-stories-jest.md b/docs/api/portable-stories-jest.md
index 3abe9798112c..cbe7fea550fc 100644
--- a/docs/api/portable-stories-jest.md
+++ b/docs/api/portable-stories-jest.md
@@ -2,6 +2,8 @@
title: 'Portable stories in Jest'
---
+
+
export const SUPPORTED_RENDERERS = ['react', 'vue'];
diff --git a/docs/api/portable-stories-vitest.md b/docs/api/portable-stories-vitest.md
index 2a934f68736e..75b15935fb68 100644
--- a/docs/api/portable-stories-vitest.md
+++ b/docs/api/portable-stories-vitest.md
@@ -2,6 +2,8 @@
title: 'Portable stories in Vitest'
---
+
+
export const SUPPORTED_RENDERERS = ['react', 'vue', 'svelte'];
diff --git a/docs/toc.js b/docs/toc.js
index a4d3e85cf6be..3a8c7f59c8ef 100644
--- a/docs/toc.js
+++ b/docs/toc.js
@@ -745,21 +745,23 @@ module.exports = {
pathSegment: '',
type: 'menu',
children: [
- {
- pathSegment: 'portable-stories-jest',
- title: 'Jest',
- type: 'link',
- },
+ // Unpublished for now
+ // {
+ // pathSegment: 'portable-stories-jest',
+ // title: 'Jest',
+ // type: 'link',
+ // },
{
pathSegment: 'portable-stories-playwright',
title: 'Playwright',
type: 'link',
},
- {
- pathSegment: 'portable-stories-vitest',
- title: 'Vitest',
- type: 'link',
- },
+ // Unpublished for now
+ // {
+ // pathSegment: 'portable-stories-vitest',
+ // title: 'Vitest',
+ // type: 'link',
+ // },
],
},
From 149dd068007da38224d69e6cce76fe9436c63612 Mon Sep 17 00:00:00 2001
From: Michael Shilman
Date: Mon, 6 May 2024 16:32:41 +0800
Subject: [PATCH 078/117] Replace `docs` tag by `autodocs`
---
code/addons/docs/src/preview.ts | 2 +-
.../core-server/src/utils/StoryIndexGenerator.ts | 2 +-
.../src/modules/store/csf/prepareStory.test.ts | 14 +++-----------
.../src/modules/store/csf/prepareStory.ts | 2 +-
4 files changed, 6 insertions(+), 14 deletions(-)
diff --git a/code/addons/docs/src/preview.ts b/code/addons/docs/src/preview.ts
index 0e6dcbfed2fd..7a352b15b0a6 100644
--- a/code/addons/docs/src/preview.ts
+++ b/code/addons/docs/src/preview.ts
@@ -22,7 +22,7 @@ export const parameters: any = {
filter: (story: PreparedStory) => {
const tags = story.tags || [];
return (
- tags.includes('docs') &&
+ tags.includes('autodocs') &&
tags.filter((tag) => excludeTags[tag]).length === 0 &&
!story.parameters.docs?.disable
);
diff --git a/code/lib/core-server/src/utils/StoryIndexGenerator.ts b/code/lib/core-server/src/utils/StoryIndexGenerator.ts
index 476e0cf4795a..11b063b6fb5e 100644
--- a/code/lib/core-server/src/utils/StoryIndexGenerator.ts
+++ b/code/lib/core-server/src/utils/StoryIndexGenerator.ts
@@ -666,7 +666,7 @@ export class StoryIndexGenerator {
getProjectTags(previewCode: string) {
const projectAnnotations = loadConfig(previewCode).parse();
- const defaultTags = ['dev', 'docs', 'test'];
+ const defaultTags = ['dev', 'test'];
const extraTags = this.options.docs.autodocs === true ? [AUTODOCS_TAG] : [];
const projectTags = projectAnnotations.getFieldValue(['tags']) ?? [];
return [...defaultTags, ...projectTags, ...extraTags];
diff --git a/code/lib/preview-api/src/modules/store/csf/prepareStory.test.ts b/code/lib/preview-api/src/modules/store/csf/prepareStory.test.ts
index 893fc2133eb3..b5ab8b9fb407 100644
--- a/code/lib/preview-api/src/modules/store/csf/prepareStory.test.ts
+++ b/code/lib/preview-api/src/modules/store/csf/prepareStory.test.ts
@@ -44,15 +44,7 @@ describe('prepareStory', () => {
{ render }
);
- expect(tags).toEqual([
- 'dev',
- 'docs',
- 'test',
- 'component-1',
- 'component-2',
- 'story-1',
- 'story-2',
- ]);
+ expect(tags).toEqual(['dev', 'test', 'component-1', 'component-2', 'story-1', 'story-2']);
});
it('component tags work if story are unset', () => {
@@ -66,13 +58,13 @@ describe('prepareStory', () => {
{ render }
);
- expect(tags).toEqual(['dev', 'docs', 'test', 'component-1', 'component-2']);
+ expect(tags).toEqual(['dev', 'test', 'component-1', 'component-2']);
});
it('sets a value even if annotations do not have tags', () => {
const { tags } = prepareStory({ id, name, moduleExport }, { id, title }, { render });
- expect(tags).toEqual(['dev', 'docs', 'test']);
+ expect(tags).toEqual(['dev', 'test']);
});
});
diff --git a/code/lib/preview-api/src/modules/store/csf/prepareStory.ts b/code/lib/preview-api/src/modules/store/csf/prepareStory.ts
index 78b6319c4abc..ae976d19ac5e 100644
--- a/code/lib/preview-api/src/modules/store/csf/prepareStory.ts
+++ b/code/lib/preview-api/src/modules/store/csf/prepareStory.ts
@@ -143,7 +143,7 @@ function preparePartialAnnotations(
// anything at render time. The assumption is that as we don't load all the stories at once, this
// will have a limited cost. If this proves misguided, we can refactor it.
- const defaultTags = ['dev', 'docs', 'test'];
+ const defaultTags = ['dev', 'test'];
if (typeof globalThis.DOCS_OPTIONS?.autodocs !== 'undefined') {
once.warn(dedent`
The \`docs.autodocs\` setting in '.storybook/main.js' is deprecated. Use \`tags: ['autodocs']\` in \`.storybook/preview.js\` instead.
From 5fc32eed2cf74064c15b454b68469b4cc805174d Mon Sep 17 00:00:00 2001
From: Michael Shilman
Date: Mon, 6 May 2024 16:54:28 +0800
Subject: [PATCH 079/117] Fix MDX detection now that MDX can inherit autodocs
tags
---
code/lib/core-server/src/utils/StoryIndexGenerator.ts | 4 +++-
.../src/modules/preview-web/PreviewWithSelection.tsx | 3 ++-
2 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/code/lib/core-server/src/utils/StoryIndexGenerator.ts b/code/lib/core-server/src/utils/StoryIndexGenerator.ts
index 11b063b6fb5e..4c1de76651fc 100644
--- a/code/lib/core-server/src/utils/StoryIndexGenerator.ts
+++ b/code/lib/core-server/src/utils/StoryIndexGenerator.ts
@@ -53,11 +53,13 @@ export type StoryIndexGeneratorOptions = {
export const AUTODOCS_TAG = 'autodocs';
export const STORIES_MDX_TAG = 'stories-mdx';
+export const UNATTACHED_MDX_TAG = 'unattached-mdx';
+export const ATTACHED_MDX_TAG = 'attached-mdx';
export const PLAY_FN_TAG = 'play-fn';
/** Was this docs entry generated by a .mdx file? (see discussion below) */
export function isMdxEntry({ tags }: DocsIndexEntry) {
- return !tags?.includes(AUTODOCS_TAG) && !tags?.includes(STORIES_MDX_TAG);
+ return tags?.includes(UNATTACHED_MDX_TAG) || tags?.includes(ATTACHED_MDX_TAG);
}
const makeAbsolute = (otherImport: Path, normalizedPath: Path, workingDir: Path) =>
diff --git a/code/lib/preview-api/src/modules/preview-web/PreviewWithSelection.tsx b/code/lib/preview-api/src/modules/preview-web/PreviewWithSelection.tsx
index b183a7488ba9..6480d4d4b9f8 100644
--- a/code/lib/preview-api/src/modules/preview-web/PreviewWithSelection.tsx
+++ b/code/lib/preview-api/src/modules/preview-web/PreviewWithSelection.tsx
@@ -55,10 +55,11 @@ function focusInInput(event: Event) {
export const AUTODOCS_TAG = 'autodocs';
export const STORIES_MDX_TAG = 'stories-mdx';
export const ATTACHED_MDX_TAG = 'attached-mdx';
+export const UNATTACHED_MDX_TAG = 'unattached-mdx';
/** Was this docs entry generated by a .mdx file? (see discussion below) */
export function isMdxEntry({ tags }: DocsIndexEntry) {
- return !tags?.includes(AUTODOCS_TAG) && !tags?.includes(STORIES_MDX_TAG);
+ return tags?.includes(UNATTACHED_MDX_TAG) || tags?.includes(ATTACHED_MDX_TAG);
}
type PossibleRender =
From 8f7d5695b7b3892dcfe77af227e23831f44d8fe1 Mon Sep 17 00:00:00 2001
From: Michael Shilman
Date: Mon, 6 May 2024 17:31:32 +0800
Subject: [PATCH 080/117] Update snapshots
---
MIGRATION.md | 4 ++
.../src/utils/StoryIndexGenerator.test.ts | 56 +------------------
.../src/utils/stories-json.test.ts | 12 ----
.../src/utils/summarizeIndex.test.ts | 4 +-
.../modules/preview-web/PreviewWeb.test.ts | 8 ---
.../src/modules/store/StoryStore.test.ts | 20 -------
6 files changed, 8 insertions(+), 96 deletions(-)
diff --git a/MIGRATION.md b/MIGRATION.md
index d8b631e557b2..bd3e3869cfee 100644
--- a/MIGRATION.md
+++ b/MIGRATION.md
@@ -2,6 +2,7 @@
- [From version 8.0 to 8.1.0](#from-version-80-to-810)
- [main.js `docs.autodocs` is deprecated](#mainjs-docsautodocs-is-deprecated)
+ - [`docs` and `story` system tags removed](#docs-and-story-system-tags-removed)
- [Subtitle block and `parameters.componentSubtitle`](#subtitle-block-and-parameterscomponentsubtitle)
- [Title block `of` prop](#title-block-of-prop)
- [From version 7.x to 8.0.0](#from-version-7x-to-800)
@@ -443,6 +444,9 @@ If you had set `docs.autodocs = 'tag'`, the default setting, you can remove the
If you had set `docs.autodocs = false`, this still works in 8.x, but will go away in 9.0 as a breaking change. If you don't want autodocs at all, simply remove the `'autodocs'` tag throughout your Storybook and autodocs will not be created.
+### `docs` and `story` system tags removed
+
+Storybook automatically added the tag `'docs'` to any docs entry in the index and `'story'` to any story entry in the index. This behavior was undocumented, and in an effort to reduce the number of tags we've removed them in 8.1. If you depended on these tags, please file an issue on the [Storybook monorepo](https://github.com/storybookjs/storybook) and let us know!
### Subtitle block and `parameters.componentSubtitle`
diff --git a/code/lib/core-server/src/utils/StoryIndexGenerator.test.ts b/code/lib/core-server/src/utils/StoryIndexGenerator.test.ts
index ed9ed2a20b2c..0e51a725470a 100644
--- a/code/lib/core-server/src/utils/StoryIndexGenerator.test.ts
+++ b/code/lib/core-server/src/utils/StoryIndexGenerator.test.ts
@@ -80,7 +80,6 @@ describe('StoryIndexGenerator', () => {
"name": "Story One",
"tags": [
"dev",
- "docs",
"test",
"component-tag",
"story-tag",
@@ -113,7 +112,6 @@ describe('StoryIndexGenerator', () => {
"name": "Story One",
"tags": [
"dev",
- "docs",
"test",
"autodocs",
],
@@ -145,7 +143,6 @@ describe('StoryIndexGenerator', () => {
"name": "Story One",
"tags": [
"dev",
- "docs",
"test",
"autodocs",
],
@@ -177,7 +174,6 @@ describe('StoryIndexGenerator', () => {
"name": "Story One",
"tags": [
"dev",
- "docs",
"test",
"component-tag",
],
@@ -190,7 +186,6 @@ describe('StoryIndexGenerator', () => {
"name": "Story One",
"tags": [
"dev",
- "docs",
"test",
],
"title": "second-nested/G",
@@ -221,7 +216,6 @@ describe('StoryIndexGenerator', () => {
"name": "Story One",
"tags": [
"dev",
- "docs",
"test",
"component-tag",
"story-tag",
@@ -235,7 +229,6 @@ describe('StoryIndexGenerator', () => {
"name": "Story One",
"tags": [
"dev",
- "docs",
"test",
"autodocs",
],
@@ -248,7 +241,6 @@ describe('StoryIndexGenerator', () => {
"name": "Story One",
"tags": [
"dev",
- "docs",
"test",
"autodocs",
],
@@ -261,7 +253,6 @@ describe('StoryIndexGenerator', () => {
"name": "Story One",
"tags": [
"dev",
- "docs",
"test",
],
"title": "first-nested/deeply/F",
@@ -273,7 +264,6 @@ describe('StoryIndexGenerator', () => {
"name": "Story One",
"tags": [
"dev",
- "docs",
"test",
"autodocs",
],
@@ -286,7 +276,6 @@ describe('StoryIndexGenerator', () => {
"name": "Story One",
"tags": [
"dev",
- "docs",
"test",
"component-tag",
],
@@ -299,7 +288,6 @@ describe('StoryIndexGenerator', () => {
"name": "Story One",
"tags": [
"dev",
- "docs",
"test",
],
"title": "second-nested/G",
@@ -335,7 +323,6 @@ describe('StoryIndexGenerator', () => {
"name": "Story One",
"tags": [
"dev",
- "docs",
"test",
"component-tag",
"story-tag",
@@ -350,7 +337,6 @@ describe('StoryIndexGenerator', () => {
"storiesImports": [],
"tags": [
"dev",
- "docs",
"test",
"autodocs",
],
@@ -363,7 +349,6 @@ describe('StoryIndexGenerator', () => {
"name": "Story One",
"tags": [
"dev",
- "docs",
"test",
"autodocs",
],
@@ -377,7 +362,6 @@ describe('StoryIndexGenerator', () => {
"storiesImports": [],
"tags": [
"dev",
- "docs",
"test",
"autodocs",
],
@@ -390,7 +374,6 @@ describe('StoryIndexGenerator', () => {
"name": "Story One",
"tags": [
"dev",
- "docs",
"test",
"autodocs",
],
@@ -403,7 +386,6 @@ describe('StoryIndexGenerator', () => {
"name": "Story One",
"tags": [
"dev",
- "docs",
"test",
],
"title": "first-nested/deeply/F",
@@ -416,7 +398,6 @@ describe('StoryIndexGenerator', () => {
"storiesImports": [],
"tags": [
"dev",
- "docs",
"test",
"autodocs",
],
@@ -429,7 +410,6 @@ describe('StoryIndexGenerator', () => {
"name": "Story One",
"tags": [
"dev",
- "docs",
"test",
"autodocs",
],
@@ -442,7 +422,6 @@ describe('StoryIndexGenerator', () => {
"name": "Story One",
"tags": [
"dev",
- "docs",
"test",
"component-tag",
],
@@ -455,7 +434,6 @@ describe('StoryIndexGenerator', () => {
"name": "Story One",
"tags": [
"dev",
- "docs",
"test",
],
"title": "second-nested/G",
@@ -580,7 +558,6 @@ describe('StoryIndexGenerator', () => {
"storiesImports": [],
"tags": [
"dev",
- "docs",
"test",
"autodocs",
],
@@ -596,7 +573,6 @@ describe('StoryIndexGenerator', () => {
],
"tags": [
"dev",
- "docs",
"test",
"autodocs",
"attached-mdx",
@@ -610,7 +586,6 @@ describe('StoryIndexGenerator', () => {
"name": "Story One",
"tags": [
"dev",
- "docs",
"test",
"autodocs",
],
@@ -646,7 +621,6 @@ describe('StoryIndexGenerator', () => {
"storiesImports": [],
"tags": [
"dev",
- "docs",
"test",
"autodocs",
],
@@ -662,7 +636,6 @@ describe('StoryIndexGenerator', () => {
],
"tags": [
"dev",
- "docs",
"test",
"autodocs",
"attached-mdx",
@@ -676,7 +649,6 @@ describe('StoryIndexGenerator', () => {
"name": "Story One",
"tags": [
"dev",
- "docs",
"test",
"autodocs",
],
@@ -711,19 +683,18 @@ describe('StoryIndexGenerator', () => {
"entries": {
"a--docs": {
"id": "a--docs",
- "importPath": "./src/A.stories.js",
+ "importPath": "./errors/A.mdx",
"name": "docs",
"storiesImports": [
- "./errors/A.mdx",
"./src/A.stories.js",
],
"tags": [
"dev",
- "docs",
"test",
"autodocs",
"component-tag",
"story-tag",
+ "attached-mdx",
],
"title": "A",
"type": "docs",
@@ -734,7 +705,6 @@ describe('StoryIndexGenerator', () => {
"name": "Story One",
"tags": [
"dev",
- "docs",
"test",
"autodocs",
"component-tag",
@@ -770,7 +740,6 @@ describe('StoryIndexGenerator', () => {
],
"tags": [
"dev",
- "docs",
"test",
"autodocs",
],
@@ -783,7 +752,6 @@ describe('StoryIndexGenerator', () => {
"name": "Story One",
"tags": [
"dev",
- "docs",
"test",
"autodocs",
],
@@ -796,7 +764,6 @@ describe('StoryIndexGenerator', () => {
"name": "Story Two",
"tags": [
"dev",
- "docs",
"test",
"autodocs",
],
@@ -846,7 +813,6 @@ describe('StoryIndexGenerator', () => {
"storiesImports": [],
"tags": [
"dev",
- "docs",
"test",
"autodocs",
],
@@ -859,7 +825,6 @@ describe('StoryIndexGenerator', () => {
"name": "Story One",
"tags": [
"dev",
- "docs",
"test",
"autodocs",
],
@@ -890,7 +855,6 @@ describe('StoryIndexGenerator', () => {
],
"tags": [
"dev",
- "docs",
"test",
"component-tag",
"story-tag",
@@ -908,7 +872,6 @@ describe('StoryIndexGenerator', () => {
],
"tags": [
"dev",
- "docs",
"test",
"component-tag",
"story-tag",
@@ -923,7 +886,6 @@ describe('StoryIndexGenerator', () => {
"name": "Story One",
"tags": [
"dev",
- "docs",
"test",
"component-tag",
"story-tag",
@@ -938,7 +900,6 @@ describe('StoryIndexGenerator', () => {
"storiesImports": [],
"tags": [
"dev",
- "docs",
"test",
"unattached-mdx",
],
@@ -952,7 +913,6 @@ describe('StoryIndexGenerator', () => {
"storiesImports": [],
"tags": [
"dev",
- "docs",
"test",
"unattached-mdx",
],
@@ -966,7 +926,6 @@ describe('StoryIndexGenerator', () => {
"storiesImports": [],
"tags": [
"dev",
- "docs",
"test",
"unattached-mdx",
],
@@ -1029,7 +988,6 @@ describe('StoryIndexGenerator', () => {
],
"tags": [
"dev",
- "docs",
"test",
"component-tag",
"story-tag",
@@ -1047,7 +1005,6 @@ describe('StoryIndexGenerator', () => {
],
"tags": [
"dev",
- "docs",
"test",
"component-tag",
"story-tag",
@@ -1062,7 +1019,6 @@ describe('StoryIndexGenerator', () => {
"name": "Story One",
"tags": [
"dev",
- "docs",
"test",
"component-tag",
"story-tag",
@@ -1077,7 +1033,6 @@ describe('StoryIndexGenerator', () => {
"storiesImports": [],
"tags": [
"dev",
- "docs",
"test",
"unattached-mdx",
],
@@ -1091,7 +1046,6 @@ describe('StoryIndexGenerator', () => {
"storiesImports": [],
"tags": [
"dev",
- "docs",
"test",
"unattached-mdx",
],
@@ -1105,7 +1059,6 @@ describe('StoryIndexGenerator', () => {
"storiesImports": [],
"tags": [
"dev",
- "docs",
"test",
"unattached-mdx",
],
@@ -1137,7 +1090,6 @@ describe('StoryIndexGenerator', () => {
"name": "Story One",
"tags": [
"dev",
- "docs",
"test",
"component-tag",
"story-tag",
@@ -1151,7 +1103,6 @@ describe('StoryIndexGenerator', () => {
"name": "Story One",
"tags": [
"dev",
- "docs",
"test",
"autodocs",
],
@@ -1168,7 +1119,6 @@ describe('StoryIndexGenerator', () => {
],
"tags": [
"dev",
- "docs",
"test",
"autodocs",
"attached-mdx",
@@ -1208,7 +1158,6 @@ describe('StoryIndexGenerator', () => {
],
"tags": [
"dev",
- "docs",
"test",
"attached-mdx",
],
@@ -1221,7 +1170,6 @@ describe('StoryIndexGenerator', () => {
"name": "Story One",
"tags": [
"dev",
- "docs",
"test",
],
"title": "B",
diff --git a/code/lib/core-server/src/utils/stories-json.test.ts b/code/lib/core-server/src/utils/stories-json.test.ts
index 81fd7b8bfa3a..cece84537367 100644
--- a/code/lib/core-server/src/utils/stories-json.test.ts
+++ b/code/lib/core-server/src/utils/stories-json.test.ts
@@ -114,7 +114,6 @@ describe('useStoriesJson', () => {
],
"tags": [
"dev",
- "docs",
"test",
"component-tag",
"story-tag",
@@ -132,7 +131,6 @@ describe('useStoriesJson', () => {
],
"tags": [
"dev",
- "docs",
"test",
"component-tag",
"story-tag",
@@ -147,7 +145,6 @@ describe('useStoriesJson', () => {
"name": "Story One",
"tags": [
"dev",
- "docs",
"test",
"component-tag",
"story-tag",
@@ -161,7 +158,6 @@ describe('useStoriesJson', () => {
"name": "Story One",
"tags": [
"dev",
- "docs",
"test",
"autodocs",
],
@@ -174,7 +170,6 @@ describe('useStoriesJson', () => {
"name": "Story One",
"tags": [
"dev",
- "docs",
"test",
"autodocs",
],
@@ -188,7 +183,6 @@ describe('useStoriesJson', () => {
"storiesImports": [],
"tags": [
"dev",
- "docs",
"test",
"unattached-mdx",
],
@@ -202,7 +196,6 @@ describe('useStoriesJson', () => {
"storiesImports": [],
"tags": [
"dev",
- "docs",
"test",
"unattached-mdx",
],
@@ -216,7 +209,6 @@ describe('useStoriesJson', () => {
"storiesImports": [],
"tags": [
"dev",
- "docs",
"test",
"unattached-mdx",
],
@@ -229,7 +221,6 @@ describe('useStoriesJson', () => {
"name": "Story One",
"tags": [
"dev",
- "docs",
"test",
],
"title": "first-nested/deeply/F",
@@ -241,7 +232,6 @@ describe('useStoriesJson', () => {
"name": "Story One",
"tags": [
"dev",
- "docs",
"test",
"autodocs",
],
@@ -254,7 +244,6 @@ describe('useStoriesJson', () => {
"name": "Story One",
"tags": [
"dev",
- "docs",
"test",
"component-tag",
],
@@ -267,7 +256,6 @@ describe('useStoriesJson', () => {
"name": "Story One",
"tags": [
"dev",
- "docs",
"test",
],
"title": "second-nested/G",
diff --git a/code/lib/core-server/src/utils/summarizeIndex.test.ts b/code/lib/core-server/src/utils/summarizeIndex.test.ts
index 8647c007463b..a2702a38a704 100644
--- a/code/lib/core-server/src/utils/summarizeIndex.test.ts
+++ b/code/lib/core-server/src/utils/summarizeIndex.test.ts
@@ -457,7 +457,7 @@ describe('summarizeIndex', () => {
importPath: './template-stories/addons/docs/docs2/NoTitle.mdx',
storiesImports: [],
type: 'docs',
- tags: ['docs'],
+ tags: ['docs', 'attached-mdx'],
},
'addons-docs-yabbadabbadooo--docs': {
id: 'addons-docs-yabbadabbadooo--docs',
@@ -466,7 +466,7 @@ describe('summarizeIndex', () => {
importPath: './template-stories/addons/docs/docs2/Title.mdx',
storiesImports: [],
type: 'docs',
- tags: ['docs'],
+ tags: ['docs', 'attached-mdx'],
},
},
})
diff --git a/code/lib/preview-api/src/modules/preview-web/PreviewWeb.test.ts b/code/lib/preview-api/src/modules/preview-web/PreviewWeb.test.ts
index 276f6da66bc4..1654772bbe3a 100644
--- a/code/lib/preview-api/src/modules/preview-web/PreviewWeb.test.ts
+++ b/code/lib/preview-api/src/modules/preview-web/PreviewWeb.test.ts
@@ -3619,8 +3619,6 @@ describe('PreviewWeb', () => {
"subcomponents": undefined,
"tags": [
"dev",
- "docs",
- "story",
"test",
],
"title": "Component One",
@@ -3669,8 +3667,6 @@ describe('PreviewWeb', () => {
"subcomponents": undefined,
"tags": [
"dev",
- "docs",
- "story",
"test",
],
"title": "Component One",
@@ -3697,8 +3693,6 @@ describe('PreviewWeb', () => {
"subcomponents": undefined,
"tags": [
"dev",
- "docs",
- "story",
"test",
],
"title": "Component One",
@@ -3735,8 +3729,6 @@ describe('PreviewWeb', () => {
"subcomponents": undefined,
"tags": [
"dev",
- "docs",
- "story",
"test",
],
"title": "Component Two",
diff --git a/code/lib/preview-api/src/modules/store/StoryStore.test.ts b/code/lib/preview-api/src/modules/store/StoryStore.test.ts
index 5bb56dc106d9..7dee9f321cd1 100644
--- a/code/lib/preview-api/src/modules/store/StoryStore.test.ts
+++ b/code/lib/preview-api/src/modules/store/StoryStore.test.ts
@@ -312,8 +312,6 @@ describe('StoryStore', () => {
"subcomponents": undefined,
"tags": [
"dev",
- "docs",
- "story",
"test",
],
"title": "Component One",
@@ -480,8 +478,6 @@ describe('StoryStore', () => {
"subcomponents": undefined,
"tags": [
"dev",
- "docs",
- "story",
"test",
],
"title": "Component One",
@@ -521,8 +517,6 @@ describe('StoryStore', () => {
"subcomponents": undefined,
"tags": [
"dev",
- "docs",
- "story",
"test",
],
"title": "Component One",
@@ -562,8 +556,6 @@ describe('StoryStore', () => {
"subcomponents": undefined,
"tags": [
"dev",
- "docs",
- "story",
"test",
],
"title": "Component Two",
@@ -672,9 +664,7 @@ describe('StoryStore', () => {
"subcomponents": undefined,
"tags": [
"dev",
- "docs",
"test",
- "story",
],
"title": "Component One",
"unboundStoryFn": [Function],
@@ -720,9 +710,7 @@ describe('StoryStore', () => {
"subcomponents": undefined,
"tags": [
"dev",
- "docs",
"test",
- "story",
],
"title": "Component One",
"unboundStoryFn": [Function],
@@ -768,9 +756,7 @@ describe('StoryStore', () => {
"subcomponents": undefined,
"tags": [
"dev",
- "docs",
"test",
- "story",
],
"title": "Component Two",
"unboundStoryFn": [Function],
@@ -832,8 +818,6 @@ describe('StoryStore', () => {
"subcomponents": undefined,
"tags": [
"dev",
- "docs",
- "story",
"test",
],
"title": "Component One",
@@ -873,8 +857,6 @@ describe('StoryStore', () => {
"subcomponents": undefined,
"tags": [
"dev",
- "docs",
- "story",
"test",
],
"title": "Component One",
@@ -914,8 +896,6 @@ describe('StoryStore', () => {
"subcomponents": undefined,
"tags": [
"dev",
- "docs",
- "story",
"test",
],
"title": "Component Two",
From 5b467170f49077f0ffaf0e88737c41ae054e74fb Mon Sep 17 00:00:00 2001
From: Michael Shilman
Date: Mon, 6 May 2024 18:12:09 +0800
Subject: [PATCH 081/117] Fix portable stories tests
---
.../src/modules/store/csf/composeConfigs.test.ts | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/code/lib/preview-api/src/modules/store/csf/composeConfigs.test.ts b/code/lib/preview-api/src/modules/store/csf/composeConfigs.test.ts
index bfad2ebe5f21..ba9ead34d119 100644
--- a/code/lib/preview-api/src/modules/store/csf/composeConfigs.test.ts
+++ b/code/lib/preview-api/src/modules/store/csf/composeConfigs.test.ts
@@ -23,6 +23,7 @@ describe('composeConfigs', () => {
loaders: [],
beforeEach: [],
runStep: expect.any(Function),
+ tags: [],
});
});
@@ -48,6 +49,7 @@ describe('composeConfigs', () => {
loaders: [],
beforeEach: [],
runStep: expect.any(Function),
+ tags: [],
});
});
@@ -77,6 +79,7 @@ describe('composeConfigs', () => {
loaders: [],
beforeEach: [],
runStep: expect.any(Function),
+ tags: [],
});
});
@@ -112,6 +115,7 @@ describe('composeConfigs', () => {
loaders: [],
beforeEach: [],
runStep: expect.any(Function),
+ tags: [],
});
});
@@ -150,6 +154,7 @@ describe('composeConfigs', () => {
loaders: [],
beforeEach: [],
runStep: expect.any(Function),
+ tags: [],
});
});
@@ -179,6 +184,7 @@ describe('composeConfigs', () => {
loaders: ['1', '2', '3', '4'],
beforeEach: [],
runStep: expect.any(Function),
+ tags: [],
});
});
@@ -208,6 +214,7 @@ describe('composeConfigs', () => {
loaders: ['1', '2', '3'],
beforeEach: [],
runStep: expect.any(Function),
+ tags: [],
});
});
@@ -233,6 +240,7 @@ describe('composeConfigs', () => {
loaders: [],
beforeEach: [],
runStep: expect.any(Function),
+ tags: [],
});
});
@@ -259,6 +267,7 @@ describe('composeConfigs', () => {
loaders: [],
beforeEach: [],
runStep: expect.any(Function),
+ tags: [],
});
});
@@ -291,6 +300,7 @@ describe('composeConfigs', () => {
renderToCanvas: 'renderToCanvas-2',
applyDecorators: 'applyDecorators-2',
runStep: expect.any(Function),
+ tags: [],
});
});
From f5447a33dc4a57b563ffa7685e46e4665c09f25f Mon Sep 17 00:00:00 2001
From: Michael Shilman
Date: Mon, 6 May 2024 18:43:43 +0800
Subject: [PATCH 082/117] Fix tags inheritance story
---
code/lib/preview-api/template/stories/tags.stories.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/code/lib/preview-api/template/stories/tags.stories.ts b/code/lib/preview-api/template/stories/tags.stories.ts
index f86b36f08491..64e715b1e0f7 100644
--- a/code/lib/preview-api/template/stories/tags.stories.ts
+++ b/code/lib/preview-api/template/stories/tags.stories.ts
@@ -20,7 +20,7 @@ export const Inheritance = {
play: async ({ canvasElement }: PlayFunctionContext) => {
const canvas = within(canvasElement);
await expect(JSON.parse(canvas.getByTestId('pre').innerText)).toEqual({
- tags: ['story-one', 'story-two', 'story'],
+ tags: ['dev', 'test', 'component-one', 'component-two', 'story-one', 'story-two'],
});
},
parameters: { chromatic: { disable: false } },
From 74afbb131ea1988c21c14ae270509a4b92b997f9 Mon Sep 17 00:00:00 2001
From: Jeppe Reinhold
Date: Mon, 6 May 2024 13:37:19 +0200
Subject: [PATCH 083/117] add react types resolutions to
react-webpack/prerelease template.
---
code/lib/cli/src/sandbox-templates.ts | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)
diff --git a/code/lib/cli/src/sandbox-templates.ts b/code/lib/cli/src/sandbox-templates.ts
index 5f2ff4634c02..14d5fe69d675 100644
--- a/code/lib/cli/src/sandbox-templates.ts
+++ b/code/lib/cli/src/sandbox-templates.ts
@@ -246,8 +246,17 @@ const baseTemplates = {
},
'react-webpack/prerelease-ts': {
name: 'React Prerelease (Webpack | TypeScript)',
- script:
- 'yarn create webpack5-react {{beforeDir}} --version-react="beta" --version-react-dom="beta"',
+ /**
+ * 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',
From 638b96a33e68d6339433cafcc48b1edb8bdb5276 Mon Sep 17 00:00:00 2001
From: Jeppe Reinhold
Date: Mon, 6 May 2024 15:41:47 +0200
Subject: [PATCH 084/117] only allow peer dependency errors in react
prereleases
---
scripts/utils/yarn.ts | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/scripts/utils/yarn.ts b/scripts/utils/yarn.ts
index 03b05ff3bf40..dd261eb851d2 100644
--- a/scripts/utils/yarn.ts
+++ b/scripts/utils/yarn.ts
@@ -98,7 +98,12 @@ export const configureYarn2ForVerdaccio = async ({
`yarn config set enableImmutableInstalls false`,
];
- if (key.includes('svelte-kit') || key.includes('prerelease')) {
+ if (
+ key.includes('svelte-kit') ||
+ // React prereleases will have INCOMPATIBLE_PEER_DEPENDENCY errors because of transitive dependencies not allowing v19 betas
+ key.includes('react-vite/prerelease') ||
+ key.includes('react-webpack/prerelease')
+ ) {
// Don't error with INCOMPATIBLE_PEER_DEPENDENCY for SvelteKit sandboxes, it is expected to happen with @sveltejs/vite-plugin-svelte
command.push(
`yarn config set logFilters --json '[ { "code": "YN0013", "level": "discard" } ]'`
From 01923cdc081a2af54a46d5704ad40062c09ec180 Mon Sep 17 00:00:00 2001
From: Michael Shilman
Date: Mon, 6 May 2024 21:54:21 +0800
Subject: [PATCH 085/117] Remove bogus console.log
---
code/lib/core-server/src/utils/StoryIndexGenerator.ts | 1 -
1 file changed, 1 deletion(-)
diff --git a/code/lib/core-server/src/utils/StoryIndexGenerator.ts b/code/lib/core-server/src/utils/StoryIndexGenerator.ts
index 4c1de76651fc..7e65fc75dfca 100644
--- a/code/lib/core-server/src/utils/StoryIndexGenerator.ts
+++ b/code/lib/core-server/src/utils/StoryIndexGenerator.ts
@@ -440,7 +440,6 @@ export class StoryIndexGenerator {
...(result.metaTags ?? []),
csfEntry ? 'attached-mdx' : 'unattached-mdx'
);
- console.log({ projectTags, tags });
const docsEntry: DocsCacheEntry = {
id,
From dc28521b68c7d4824a51b2387783ffb952821aab Mon Sep 17 00:00:00 2001
From: Michael Shilman
Date: Mon, 6 May 2024 21:55:06 +0800
Subject: [PATCH 086/117] Try clearing the webpack mock
---
code/lib/core-server/src/utils/stories-json.test.ts | 1 +
1 file changed, 1 insertion(+)
diff --git a/code/lib/core-server/src/utils/stories-json.test.ts b/code/lib/core-server/src/utils/stories-json.test.ts
index cece84537367..21a901ada040 100644
--- a/code/lib/core-server/src/utils/stories-json.test.ts
+++ b/code/lib/core-server/src/utils/stories-json.test.ts
@@ -75,6 +75,7 @@ describe('useStoriesJson', () => {
send.mockClear();
write.mockClear();
vi.mocked(debounce).mockImplementation((cb) => cb as any);
+ Watchpack.mockClear();
});
const request: Request = {
From d810f8e209fead6aaee0494371a8a249b455c455 Mon Sep 17 00:00:00 2001
From: Michael Shilman
Date: Mon, 6 May 2024 23:06:03 +0800
Subject: [PATCH 087/117] Try to fix CI
---
code/lib/core-server/src/utils/watchConfig.ts | 1 -
code/renderers/react/src/__test__/portable-stories.test.tsx | 1 +
2 files changed, 1 insertion(+), 1 deletion(-)
diff --git a/code/lib/core-server/src/utils/watchConfig.ts b/code/lib/core-server/src/utils/watchConfig.ts
index 14b925f1c1d8..99b85fe62e2a 100644
--- a/code/lib/core-server/src/utils/watchConfig.ts
+++ b/code/lib/core-server/src/utils/watchConfig.ts
@@ -12,7 +12,6 @@ export function watchConfig(
ignored: ['**/.git', '**/node_modules'],
});
- console.log('watching previewjs', configDir);
wp.watch({
directories: [configDir],
});
diff --git a/code/renderers/react/src/__test__/portable-stories.test.tsx b/code/renderers/react/src/__test__/portable-stories.test.tsx
index 15e953c3fecb..aab2585743d5 100644
--- a/code/renderers/react/src/__test__/portable-stories.test.tsx
+++ b/code/renderers/react/src/__test__/portable-stories.test.tsx
@@ -1,3 +1,4 @@
+/* eslint-disable import/namespace */
import React from 'react';
import { vi, it, expect, afterEach, describe } from 'vitest';
import { render, screen, cleanup } from '@testing-library/react';
From dfa82ab4f5adae12f467760b99ab663e4e9a5146 Mon Sep 17 00:00:00 2001
From: Kyle Gach
Date: Mon, 6 May 2024 09:08:36 -0600
Subject: [PATCH 088/117] Address feedback
---
docs/snippets/common/before-each-in-meta-mock-date.js.mdx | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/docs/snippets/common/before-each-in-meta-mock-date.js.mdx b/docs/snippets/common/before-each-in-meta-mock-date.js.mdx
index 61151b7a4850..b39e8d8120b0 100644
--- a/docs/snippets/common/before-each-in-meta-mock-date.js.mdx
+++ b/docs/snippets/common/before-each-in-meta-mock-date.js.mdx
@@ -1,5 +1,5 @@
-```ts
-// Page.stories.ts
+```js
+// Page.stories.js
import MockDate from 'mockdate';
import { getUserFromSession } from '#api/session.mock';
@@ -18,7 +18,7 @@ export default {
},
};
-export const Default: Story = {
+export const Default = {
async play({ canvasElement }) {
// ... This will run with the mocked Date
},
From bdbd3322f794ce61fa38c22147273719c1f7df48 Mon Sep 17 00:00:00 2001
From: Michael Shilman
Date: Mon, 6 May 2024 23:13:14 +0800
Subject: [PATCH 089/117] Fix bad merge
---
code/lib/codemod/package.json | 6 +++---
code/yarn.lock | 6 +++---
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/code/lib/codemod/package.json b/code/lib/codemod/package.json
index 3d30ffe9ab47..651182b854fc 100644
--- a/code/lib/codemod/package.json
+++ b/code/lib/codemod/package.json
@@ -54,9 +54,9 @@
"prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts"
},
"dependencies": {
- "@babel/core": "^7.23.2",
- "@babel/preset-env": "^7.23.2",
- "@babel/types": "^7.23.0",
+ "@babel/core": "^7.24.4",
+ "@babel/preset-env": "^7.24.4",
+ "@babel/types": "^7.24.0",
"@storybook/csf": "^0.1.7",
"@storybook/csf-tools": "workspace:*",
"@storybook/node-logger": "workspace:*",
diff --git a/code/yarn.lock b/code/yarn.lock
index d37cc3db6007..d559b86e7189 100644
--- a/code/yarn.lock
+++ b/code/yarn.lock
@@ -5994,9 +5994,9 @@ __metadata:
version: 0.0.0-use.local
resolution: "@storybook/codemod@workspace:lib/codemod"
dependencies:
- "@babel/core": "npm:^7.23.2"
- "@babel/preset-env": "npm:^7.23.2"
- "@babel/types": "npm:^7.23.0"
+ "@babel/core": "npm:^7.24.4"
+ "@babel/preset-env": "npm:^7.24.4"
+ "@babel/types": "npm:^7.24.0"
"@storybook/csf": "npm:^0.1.7"
"@storybook/csf-tools": "workspace:*"
"@storybook/node-logger": "workspace:*"
From 64c67b4dbb85e2ad63d76f1599a905d2b39e4a47 Mon Sep 17 00:00:00 2001
From: Kyle Gach
Date: Mon, 6 May 2024 16:32:58 -0600
Subject: [PATCH 090/117] Revise tags docs
- Document new API
- Add examples
- Remove `main-config-tags`
- Remove `--tags` and `--exclude-tags` CLI flags
- Remove `autodocs: true | false | 'tag'`
- Remove old snippets
---
docs/api/cli-options.md | 4 -
docs/api/main-config-tags.md | 40 -----
docs/api/main-config.md | 1 -
.../angular/button-story-auto-docs.ts.mdx | 33 ----
.../storybook-auto-docs-full-config.js.mdx | 1 -
.../storybook-auto-docs-full-config.ts.mdx | 1 -
.../react/button-story-auto-docs.js.mdx | 28 ----
.../react/button-story-auto-docs.ts-4-9.mdx | 33 ----
.../react/button-story-auto-docs.ts.mdx | 33 ----
.../solid/button-story-auto-docs.js.mdx | 28 ----
.../solid/button-story-auto-docs.ts-4-9.mdx | 33 ----
.../solid/button-story-auto-docs.ts.mdx | 33 ----
.../svelte/button-story-auto-docs.js.mdx | 28 ----
.../svelte/button-story-auto-docs.ts-4-9.mdx | 33 ----
.../svelte/button-story-auto-docs.ts.mdx | 33 ----
.../vue/button-story-auto-docs.js.mdx | 28 ----
.../vue/button-story-auto-docs.ts-4-9.mdx | 33 ----
.../vue/button-story-auto-docs.ts.mdx | 33 ----
.../button-story-auto-docs.js.mdx | 26 ---
.../button-story-auto-docs.ts.mdx | 31 ----
docs/toc.js | 5 -
docs/writing-docs/autodocs.md | 100 +++++++++---
docs/writing-stories/tags.md | 152 +++++++++++++-----
23 files changed, 185 insertions(+), 585 deletions(-)
delete mode 100644 docs/api/main-config-tags.md
delete mode 100644 docs/snippets/angular/button-story-auto-docs.ts.mdx
delete mode 100644 docs/snippets/react/button-story-auto-docs.js.mdx
delete mode 100644 docs/snippets/react/button-story-auto-docs.ts-4-9.mdx
delete mode 100644 docs/snippets/react/button-story-auto-docs.ts.mdx
delete mode 100644 docs/snippets/solid/button-story-auto-docs.js.mdx
delete mode 100644 docs/snippets/solid/button-story-auto-docs.ts-4-9.mdx
delete mode 100644 docs/snippets/solid/button-story-auto-docs.ts.mdx
delete mode 100644 docs/snippets/svelte/button-story-auto-docs.js.mdx
delete mode 100644 docs/snippets/svelte/button-story-auto-docs.ts-4-9.mdx
delete mode 100644 docs/snippets/svelte/button-story-auto-docs.ts.mdx
delete mode 100644 docs/snippets/vue/button-story-auto-docs.js.mdx
delete mode 100644 docs/snippets/vue/button-story-auto-docs.ts-4-9.mdx
delete mode 100644 docs/snippets/vue/button-story-auto-docs.ts.mdx
delete mode 100644 docs/snippets/web-components/button-story-auto-docs.js.mdx
delete mode 100644 docs/snippets/web-components/button-story-auto-docs.ts.mdx
diff --git a/docs/api/cli-options.md b/docs/api/cli-options.md
index 39b366429daf..58aef51a3787 100644
--- a/docs/api/cli-options.md
+++ b/docs/api/cli-options.md
@@ -50,8 +50,6 @@ Options include:
| `--stats-json` | Write stats JSON to disk `storybook dev ---stats-json /tmp/stats` NOTE: only works for webpack. |
| `--docs` | Starts Storybook in documentation mode. Learn more about it in [here](../writing-docs/build-documentation.md#preview-storybooks-documentation) `storybook dev --docs` |
| `--disable-telemetry` | Disables Storybook's telemetry. Learn more about it [here](../configure/telemetry.md) `storybook dev --disable-telemetry` |
-| `--tags` | Select which [story tags](../writing-stories/tags.md) to include. Accepts a comma-seperated string. `storybook dev --tags "dev,test"` |
-| `--exclude-tags` | Select which [story tags](../writing-stories/tags.md) to exclude. Accepts a comma-seperated string. `storybook dev --tags "docs, test"` |
@@ -82,8 +80,6 @@ Options include:
| `--docs` | Builds Storybook in documentation mode. Learn more about it in [here](../writing-docs/build-documentation.md#publish-storybooks-documentation) `storybook build --docs` |
| `--disable-telemetry` | Disables Storybook's telemetry. Learn more about it [here](../configure/telemetry.md). `storybook build --disable-telemetry` |
| `--test` | Optimize Storybook's production build for performance and tests by removing unnecessary features with the `test` option. Learn more [here](../api/main-config-build.md). `storybook build --test` |
-| `--tags` | Select which [story tags](../writing-stories/tags.md) to include. Accepts a comma-seperated string. `storybook dev --tags "dev,test"` |
-| `--exclude-tags` | Select which [story tags](../writing-stories/tags.md) to exclude. Accepts a comma-seperated string. `storybook dev --tags "docs, test"` |
diff --git a/docs/api/main-config-tags.md b/docs/api/main-config-tags.md
deleted file mode 100644
index cb593d36afb5..000000000000
--- a/docs/api/main-config-tags.md
+++ /dev/null
@@ -1,40 +0,0 @@
----
-title: 'tags'
-hideRendererSelector: true
----
-
-Parent: [main.js|ts configuration](./main-config.md)
-
-Type: `{ [key: string]: { description?: string } }`
-
-[Tags](../writing-stories/tags.md) allow you to control which stories are included in your Storybook. They are defined in the `tags` property of the Storybook configuration. Then they can be [applied to stories](../writing-stories/tags.md#tagging-stories) at the project, component, and story levels using the `tags` array.
-
-## Default tags
-
-The following tags are available by default in your project:
-
-```js
-{
- dev: { description: 'Rendered in the Storybook UI, but only in development mode. They do not appear in the sidebar in production mode.' },
- docs: { description: 'Rendered in the Storybook UI, but only in the docs page. They do not appear in the sidebar.' },
- test: { description: 'Not rendered in the Storybook UI, in either development or production mode.' },
-}
-```
-
-## Custom tags
-
-You can [define your own tags](../writing-stories/tags.md#custom-tags). When doing so, the key is the tag name, and the value is an object with an optional `description` property.
-
-```ts
-// .storybook/main.ts
-import { StorybookConfig } from '@storybook/';
-
-const config: StorybookConfig = {
- // ...rest of config
- tags: {
- experimental: { description: 'Stories for experimental components or features' },
- },
-};
-
-export default config;
-```
diff --git a/docs/api/main-config.md b/docs/api/main-config.md
index e347bc355b5f..a9013268e45c 100644
--- a/docs/api/main-config.md
+++ b/docs/api/main-config.md
@@ -44,7 +44,6 @@ An object to configure Storybook containing the following properties:
- [`refs`](./main-config-refs.md)
- [`staticDirs`](./main-config-static-dirs.md)
- [`swc`](./main-config-swc.md)
-- [`tags`](./main-config-tags.md)
- [`typescript`](./main-config-typescript.md)
- [`viteFinal`](./main-config-vite-final.md)
- [`webpackFinal`](./main-config-webpack-final.md)
diff --git a/docs/snippets/angular/button-story-auto-docs.ts.mdx b/docs/snippets/angular/button-story-auto-docs.ts.mdx
deleted file mode 100644
index d0b433899e96..000000000000
--- a/docs/snippets/angular/button-story-auto-docs.ts.mdx
+++ /dev/null
@@ -1,33 +0,0 @@
-```ts
-// Button.stories.ts
-
-import type { Meta, StoryObj } from '@storybook/angular';
-
-import { Button } from './button.component';
-
-const meta: Meta