diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0a0689881ff9..7fcd6003f4e3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,10 @@
+## 10.1.6
+
+- Manager: Do not display non-existing shortcuts in the settings page - [#32711](https://github.com/storybookjs/storybook/pull/32711), thanks @DKER2!
+- Preview: Enforce inert body if manager is focus-trapped - [#33186](https://github.com/storybookjs/storybook/pull/33186), thanks @Sidnioulz!
+- Telemetry: Await pending operations in getLastEvents to prevent race conditions - [#33285](https://github.com/storybookjs/storybook/pull/33285), thanks @valentinpalkovic!
+- UI: Fix keyboard navigation bug for "reset" option in `Select` - [#33268](https://github.com/storybookjs/storybook/pull/33268), thanks @Sidnioulz!
+
## 10.1.5
- Addon-Vitest: Isolate error reasons during postinstall - [#33295](https://github.com/storybookjs/storybook/pull/33295), thanks @valentinpalkovic!
diff --git a/CHANGELOG.prerelease.md b/CHANGELOG.prerelease.md
index 8266aa3fd22e..a0f656be416f 100644
--- a/CHANGELOG.prerelease.md
+++ b/CHANGELOG.prerelease.md
@@ -1,3 +1,12 @@
+## 10.2.0-alpha.6
+
+- Controls: Fix displaying as object instead of select for optional union types - [#33200](https://github.com/storybookjs/storybook/pull/33200), thanks @tanujbhaud!
+- Controls: Force object control JSON mode to reset - [#33330](https://github.com/storybookjs/storybook/pull/33330), thanks @Sidnioulz!
+- Docs-Blocks: Fix broken tooltip in ArgValue details - [#33264](https://github.com/storybookjs/storybook/pull/33264), thanks @Sidnioulz!
+- Manager: Ensure reset item only appears in globals toolbar when specified - [#33276](https://github.com/storybookjs/storybook/pull/33276), thanks @mrginglymus!
+- Nextjs-Vite: Install `vite` during migration if not installed yet - [#33316](https://github.com/storybookjs/storybook/pull/33316), thanks @ghengeveld!
+- UI: Make vertical alignment of TestStatusIcon more robust - [#33305](https://github.com/storybookjs/storybook/pull/33305), thanks @Sidnioulz!
+
## 10.2.0-alpha.5
- Addon-Vitest: Added timeout for fetching localhost 6006 during global setup. - [#33232](https://github.com/storybookjs/storybook/pull/33232), thanks @snippy4!
diff --git a/MIGRATION.md b/MIGRATION.md
index ba81b2731579..adb41e1c2f99 100644
--- a/MIGRATION.md
+++ b/MIGRATION.md
@@ -204,17 +204,17 @@
- [Tab addons cannot manually route, Tool addons can filter their visibility via tabId](#tab-addons-cannot-manually-route-tool-addons-can-filter-their-visibility-via-tabid)
- [Removed `config` preset](#removed-config-preset-1)
- [From version 7.5.0 to 7.6.0](#from-version-750-to-760)
- - [CommonJS with Vite is deprecated](#commonjs-with-vite-is-deprecated)
- - [Using implicit actions during rendering is deprecated](#using-implicit-actions-during-rendering-is-deprecated)
- - [typescript.skipBabel deprecated](#typescriptskipbabel-deprecated)
- - [Primary doc block accepts of prop](#primary-doc-block-accepts-of-prop)
- - [Addons no longer need a peer dependency on React](#addons-no-longer-need-a-peer-dependency-on-react)
+ - [CommonJS with Vite is deprecated](#commonjs-with-vite-is-deprecated)
+ - [Using implicit actions during rendering is deprecated](#using-implicit-actions-during-rendering-is-deprecated)
+ - [typescript.skipBabel deprecated](#typescriptskipbabel-deprecated)
+ - [Primary doc block accepts of prop](#primary-doc-block-accepts-of-prop)
+ - [Addons no longer need a peer dependency on React](#addons-no-longer-need-a-peer-dependency-on-react)
- [From version 7.4.0 to 7.5.0](#from-version-740-to-750)
- - [`storyStoreV6` and `storiesOf` is deprecated](#storystorev6-and-storiesof-is-deprecated)
- - [`storyIndexers` is replaced with `experimental_indexers`](#storyindexers-is-replaced-with-experimental_indexers)
+ - [`storyStoreV6` and `storiesOf` is deprecated](#storystorev6-and-storiesof-is-deprecated)
+ - [`storyIndexers` is replaced with `experimental_indexers`](#storyindexers-is-replaced-with-experimental_indexers)
- [From version 7.0.0 to 7.2.0](#from-version-700-to-720)
- - [Addon API is more type-strict](#addon-api-is-more-type-strict)
- - [Addon-controls hideNoControlsWarning parameter is deprecated](#addon-controls-hidenocontrolswarning-parameter-is-deprecated)
+ - [Addon API is more type-strict](#addon-api-is-more-type-strict)
+ - [Addon-controls hideNoControlsWarning parameter is deprecated](#addon-controls-hidenocontrolswarning-parameter-is-deprecated)
- [From version 6.5.x to 7.0.0](#from-version-65x-to-700)
- [7.0 breaking changes](#70-breaking-changes)
- [Dropped support for Node 15 and below](#dropped-support-for-node-15-and-below)
@@ -240,7 +240,7 @@
- [Deploying build artifacts](#deploying-build-artifacts)
- [Dropped support for file URLs](#dropped-support-for-file-urls)
- [Serving with nginx](#serving-with-nginx)
- - [Ignore story files from node\_modules](#ignore-story-files-from-node_modules)
+ - [Ignore story files from node_modules](#ignore-story-files-from-node_modules)
- [7.0 Core changes](#70-core-changes)
- [7.0 feature flags removed](#70-feature-flags-removed)
- [Story context is prepared before for supporting fine grained updates](#story-context-is-prepared-before-for-supporting-fine-grained-updates)
@@ -254,7 +254,7 @@
- [Addon-interactions: Interactions debugger is now default](#addon-interactions-interactions-debugger-is-now-default)
- [7.0 Vite changes](#70-vite-changes)
- [Vite builder uses Vite config automatically](#vite-builder-uses-vite-config-automatically)
- - [Vite cache moved to node\_modules/.cache/.vite-storybook](#vite-cache-moved-to-node_modulescachevite-storybook)
+ - [Vite cache moved to node_modules/.cache/.vite-storybook](#vite-cache-moved-to-node_modulescachevite-storybook)
- [7.0 Webpack changes](#70-webpack-changes)
- [Webpack4 support discontinued](#webpack4-support-discontinued)
- [Babel mode v7 exclusively](#babel-mode-v7-exclusively)
@@ -305,7 +305,7 @@
- [Dropped addon-docs manual babel configuration](#dropped-addon-docs-manual-babel-configuration)
- [Dropped addon-docs manual configuration](#dropped-addon-docs-manual-configuration)
- [Autoplay in docs](#autoplay-in-docs)
- - [Removed STORYBOOK\_REACT\_CLASSES global](#removed-storybook_react_classes-global)
+ - [Removed STORYBOOK_REACT_CLASSES global](#removed-storybook_react_classes-global)
- [7.0 Deprecations and default changes](#70-deprecations-and-default-changes)
- [storyStoreV7 enabled by default](#storystorev7-enabled-by-default)
- [`Story` type deprecated](#story-type-deprecated)
@@ -518,7 +518,6 @@
- [Packages renaming](#packages-renaming)
- [Deprecated embedded addons](#deprecated-embedded-addons)
-
## From version 10.0.0 to 10.1.0
### API and Component Changes
@@ -526,6 +525,7 @@
#### Button Component API Changes
##### Added: ariaLabel
+
The Button component now has an `ariaLabel` prop, to ensure that Storybook UI code is accessible to screenreader users. The prop will become mandatory in Storybook 11.
When buttons have text content as children, and when that text content does not rely on visual context to be understood, you may pass `false` to the `ariaLabel` prop to indicate that an ARIA label is not necessary.
@@ -557,15 +557,16 @@ IconButton will be removed in future versions.
The `Bar` component's internal layout has changed, to fix a height bug in scrollable bars. It now applies flex positioning and applies a default item gap, that can be controlled with the `innerStyle` prop. You may see slight changes in default padding as a result of this change.
##### Added: innerStyle
+
When `scrollable` is set to `true`, `Bar` now adds an inner container that is used to ensure the scrollbar size does not impact the height of the bar. This inner container displays as 'flex' and has the following default style:
```css
- width: 100%;
- min-height: 40;
- display: flex;
- align-items: center;
- gap: 6px;
- padding-inline: 6px;
+width: 100%;
+min-height: 40;
+display: flex;
+align-items: center;
+gap: 6px;
+padding-inline: 6px;
```
The inner container's style can be overridden by passing CSS properties to `innerStyle`.
@@ -597,18 +598,23 @@ The `TabBar` component, a styled bar used inside `Tabs` and not intended to be p
#### Modal Component API Changes
##### Deprecated: onInteractOutside
+
The `onInteractOutside` prop is deprecated in favor of `dismissOnClickOutside`, because it was only used to close the modal when clicking outside. Use `dismissOnClickOutside` to control whether clicking outside the modal should close it or not.
##### Deprecated: onEscapeKeyDown
+
The `onEscapeKeyDown` prop is deprecated in favor of `dismissOnEscape`, because it was only used to close the modal when pressing Escape. Use `dismissOnEscape` to control whether pressing Escape should close it or not.
##### Added: `ariaLabel`
+
Modal elements must have a title to be accessible. Set that title through the `ariaLabel` prop. It will become mandatory in Storybook 11.
##### Renamed: Modal.Dialog.Close and Modal.CloseButton
+
The `Modal.Dialog.Close` component and `Modal.CloseButton` components are replaced by `Modal.Close` for consistency with other components. Those names are deprecated and will be removed in Storybook 11. You may call `` for a default close button, or `...` to wrap your own custom button.
The `Modal.Close` component no longer requires an `onClick` handler to close the modal. It will automatically close the modal when clicked. If you need to perform additional actions when the close button is clicked, you can still provide an `onClick` handler, and it will be called in addition to closing the modal.
+
#### ListItem, TooltipLinkList and TooltipMessage are deprecated
The ListItem and TooltipLinkList components were used in Storybook to make menus, and TooltipMessage to make message popovers. However, WithTooltip does not support keyboard interactions, so these components were not accessible.
@@ -626,34 +632,41 @@ PopoverProvider is based on react-aria. It must have a single child that acts as
The WithTooltip component has been reimplemented from the ground up, under the new name `TooltipProvider`. The new implementation will replace `WithTooltip` entirely in Storybook 11. Below is a summary of the changes between both APIs, which will take full effect in Storybook 11.
##### Removed: trigger
+
The `trigger` prop was removed to enforce better accessibility compliance. WithTooltip must not be triggered on click, as it is not reachable by keyboard. Buttons that open a popover, menu or select must use appropriate components instead.
#### Added: triggerOnFocusOnly
+
The `triggerOnFocusOnly` prop was added. When set, tooltips will only show on focus. Use this to provide keyboard navigation hints to keyboard users. Do not use it for other purposes.
#### Renamed: startOpen
+
The `startOpen` prop was renamed `defaultVisible` to match naming in other components that expose both controlled and uncontrolled visibility. The `startOpen` prop will be removed in future versions.
#### Removed: svg, strategy, withArrows, mutationObserverOptions
-These prop were not used inside Storybook and have been removed.
+
+These props were not used inside Storybook and have been removed.
#### Removed: hasChrome
+
The `hasChrome` prop was removed because it should be handled by the tooltip being shown instead. Popover and Tooltip both have a `hasChrome` prop. TooltipNote never needs this prop and does not have it.
#### Removed: closeOnTriggerHidden, followCursor, closeOnOutsideClick
-The `closeOnTriggerHidden`, `followCursor` and `closeOnOutsideClick` prop has been removed. WithTooltip will now authoritatively decide when and where to show or hide its tooltip. It will always close on clicks outside the tooltip, because tooltips should never be modal.
+
+The `closeOnTriggerHidden`, `followCursor` and `closeOnOutsideClick` props have been removed. WithTooltip will now authoritatively decide when and where to show or hide its tooltip. It will always close on clicks outside the tooltip, because tooltips should never be modal.
#### Removed: interactive
-Thed `interactive` prop has been removed as it does not align with our vision for accessible components with a well-defined role. Use PopoverProvider instead of WithTooltip to show interactive overlays.
+
+The `interactive` prop has been removed as it does not align with our vision for accessible components with a well-defined role. Use PopoverProvider instead of WithTooltip to show interactive overlays.
##### Other changes
+
The underlying implementation was switched from Popper.js to react-aria. Due to these changes, WithTooltip must now have a single child that has a focusable role and that can receive React refs. Wrap your trigger component in `forwardRef` if you notice placement issues for your tooltip.
#### WithTooltipPure and WithTooltipState are deprecated
Instead, use `WithTooltipNew` in Storybook 10, or `WithTooltip` in Storybook 11 or newer. For a controlled tooltip, use the `onVisibleChange` and `visible` props. For an uncontrolled tooltip with a default open state, use the `defaultVisible` prop.
-
## From version 9.x to 10.0.0
### Core Changes
@@ -666,7 +679,7 @@ In Storybook 9 it was possible to do reference local addons by a relative path,
// main.ts
export default {
- addons: ["./my-addon.ts"],
+ addons: ['./my-addon.ts'],
};
```
@@ -676,7 +689,7 @@ In Storybook 10 this relative path, should be fully resolved, like so:
// main.ts
export default {
- addons: [import.meta.resolve("./my-addon.ts")],
+ addons: [import.meta.resolve('./my-addon.ts')],
};
```
@@ -688,7 +701,7 @@ For example:
// main.ts
export default {
managerEntries(entry = []) {
- return [...entry, require.resolve("./iframe.js")];
+ return [...entry, require.resolve('./iframe.js')];
},
};
```
@@ -697,11 +710,11 @@ Would become:
```ts
// main.ts
-import { fileURLToPath } from "node:url";
+import { fileURLToPath } from 'node:url';
export default {
managerEntries(entry = []) {
- return [...entry, fileURLToPath(import.meta.resolve("./iframe.js"))];
+ return [...entry, fileURLToPath(import.meta.resolve('./iframe.js'))];
},
};
```
@@ -714,9 +727,9 @@ Thus CJS constants (`require`, `__dirname`, `__filename`) will not be defined.
You can define these constants yourself, like so:
```ts
-import { createRequire } from "node:module";
-import { dirname } from "node:path";
-import { fileURLToPath } from "node:url";
+import { createRequire } from 'node:module';
+import { dirname } from 'node:path';
+import { fileURLToPath } from 'node:url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
@@ -728,12 +741,14 @@ A `main.ts` file that's CJS is no longer supported. The same applies to any cust
Additionally, **extensionless relative imports are no longer supported** in JavaScript-based configuration files (`.storybook/main.js`) and custom presets. All relative imports must now include explicit file extensions.
**Before (no longer works):**
+
```js
// .storybook/main.js
import myPreset from './my-file';
```
**After:**
+
```js
// .storybook/main.js
import myPreset from './my-file.js';
@@ -742,6 +757,7 @@ import myPreset from './my-file.js';
This change aligns with Node.js ESM requirements, where relative imports must specify the full file extension. This applies to `.storybook/main.js` and any custom preset files. While TypeScript-based files (`.storybook/main.ts`) will continue to work with extensionless imports for now through automatic resolution, we recommend migrating to explicit extensions for consistency and better compatibility.
**Recommended approach for all files:**
+
- Use `.js` for JavaScript files
- Use `.mjs` for ES modules
- Use `.ts` for TypeScript files
@@ -799,8 +815,8 @@ export const core = {
```
#### Removed x-only builtin tags
+
During development of Storybook [Tags](https://storybook.js.org/docs/writing-stories/tags), we created `dev-only`, `docs-only`, and `test-only` built-in tags. These tags were never documented and superseded by the currently-documented `dev`, `autodocs`, and `test` tags which provide more precise control. The outdated `x-only` tags are removed in 10.0.
-During development of Storybook [Tags](https://storybook.js.org/docs/writing-stories/tags), we created `dev-only`, `docs-only`, and `test-only` built-in tags. These tags were never documented and superceded by the currently-documented `dev`, `autodocs`, and `test` tags which provide more precise control. The outdated `x-only` tags are removed in 10.0.
## From version 8.x to 9.0.0
@@ -916,7 +932,7 @@ If you're using framework-specific Vite plugins, ensure they are compatible with
- `@sveltejs/vite-plugin-svelte`
- etc.
-For more information on upgrading to Vite 5, see the [Vite Migration Guide](https://vitejs.dev/guide/migration).
+For more information on upgrading to Vite 5, see the [Vite Migration Guide](https://v6.vite.dev/guide/migration).
##### TypeScript < 4.9
@@ -1298,12 +1314,12 @@ export default {
docs: {
source: {
transform: async (source) => {
- const prettier = await import("prettier/standalone");
- const prettierPluginBabel = await import("prettier/plugins/babel");
- const prettierPluginEstree = await import("prettier/plugins/estree");
+ const prettier = await import('prettier/standalone');
+ const prettierPluginBabel = await import('prettier/plugins/babel');
+ const prettierPluginEstree = await import('prettier/plugins/estree');
return prettier.format(source, {
- parser: "babel",
+ parser: 'babel',
plugins: [prettierPluginBabel, prettierPluginEstree],
});
},
@@ -1322,7 +1338,7 @@ export const MyStory = {
parameters: {
docs: {
source: {
- format: "html",
+ format: 'html',
},
},
},
@@ -1496,7 +1512,7 @@ For all affected frameworks, update your configuration to use the Vite builder:
```tsx
export default {
framework: {
- name: "@storybook/[framework]-vite", // replace [framework] with preact, vue3, or web-components
+ name: '@storybook/[framework]-vite', // replace [framework] with preact, vue3, or web-components
options: {},
},
// ... other configurations
@@ -1595,7 +1611,7 @@ Previously, once you made changes to a component's props, the controls and args
const config = {
// ...
typescript: {
- reactDocgen: "react-docgen-typescript",
+ reactDocgen: 'react-docgen-typescript',
reactDocgenTypescriptOptions: {
EXPERIMENTAL_useWatchProgram: true,
},
@@ -1842,8 +1858,8 @@ export default {
In order to properly mock the `next/router`, `next/header`, `next/navigation` and `next/cache` APIs, the `@storybook/nextjs` framework includes internal Webpack aliases to those modules. If you use portable stories in your Jest tests, you should set the aliases in your Jest config files `moduleNameMapper` property using the `getPackageAliases` helper from `@storybook/nextjs/export-mocks`:
```js
-const nextJest = require("next/jest.js");
-const { getPackageAliases } = require("@storybook/nextjs/export-mocks");
+const nextJest = require('next/jest.js');
+const { getPackageAliases } = require('@storybook/nextjs/export-mocks');
const createJestConfig = nextJest();
const customJestConfig = {
moduleNameMapper: {
@@ -1872,7 +1888,7 @@ Starting in 8.1, to generate autodocs for every component (`docs.autodocs = true
```js
// .storybook/preview.js
export default {
- tags: ["autodocs"],
+ tags: ['autodocs'],
};
```
@@ -1882,7 +1898,7 @@ Tags cascade, so setting `'autodocs'` at the project level automatically propaga
// Button.stories.ts
export default {
component: Button,
- tags: ["!autodocs"],
+ tags: ['!autodocs'],
};
```
@@ -1915,12 +1931,8 @@ It still accepts being passed `children`.
When passing project annotations overrides via `composeStory` such as:
```tsx
-const projectAnnotationOverrides = { parameters: { foo: "bar" } };
-const Primary = composeStory(
- stories.Primary,
- stories,
- projectAnnotationOverrides
-);
+const projectAnnotationOverrides = { parameters: { foo: 'bar' } };
+const Primary = composeStory(stories.Primary, stories, projectAnnotationOverrides);
```
they are now merged with the annotations passed via `setProjectAnnotations` rather than completely overwriting them. This was seen as a bug and it's now fixed. If you have a use case where you really need this, please open an issue to elaborate.
@@ -1986,12 +1998,12 @@ The UI added to the manager via addons is now rendered with React 18.
Example:
```tsx
-import { addons, types } from "@storybook/manager-api";
+import { addons, types } from '@storybook/manager-api';
-addons.register("my-addon", () => {
- addons.add("my-addon/panel", {
+addons.register('my-addon', () => {
+ addons.add('my-addon/panel', {
type: types.PANEL,
- title: "My Addon",
+ title: 'My Addon',
// This will be called as a JSX element by react 18
render: ({ active }) => (active ?
Hello World
: null),
});
@@ -2043,8 +2055,8 @@ In Storybook 8 those plugins have to be added explicitly in the user's `vite.con
#### For React:
```ts
-import { defineConfig } from "vite";
-import react from "@vitejs/plugin-react";
+import react from '@vitejs/plugin-react';
+import { defineConfig } from 'vite';
export default defineConfig({
plugins: [react()],
@@ -2054,8 +2066,8 @@ export default defineConfig({
#### For Vue:
```ts
-import { defineConfig } from "vite";
-import vue from "@vitejs/plugin-vue";
+import vue from '@vitejs/plugin-vue';
+import { defineConfig } from 'vite';
export default defineConfig({
plugins: [vue()],
@@ -2065,8 +2077,8 @@ export default defineConfig({
#### For Svelte (without Sveltekit):
```ts
-import { defineConfig } from "vite";
-import { svelte } from "@sveltejs/vite-plugin-svelte";
+import { svelte } from '@sveltejs/vite-plugin-svelte';
+import { defineConfig } from 'vite';
export default defineConfig({
plugins: [svelte()],
@@ -2076,8 +2088,8 @@ export default defineConfig({
#### For Preact:
```ts
-import { defineConfig } from "vite";
-import preact from "@preact/preset-vite";
+import preact from '@preact/preset-vite';
+import { defineConfig } from 'vite';
export default defineConfig({
plugins: [preact()],
@@ -2087,8 +2099,8 @@ export default defineConfig({
#### For Solid:
```ts
-import { defineConfig } from "vite";
-import solid from "vite-plugin-solid";
+import { defineConfig } from 'vite';
+import solid from 'vite-plugin-solid';
export default defineConfig({
plugins: [solid()],
@@ -2098,8 +2110,8 @@ export default defineConfig({
#### For Qwik:
```ts
-import { defineConfig } from "vite";
-import qwik from "vite-plugin-qwik";
+import { defineConfig } from 'vite';
+import qwik from 'vite-plugin-qwik';
export default defineConfig({
plugins: [qwik()],
@@ -2129,7 +2141,7 @@ export default {
export const ButtonClick = {
play: async ({ args, canvasElement }) => {
- await userEvent.click(within(canvasElement).getByRole("button"));
+ await userEvent.click(within(canvasElement).getByRole('button'));
// args.onClick is a jest spy in 7.0
await expect(args.onClick).toHaveBeenCalled();
},
@@ -2139,7 +2151,7 @@ export const ButtonClick = {
In Storybook 8 this feature is removed, and spies have to added explicitly:
```ts
-import { fn } from "@storybook/test";
+import { fn } from '@storybook/test';
export default {
component: Button,
@@ -2150,7 +2162,7 @@ export default {
export const ButtonClick = {
play: async ({ args, canvasElement }) => {
- await userEvent.click(within(canvasElement).getByRole("button"));
+ await userEvent.click(within(canvasElement).getByRole('button'));
await expect(args.onClick).toHaveBeenCalled();
},
};
@@ -2173,14 +2185,22 @@ Storybook now uses MDX3 under the hood. This change contains many improvements a
#### Dropping support for \*.stories.mdx (CSF in MDX) format and MDX1 support
-In Storybook 7, we deprecated the ability of using MDX both for documentation and for defining stories in the same .stories.mdx file. It is now removed, and Storybook won't support .stories.mdx files anymore. We provide migration scripts to help you onto the new format.
+In Storybook 7, we deprecated the ability to use MDX for both documentation and story definition in the same .stories.mdx file. It is now removed, and Storybook won't support `.stories.mdx` files anymore. We provide migration scripts to help you onto the new format. To migrate your `.stories.mdx` files to the new format, you can run the following command:
+
+```sh
+# Convert stories in MDX to CSF
+npx storybook@latest migrate mdx-to-csf --glob "src/**/*.stories.mdx"
+```
+
+> To ensure a smooth migration, you'll also need to:
+>
+> - Update your stories glob in your `.storybook/main.js` configuration to include the newly created `.mdx` and `.stories.js|ts` files.
+> - Manually remove the stories from the original `.stories.mdx` files, as by default the codemod won't delete them.
If you were using the [legacy MDX1 format](#legacy-mdx1-support), you will have to remove the `legacyMdx1` main.js feature flag and the `@storybook/mdx1-csf` package.
Alongside with this change, the `jsxOptions` configuration was removed as it is not used anymore.
-[More info here](https://storybook.js.org/docs/migration-guide#storiesmdx-to-mdxcsf).
-
#### Dropping support for id, name and story in Story block
Referencing stories by `id`, `name` or `story` in the Story block is not possible anymore. [More info here](#story-block).
@@ -2290,11 +2310,11 @@ In Storybook 7, the file name `path/to/foo.bar.stories.js` would result in the [
```js
export default {
- title: "path/to/foo",
+ title: 'path/to/foo',
};
```
-Alternatively, if you need to achieve a different behavior for a large number of files, you can provide a [custom indexer](https://storybook.js.org/docs/7.0/vue/configure/sidebar-and-urls#processing-custom-titles) to generate the titles dynamically.
+Alternatively, if you need to achieve a different behavior for a large number of files, you can provide a [custom indexer](https://storybook.js.org/docs/8/configure/user-interface/sidebar-and-urls#story-index) to generate the titles dynamically.
#### Storyshots has been removed
@@ -2319,7 +2339,7 @@ Please check the [migration guide](https://storybook.js.org/docs/writing-tests/s
#### UI layout state has changed shape
-In Storybook 7 it was possible to use `addons.setConfig({...});` to configure Storybook UI features and behavior as documented [here (v7)](https://storybook.js.org/docs/7.3/react/configure/features-and-behavior), [(latest)](https://storybook.js.org/docs/react/configure/features-and-behavior). The state and API for the UI layout has changed:
+In Storybook 7 it was possible to use `addons.setConfig({...});` to configure Storybook UI features and behavior as documented [here (v7)](https://github.com/storybookjs/storybook/blob/release-7-3/docs/configure/features-and-behavior.md), [(latest)](https://storybook.js.org/docs/react/configure/features-and-behavior). The state and API for the UI layout has changed:
- `showNav: boolean` is now `navSize: number`, where the number represents the size of the sidebar in pixels.
- `showPanel: boolean` is now split into `bottomPanelHeight: number` and `rightPanelWidth: number`, where the numbers represents the size of the panel in pixels.
@@ -2386,7 +2406,7 @@ We feel `react-docgen` is the right tradeoff for most React projects. However, i
```js
export default {
typescript: {
- reactDocgen: "react-docgen-typescript",
+ reactDocgen: 'react-docgen-typescript',
},
};
```
@@ -2619,7 +2639,7 @@ import { setProjectAnnotations } from `@storybook/react`.
The `StorybookViteConfig` type is now removed in favor of `StorybookConfig`:
```ts
-import type { StorybookConfig } from "@storybook/react-vite";
+import type { StorybookConfig } from '@storybook/react-vite';
```
#### props from WithTooltipComponent from @storybook/components
@@ -2633,7 +2653,7 @@ The deprecated properties `tooltipShown`, `closeOnClick`, and `onVisibilityChang
onVisibilityChange // becomes onVisibleChange
>
...
-
+;
```
#### LinkTo direct import from addon-links
@@ -2642,10 +2662,10 @@ The `LinkTo` (React component) direct import from `@storybook/addon-links` is no
```ts
// before
-import LinkTo from "@storybook/addon-links";
+import LinkTo from '@storybook/addon-links';
// after
-import LinkTo from "@storybook/addon-links/react";
+import LinkTo from '@storybook/addon-links/react';
```
#### DecoratorFn, Story, ComponentStory, ComponentStoryObj, ComponentStoryFn and ComponentMeta TypeScript types
@@ -2670,7 +2690,7 @@ export const Component = () => {
const someHandler = () => {
// Old method: api.navigateToSettingsPage('/settings/about');
- api.changeSettingsTab("about"); // the /settings path is not necessary anymore
+ api.changeSettingsTab('about'); // the /settings path is not necessary anymore
};
// ...
@@ -2710,11 +2730,8 @@ Additionally, given that CSF in MDX is not supported anymore, the following prop
The `collapseAll` and `expandAll` APIs (possibly used by addons) are now removed. Please emit events for these actions instead:
```ts
-import {
- STORIES_COLLAPSE_ALL,
- STORIES_EXPAND_ALL,
-} from "@storybook/core-events";
-import { useStorybookApi } from "@storybook/manager-api";
+import { STORIES_COLLAPSE_ALL, STORIES_EXPAND_ALL } from '@storybook/core-events';
+import { useStorybookApi } from '@storybook/manager-api';
const api = useStorybookApi();
api.collapseAll(); // becomes api.emit(STORIES_COLLAPSE_ALL)
@@ -2775,12 +2792,12 @@ Instead storybook will automatically show the addon's rendered content when the
Example:
```tsx
-import { addons, types } from "@storybook/manager-api";
+import { addons, types } from '@storybook/manager-api';
-addons.register("my-addon", () => {
- addons.add("my-addon/tab", {
+addons.register('my-addon', () => {
+ addons.add('my-addon/tab', {
type: types.TAB,
- title: "My Addon",
+ title: 'My Addon',
render: () =>
Hello World
,
});
});
@@ -2794,13 +2811,13 @@ When the canvas is shown, the `tabId` will be set to `undefined`.
Example:
```tsx
-import { addons, types } from "@storybook/manager-api";
+import { addons, types } from '@storybook/manager-api';
-addons.register("my-addon", () => {
- addons.add("my-addon/tool", {
+addons.register('my-addon', () => {
+ addons.add('my-addon/tool', {
type: types.TOOL,
- title: "My Addon",
- match: ({ tabId }) => tabId === "my-addon/tab",
+ title: 'My Addon',
+ match: ({ tabId }) => tabId === 'my-addon/tab',
render: () =>
👀
,
});
});
@@ -2840,7 +2857,7 @@ export default {
export const ButtonClick = {
play: async ({ args, canvasElement }) => {
- await userEvent.click(within(canvasElement).getByRole("button"));
+ await userEvent.click(within(canvasElement).getByRole('button'));
// args.onClick is a jest spy in 7.0
await expect(args.onClick).toHaveBeenCalled();
},
@@ -2850,7 +2867,7 @@ export const ButtonClick = {
In Storybook 8 this feature will be removed, and spies have to added explicitly:
```ts
-import { fn } from "@storybook/test";
+import { fn } from '@storybook/test';
export default {
component: Button,
@@ -2861,7 +2878,7 @@ export default {
export const ButtonClick = {
play: async ({ args, canvasElement }) => {
- await userEvent.click(within(canvasElement).getByRole("button"));
+ await userEvent.click(within(canvasElement).getByRole('button'));
await expect(args.onClick).toHaveBeenCalled();
},
};
@@ -2897,9 +2914,8 @@ As of Storybook 7.6.0 the list of globalized packages can be imported like this:
```ts
// tsup.config.ts
-
-import { globalPackages as globalManagerPackages } from "@storybook/manager/globals";
-import { globalPackages as globalPreviewPackages } from "@storybook/preview/globals";
+import { globalPackages as globalManagerPackages } from '@storybook/manager/globals';
+import { globalPackages as globalPreviewPackages } from '@storybook/preview/globals';
const allGlobalPackages = [...globalManagerPackages, ...globalPreviewPackages];
```
@@ -2984,12 +3000,12 @@ The `type` property is now a required field, and the `id` property should not be
Here's a correct example:
```tsx
-import { addons, types } from "@storybook/manager-api";
+import { addons, types } from '@storybook/manager-api';
-addons.register("my-addon", () => {
- addons.add("my-addon/panel", {
+addons.register('my-addon', () => {
+ addons.add('my-addon/panel', {
type: types.PANEL,
- title: "My Addon",
+ title: 'My Addon',
render: ({ active }) => (active ?
Hello World
: null),
});
});
@@ -3029,7 +3045,7 @@ If your `preview.js` file looks like this:
```js
export const parameters = {
- actions: { argTypesRegex: "^on[A-Z].*" },
+ actions: { argTypesRegex: '^on[A-Z].*' },
};
```
@@ -3038,7 +3054,7 @@ Please migrate it to use a default export instead:
```js
const preview = {
parameters: {
- actions: { argTypesRegex: "^on[A-Z].*" },
+ actions: { argTypesRegex: '^on[A-Z].*' },
},
};
export default preview;
@@ -3049,11 +3065,11 @@ Additionally, we introduced typings for that default export (Preview), so you ca
The `Preview` type will come from the Storybook package for the **renderer** you are using. For example, if you are using Angular, you will import it from `@storybook/angular`, or if you're using Vue3, you will import it from `@storybook/vue3`:
```ts
-import { Preview } from "@storybook/react";
+import { Preview } from '@storybook/react';
const preview: Preview = {
parameters: {
- actions: { argTypesRegex: "^on[A-Z].*" },
+ actions: { argTypesRegex: '^on[A-Z].*' },
},
};
export default preview;
@@ -3062,10 +3078,10 @@ export default preview;
In JavaScript projects using `preview.js`, it's also possible to use the `Preview` type (for autocompletion, not type safety), via the JSDoc @type tag:
```js
-/** @type { import('@storybook/react').Preview } */
+/** @type {import('@storybook/react').Preview} */
const preview = {
parameters: {
- actions: { argTypesRegex: "^on[A-Z].*" },
+ actions: { argTypesRegex: '^on[A-Z].*' },
},
};
export default preview;
@@ -3079,33 +3095,24 @@ If your main.js file looks like this:
```js
module.exports = {
- stories: [
- "../stories/**/*.stories.mdx",
- "../stories/**/*.stories.@(js|jsx|ts|tsx)",
- ],
- framework: { name: "@storybook/react-vite" },
+ stories: ['../stories/**/*.stories.mdx', '../stories/**/*.stories.@(js|jsx|ts|tsx)'],
+ framework: { name: '@storybook/react-vite' },
};
```
Or like this:
```js
-export const stories = [
- "../stories/**/*.stories.mdx",
- "../stories/**/*.stories.@(js|jsx|ts|tsx)",
-];
-export const framework = { name: "@storybook/react-vite" };
+export const stories = ['../stories/**/*.stories.mdx', '../stories/**/*.stories.@(js|jsx|ts|tsx)'];
+export const framework = { name: '@storybook/react-vite' };
```
Please migrate it to use a default export instead:
```js
const config = {
- stories: [
- "../stories/**/*.stories.mdx",
- "../stories/**/*.stories.@(js|jsx|ts|tsx)",
- ],
- framework: { name: "@storybook/react-vite" },
+ stories: ['../stories/**/*.stories.mdx', '../stories/**/*.stories.@(js|jsx|ts|tsx)'],
+ framework: { name: '@storybook/react-vite' },
};
export default config;
```
@@ -3115,14 +3122,11 @@ Additionally, we introduced typings for that default export (StorybookConfig), s
The `StorybookConfig` type will come from the Storybook package for the **framework** you are using, which relates to the package in the "framework" field you have in your main.ts file. For example, if you are using React Vite, you will import it from `@storybook/react-vite`:
```ts
-import { StorybookConfig } from "@storybook/react-vite";
+import { StorybookConfig } from '@storybook/react-vite';
const config: StorybookConfig = {
- stories: [
- "../stories/**/*.stories.mdx",
- "../stories/**/*.stories.@(js|jsx|ts|tsx)",
- ],
- framework: { name: "@storybook/react-vite" },
+ stories: ['../stories/**/*.stories.mdx', '../stories/**/*.stories.@(js|jsx|ts|tsx)'],
+ framework: { name: '@storybook/react-vite' },
};
export default config;
```
@@ -3130,13 +3134,10 @@ export default config;
In JavaScript projects using `main.js`, it's also possible to use the `StorybookConfig` type (for autocompletion, not type safety), via the JSDoc @type tag:
```ts
-/** @type { import('@storybook/react-vite').StorybookConfig } */
+/** @type {import('@storybook/react-vite').StorybookConfig} */
const config = {
- stories: [
- "../stories/**/*.stories.mdx",
- "../stories/**/*.stories.@(js|jsx|ts|tsx)",
- ],
- framework: { name: "@storybook/react-vite" },
+ stories: ['../stories/**/*.stories.mdx', '../stories/**/*.stories.@(js|jsx|ts|tsx)'],
+ framework: { name: '@storybook/react-vite' },
};
export default config;
```
@@ -3162,13 +3163,13 @@ To fix these errors, the addon will have to be re-released with a newer browser-
module.exports = {
presets: [
[
- "@babel/preset-env",
+ '@babel/preset-env',
{
shippedProposals: true,
- useBuiltIns: "usage",
- corejs: "3",
+ useBuiltIns: 'usage',
+ corejs: '3',
modules: false,
- targets: { chrome: "100" },
+ targets: { chrome: '100' },
},
],
],
@@ -3297,7 +3298,7 @@ Here's an example, in case you picked `@storybook/react-vite`:
export default {
// ... your configuration
framework: {
- name: "@storybook/react-vite",
+ name: '@storybook/react-vite',
options: {},
},
};
@@ -3313,7 +3314,7 @@ For React, what used to be:
export default {
reactOptions: { fastRefresh: true },
framework: {
- name: "@storybook/react-webpack5",
+ name: '@storybook/react-webpack5',
options: {},
},
};
@@ -3324,7 +3325,7 @@ Becomes:
```js
export default {
framework: {
- name: "@storybook/react-webpack5",
+ name: '@storybook/react-webpack5',
options: { fastRefresh: true },
},
};
@@ -3336,7 +3337,7 @@ For Angular, what used to be:
export default {
angularOptions: { enableIvy: true },
framework: {
- name: "@storybook/angular",
+ name: '@storybook/angular',
options: {},
},
};
@@ -3347,7 +3348,7 @@ Becomes:
```js
export default {
framework: {
- name: "@storybook/angular",
+ name: '@storybook/angular',
options: { enableIvy: true },
},
};
@@ -3379,7 +3380,7 @@ Becomes:
```js
export default {
framework: {
- name: "@storybook/react-webpack5",
+ name: '@storybook/react-webpack5',
options: {
builder: { lazyCompilation: true },
},
@@ -3397,10 +3398,11 @@ If you are using TypeScript you should import the `StorybookConfig` type from yo
For example:
```ts
-import type { StorybookConfig } from "@storybook/react-vite";
+import type { StorybookConfig } from '@storybook/react-vite';
+
const config: StorybookConfig = {
framework: {
- name: "@storybook/react-vite",
+ name: '@storybook/react-vite',
options: {},
},
// ... your configuration
@@ -3456,17 +3458,17 @@ In 7.0 the location of the standalone node API has moved to `@storybook/core-ser
If you used the React standalone API, for example, you might have written:
```js
-const buildStandalone = require("@storybook/react/standalone");
+const buildStandalone = require('@storybook/react/standalone');
const options = {};
-buildStandalone(options).then(() => console.log("done"));
+buildStandalone(options).then(() => console.log('done'));
```
In 7.0, you would now use:
```js
-const { build } = require("@storybook/core-server");
+const { build } = require('@storybook/core-server');
const options = {};
-build(options).then(() => console.log("done"));
+build(options).then(() => console.log('done'));
```
#### Change of root html IDs
@@ -3495,8 +3497,8 @@ In 7.0, this pattern will also match `.mdx` files (the new extension for docs fi
export default {
stories: [
{
- directory: "../path/to/directory",
- files: "**/*.stories.@(mdx|tsx|ts|jsx|js)",
+ directory: '../path/to/directory',
+ files: '**/*.stories.@(mdx|tsx|ts|jsx|js)',
},
],
};
@@ -3572,7 +3574,7 @@ Given the following `main.js`:
```js
export default {
- stories: ["../**/*.stories.*"],
+ stories: ['../**/*.stories.*'],
};
```
@@ -3580,7 +3582,7 @@ If you want to restore the previous behavior to include `node_modules`, you can
```js
export default {
- stories: ["../**/*.stories.*", "../**/node_modules/**/*.stories.*"],
+ stories: ['../**/*.stories.*', '../**/node_modules/**/*.stories.*'],
};
```
@@ -3820,11 +3822,11 @@ export const Default: Story = {
You can also use the `provide-style` decorator to provide an application-wide service:
```js
-import { provideAnimations } from "@angular/platform-browser/animations";
-import { moduleMetadata } from "@storybook/angular";
+import { moduleMetadata } from '@storybook/angular';
+import { provideAnimations } from '@angular/platform-browser/animations';
export default {
- title: "Example",
+ title: 'Example',
decorators: [
applicationConfig({
providers: [provideAnimations()],
@@ -3861,7 +3863,7 @@ For existing users, SvelteKit projects need to use the `@storybook/sveltekit` fr
```js
// .storybook/main.js
export default {
- framework: "@storybook/sveltekit",
+ framework: '@storybook/sveltekit',
};
```
@@ -3874,20 +3876,20 @@ In 6.x `@storybook/vue3` exported a Vue application instance called `app`. In 7.
Before:
```js
-import { app } from "@storybook/vue3";
-import Button from "./Button.vue";
+import { app } from '@storybook/vue3';
+import Button from './Button.vue';
-app.component("GlobalButton", Button);
+app.component('GlobalButton', Button);
```
After:
```js
-import { setup } from "@storybook/vue3";
-import Button from "./Button.vue";
+import { setup } from '@storybook/vue3';
+import Button from './Button.vue';
setup((app) => {
- app.component("GlobalButton", Button);
+ app.component('GlobalButton', Button);
});
```
@@ -3915,10 +3917,10 @@ These 2 packages replace `@storybook/addons`.
When adding addons to storybook, you can (for example) add panels:
```js
-import { addons } from "@storybook/manager-api";
+import { addons } from '@storybook/manager-api';
-addons.addPanel("my-panel", {
- title: "My Panel",
+addons.addPanel('my-panel', {
+ title: 'My Panel',
render: ({ active, key }) =>
My Panel
,
});
```
@@ -3942,11 +3944,11 @@ Here's an example of using the new API:
The `@storybook/preview-api` is used here, because the `useEffect` hook is used in a decorator.
```js
-import { useEffect, makeDecorator } from "@storybook/preview-api";
+import { useEffect, makeDecorator } from '@storybook/preview-api';
export const withMyAddon = makeDecorator({
- name: "withMyAddon",
- parameterName: "myAddon",
+ name: 'withMyAddon',
+ parameterName: 'myAddon',
wrapper: (getStory) => {
useEffect(() => {
// do something with the options
@@ -4025,7 +4027,7 @@ If your `.manager.js` config references `register.js` of any of the following ad
The default export from `@storybook/addons` has been removed. Please use the named exports instead:
```js
-import { addons } from "@storybook/addons";
+import { addons } from '@storybook/addons';
```
The named export has been available since 6.0 or earlier, so your updated code will be backwards-compatible with older versions of Storybook.
@@ -4092,7 +4094,7 @@ You can configure Autodocs in `main.js`:
module.exports = {
docs: {
autodocs: true, // see below for alternatives
- defaultName: "Docs", // set to change the name of generated docs entries
+ defaultName: 'Docs', // set to change the name of generated docs entries
},
};
```
@@ -4124,8 +4126,8 @@ Previously `.stories.mdx` files were used to both define and document stories. I
If you were using `.stories.mdx` files to write stories, we encourage you to move the stories to a CSF file, and _attach_ an `.mdx` file to that CSF file to document them. You can use the `Meta` block to attach a MDX file to a CSF file, and the `Story` block to render the stories:
```mdx
-import { Meta, Story } from "@storybook/blocks";
-import * as ComponentStories from "./some-component.stories";
+import { Meta, Story } from '@storybook/blocks';
+import * as ComponentStories from './some-component.stories';
@@ -4143,7 +4145,7 @@ By default docs entries are listed first for the component. You can sort them us
In Storybook 6.x, to create a unattached docs MDX file (that is, one not attached to story or a CSF file), you'd have to create a `.stories.mdx` file, and describe its location with the `Meta` doc block:
```mdx
-import { Meta } from "@storybook/addon-docs";
+import { Meta } from '@storybook/addon-docs';
```
@@ -4192,8 +4194,8 @@ Notice the `?raw` suffix in the markdown import is needed for this to work.
To reference a story in a MDX file, you should reference it with `of`:
```mdx
-import { Meta, Story } from "@storybook/blocks";
-import * as ComponentStories from "./some-component.stories";
+import { Meta, Story } from '@storybook/blocks';
+import * as ComponentStories from './some-component.stories';
@@ -4203,9 +4205,9 @@ import * as ComponentStories from "./some-component.stories";
You can also reference a story from a different component:
```mdx
-import { Meta, Story } from "@storybook/blocks";
-import * as ComponentStories from "./some-component.stories";
-import * as SecondComponentStories from "./second-component.stories";
+import { Meta, Story } from '@storybook/blocks';
+import * as SecondComponentStories from './second-component.stories';
+import * as ComponentStories from './some-component.stories';
@@ -4237,8 +4239,8 @@ That also means the Canvas block no longer supports containing multiple stories
Here's a full example of the new API:
```mdx
-import { Meta, Canvas } from "@storybook/blocks";
-import * as ComponentStories from "./some-component.stories";
+import { Meta, Canvas } from '@storybook/blocks';
+import * as ComponentStories from './some-component.stories';
@@ -4304,9 +4306,9 @@ To override the theme, you can continue to use the `docs.theme` parameter.
If you want to override the MDX components supplied to your docs page, use the `MDXProvider` from `@mdx-js/react`:
```js
-import { MDXProvider } from "@mdx-js/react";
-import { DocsContainer } from "@storybook/blocks";
-import * as DesignSystem from "your-design-system";
+import { DocsContainer } from '@storybook/blocks';
+import { MDXProvider } from '@mdx-js/react';
+import * as DesignSystem from 'your-design-system';
export const MyDocsContainer = (props) => (
;
```
@@ -4480,7 +4482,7 @@ export const MyStory: Story = () => ;
However with the introduction of CSF3, the `Story` type has been deprecated in favor of two other types: `StoryFn` for CSF2 and `StoryObj` for CSF3.
```ts
-import type { StoryFn, StoryObj } from "@storybook/react";
+import type { StoryFn, StoryObj } from '@storybook/react';
export const MyCsf2Story: StoryFn = () => ;
export const MyCsf3Story: StoryObj = {
@@ -4513,24 +4515,24 @@ ComponentMeta -> Meta
Here are a few examples:
```ts
-import type { StoryFn, StoryObj } from "@storybook/react";
-import { Button, ButtonProps } from "./Button";
+import type { StoryFn, StoryObj } from '@storybook/react';
+import { Button, ButtonProps } from './Button';
// This works in 7.0, making the ComponentX types redundant.
const meta: Meta = { component: Button };
-export const CSF3Story: StoryObj = { args: { label: "Label" } };
+export const CSF3Story: StoryObj = { args: { label: 'Label' } };
export const CSF2Story: StoryFn = (args) => ;
-CSF2Story.args = { label: "Label" };
+CSF2Story.args = { label: 'Label' };
// Passing props directly still works as well.
const meta: Meta = { component: Button };
-export const CSF3Story: StoryObj = { args: { label: "Label" } };
+export const CSF3Story: StoryObj = { args: { label: 'Label' } };
export const CSF2Story: StoryFn = (args) => ;
-CSF2Story.args = { label: "Label" };
+CSF2Story.args = { label: 'Label' };
```
We have set up a codemod that attempts to automatically migrate your code for you (update the glob to suit your needs):
@@ -4548,9 +4550,9 @@ The "rendering" function that renderers (ex-frameworks) must export (`renderToDO
In 6.x you could import XFramework types:
```ts
-import type { ReactFramework } from "@storybook/react";
-import type { VueFramework } from "@storybook/vue";
-import type { SvelteFramework } from "@storybook/svelte";
+import type { ReactFramework } from '@storybook/react';
+import type { SvelteFramework } from '@storybook/svelte';
+import type { VueFramework } from '@storybook/vue';
// etc.
```
@@ -4558,9 +4560,9 @@ import type { SvelteFramework } from "@storybook/svelte";
Those are deprecated in 7.0 as they are renamed to:
```ts
-import type { ReactRenderer } from "@storybook/react";
-import type { VueRenderer } from "@storybook/vue";
-import type { SvelteRenderer } from "@storybook/svelte";
+import type { ReactRenderer } from '@storybook/react';
+import type { SvelteRenderer } from '@storybook/svelte';
+import type { VueRenderer } from '@storybook/vue';
// etc.
```
@@ -4570,27 +4572,27 @@ import type { SvelteRenderer } from "@storybook/svelte";
In 6.x you could import the type `DecoratorFn`:
```ts
-import type { DecoratorFn } from "@storybook/react";
+import type { DecoratorFn } from '@storybook/react';
```
This type is deprecated in 7.0, instead you can use the type `Decorator`, which is now available for all renderers:
```ts
-import type { Decorator } from "@storybook/react";
+import type { Decorator } from '@storybook/react';
// or
-import type { Decorator } from "@storybook/vue";
+import type { Decorator } from '@storybook/vue';
// or
-import type { Decorator } from "@storybook/svelte";
+import type { Decorator } from '@storybook/svelte';
// etc.
```
The type `Decorator` accepts a generic parameter `TArgs`. This can be used like this:
```tsx
-import type { Decorator } from "@storybook/react";
-import { LocaleProvider } from "./locale";
+import type { Decorator } from '@storybook/react';
+import { LocaleProvider } from './locale';
-const withLocale: Decorator<{ locale: "en" | "es" }> = (Story, { args }) => (
+const withLocale: Decorator<{ locale: 'en' | 'es' }> = (Story, { args }) => (
@@ -4642,7 +4644,7 @@ Example:
```js
// .storybook/manager.js
-import { addons } from "@storybook/addons";
+import { addons } from '@storybook/addons';
addons.setConfig({
showToolbar: false,
@@ -4712,13 +4714,12 @@ This might be considered a breaking change. However, we feel justified to releas
If you want to restore the old titles in the UI, you can customize your sidebar with the following code snippet in `.storybook/manager.js`:
```js
-import { addons } from "@storybook/addons";
-import startCase from "lodash/startCase";
+import { addons } from '@storybook/addons';
+import startCase from 'lodash/startCase';
addons.setConfig({
sidebar: {
- renderLabel: ({ name, type }) =>
- type === "story" ? name : startCase(name),
+ renderLabel: ({ name, type }) => (type === 'story' ? name : startCase(name)),
},
});
```
@@ -4733,7 +4734,7 @@ Since CSF3 is experimental, we are introducing this technically breaking change
```js
// atoms/Button/Button.stories.js
-export default { title: "Atoms/Button/Button" };
+export default { title: 'Atoms/Button/Button' };
```
#### Auto-title always prefixes
@@ -4776,13 +4777,13 @@ In ancient versions of Storybook, addons were registered by referring to `addon-
Before:
```js
-module.exports = { addons: ["my-addon/register.js"] };
+module.exports = { addons: ['my-addon/register.js'] };
```
After:
```js
-module.exports = { addons: ["my-addon"] };
+module.exports = { addons: ['my-addon'] };
```
## From version 6.3.x to 6.4.0
@@ -4828,7 +4829,7 @@ Then edit your `.storybook/main.js` config:
```js
module.exports = {
core: {
- builder: "webpack5",
+ builder: 'webpack5',
},
};
```
@@ -4845,13 +4846,14 @@ If you don't specify a component file, it will be inferred from the file's locat
Consider a project configuration `/path/to/project/.storybook/main.js` containing:
```js
-module.exports = { stories: ["../src/**/*.stories.*"] };
+module.exports = { stories: ['../src/**/*.stories.*'] };
```
And the file `/path/to/project/src/components/Button.stories.tsx` containing the default export:
```js
-import { Button } from "./Button";
+import { Button } from './Button';
+
export default { component: Button };
// named exports...
```
@@ -4895,20 +4897,18 @@ If you are migrating to experimental CSF3, the following is compatible with 6.4
```ts
// CSF2 function stories, current API, will break in 7.0
-import type { Story } from "@storybook/";
-
+import type { Story } from '@storybook/';
// CSF3 object stories, will persist in 7.0
-import type { StoryObj } from "@storybook/";
+import type { StoryObj } from '@storybook/';
```
The following is compatible with 6.4 and also forward-compatible with anticipated 7.0 changes:
```ts
// CSF2 function stories, forward-compatible mode
-import type { StoryFn } from "@storybook/";
-
+import type { StoryFn } from '@storybook/';
// CSF3 object stories, using future 7.0 types
-import type { Story } from "@storybook//types-7-0";
+import type { Story } from '@storybook//types-7-0';
```
### Story Store v7
@@ -4941,7 +4941,7 @@ In 7.0, we're moving towards a model where the user specifies their framework in
```js
module.exports = {
// ... your existing config
- framework: "@storybook/react", // OR whatever framework you're using
+ framework: '@storybook/react', // OR whatever framework you're using
};
```
@@ -4954,7 +4954,7 @@ To activate the v7 mode set the feature flag in your `.storybook/main.js` config
```js
module.exports = {
// ... your existing config
- framework: "@storybook/react", // OR whatever framework you're using
+ framework: '@storybook/react', // OR whatever framework you're using
features: {
storyStoreV7: true,
},
@@ -5017,7 +5017,7 @@ export default {
parameters: {
options: {
storySort: {
- order: ["Introduction", "*"],
+ order: ['Introduction', '*'],
},
},
},
@@ -5048,7 +5048,7 @@ Storyshots is not currently compatible with the v7 store. However, you can use t
```js
module.exports = {
features: {
- storyStoreV7: !global.navigator?.userAgent?.match?.("jsdom"),
+ storyStoreV7: !global.navigator?.userAgent?.match?.('jsdom'),
},
};
```
@@ -5200,11 +5200,7 @@ After:
```js
// .storybook/main.js
module.exports = {
- staticDirs: [
- "../public",
- "../static",
- { from: "../foo/assets", to: "/assets" },
- ],
+ staticDirs: ['../public', '../static', { from: '../foo/assets', to: '/assets' }],
};
```
@@ -5249,7 +5245,7 @@ Then edit your `.storybook/main.js` config:
```js
module.exports = {
core: {
- builder: "webpack5",
+ builder: 'webpack5',
},
};
```
@@ -5315,7 +5311,7 @@ If you were using `argType.defaultValue` or relying on inference to set a defaul
export default {
component: MyComponent,
args: {
- argName: "default-value",
+ argName: 'default-value',
},
};
```
@@ -5327,7 +5323,7 @@ export default {
component: MyComponent,
argTypes: {
argName: {
- table: { defaultValue: { summary: "SomeType" } },
+ table: { defaultValue: { summary: 'SomeType' } },
},
},
};
@@ -5350,10 +5346,10 @@ To update your code, you should be able to global replace `@storybook/addon-docs
```js
// before
-import { Meta, Story } from "@storybook/addon-docs/blocks";
+import { Meta, Story } from '@storybook/addon-docs/blocks';
// after
-import { Meta, Story } from "@storybook/addon-docs";
+import { Meta, Story } from '@storybook/addon-docs';
```
#### Deprecated layout URL params
@@ -5387,19 +5383,19 @@ Storybook 6.2 for Angular uses `parameters.component` as the preferred way to sp
Consider the existing story from 6.1 or earlier:
```ts
-export default { title: "Button" };
+export default { title: 'Button' };
export const Basic = () => ({
component: Button,
- props: { label: "Label" },
+ props: { label: 'Label' },
});
```
From 6.2 this should be rewritten as:
```ts
-export default { title: "Button", component: Button };
+export default { title: 'Button', component: Button };
export const Basic = () => ({
- props: { label: "Label" },
+ props: { label: 'Label' },
});
```
@@ -5463,9 +5459,9 @@ If you depend upon these plugins being applied, install them and create a `postc
```js
module.exports = {
plugins: [
- require("postcss-flexbugs-fixes"),
- require("autoprefixer")({
- flexbox: "no-2009",
+ require('postcss-flexbugs-fixes'),
+ require('autoprefixer')({
+ flexbox: 'no-2009',
}),
],
};
@@ -5519,10 +5515,10 @@ Storybook HTML components are now exported directly from '@storybook/components'
```js
// before
-import { components } from "@storybook/components/html";
+import { components } from '@storybook/components/html';
// after
-import { components } from "@storybook/components";
+import { components } from '@storybook/components';
```
## From version 6.0.x to 6.1.0
@@ -5535,8 +5531,8 @@ The addon uses decorators which are set up automatically by a preset. The requir
```js
module.exports = {
- stories: ["../**/*.stories.js"],
- addons: ["@storybook/addon-backgrounds/register"],
+ stories: ['../**/*.stories.js'],
+ addons: ['@storybook/addon-backgrounds/register'],
};
```
@@ -5544,8 +5540,8 @@ To fix it, just replace `@storybook/addon-backgrounds/register` with `@storybook
```js
module.exports = {
- stories: ["../**/*.stories.js"],
- addons: ["@storybook/addon-backgrounds"],
+ stories: ['../**/*.stories.js'],
+ addons: ['@storybook/addon-backgrounds'],
};
```
@@ -5596,14 +5592,14 @@ Each item in the story store contains a field called `storyFn`, which is a fully
Before:
```js
-const { storyFn } = store.fromId("some--id");
+const { storyFn } = store.fromId('some--id');
console.log(storyFn());
```
After:
```js
-const { unboundStoryFn, applyLoaders } = store.fromId("some--id");
+const { unboundStoryFn, applyLoaders } = store.fromId('some--id');
const context = await applyLoaders();
console.log(unboundStoryFn(context));
```
@@ -5752,17 +5748,13 @@ This breaking change only affects you if your stories actually use the context,
Consider the following story that uses the context:
```js
-export const Dummy = ({ parameters }) => (
-
{JSON.stringify(parameters)}
-);
+export const Dummy = ({ parameters }) =>
{JSON.stringify(parameters)}
;
```
Here's an updated story for 6.0 that ignores the args object:
```js
-export const Dummy = (_args, { parameters }) => (
-
;
```
Alternatively, if you want to opt out of the new behavior, you can add the following to your `.storybook/preview.js` config:
@@ -5826,7 +5818,7 @@ If you need to restore the old JSX handling behavior, you can configure `.storyb
module.exports = {
addons: [
{
- name: "@storybook/addon-docs",
+ name: '@storybook/addon-docs',
options: { configureJSX: false },
},
],
@@ -5843,7 +5835,7 @@ To restore the old behavior, or pass any MDX-specific babel options, you can con
module.exports = {
addons: [
{
- name: "@storybook/addon-docs",
+ name: '@storybook/addon-docs',
options: { mdxBabelOptions: { babelrc: true, configFile: true } },
},
],
@@ -5886,15 +5878,15 @@ Consider the following `main.js` config for `addon-knobs`:
```js
module.exports = {
- stories: ["../**/*.stories.js"],
- addons: ["@storybook/addon-knobs"],
+ stories: ['../**/*.stories.js'],
+ addons: ['@storybook/addon-knobs'],
};
```
In earlier versions of Storybook, this would automatically call `@storybook/addon-knobs/register`, which adds the knobs panel to the Storybook UI. As a user you would also add a decorator:
```js
-import { withKnobs } from "../index";
+import { withKnobs } from '../index';
addDecorator(withKnobs);
```
@@ -5905,8 +5897,8 @@ If you wish to disable this new behavior, you can modify your `main.js` to force
```js
module.exports = {
- stories: ["../**/*.stories.js"],
- addons: ["@storybook/addon-knobs/register"],
+ stories: ['../**/*.stories.js'],
+ addons: ['@storybook/addon-knobs/register'],
};
```
@@ -5965,7 +5957,7 @@ The `setStories`/`SET_STORIES` event has changed and now denormalizes global and
If you want the full denormalized parameters for a story, you can do something like:
```js
-import { combineParameters } from "@storybook/api";
+import { combineParameters } from '@storybook/api';
const story = data.stories[storyId];
const parameters = combineParameters(
@@ -5992,7 +5984,7 @@ npx sb@latest migrate upgrade-hierarchy-separators --glob="*/**/*.stories.@(tsx|
We also now default to showing "roots", which are non-expandable groupings in the sidebar for the top-level groups. If you'd like to disable this, set the `showRoots` option in `.storybook/manager.js`:
```js
-import { addons } from "@storybook/addons";
+import { addons } from '@storybook/addons';
addons.setConfig({
showRoots: false,
@@ -6090,7 +6082,7 @@ export const Example = () => ;
Example.story = {
parameters: {
storySource: {
- source: "custom source",
+ source: 'custom source',
},
},
};
@@ -6098,8 +6090,8 @@ Example.story = {
The MDX analog:
-```jsx
-
+```mdx
+
```
@@ -6115,13 +6107,13 @@ In 5.3, `addons` paths were relative to the project root, which was inconsistent
So, for example, if you had:
```js
-module.exports = { addons: ["./.storybook/my-local-addon/register"] };
+module.exports = { addons: ['./.storybook/my-local-addon/register'] };
```
You'd need to update this to:
```js
-module.exports = { addons: ["./my-local-addon/register"] };
+module.exports = { addons: ['./my-local-addon/register'] };
```
#### Deprecated setAddon
@@ -6157,7 +6149,6 @@ export StoryOne = ...;
StoryOne.story = {
decorators: [withActions('mouseover', 'click .btn')],
}
-
```
You should replace it with:
@@ -6182,7 +6173,7 @@ To configure a11y now, you have to specify configuration using story parameters,
```js
export const parameters = {
a11y: {
- element: "#storybook-root",
+ element: '#storybook-root',
config: {},
options: {},
manual: true,
@@ -6203,11 +6194,11 @@ Consider the following example of its usage in `Button.stories.js`:
```jsx
// Button.stories.js
export default {
- title: "Button",
+ title: 'Button',
parameters: {
backgrounds: [
- { name: "twitter", value: "#00aced", default: true },
- { name: "facebook", value: "#3b5998" },
+ { name: 'twitter', value: '#00aced', default: true },
+ { name: 'facebook', value: '#3b5998' },
],
},
};
@@ -6218,13 +6209,13 @@ Here's an updated version of the example, using the new api:
```jsx
// Button.stories.js
export default {
- title: "Button",
+ title: 'Button',
parameters: {
backgrounds: {
- default: "twitter",
+ default: 'twitter',
values: [
- { name: "twitter", value: "#00aced" },
- { name: "facebook", value: "#3b5998" },
+ { name: 'twitter', value: '#00aced' },
+ { name: 'facebook', value: '#3b5998' },
],
},
},
@@ -6261,7 +6252,7 @@ Remove the addon-centered decorator and instead add a `layout` parameter:
```js
export const MyStory = () =>
my story
;
MyStory.story = {
- parameters: { layout: "centered" },
+ parameters: { layout: 'centered' },
};
```
@@ -6279,7 +6270,7 @@ You should use `addon.setConfig` to set them:
```js
// in .storybook/manager.js
-import { addons } from "@storybook/addons";
+import { addons } from '@storybook/addons';
addons.setConfig({
showRoots: false,
@@ -6305,7 +6296,7 @@ To load stories, use the `stories` field in `main.js`. You can pass a glob or ar
```js
// in .storybook/main.js
module.exports = {
- stories: ["../src/**/*.stories.js"],
+ stories: ['../src/**/*.stories.js'],
};
```
@@ -6315,9 +6306,9 @@ You can also pass an array of single file names if you want to be careful about
// in .storybook/main.js
module.exports = {
stories: [
- "../src/components/Button.stories.js",
- "../src/components/Table.stories.js",
- "../src/components/Page.stories.js",
+ '../src/components/Button.stories.js',
+ '../src/components/Table.stories.js',
+ '../src/components/Page.stories.js',
],
};
```
@@ -6363,8 +6354,8 @@ These files are now soft-deprecated, (_they still work, but over time we will pr
```js
module.exports = {
- stories: ["../**/*.stories.js"],
- addons: ["@storybook/addon-knobs"],
+ stories: ['../**/*.stories.js'],
+ addons: ['@storybook/addon-knobs'],
};
```
@@ -6376,11 +6367,11 @@ If you had a `presets.js` file before you can add the array of presets to the ma
```js
module.exports = {
- stories: ["../**/*.stories.js"],
+ stories: ['../**/*.stories.js'],
addons: [
- "@storybook/preset-create-react-app",
+ '@storybook/preset-create-react-app',
{
- name: "@storybook/addon-docs",
+ name: '@storybook/addon-docs',
options: { configureJSX: true },
},
],
@@ -6404,16 +6395,16 @@ This file can also be used to inject global stylesheets, fonts etc, into the pre
If you are setting storybook options in `config.js`, especially `theme`, you should migrate it to `manager.js`:
```js
-import { addons } from "@storybook/addons";
-import { create } from "@storybook/theming/create";
+import { addons } from '@storybook/addons';
+import { create } from '@storybook/theming/create';
const theme = create({
- base: "light",
- brandTitle: "My custom title",
+ base: 'light',
+ brandTitle: 'My custom title',
});
addons.setConfig({
- panelPosition: "bottom",
+ panelPosition: 'bottom',
theme,
});
```
@@ -6522,7 +6513,7 @@ The default viewports have been reduced to a smaller set, we think is enough for
You can get the old default back by adding the following to your `config.js`:
```js
-import { INITIAL_VIEWPORTS } from "@storybook/addon-viewport";
+import { INITIAL_VIEWPORTS } from '@storybook/addon-viewport';
addParameters({
viewport: {
@@ -6552,9 +6543,7 @@ For example, here's how to sort by story ID using `storySort`:
addParameters({
options: {
storySort: (a, b) =>
- a[1].kind === b[1].kind
- ? 0
- : a[1].id.localeCompare(b[1].id, undefined, { numeric: true }),
+ a[1].kind === b[1].kind ? 0 : a[1].id.localeCompare(b[1].id, undefined, { numeric: true }),
},
});
```
@@ -6600,9 +6589,7 @@ Storybook 5.1 relies on `core-js@^3.0.0` and therefore causes a conflict with An
{
"compilerOptions": {
"paths": {
- "core-js/es7/reflect": [
- "node_modules/core-js/proposals/reflect-metadata"
- ],
+ "core-js/es7/reflect": ["node_modules/core-js/proposals/reflect-metadata"],
"core-js/es6/*": ["node_modules/core-js/es"]
}
}
@@ -6677,7 +6664,7 @@ We're working on providing a better way of sorting stories for now the feature h
If you're using webpack's `require.context` to load stories, you can sort the execution of requires:
```js
-var context = require.context("../stories", true, /\.stories\.js$/);
+var context = require.context('../stories', true, /\.stories\.js$/);
var modules = context.keys();
// sort them
@@ -6747,8 +6734,8 @@ Here's an old configuration:
```js
addDecorator(
withOptions({
- name: "Storybook",
- url: "https://storybook.js.org",
+ name: 'Storybook',
+ url: 'https://storybook.js.org',
goFullScreen: false,
addonPanelInRight: true,
})
@@ -6758,18 +6745,19 @@ addDecorator(
And here's its new counterpart:
```js
-import { create } from "@storybook/theming/create";
+import { create } from '@storybook/theming/create';
+
addParameters({
options: {
theme: create({
- base: "light",
- brandTitle: "Storybook",
- brandUrl: "https://storybook.js.org",
+ base: 'light',
+ brandTitle: 'Storybook',
+ brandUrl: 'https://storybook.js.org',
// To control appearance:
// brandImage: 'http://url.of/some.svg',
}),
isFullscreen: false,
- panelPosition: "right",
+ panelPosition: 'right',
isToolshown: true,
},
});
@@ -6799,10 +6787,10 @@ The behavior of adding decorators to a kind has changed in SB5 ([#5781](https://
In SB4 it was possible to add decorators to only a subset of the stories of a kind.
```js
-storiesOf("Stories", module)
- .add("noncentered", () => "Hello")
+storiesOf('Stories', module)
+ .add('noncentered', () => 'Hello')
.addDecorator(centered)
- .add("centered", () => "Hello");
+ .add('centered', () => 'Hello');
```
The semantics has changed in SB5 so that calling `addDecorator` on a kind adds a decorator to all its stories, no matter the order. So in the previous example, both stories would be centered.
@@ -6810,9 +6798,9 @@ The semantics has changed in SB5 so that calling `addDecorator` on a kind adds a
To allow for a subset of the stories in a kind to be decorated, we've added the ability to add decorators to individual stories using parameters:
```js
-storiesOf("Stories", module)
- .add("noncentered", () => "Hello")
- .add("centered", () => "Hello", { decorators: [centered] });
+storiesOf('Stories', module)
+ .add('noncentered', () => 'Hello')
+ .add('centered', () => 'Hello', { decorators: [centered] });
```
### Addon backgrounds uses parameters
@@ -6829,7 +6817,7 @@ storiesOf('Stories', module)
You should replace it with:
```js
-storiesOf("Stories", module).addParameters({ backgrounds: options });
+storiesOf('Stories', module).addParameters({ backgrounds: options });
```
You can pass `backgrounds` parameters at the global level (via `addParameters` imported from `@storybook/react` et al.), and the story level (via the third argument to `.add()`).
@@ -6839,8 +6827,8 @@ You can pass `backgrounds` parameters at the global level (via `addParameters` i
In the options object for `@storybook/addon-cssresources`, the `name` attribute for each resource has been renamed to `id`. If you previously had:
```js
-import { withCssResources } from "@storybook/addon-cssresources";
-import { addDecorator } from "@storybook/react";
+import { addDecorator } from '@storybook/react';
+import { withCssResources } from '@storybook/addon-cssresources';
addDecorator(
withCssResources({
@@ -6858,8 +6846,8 @@ addDecorator(
You should replace it with:
```js
-import { withCssResources } from "@storybook/addon-cssresources";
-import { addDecorator } from "@storybook/react";
+import { addDecorator } from '@storybook/react';
+import { withCssResources } from '@storybook/addon-cssresources';
addDecorator(
withCssResources({
@@ -6887,7 +6875,9 @@ configureViewport(options);
You should replace it with:
```js
-import { addParameters } from "@storybook/react"; // or others
+import { addParameters } from '@storybook/react';
+
+// or others
addParameters({ viewport: options });
```
@@ -6909,7 +6899,9 @@ configureA11y(options);
You should replace it with:
```js
-import { addParameters } from "@storybook/react"; // or others
+import { addParameters } from '@storybook/react';
+
+// or others
addParameters({ a11y: options });
```
@@ -6925,19 +6917,19 @@ See the [a11y addon README](https://github.com/storybookjs/storybook/blob/master
If you previously had:
```js
-import centered from "@storybook/addon-centered";
+import centered from '@storybook/addon-centered';
```
You should replace it with the React or Vue version as appropriate
```js
-import centered from "@storybook/addon-centered/react";
+import centered from '@storybook/addon-centered/react';
```
or
```js
-import centered from "@storybook/addon-centered/vue";
+import centered from '@storybook/addon-centered/vue';
```
### New keyboard shortcuts defaults
@@ -7044,13 +7036,13 @@ at Object../node_modules/@storybook/components/dist/navigation/MenuLink.js (Menu
4.x introduces generic addon decorators that are not tied to specific view layers [#3555](https://github.com/storybookjs/storybook/pull/3555). So for example:
```js
-import { number } from "@storybook/addon-knobs/react";
+import { number } from '@storybook/addon-knobs/react';
```
Becomes:
```js
-import { number } from "@storybook/addon-knobs";
+import { number } from '@storybook/addon-knobs';
```
### Knobs select ordering
@@ -7222,7 +7214,7 @@ Knobs users: there was a bug in 3.2.x where using the knobs addon imported all f
In the case of React or React-Native, import knobs like this:
```js
-import { withKnobs, text, boolean, number } from "@storybook/addon-knobs/react";
+import { withKnobs, text, boolean, number } from '@storybook/addon-knobs/react';
```
In the case of Vue: `import { ... } from '@storybook/addon-knobs/vue';`
@@ -7250,10 +7242,10 @@ We're in the process of upgrading our addons APIs. As a first step, we've upgrad
Here's an example of using Notes and Info in 3.2 with the new API.
```js
-storiesOf("composition", module).add(
- "new addons api",
- withInfo("see Notes panel for composition info")(
- withNotes({ text: "Composition: Info(Notes())" })((context) => (
+storiesOf('composition', module).add(
+ 'new addons api',
+ withInfo('see Notes panel for composition info')(
+ withNotes({ text: 'Composition: Info(Notes())' })((context) => (
))
)
@@ -7365,17 +7357,17 @@ If you **are** using these addons, it takes two steps to migrate:
change `addons.js` like so:
```js
- import "@storybook/addon-actions/register";
- import "@storybook/addon-links/register";
+ import '@storybook/addon-actions/register';
+ import '@storybook/addon-links/register';
```
change `x.story.js` like so:
```js
- import React from "react";
- import { storiesOf } from "@storybook/react";
- import { action } from "@storybook/addon-actions";
- import { linkTo } from "@storybook/addon-links";
+ import React from 'react';
+ import { storiesOf } from '@storybook/react';
+ import { action } from '@storybook/addon-actions';
+ import { linkTo } from '@storybook/addon-links';
```
diff --git a/code/.storybook/preview.tsx b/code/.storybook/preview.tsx
index 1fc58c65300d..adc5556ffb0c 100644
--- a/code/.storybook/preview.tsx
+++ b/code/.storybook/preview.tsx
@@ -349,7 +349,6 @@ const parameters = {
plugins: [prettierPluginBabel, prettierPluginEstree],
});
} catch (error) {
- console.error(error);
return source;
}
},
diff --git a/code/addons/docs/src/blocks/components/ArgsTable/ArgRow.stories.tsx b/code/addons/docs/src/blocks/components/ArgsTable/ArgRow.stories.tsx
index 8f8e5d1ad51a..203085aa8fe5 100644
--- a/code/addons/docs/src/blocks/components/ArgsTable/ArgRow.stories.tsx
+++ b/code/addons/docs/src/blocks/components/ArgsTable/ArgRow.stories.tsx
@@ -2,6 +2,10 @@ import React from 'react';
import { ResetWrapper } from 'storybook/internal/components';
+import type { Meta, StoryObj } from '@storybook/react-vite';
+
+import { expect, screen } from 'storybook/test';
+
import { ArgRow } from './ArgRow';
import { TableWrapper } from './ArgsTable';
@@ -10,7 +14,7 @@ export default {
title: 'Components/ArgsTable/ArgRow',
decorators: [
- (getStory: any) => (
+ (getStory) => (
{getStory()}
@@ -21,7 +25,7 @@ export default {
argTypes: {
updateArgs: { action: 'updateArgs' },
},
-};
+} satisfies Meta;
export const String = {
args: {
@@ -309,6 +313,52 @@ export const Func = {
},
};
+export const LongFunc = {
+ args: {
+ row: {
+ ...Func.args.row,
+ table: {
+ ...Func.args.row.table,
+ defaultValue: {
+ summary: 'func',
+ detail: `(a, b) => {
+ // Calculate various metrics between a and b
+ const lengthA = a.length;
+ const lengthB = b.length;
+
+ // Determine the longer string
+ const maxLength = Math.max(lengthA, lengthB);
+ const minLength = Math.min(lengthA, lengthB);
+
+ // Calculate similarity score
+ let matchCount = 0;
+ for (let i = 0; i < minLength; i++) {
+ if (a[i] === b[i]) {
+ matchCount++;
+ }
+ }
+
+ // Compute weighted average
+ const similarity = (matchCount / maxLength) * 100;
+
+ // Generate result string
+ const result = \`Similarity: \${similarity.toFixed(2)}%, Length diff: \${Math.abs(lengthA - lengthB)}\`;
+
+ return result;
+}`,
+ },
+ },
+ },
+ },
+ play: async ({ canvas }) => {
+ const funcButton = canvas.getByRole('button', { name: 'func' });
+ funcButton.click();
+ expect(
+ await screen.findByText(/Calculate various metrics between a and b/)
+ ).toBeInTheDocument();
+ },
+} satisfies StoryObj;
+
const enumeration =
'"search" | "arrow-to-bottom" | "arrow-to-right" | "bell" | "check" | "check-circle"';
export const Enum = {
@@ -345,7 +395,20 @@ export const LongEnum = {
},
},
},
-};
+ play: async ({ canvas, step }) => {
+ await step('Expand long enum', async () => {
+ canvas.getByRole('button', { name: 'Show 26 more...' }).click();
+ expect(await canvas.findByText(/exclamation-triangle/)).toBeVisible();
+ });
+ await step('Collapse long enum', async () => {
+ (await canvas.findByRole('button', { name: 'Show less...' })).click();
+ expect(await canvas.findByText(/exclamation-triangle/)).not.toBeVisible();
+ });
+ await step('Re-expand for visual test', async () => {
+ (await canvas.findByRole('button', { name: 'Show 26 more...' })).click();
+ });
+ },
+} satisfies StoryObj;
const complexUnion =
'((a: string | SVGSVGElement) => void) | RefObject | [a|b] | {a|b}';
diff --git a/code/addons/docs/src/blocks/components/ArgsTable/ArgValue.tsx b/code/addons/docs/src/blocks/components/ArgsTable/ArgValue.tsx
index f257fcad3479..1be3d2075744 100644
--- a/code/addons/docs/src/blocks/components/ArgsTable/ArgValue.tsx
+++ b/code/addons/docs/src/blocks/components/ArgsTable/ArgValue.tsx
@@ -1,12 +1,11 @@
import type { FC } from 'react';
import React, { useState } from 'react';
-import { SyntaxHighlighter, TooltipProvider, codeCommon } from 'storybook/internal/components';
+import { PopoverProvider, SyntaxHighlighter, codeCommon } from 'storybook/internal/components';
import { ChevronSmallDownIcon, ChevronSmallUpIcon } from '@storybook/icons';
import { uniq } from 'es-toolkit/array';
-import memoize from 'memoizerific';
import { styled } from 'storybook/theming';
import type { PropSummaryValue } from './types';
@@ -37,6 +36,27 @@ const Summary = styled.div<{ isExpanded?: boolean }>(({ isExpanded }) => ({
minWidth: 100,
}));
+const DetailsContainer = styled.details({
+ display: 'flex',
+ flexDirection: 'column',
+ summary: {
+ order: 2,
+ },
+ 'summary::-webkit-details-marker': {
+ display: 'none',
+ },
+ 'summary::marker': {
+ content: 'none',
+ },
+});
+
+const AlignedDetails = styled.div({
+ display: 'flex',
+ flexDirection: 'column',
+ flexWrap: 'wrap',
+ alignItems: 'flex-start',
+});
+
const Text = styled.span<{ simple?: boolean }>(codeCommon, ({ theme, simple = false }) => ({
flex: '0 0 auto',
fontFamily: theme.typography.fonts.mono,
@@ -57,15 +77,18 @@ const Text = styled.span<{ simple?: boolean }>(codeCommon, ({ theme, simple = fa
}),
}));
-const ExpandButton = styled.button(({ theme }) => ({
+const ExpandButton = styled.summary(({ theme }) => ({
fontFamily: theme.typography.fonts.mono,
color: theme.color.secondary,
- marginBottom: '4px',
+ cursor: 'pointer',
+ lineHeight: 'normal',
+ margin: '0 0 4px',
+ padding: '1px 6px',
background: 'none',
border: 'none',
}));
-const Expandable = styled.div(codeCommon, ({ theme }) => ({
+const Expandable = styled.button(codeCommon, ({ theme }) => ({
fontFamily: theme.typography.fonts.mono,
color: theme.color.secondary,
fontSize: theme.typography.size.s1, // overrides codeCommon
@@ -73,13 +96,21 @@ const Expandable = styled.div(codeCommon, ({ theme }) => ({
whiteSpace: 'nowrap',
display: 'flex',
alignItems: 'center',
+ cursor: 'pointer',
+ '&:hover': {
+ border:
+ theme.base === 'light' ? '1px solid hsl(0 0 0 / 0.15)' : '1px solid hsl(0 0 100 / 0.15)',
+ },
+ '&:focus-visible': {
+ outline: `2px solid ${theme.color.secondary}`,
+ outlineOffset: '2px',
+ },
}));
-const Detail = styled.div<{ width: string }>(({ theme, width }) => ({
- width,
+const Detail = styled.div(({ theme }) => ({
minWidth: 200,
maxWidth: 800,
- padding: 15,
+ paddingRight: 16,
// Don't remove the mono fontFamily here even if it seems useless, this is used by the browser to calculate the length of a "ch" unit.
fontFamily: theme.typography.fonts.mono,
fontSize: theme.typography.size.s1,
@@ -107,12 +138,6 @@ const ArgText: FC = ({ text, simple }) => {
return {text};
};
-const calculateDetailWidth = memoize(1000)((detail: string): string => {
- const lines = detail.split(/\r?\n/);
-
- return `${Math.max(...lines.map((x) => x.length))}ch`;
-});
-
const getSummaryItems = (summary: string) => {
if (!summary) {
return [summary];
@@ -123,13 +148,16 @@ const getSummaryItems = (summary: string) => {
return uniq(summaryItems);
};
-const renderSummaryItems = (summaryItems: string[], isExpanded = true) => {
- let items = summaryItems;
- if (!isExpanded) {
- items = summaryItems.slice(0, ITEMS_BEFORE_EXPANSION);
- }
+const renderSummaryItems = (summaryItems: string[]) => {
+ return summaryItems
+ .slice(0, ITEMS_BEFORE_EXPANSION)
+ .map((item) => );
+};
- return items.map((item) => );
+const renderExpandedItems = (summaryItems: string[]) => {
+ return summaryItems
+ .slice(ITEMS_BEFORE_EXPANSION)
+ .map((item) => );
};
const ArgSummary: FC = ({ value, initialExpandedArgs }) => {
@@ -160,10 +188,13 @@ const ArgSummary: FC = ({ value, initialExpandedArgs }) => {
return hasManyItems ? (
- {renderSummaryItems(summaryItems, isExpanded)}
- setIsExpanded(!isExpanded)}>
- {isExpanded ? 'Show less...' : `Show ${itemsCount - ITEMS_BEFORE_EXPANSION} more...`}
-
+ {renderSummaryItems(summaryItems)}
+ setIsExpanded(e.currentTarget.open)}>
+ {renderExpandedItems(summaryItems)}
+
+ {isExpanded ? 'Show less...' : `Show ${itemsCount - ITEMS_BEFORE_EXPANSION} more...`}
+
+
) : (
{renderSummaryItems(summaryItems)}
@@ -171,14 +202,15 @@ const ArgSummary: FC = ({ value, initialExpandedArgs }) => {
}
return (
- {
setIsOpen(isVisible);
}}
- tooltip={
-
+ hasCloseButton
+ popover={
+
{detail}
@@ -189,7 +221,7 @@ const ArgSummary: FC = ({ value, initialExpandedArgs }) => {
{summaryAsString}
{isOpen ? : }
-
+
);
};
diff --git a/code/addons/docs/src/blocks/controls/Object.tsx b/code/addons/docs/src/blocks/controls/Object.tsx
index 4ef8ba15cde2..a817cd1e368a 100644
--- a/code/addons/docs/src/blocks/controls/Object.tsx
+++ b/code/addons/docs/src/blocks/controls/Object.tsx
@@ -207,6 +207,11 @@ export const ObjectControl: FC = ({ name, value, onChange, argType
}
}, [forceVisible]);
+ // Use string value as key to force re-render on Arg value reset.
+ const jsonString = useMemo(() => {
+ return JSON.stringify(data ?? '', null, 2);
+ }, [data]);
+
if (!hasData) {
return (