diff --git a/CHANGELOG.md b/CHANGELOG.md index 38e3d961b3c3..ee61820d6939 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## 7.6.10 + +- CLI: Fix existing version detection in `upgrade` - [#25642](https://github.com/storybookjs/storybook/pull/25642), thanks [@JReinhold](https://github.com/JReinhold)! +- React: Fix acorn ecma version warning - [#25634](https://github.com/storybookjs/storybook/pull/25634), thanks [@dannyhw](https://github.com/dannyhw)! + ## 7.6.9 - ConfigFile: Fix export specifiers - [#25590](https://github.com/storybookjs/storybook/pull/25590), thanks [@shilman](https://github.com/shilman)! diff --git a/CHANGELOG.prerelease.md b/CHANGELOG.prerelease.md index db7bfa975429..9458340d6ff9 100644 --- a/CHANGELOG.prerelease.md +++ b/CHANGELOG.prerelease.md @@ -1,3 +1,16 @@ +## 8.0.0-alpha.12 + +- Blocks: Fix Controls block not having controls - [#25663](https://github.com/storybookjs/storybook/pull/25663), thanks [@JReinhold](https://github.com/JReinhold)! +- Blocks: Support `subcomponents` in `ArgTypes` and `Controls`, remove `ArgsTable` block - [#25614](https://github.com/storybookjs/storybook/pull/25614), thanks [@JReinhold](https://github.com/JReinhold)! +- CLI: Fix existing version detection in `upgrade` - [#25642](https://github.com/storybookjs/storybook/pull/25642), thanks [@JReinhold](https://github.com/JReinhold)! +- Core: Add preset with experimental server API - [#25664](https://github.com/storybookjs/storybook/pull/25664), thanks [@shilman](https://github.com/shilman)! +- MDX: Replace remark by rehype plugins - [#25615](https://github.com/storybookjs/storybook/pull/25615), thanks [@valentinpalkovic](https://github.com/valentinpalkovic)! +- React: Fix acorn ecma version warning - [#25634](https://github.com/storybookjs/storybook/pull/25634), thanks [@dannyhw](https://github.com/dannyhw)! +- Shortcuts: Require modifier key to trigger shortcuts (`F`,`A`,`D`,`S`,`T`,`/`) - [#25625](https://github.com/storybookjs/storybook/pull/25625), thanks [@cdedreuille](https://github.com/cdedreuille)! +- Theming: Fix export of module augmentation - [#25643](https://github.com/storybookjs/storybook/pull/25643), thanks [@valentinpalkovic](https://github.com/valentinpalkovic)! +- UI: Add links to documentation and videos in UI - [#25565](https://github.com/storybookjs/storybook/pull/25565), thanks [@Integrayshaun](https://github.com/Integrayshaun)! +- Webpack: Use `node:assert` used in `export-order-loader` - [#25622](https://github.com/storybookjs/storybook/pull/25622), thanks [@JReinhold](https://github.com/JReinhold)! + ## 8.0.0-alpha.11 - Angular: Remove cached NgModules and introduce a global queue during bootstrapping - [#24982](https://github.com/storybookjs/storybook/pull/24982), thanks [@Marklb](https://github.com/Marklb)! diff --git a/MIGRATION.md b/MIGRATION.md index e60f5779db7c..3a1b66b23c5b 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -1,6 +1,7 @@

Migration

- [From version 7.x to 8.0.0](#from-version-7x-to-800) + - [Default keyboard shortcuts changed](#default-keyboard-shortcuts-changed) - [Manager addons are now rendered with React 18](#manager-addons-are-now-rendered-with-react-18) - [Removal of `storiesOf`-API](#removal-of-storiesof-api) - [Removed deprecated shim packages](#removed-deprecated-shim-packages) @@ -62,25 +63,26 @@ - [Description Doc block properties](#description-doc-block-properties) - [Story Doc block properties](#story-doc-block-properties) - [Manager API expandAll and collapseAll methods](#manager-api-expandall-and-collapseall-methods) - - [Source Doc block properties](#source-doc-block-properties) - - [Canvas Doc block properties](#canvas-doc-block-properties) + - [`ArgsTable` Doc block removed](#argstable-doc-block-removed) + - [`Source` Doc block properties](#source-doc-block-properties) + - [`Canvas` Doc block properties](#canvas-doc-block-properties) - [`Primary` Doc block properties](#primary-doc-block-properties) - [`createChannel` from `@storybook/postmessage` and `@storybook/channel-websocket`](#createchannel-from-storybookpostmessage-and-storybookchannel-websocket) - [StoryStore and methods deprecated](#storystore-and-methods-deprecated) - [Addon author changes](#addon-author-changes) - [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) @@ -384,6 +386,10 @@ ## From version 7.x to 8.0.0 +### Default keyboard shortcuts changed + +The default keyboard shortcuts have changed to avoid any conflicts with the browser's default shortcuts or when you are directly typing in the Manager. If you want to get the new default shortcuts, you can reset your shortcuts in the keyboard shortcuts panel by pressing the `Restore default` button. + ### Manager addons are now rendered with React 18 The UI added to the manager via addons is now rendered with React 18. @@ -1002,11 +1008,17 @@ api.collapseAll(); // becomes api.emit(STORIES_COLLAPSE_ALL) api.expandAll(); // becomes api.emit(STORIES_EXPAND_ALL) ``` -#### Source Doc block properties +#### `ArgsTable` Doc block removed + +The `ArgsTable` doc block has been removed in favor of `ArgTypes` and `Controls`. [More info](#argstable-block). + +With this removal we've reintroduced `subcomponents` support to `ArgTypes`, `Controls`, and autodocs. We've also undeprecated `subcomponents`, by popular demand. + +#### `Source` Doc block properties `id` and `ids` are now removed in favor of the `of` property. [More info](#doc-blocks). -#### Canvas Doc block properties +#### `Canvas` Doc block properties The following properties were removed from the Canvas Doc block: diff --git a/code/addons/docs/package.json b/code/addons/docs/package.json index 38586865a50e..503dcd44db84 100644 --- a/code/addons/docs/package.json +++ b/code/addons/docs/package.json @@ -114,8 +114,8 @@ "fs-extra": "^11.1.0", "react": "^18.2.0", "react-dom": "^18.2.0", - "remark-external-links": "^8.0.0", - "remark-slug": "^6.0.0", + "rehype-external-links": "^3.0.0", + "rehype-slug": "^6.0.0", "ts-dedent": "^2.0.0" }, "devDependencies": { diff --git a/code/addons/docs/src/plugins/mdx-plugin.ts b/code/addons/docs/src/plugins/mdx-plugin.ts index c011a9202fb7..a75194fa7309 100644 --- a/code/addons/docs/src/plugins/mdx-plugin.ts +++ b/code/addons/docs/src/plugins/mdx-plugin.ts @@ -1,10 +1,10 @@ import type { Options } from '@storybook/types'; import type { Plugin } from 'vite'; -import remarkSlug from 'remark-slug'; -import remarkExternalLinks from 'remark-external-links'; +import rehypeSlug from 'rehype-slug'; +import rehypeExternalLinks from 'rehype-external-links'; import { createFilter } from '@rollup/pluginutils'; import { dirname, join } from 'path'; - +import type { CompileOptions } from '../compiler'; import { compile } from '../compiler'; /** @@ -18,7 +18,8 @@ export async function mdxPlugin(options: Options): Promise { const include = /\.mdx$/; const filter = createFilter(include); const { presets } = options; - const { mdxPluginOptions } = await presets.apply>('options', {}); + const presetOptions = await presets.apply>('options', {}); + const mdxPluginOptions = presetOptions?.mdxPluginOptions as CompileOptions; return { name: 'storybook:mdx-plugin', @@ -26,7 +27,7 @@ export async function mdxPlugin(options: Options): Promise { async transform(src, id) { if (!filter(id)) return undefined; - const mdxLoaderOptions = await options.presets.apply('mdxLoaderOptions', { + const mdxLoaderOptions: CompileOptions = await presets.apply('mdxLoaderOptions', { ...mdxPluginOptions, mdxCompileOptions: { providerImportSource: join( @@ -34,17 +35,15 @@ export async function mdxPlugin(options: Options): Promise { '/dist/shims/mdx-react-shim' ), ...mdxPluginOptions?.mdxCompileOptions, - remarkPlugins: [remarkSlug, remarkExternalLinks].concat( - mdxPluginOptions?.mdxCompileOptions?.remarkPlugins ?? [] - ), + rehypePlugins: [ + ...(mdxPluginOptions?.mdxCompileOptions?.rehypePlugins ?? []), + rehypeSlug, + rehypeExternalLinks, + ], }, }); - const code = String( - await compile(src, { - ...mdxLoaderOptions, - }) - ); + const code = String(await compile(src, mdxLoaderOptions)); return { code, diff --git a/code/addons/docs/src/preset.ts b/code/addons/docs/src/preset.ts index 8d623fe5b97f..ae79832240d1 100644 --- a/code/addons/docs/src/preset.ts +++ b/code/addons/docs/src/preset.ts @@ -1,6 +1,6 @@ import { dirname, join } from 'path'; -import remarkSlug from 'remark-slug'; -import remarkExternalLinks from 'remark-external-links'; +import rehypeSlug from 'rehype-slug'; +import rehypeExternalLinks from 'rehype-external-links'; import type { DocsOptions, Options, PresetProperty } from '@storybook/types'; import type { CsfPluginOptions } from '@storybook/csf-plugin'; @@ -48,9 +48,11 @@ async function webpack( '/dist/shims/mdx-react-shim' ), ...mdxPluginOptions.mdxCompileOptions, - remarkPlugins: [remarkSlug, remarkExternalLinks].concat( - mdxPluginOptions?.mdxCompileOptions?.remarkPlugins ?? [] - ), + rehypePlugins: [ + ...(mdxPluginOptions?.mdxCompileOptions?.rehypePlugins ?? []), + rehypeSlug, + rehypeExternalLinks, + ], }, }); diff --git a/code/addons/docs/src/typings.d.ts b/code/addons/docs/src/typings.d.ts index a3efeb653c83..7349466f2a65 100644 --- a/code/addons/docs/src/typings.d.ts +++ b/code/addons/docs/src/typings.d.ts @@ -1,6 +1,4 @@ declare module '@egoist/vue-to-react'; -declare module 'remark-slug'; -declare module 'remark-external-links'; declare module 'acorn-jsx'; declare module 'vue/dist/vue'; diff --git a/code/addons/docs/template/stories/docspage/description.stories.ts b/code/addons/docs/template/stories/docspage/description.stories.ts index f42edc5901c3..e6a2eac1587f 100644 --- a/code/addons/docs/template/stories/docspage/description.stories.ts +++ b/code/addons/docs/template/stories/docspage/description.stories.ts @@ -2,7 +2,6 @@ import { global as globalThis } from '@storybook/global'; export default { component: globalThis.Components.Button, - // FIXME: remove array subcomponents in 7.0? subcomponents: { Pre: globalThis.Components.Pre, }, diff --git a/code/addons/interactions/src/components/EmptyState.tsx b/code/addons/interactions/src/components/EmptyState.tsx new file mode 100644 index 000000000000..d4fa62c144a4 --- /dev/null +++ b/code/addons/interactions/src/components/EmptyState.tsx @@ -0,0 +1,99 @@ +import React, { useEffect, useState } from 'react'; +import { Link } from '@storybook/components'; +import { DocumentIcon, VideoIcon } from '@storybook/icons'; +import { Consumer, useStorybookApi } from '@storybook/manager-api'; +import { styled } from '@storybook/theming'; + +import { DOCUMENTATION_LINK, TUTORIAL_VIDEO_LINK } from '../constants'; + +const Wrapper = styled.div(({ theme }) => ({ + height: '100%', + display: 'flex', + padding: 0, + alignItems: 'center', + justifyContent: 'center', + flexDirection: 'column', + gap: 15, + background: theme.background.content, +})); + +const Content = styled.div({ + display: 'flex', + flexDirection: 'column', + gap: 4, + maxWidth: 415, +}); + +const Title = styled.div(({ theme }) => ({ + fontWeight: theme.typography.weight.bold, + fontSize: theme.typography.size.s2 - 1, + textAlign: 'center', + color: theme.textColor, +})); + +const Description = styled.div(({ theme }) => ({ + fontWeight: theme.typography.weight.regular, + fontSize: theme.typography.size.s2 - 1, + textAlign: 'center', + color: theme.textMutedColor, +})); + +const Links = styled.div(({ theme }) => ({ + display: 'flex', + fontSize: theme.typography.size.s2 - 1, + gap: 25, +})); + +const Divider = styled.div(({ theme }) => ({ + width: 1, + height: 16, + backgroundColor: theme.appBorderColor, +})); + +export const Empty = () => { + const [isLoading, setIsLoading] = useState(true); + const api = useStorybookApi(); + const docsUrl = api.getDocsUrl({ + subpath: DOCUMENTATION_LINK, + versioned: true, + renderer: true, + }); + + // We are adding a small delay to avoid flickering when the story is loading. + // It takes a bit of time for the controls to appear, so we don't want + // to show the empty state for a split second. + useEffect(() => { + const load = setTimeout(() => { + setIsLoading(false); + }, 100); + + return () => clearTimeout(load); + }, []); + + if (isLoading) return null; + + return ( + + + Interaction testing + + Interaction tests allow you to verify the functional aspects of UIs. Write a play function + for your story and you'll see it run here. + + + + + Watch 8m video + + + + {({ state }) => ( + + Read docs + + )} + + + + ); +}; diff --git a/code/addons/interactions/src/components/InteractionsPanel.tsx b/code/addons/interactions/src/components/InteractionsPanel.tsx index 0663656cc14b..a5b58d94b680 100644 --- a/code/addons/interactions/src/components/InteractionsPanel.tsx +++ b/code/addons/interactions/src/components/InteractionsPanel.tsx @@ -1,5 +1,4 @@ import * as React from 'react'; -import { Link, Placeholder } from '@storybook/components'; import { type Call, CallStates, type ControlStates } from '@storybook/instrumenter'; import { styled } from '@storybook/theming'; import { transparentize } from 'polished'; @@ -8,6 +7,7 @@ import { Subnav } from './Subnav'; import { Interaction } from './Interaction'; import { isTestAssertionError } from '../utils'; +import { Empty } from './EmptyState'; export interface Controls { start: (args: any) => void; @@ -40,7 +40,7 @@ interface InteractionsPanelProps { } const Container = styled.div(({ theme }) => ({ - minHeight: '100%', + height: '100%', background: theme.background.content, })); @@ -153,18 +153,7 @@ export const InteractionsPanel: React.FC = React.memo( )}
- {!isPlaying && !caughtException && interactions.length === 0 && ( - - No interactions found - - Learn how to add interactions to your story - - - )} + {!isPlaying && !caughtException && interactions.length === 0 && } ); } diff --git a/code/addons/interactions/src/constants.ts b/code/addons/interactions/src/constants.ts index 3c723e93852c..1b70ededc7c4 100644 --- a/code/addons/interactions/src/constants.ts +++ b/code/addons/interactions/src/constants.ts @@ -1,2 +1,5 @@ export const ADDON_ID = 'storybook/interactions'; export const PANEL_ID = `${ADDON_ID}/panel`; + +export const TUTORIAL_VIDEO_LINK = 'https://youtu.be/Waht9qq7AoA'; +export const DOCUMENTATION_LINK = 'writing-tests/interaction-testing'; diff --git a/code/addons/outline/src/OutlineSelector.tsx b/code/addons/outline/src/OutlineSelector.tsx index 012477bae60c..10d5eec131f6 100644 --- a/code/addons/outline/src/OutlineSelector.tsx +++ b/code/addons/outline/src/OutlineSelector.tsx @@ -20,8 +20,8 @@ export const OutlineSelector = memo(function OutlineSelector() { useEffect(() => { api.setAddonShortcut(ADDON_ID, { - label: 'Toggle Outline [O]', - defaultShortcut: ['O'], + label: 'Toggle Outline', + defaultShortcut: ['alt', 'O'], actionName: 'outline', showInMenu: false, action: toggleOutline, diff --git a/code/addons/themes/docs/getting-started/bootstrap.md b/code/addons/themes/docs/getting-started/bootstrap.md index a5070d6a3032..fd0a59d6fdce 100644 --- a/code/addons/themes/docs/getting-started/bootstrap.md +++ b/code/addons/themes/docs/getting-started/bootstrap.md @@ -4,7 +4,7 @@ -To get started, **install the package** as a dev dependency +To get started, **install the package** as a dev dependency. yarn: @@ -29,14 +29,11 @@ pnpm add -D @storybook/addon-themes Now, **include the addon** in your `.storybook/main.js` file. ```diff -module.exports = { - stories: [ - "../stories/**/*.stories.mdx", - "../stories/**/*.stories.@(js|jsx|ts|tsx)", - ], +export default { + stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'], addons: [ - "@storybook/addon-essentials", -+ "@storybook/addon-themes" + '@storybook/addon-essentials', ++ '@storybook/addon-themes', ], }; ``` @@ -46,10 +43,10 @@ module.exports = { To give your stories access to Bootstrap's styles and JavaScript, import them into your `.storybook/preview.js` file. ```diff -import { Preview } from "@storybook/your-renderer"; +import { Preview } from '@storybook/your-renderer'; -+import "bootstrap/dist/css/bootstrap.min.css"; -+import "bootstrap/dist/js/bootstrap.bundle"; ++import 'bootstrap/dist/css/bootstrap.min.css'; ++import 'bootstrap/dist/js/bootstrap.bundle'; const preview: Preview = { parameters: { /* ... */ }, @@ -65,23 +62,23 @@ Bootstrap now supports light and dark color modes out of the box as well as the To enable switching between these modes in a click for your stories, use our `withThemeByDataAttribute` decorator by adding the following code to your `.storybook/preview.js` file. ```diff --import { Preview } from "@storybook/your-renderer"; -+import { Preview, Renderer } from "@storybook/your-renderer"; -+import { withThemeByDataAttribute } from "@storybook/addon-themes"; +-import { Preview } from '@storybook/your-renderer'; ++import { Preview, Renderer } from '@storybook/your-renderer'; ++import { withThemeByDataAttribute } from '@storybook/addon-themes'; -import "bootstrap/dist/css/bootstrap.min.css"; -import "bootstrap/dist/js/bootstrap.bundle"; +import 'bootstrap/dist/css/bootstrap.min.css'; +import 'bootstrap/dist/js/bootstrap.bundle'; const preview: Preview = { parameters: { /* ... */ }, + decorators: [ + withThemeByDataAttribute({ + themes: { -+ light: "light", -+ dark: "dark", ++ light: 'light', ++ dark: 'dark', + }, -+ defaultTheme: "light", -+ attributeName: "data-bs-theme", ++ defaultTheme: 'light', ++ attributeName: 'data-bs-theme', + }), + ] }; diff --git a/code/addons/themes/docs/getting-started/emotion.md b/code/addons/themes/docs/getting-started/emotion.md index 4d5118d0785d..1671b93b3583 100644 --- a/code/addons/themes/docs/getting-started/emotion.md +++ b/code/addons/themes/docs/getting-started/emotion.md @@ -4,7 +4,7 @@ -To get started, **install the package** as a dev dependency +To get started, **install the package** as a dev dependency. yarn: @@ -26,17 +26,14 @@ pnpm add -D @storybook/addon-themes ## 🧩 Register Addon -Now, **include the addon** in your `.storybook/main.js` file +Now, **include the addon** in your `.storybook/main.js` file. ```diff -module.exports = { - stories: [ - "../stories/**/*.stories.mdx", - "../stories/**/*.stories.@(js|jsx|ts|tsx)", - ], +export default { + stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'], addons: [ - "@storybook/addon-essentials", -+ "@storybook/addon-themes" + '@storybook/addon-essentials', ++ '@storybook/addon-themes', ], }; ``` @@ -45,14 +42,14 @@ module.exports = { Finally, provide your theme(s) and global styles component to your stories with our `withThemeFromJSXProvider` decorator. -Make the following changes to your `.storybook/preview.js` +Make the following changes to your `.storybook/preview.js`: ```diff --import { Preview } from "@storybook/your-renderer"; -+import { Preview, Renderer } from "@storybook/your-renderer"; -+import { withThemeFromJSXProvider } from "@storybook/addon-themes"; +-import { Preview } from '@storybook/your-renderer'; ++import { Preview, Renderer } from '@storybook/your-renderer'; ++import { withThemeFromJSXProvider } from '@storybook/addon-themes'; +import { ThemeProvider } from '@emotion/react'; -+import { GlobalStyles, lightTheme, darkTheme } from "../src/themes"; // import your custom theme configs ++import { GlobalStyles, lightTheme, darkTheme } from '../src/themes'; // Import your custom theme configs const preview: Preview = { @@ -63,7 +60,7 @@ const preview: Preview = { + light: lightTheme, + dark: darkTheme, + }, -+ defaultTheme: "light", ++ defaultTheme: 'light', + Provider: ThemeProvider, + GlobalStyles: GlobalStyles, + }), diff --git a/code/addons/themes/docs/getting-started/material-ui.md b/code/addons/themes/docs/getting-started/material-ui.md index 267705d29340..1b88fae1451a 100644 --- a/code/addons/themes/docs/getting-started/material-ui.md +++ b/code/addons/themes/docs/getting-started/material-ui.md @@ -4,7 +4,7 @@ -To get started, **install the package** as a dev dependency +To get started, **install the package** as a dev dependency. yarn: @@ -26,17 +26,14 @@ pnpm add -D @storybook/addon-themes ## 🧩 Register Addon -Now, **include the addon** in your `.storybook/main.js` file +Now, **include the addon** in your `.storybook/main.js` file. ```diff -module.exports = { - stories: [ - "../stories/**/*.stories.mdx", - "../stories/**/*.stories.@(js|jsx|ts|tsx)", - ], +export default { + stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'], addons: [ - "@storybook/addon-essentials", -+ "@storybook/addon-themes", + '@storybook/addon-essentials', ++ '@storybook/addon-themes', ], }; ``` @@ -48,14 +45,14 @@ module.exports = { These can be imported into your `.storybook/preview.js` file. ```diff -import { Preview } from "@storybook/your-renderer"; +import { Preview } from '@storybook/your-renderer'; +// Load Material UI fonts -+import "@fontsource/roboto/300.css"; -+import "@fontsource/roboto/400.css"; -+import "@fontsource/roboto/500.css"; -+import "@fontsource/roboto/700.css"; -+import "@fontsource/material-icons"; ++import '@fontsource/roboto/300.css'; ++import '@fontsource/roboto/400.css'; ++import '@fontsource/roboto/500.css'; ++import '@fontsource/roboto/700.css'; ++import '@fontsource/material-icons'; const preview: Preview = { parameters: { /* ... */ }, @@ -68,21 +65,21 @@ export default preview; While Material UI comes with a default theme that works out of the box. You can create your own theme(s) and provide them to your stories with our `withThemeFromJSXProvider` decorator. -Make the following changes to your `.storybook/preview.js` +Make the following changes to your `.storybook/preview.js`: ```diff --import { Preview } from "@storybook/your-renderer"; -+import { Preview, Renderer } from "@storybook/your-renderer"; -+import { withThemeFromJSXProvider } from "@storybook/addon-themes"; -+import { CssBaseline, ThemeProvider } from "@mui/material"; -+import { lightTheme, darkTheme } from "../src/themes"; // import your custom theme configs +-import { Preview } from '@storybook/your-renderer'; ++import { Preview, Renderer } from '@storybook/your-renderer'; ++import { withThemeFromJSXProvider } from '@storybook/addon-themes'; ++import { CssBaseline, ThemeProvider } from '@mui/material'; ++import { lightTheme, darkTheme } from '../src/themes'; // Import your custom theme configs // Load Roboto fonts -import "@fontsource/roboto/300.css"; -import "@fontsource/roboto/400.css"; -import "@fontsource/roboto/500.css"; -import "@fontsource/roboto/700.css"; -import "@fontsource/material-icons"; +import '@fontsource/roboto/300.css'; +import '@fontsource/roboto/400.css'; +import '@fontsource/roboto/500.css'; +import '@fontsource/roboto/700.css'; +import '@fontsource/material-icons'; const preview: Preview = { parameters: { /* ... */ }, @@ -92,7 +89,7 @@ const preview: Preview = { + light: lightTheme, + dark: darkTheme, + }, -+ defaultTheme: "light", ++ defaultTheme: 'light', + Provider: ThemeProvider, + GlobalStyles: CssBaseline, + }), diff --git a/code/addons/themes/docs/getting-started/styled-components.md b/code/addons/themes/docs/getting-started/styled-components.md index 514e95f25a85..e5e31361f121 100644 --- a/code/addons/themes/docs/getting-started/styled-components.md +++ b/code/addons/themes/docs/getting-started/styled-components.md @@ -4,7 +4,7 @@ -To get started, **install the package** as a dev dependency +To get started, **install the package** as a dev dependency. yarn: @@ -26,17 +26,14 @@ pnpm add -D @storybook/addon-themes ## 🧩 Register Addon -Now, **include the addon** in your `.storybook/main.js` file +Now, **include the addon** in your `.storybook/main.js` file. ```diff -module.exports = { - stories: [ - "../stories/**/*.stories.mdx", - "../stories/**/*.stories.@(js|jsx|ts|tsx)", - ], +export default { + stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'], addons: [ - "@storybook/addon-essentials", -+ "@storybook/addon-themes" + '@storybook/addon-essentials', ++ '@storybook/addon-themes', ], }; ``` @@ -45,14 +42,14 @@ module.exports = { Finally, provide your theme(s) and global styles component to your stories with our `withThemeFromJSXProvider` decorator. -Make the following changes to your `.storybook/preview.js` +Make the following changes to your `.storybook/preview.js`: ```diff --import { Preview } from "@storybook/your-renderer"; -+import { Preview, Renderer } from "@storybook/your-renderer"; -+import { withThemeFromJSXProvider } from "@storybook/addon-themes"; +-import { Preview } from '@storybook/your-renderer'; ++import { Preview, Renderer } from '@storybook/your-renderer'; ++import { withThemeFromJSXProvider } from '@storybook/addon-themes'; +import { ThemeProvider } from 'styled-components'; -+import { GlobalStyles, lightTheme, darkTheme } from "../src/themes"; // import your custom theme configs ++import { GlobalStyles, lightTheme, darkTheme } from '../src/themes'; // Import your custom theme configs const preview: Preview = { parameters: { /* ... */ }, @@ -62,7 +59,7 @@ const preview: Preview = { + light: lightTheme, + dark: darkTheme, + }, -+ defaultTheme: "light", ++ defaultTheme: 'light', + Provider: ThemeProvider, + GlobalStyles: GlobalStyles, + }), diff --git a/code/addons/themes/docs/getting-started/tailwind.md b/code/addons/themes/docs/getting-started/tailwind.md index 6156c45757a0..c5475c621550 100644 --- a/code/addons/themes/docs/getting-started/tailwind.md +++ b/code/addons/themes/docs/getting-started/tailwind.md @@ -4,7 +4,7 @@ -To get started, **install the package** as a dev dependency +To get started, **install the package** as a dev dependency. yarn: @@ -29,14 +29,11 @@ pnpm add -D @storybook/addon-themes Now, **include the addon** in your `.storybook/main.js` file. ```diff -module.exports = { - stories: [ - "../stories/**/*.stories.mdx", - "../stories/**/*.stories.@(js|jsx|ts|tsx)", - ], +export default { + stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'], addons: [ - "@storybook/addon-essentials", -+ "@storybook/addon-themes" + '@storybook/addon-essentials', ++ '@storybook/addon-themes', ], }; ``` @@ -46,9 +43,9 @@ module.exports = { To give your stories access to Tailwind styles, import them into your `.storybook/preview.js` file. ```diff -import { Preview } from "@storybook/your-renderer"; +import { Preview } from '@storybook/your-renderer'; -+import "../src/index.css"; ++import '../src/index.css'; const preview: Preview = { parameters: { /* ... */ }, @@ -64,11 +61,11 @@ Tailwind supports light and dark color modes out of the box. These modes can be To enable switching between these modes in a click for your stories, use our `withThemeByClassName` decorator by adding the following code to your `.storybook/preview.js` file. ```diff --import { Preview } from "@storybook/your-renderer"; -+import { Preview, Renderer } from "@storybook/your-renderer"; -+import { withThemeByClassName } from "@storybook/addon-themes"; +-import { Preview } from '@storybook/your-renderer'; ++import { Preview, Renderer } from '@storybook/your-renderer'; ++import { withThemeByClassName } from '@storybook/addon-themes'; -import "../src/index.css"; +import '../src/index.css'; const preview: Preview = { @@ -76,10 +73,10 @@ const preview: Preview = { + decorators: [ + withThemeByClassName({ + themes: { -+ light: "", -+ dark: "dark", ++ light: '', ++ dark: 'dark', + }, -+ defaultTheme: "light", ++ defaultTheme: 'light', + }), + ] }; @@ -92,11 +89,11 @@ export default preview; If you've configured Tailwind to toggle themes with a data attribute, use our `withThemeByDataAttribute` decorator by adding the following code to your `.storybook/preview.js` file. ```diff --import { Preview } from "@storybook/your-renderer"; -+import { Preview, Renderer } from "@storybook/your-renderer"; -+import { withThemeByDataAttribute } from "@storybook/addon-themes"; +-import { Preview } from '@storybook/your-renderer'; ++import { Preview, Renderer } from '@storybook/your-renderer'; ++import { withThemeByDataAttribute } from '@storybook/addon-themes'; -import "../src/index.css"; +import '../src/index.css'; const preview: Preview = { @@ -104,11 +101,11 @@ const preview: Preview = { + decorators: [ + withThemeByDataAttribute({ + themes: { -+ light: "light", -+ dark: "dark", ++ light: 'light', ++ dark: 'dark', + }, -+ defaultTheme: "light", -+ attributeName: "data-theme", ++ defaultTheme: 'light', ++ attributeName: 'data-theme', + }), + ] }; diff --git a/code/addons/viewport/src/shortcuts.ts b/code/addons/viewport/src/shortcuts.ts index 47fd37cbb996..974abe477d6d 100644 --- a/code/addons/viewport/src/shortcuts.ts +++ b/code/addons/viewport/src/shortcuts.ts @@ -1,4 +1,4 @@ -import type { API } from '@storybook/manager-api'; +import { type API } from '@storybook/manager-api'; import { ADDON_ID } from './constants'; import { globals as defaultGlobals } from './preview'; @@ -27,7 +27,7 @@ export const registerShortcuts = async ( ) => { await api.setAddonShortcut(ADDON_ID, { label: 'Previous viewport', - defaultShortcut: ['shift', 'V'], + defaultShortcut: ['alt', 'shift', 'V'], actionName: 'previous', action: () => { updateGlobals({ @@ -38,7 +38,7 @@ export const registerShortcuts = async ( await api.setAddonShortcut(ADDON_ID, { label: 'Next viewport', - defaultShortcut: ['V'], + defaultShortcut: ['alt', 'V'], actionName: 'next', action: () => { updateGlobals({ @@ -49,7 +49,7 @@ export const registerShortcuts = async ( await api.setAddonShortcut(ADDON_ID, { label: 'Reset viewport', - defaultShortcut: ['alt', 'V'], + defaultShortcut: ['alt', 'control', 'V'], actionName: 'reset', action: () => { updateGlobals(defaultGlobals); diff --git a/code/builders/builder-manager/src/index.ts b/code/builders/builder-manager/src/index.ts index ef71811029f4..28e6fdd5a55e 100644 --- a/code/builders/builder-manager/src/index.ts +++ b/code/builders/builder-manager/src/index.ts @@ -24,6 +24,7 @@ import type { import { getData } from './utils/data'; import { safeResolve } from './utils/safeResolve'; import { readOrderedFiles } from './utils/files'; +import { buildFrameworkGlobalsFromOptions } from './utils/framework'; let compilation: Compilation; let asyncIterator: ReturnType | ReturnType; @@ -163,6 +164,9 @@ const starter: StarterFunction = async function* starterGeneratorFn({ const { cssFiles, jsFiles } = await readOrderedFiles(addonsDir, compilation?.outputFiles); + // Build additional global values + const globals: Record = await buildFrameworkGlobalsFromOptions(options); + yield; const html = await renderHTML( @@ -177,7 +181,8 @@ const starter: StarterFunction = async function* starterGeneratorFn({ logLevel, docsOptions, tagsOptions, - options + options, + globals ); yield; @@ -252,6 +257,9 @@ const builder: BuilderFunction = async function* builderGeneratorFn({ startTime, }); const { cssFiles, jsFiles } = await readOrderedFiles(addonsDir, compilation?.outputFiles); + // Build additional global values + const globals: Record = await buildFrameworkGlobalsFromOptions(options); + yield; const html = await renderHTML( @@ -266,7 +274,8 @@ const builder: BuilderFunction = async function* builderGeneratorFn({ startTime, logLevel, docsOptions, tagsOptions, - options + options, + globals ); await Promise.all([ diff --git a/code/builders/builder-manager/src/utils/framework.test.ts b/code/builders/builder-manager/src/utils/framework.test.ts new file mode 100644 index 000000000000..ecdcb42ead50 --- /dev/null +++ b/code/builders/builder-manager/src/utils/framework.test.ts @@ -0,0 +1,48 @@ +import path from 'node:path'; +import { describe, it, expect } from 'vitest'; + +import { + pluckNameFromConfigProperty, + pluckStorybookPackageFromPath, + pluckThirdPartyPackageFromPath, +} from './framework'; + +describe('UTILITIES: Framework information', () => { + describe('UTILITY: pluckNameFromConfigProperty', () => { + it('should return undefined if the property is undefined', () => { + expect(pluckNameFromConfigProperty(undefined)).toBe(undefined); + }); + + it('should return the name if the property is a string', () => { + expect(pluckNameFromConfigProperty('foo')).toBe('foo'); + }); + + it('should return the name if the property is an object', () => { + expect(pluckNameFromConfigProperty({ name: 'foo' })).toBe('foo'); + }); + }); + + describe('UTILITY: pluckStorybookPackageFromPath', () => { + it('should return the package name if the path is a storybook package', () => { + const packagePath = path.join(process.cwd(), 'node_modules', '@storybook', 'foo'); + expect(pluckStorybookPackageFromPath(packagePath)).toBe('@storybook/foo'); + }); + + it('should return undefined if the path is not a storybook package', () => { + const packagePath = path.join(process.cwd(), 'foo'); + expect(pluckStorybookPackageFromPath(packagePath)).toBe(undefined); + }); + }); + + describe('UTILITY: pluckThirdPartyPackageFromPath', () => { + it('should return the package name if the path is a third party package', () => { + const packagePath = path.join(process.cwd(), 'node_modules', 'bar'); + expect(pluckThirdPartyPackageFromPath(packagePath)).toBe('bar'); + }); + + it('should return the given path if the path is not a third party package', () => { + const packagePath = path.join(process.cwd(), 'foo', 'bar', 'baz'); + expect(pluckThirdPartyPackageFromPath(packagePath)).toBe(packagePath); + }); + }); +}); diff --git a/code/builders/builder-manager/src/utils/framework.ts b/code/builders/builder-manager/src/utils/framework.ts new file mode 100644 index 000000000000..165b018ddf8f --- /dev/null +++ b/code/builders/builder-manager/src/utils/framework.ts @@ -0,0 +1,51 @@ +import path from 'path'; +import type { Options } from '@storybook/types'; + +interface PropertyObject { + name: string; + options?: Record; +} + +type Property = string | PropertyObject | undefined; + +export const pluckNameFromConfigProperty = (property: Property) => { + if (!property) { + return undefined; + } + + return typeof property === 'string' ? property : property.name; +}; + +// For replacing Windows backslashes with forward slashes +const normalizePath = (packagePath: string) => packagePath.replaceAll(path.sep, '/'); + +export const pluckStorybookPackageFromPath = (packagePath: string) => + normalizePath(packagePath).match(/(@storybook\/.*)$/)?.[1]; + +export const pluckThirdPartyPackageFromPath = (packagePath: string) => + normalizePath(packagePath).split('node_modules/')[1] ?? packagePath; + +export const buildFrameworkGlobalsFromOptions = async (options: Options) => { + const globals: Record = {}; + + const { renderer, builder } = await options.presets.apply('core'); + + const rendererName = pluckNameFromConfigProperty(renderer); + if (rendererName) { + globals.STORYBOOK_RENDERER = + pluckStorybookPackageFromPath(rendererName) ?? pluckThirdPartyPackageFromPath(rendererName); + } + + const builderName = pluckNameFromConfigProperty(builder); + if (builderName) { + globals.STORYBOOK_BUILDER = + pluckStorybookPackageFromPath(builderName) ?? pluckThirdPartyPackageFromPath(builderName); + } + + const framework = pluckNameFromConfigProperty(await options.presets.apply('framework')); + if (framework) { + globals.STORYBOOK_FRAMEWORK = framework; + } + + return globals; +}; diff --git a/code/builders/builder-manager/src/utils/template.ts b/code/builders/builder-manager/src/utils/template.ts index 4ccb2d50864a..dd2d1bc88e20 100644 --- a/code/builders/builder-manager/src/utils/template.ts +++ b/code/builders/builder-manager/src/utils/template.ts @@ -35,10 +35,15 @@ export const renderHTML = async ( logLevel: Promise, docsOptions: Promise, tagsOptions: Promise, - { versionCheck, previewUrl, configType, ignorePreview }: Options + { versionCheck, previewUrl, configType, ignorePreview }: Options, + globals: Record ) => { const titleRef = await title; const templateRef = await template; + const stringifiedGlobals = Object.entries(globals).reduce( + (transformed, [key, value]) => ({ ...transformed, [key]: JSON.stringify(value) }), + {} + ); return render(templateRef, { title: titleRef ? `${titleRef} - Storybook` : 'Storybook', @@ -54,6 +59,7 @@ export const renderHTML = async ( VERSIONCHECK: JSON.stringify(JSON.stringify(versionCheck), null, 2), PREVIEW_URL: JSON.stringify(previewUrl, null, 2), // global preview URL TAGS_OPTIONS: JSON.stringify(await tagsOptions, null, 2), + ...stringifiedGlobals, }, head: (await customHead) || '', ignorePreview, diff --git a/code/builders/builder-webpack5/src/loaders/export-order-loader.ts b/code/builders/builder-webpack5/src/loaders/export-order-loader.ts index f57c3a16eb66..8f0b90882278 100644 --- a/code/builders/builder-webpack5/src/loaders/export-order-loader.ts +++ b/code/builders/builder-webpack5/src/loaders/export-order-loader.ts @@ -1,4 +1,4 @@ -import assert from 'assert'; +import assert from 'node:assert'; import { parse as parseCjs, init as initCjsParser } from 'cjs-module-lexer'; import { parse as parseEs } from 'es-module-lexer'; import MagicString from 'magic-string'; diff --git a/code/e2e-tests/manager.spec.ts b/code/e2e-tests/manager.spec.ts index 367082e9391e..5ef2a975b4ea 100644 --- a/code/e2e-tests/manager.spec.ts +++ b/code/e2e-tests/manager.spec.ts @@ -21,9 +21,9 @@ test.describe('Manager UI', () => { await expect(sbPage.page.locator('.sidebar-container')).toBeVisible(); // toggle with keyboard shortcut - await sbPage.page.locator('html').press('s'); + await sbPage.page.locator('html').press('Alt+s'); await expect(sbPage.page.locator('.sidebar-container')).not.toBeVisible(); - await sbPage.page.locator('html').press('s'); + await sbPage.page.locator('html').press('Alt+s'); await expect(sbPage.page.locator('.sidebar-container')).toBeVisible(); // toggle with menu item @@ -51,9 +51,9 @@ test.describe('Manager UI', () => { await expectToolbarVisibility(true); // toggle with keyboard shortcut - await sbPage.page.locator('html').press('t'); + await sbPage.page.locator('html').press('Alt+t'); await expectToolbarVisibility(false); - await sbPage.page.locator('html').press('t'); + await sbPage.page.locator('html').press('Alt+t'); await expectToolbarVisibility(true); // toggle with menu item @@ -75,9 +75,9 @@ test.describe('Manager UI', () => { await expect(sbPage.page.locator('#storybook-panel-root')).not.toBeVisible(); // toggle with keyboard shortcut - await sbPage.page.locator('html').press('a'); + await sbPage.page.locator('html').press('Alt+a'); await expect(sbPage.page.locator('#storybook-panel-root')).not.toBeVisible(); - await sbPage.page.locator('html').press('a'); + await sbPage.page.locator('html').press('Alt+a'); await expect(sbPage.page.locator('#storybook-panel-root')).not.toBeVisible(); }); @@ -90,9 +90,9 @@ test.describe('Manager UI', () => { await expect(sbPage.page.locator('#storybook-panel-root')).toBeVisible(); // toggle with keyboard shortcut - await sbPage.page.locator('html').press('a'); + await sbPage.page.locator('html').press('Alt+a'); await expect(sbPage.page.locator('#storybook-panel-root')).not.toBeVisible(); - await sbPage.page.locator('html').press('a'); + await sbPage.page.locator('html').press('Alt+a'); await expect(sbPage.page.locator('#storybook-panel-root')).toBeVisible(); // toggle with menu item @@ -114,16 +114,16 @@ test.describe('Manager UI', () => { await expect(sbPage.page.locator('#storybook-panel-root')).toBeVisible(); // toggle position with keyboard shortcut - await sbPage.page.locator('html').press('d'); + await sbPage.page.locator('html').press('Alt+d'); await expect(sbPage.page.locator('#storybook-panel-root')).toBeVisible(); // TODO: how to assert panel position? // hide with keyboard shortcut - await sbPage.page.locator('html').press('a'); + await sbPage.page.locator('html').press('Alt+a'); await expect(sbPage.page.locator('#storybook-panel-root')).not.toBeVisible(); // toggling position should also show the panel again - await sbPage.page.locator('html').press('d'); + await sbPage.page.locator('html').press('Alt+d'); await expect(sbPage.page.locator('#storybook-panel-root')).toBeVisible(); }); }); @@ -138,11 +138,11 @@ test.describe('Manager UI', () => { await expect(sbPage.page.locator('.sidebar-container')).toBeVisible(); // toggle with keyboard shortcut - await sbPage.page.locator('html').press('f'); + await sbPage.page.locator('html').press('Alt+f'); await expect(sbPage.page.locator('#storybook-panel-root')).not.toBeVisible(); await expect(sbPage.page.locator('.sidebar-container')).not.toBeVisible(); - await sbPage.page.locator('html').press('f'); + await sbPage.page.locator('html').press('Alt+f'); await expect(sbPage.page.locator('#storybook-panel-root')).toBeVisible(); await expect(sbPage.page.locator('.sidebar-container')).toBeVisible(); diff --git a/code/e2e-tests/preview-web.spec.ts b/code/e2e-tests/preview-web.spec.ts index 6d8bf5526415..b4a96c4b6001 100644 --- a/code/e2e-tests/preview-web.spec.ts +++ b/code/e2e-tests/preview-web.spec.ts @@ -32,7 +32,7 @@ test.describe('preview-web', () => { // click outside, to remove focus from the input of the story, then press S to toggle sidebar await sbPage.previewRoot().click(); - await sbPage.previewRoot().press('s'); + await sbPage.previewRoot().press('Alt+s'); await expect(sbPage.page.locator('.sidebar-container')).not.toBeVisible(); }); @@ -47,7 +47,7 @@ test.describe('preview-web', () => { await expect(sbPage.page.locator('.sidebar-container')).toBeVisible(); - await sbPage.previewRoot().getByRole('button').getByText('Submit').first().press('s'); + await sbPage.previewRoot().getByRole('button').getByText('Submit').first().press('Alt+s'); await expect(sbPage.page.locator('.sidebar-container')).not.toBeVisible(); }); }); diff --git a/code/frameworks/angular/package.json b/code/frameworks/angular/package.json index fb032d500f78..f6f9dda2508b 100644 --- a/code/frameworks/angular/package.json +++ b/code/frameworks/angular/package.json @@ -38,7 +38,6 @@ }, "dependencies": { "@storybook/builder-webpack5": "workspace:*", - "@storybook/cli": "workspace:*", "@storybook/client-logger": "workspace:*", "@storybook/core-common": "workspace:*", "@storybook/core-events": "workspace:*", diff --git a/code/frameworks/angular/src/builders/build-storybook/index.spec.ts b/code/frameworks/angular/src/builders/build-storybook/index.spec.ts index 7b02bed12767..64b10c2c2ca3 100644 --- a/code/frameworks/angular/src/builders/build-storybook/index.spec.ts +++ b/code/frameworks/angular/src/builders/build-storybook/index.spec.ts @@ -19,7 +19,7 @@ const buildMock = { }; vi.doMock('@storybook/core-server', () => buildMock); -vi.doMock('@storybook/cli', () => ({ +vi.doMock('@storybook/core-common', () => ({ JsPackageManagerFactory: { getPackageManager: () => ({ runPackageCommand: mockRunScript, diff --git a/code/frameworks/angular/src/builders/build-storybook/index.ts b/code/frameworks/angular/src/builders/build-storybook/index.ts index 114f626e57f6..abe8fbd6a060 100644 --- a/code/frameworks/angular/src/builders/build-storybook/index.ts +++ b/code/frameworks/angular/src/builders/build-storybook/index.ts @@ -15,7 +15,7 @@ import { sync as readUpSync } from 'read-pkg-up'; import { BrowserBuilderOptions, StylePreprocessorOptions } from '@angular-devkit/build-angular'; import { CLIOptions } from '@storybook/types'; -import { getEnvConfig, versions } from '@storybook/cli'; +import { getEnvConfig, versions } from '@storybook/core-common'; import { addToGlobalContext } from '@storybook/telemetry'; import { buildStaticStandalone, withTelemetry } from '@storybook/core-server'; diff --git a/code/frameworks/angular/src/builders/start-storybook/index.spec.ts b/code/frameworks/angular/src/builders/start-storybook/index.spec.ts index f166cff036fc..f37547bcf3dd 100644 --- a/code/frameworks/angular/src/builders/start-storybook/index.spec.ts +++ b/code/frameworks/angular/src/builders/start-storybook/index.spec.ts @@ -21,7 +21,7 @@ vi.doMock('find-up', () => ({ sync: () => './storybook/tsconfig.ts' })); const mockRunScript = vi.fn(); -vi.mock('@storybook/cli', () => ({ +vi.mock('@storybook/core-common', () => ({ getEnvConfig: (options: any) => options, versions: { storybook: 'x.x.x', diff --git a/code/frameworks/angular/src/builders/start-storybook/index.ts b/code/frameworks/angular/src/builders/start-storybook/index.ts index 4d6795e40972..cad69e51131f 100644 --- a/code/frameworks/angular/src/builders/start-storybook/index.ts +++ b/code/frameworks/angular/src/builders/start-storybook/index.ts @@ -14,7 +14,7 @@ import { sync as findUpSync } from 'find-up'; import { sync as readUpSync } from 'read-pkg-up'; import { CLIOptions } from '@storybook/types'; -import { getEnvConfig, versions } from '@storybook/cli'; +import { getEnvConfig, versions } from '@storybook/core-common'; import { addToGlobalContext } from '@storybook/telemetry'; import { buildDevStandalone, withTelemetry } from '@storybook/core-server'; import { diff --git a/code/frameworks/angular/src/builders/utils/run-compodoc.spec.ts b/code/frameworks/angular/src/builders/utils/run-compodoc.spec.ts index 26b99317811a..cf0686b11b1d 100644 --- a/code/frameworks/angular/src/builders/utils/run-compodoc.spec.ts +++ b/code/frameworks/angular/src/builders/utils/run-compodoc.spec.ts @@ -8,7 +8,7 @@ import { runCompodoc } from './run-compodoc'; const mockRunScript = vi.fn(); -vi.mock('@storybook/cli', () => ({ +vi.mock('@storybook/core-common', () => ({ JsPackageManagerFactory: { getPackageManager: () => ({ runPackageCommandSync: mockRunScript, diff --git a/code/frameworks/angular/src/builders/utils/run-compodoc.ts b/code/frameworks/angular/src/builders/utils/run-compodoc.ts index 9a86fde5f6c7..e926c041bfa8 100644 --- a/code/frameworks/angular/src/builders/utils/run-compodoc.ts +++ b/code/frameworks/angular/src/builders/utils/run-compodoc.ts @@ -1,7 +1,7 @@ import { BuilderContext } from '@angular-devkit/architect'; import { Observable } from 'rxjs'; import * as path from 'path'; -import { JsPackageManagerFactory } from '@storybook/cli'; +import { JsPackageManagerFactory } from '@storybook/core-common'; const hasTsConfigArg = (args: string[]) => args.indexOf('-p') !== -1; const hasOutputArg = (args: string[]) => diff --git a/code/frameworks/angular/src/client/angular-beta/utils/BootstrapQueue.test.ts b/code/frameworks/angular/src/client/angular-beta/utils/BootstrapQueue.test.ts index f518e7daf6e9..76e1add01348 100644 --- a/code/frameworks/angular/src/client/angular-beta/utils/BootstrapQueue.test.ts +++ b/code/frameworks/angular/src/client/angular-beta/utils/BootstrapQueue.test.ts @@ -1,9 +1,14 @@ import { Subject, lastValueFrom } from 'rxjs'; import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest'; -import assert from 'node:assert'; import { queueBootstrapping } from './BootstrapQueue'; +const instantWaitFor = (fn: () => void) => { + return vi.waitFor(fn, { + interval: 0, + }); +}; + describe('BootstrapQueue', () => { beforeEach(async () => { vi.spyOn(console, 'log').mockImplementation(() => {}); @@ -24,8 +29,10 @@ describe('BootstrapQueue', () => { bootstrapAppFinished(); }); - await vi.waitFor(() => { - assert(bootstrapApp.mock.calls.length === 1, 'bootstrapApp should have been called once'); + await instantWaitFor(() => { + if (bootstrapApp.mock.calls.length !== 1) { + throw new Error('bootstrapApp should not have been called yet'); + } }); expect(bootstrapApp).toHaveBeenCalled(); @@ -34,11 +41,10 @@ describe('BootstrapQueue', () => { pendingSubject.next(); pendingSubject.complete(); - await vi.waitFor(() => { - assert( - bootstrapAppFinished.mock.calls.length === 1, - 'bootstrapApp should have been called once' - ); + await instantWaitFor(() => { + if (bootstrapAppFinished.mock.calls.length !== 1) { + throw new Error('bootstrapApp should have been called once'); + } }); expect(bootstrapAppFinished).toHaveBeenCalled(); @@ -67,8 +73,10 @@ describe('BootstrapQueue', () => { queueBootstrapping(bootstrapApp2).then(bootstrapAppFinished2); queueBootstrapping(bootstrapApp3).then(bootstrapAppFinished3); - await vi.waitFor(() => { - assert(bootstrapApp.mock.calls.length === 1, 'bootstrapApp should have been called once'); + await instantWaitFor(() => { + if (bootstrapApp.mock.calls.length !== 1) { + throw new Error('bootstrapApp should have been called once'); + } }); expect(bootstrapApp).toHaveBeenCalled(); @@ -81,8 +89,10 @@ describe('BootstrapQueue', () => { pendingSubject.next(); pendingSubject.complete(); - await vi.waitFor(() => { - assert(bootstrapApp2.mock.calls.length === 1, 'bootstrapApp2 should have been called once'); + await instantWaitFor(() => { + if (bootstrapApp2.mock.calls.length !== 1) { + throw new Error('bootstrapApp2 should have been called once'); + } }); expect(bootstrapApp).toHaveReturnedTimes(1); @@ -95,8 +105,10 @@ describe('BootstrapQueue', () => { pendingSubject2.next(); pendingSubject2.complete(); - await vi.waitFor(() => { - assert(bootstrapApp3.mock.calls.length === 1, 'bootstrapApp3 should have been called once'); + await instantWaitFor(() => { + if (bootstrapApp3.mock.calls.length !== 1) { + throw new Error('bootstrapApp3 should have been called once'); + } }); expect(bootstrapApp).toHaveReturnedTimes(1); @@ -109,11 +121,10 @@ describe('BootstrapQueue', () => { pendingSubject3.next(); pendingSubject3.complete(); - await vi.waitFor(() => { - assert( - bootstrapAppFinished3.mock.calls.length === 1, - 'bootstrapAppFinished3 should have been called once' - ); + await instantWaitFor(() => { + if (bootstrapAppFinished3.mock.calls.length !== 1) { + throw new Error('bootstrapAppFinished3 should have been called once'); + } }); expect(bootstrapApp).toHaveReturnedTimes(1); @@ -142,8 +153,10 @@ describe('BootstrapQueue', () => { queueBootstrapping(bootstrapApp).then(bootstrapAppFinished).catch(bootstrapAppError); queueBootstrapping(bootstrapApp2).then(bootstrapAppFinished2).catch(bootstrapAppError2); - await vi.waitFor(() => { - assert(bootstrapApp.mock.calls.length === 1, 'bootstrapApp should have been called once'); + await instantWaitFor(() => { + if (bootstrapApp.mock.calls.length !== 1) { + throw new Error('bootstrapApp should have been called once'); + } }); expect(bootstrapApp).toHaveBeenCalledTimes(1); @@ -152,11 +165,10 @@ describe('BootstrapQueue', () => { pendingSubject.error(new Error('test error')); - await vi.waitFor(() => { - assert( - bootstrapAppError.mock.calls.length === 1, - 'bootstrapAppError should have been called once' - ); + await instantWaitFor(() => { + if (bootstrapAppError.mock.calls.length !== 1) { + throw new Error('bootstrapAppError should have been called once'); + } }); expect(bootstrapApp).toHaveBeenCalledTimes(1); @@ -169,11 +181,10 @@ describe('BootstrapQueue', () => { pendingSubject2.next(); pendingSubject2.complete(); - await vi.waitFor(() => { - assert( - bootstrapAppFinished2.mock.calls.length === 1, - 'bootstrapAppFinished2 should have been called once' - ); + await instantWaitFor(() => { + if (bootstrapAppFinished2.mock.calls.length !== 1) { + throw new Error('bootstrapAppFinished2 should have been called once'); + } }); expect(bootstrapApp).toHaveBeenCalledTimes(1); diff --git a/code/frameworks/nextjs/README.md b/code/frameworks/nextjs/README.md index d1d234ab89fc..23f637900ac3 100644 --- a/code/frameworks/nextjs/README.md +++ b/code/frameworks/nextjs/README.md @@ -920,17 +920,17 @@ export default { Setting this flag automatically wraps your story in a [Suspense](https://react.dev/reference/react/Suspense) wrapper, which is able to render asynchronous components in NextJS's version of React. -If this wrapper causes problems in any of your existing stories, you can selectively disable it using the `nextjs.rsc` [parameter](https://storybook.js.org/docs/writing-stories/parameters) at the global/component/story level: +If this wrapper causes problems in any of your existing stories, you can selectively disable it using the `react.rsc` [parameter](https://storybook.js.org/docs/writing-stories/parameters) at the global/component/story level: ```js // MyServerComponent.stories.js export default { component: MyServerComponent, - parameters: { nextjs: { rsc: false } }, + parameters: { react: { rsc: false } }, }; ``` -Note that wrapping your server components in Suspense does not help if your server components access server-side resources like the file system or Node-specific libraries. To deal work around this, you'll need to mock out your data access layer using [Webpack aliases](https://webpack.js.org/configuration/resolve/#resolvealias) or an addon like [storybook-addon-module-mock](https://storybook.js.org/addons/storybook-addon-module-mock). +Note that wrapping your server components in Suspense does not help if your server components access server-side resources like the file system or Node-specific libraries. To work around this, you'll need to mock out your data access layer using [Webpack aliases](https://webpack.js.org/configuration/resolve/#resolvealias) or an addon like [storybook-addon-module-mock](https://storybook.js.org/addons/storybook-addon-module-mock). If your server components access data via the network, we recommend using the [MSW Storybook Addon](https://storybook.js.org/addons/msw-storybook-addon) to mock network requests. diff --git a/code/lib/cli/package.json b/code/lib/cli/package.json index 4f6c7734cbf7..6dfd170c808d 100644 --- a/code/lib/cli/package.json +++ b/code/lib/cli/package.json @@ -75,11 +75,9 @@ "detect-indent": "^6.1.0", "envinfo": "^7.7.3", "execa": "^5.0.0", - "express": "^4.17.3", "find-up": "^5.0.0", "fs-extra": "^11.1.0", "get-npm-tarball-url": "^2.0.3", - "get-port": "^5.1.1", "giget": "^1.0.0", "globby": "^11.0.2", "jscodeshift": "^0.15.1", @@ -92,8 +90,7 @@ "strip-json-comments": "^3.0.1", "tempy": "^1.0.1", "tiny-invariant": "^1.3.1", - "ts-dedent": "^2.0.0", - "util-deprecate": "^1.0.2" + "ts-dedent": "^2.0.0" }, "devDependencies": { "@types/cross-spawn": "^6.0.2", @@ -108,7 +105,6 @@ "access": "public" }, "bundler": { - "pre": "./scripts/generate-sb-packages-versions.js", "entries": [ "./src/generate.ts", "./src/index.ts" diff --git a/code/lib/cli/src/add.ts b/code/lib/cli/src/add.ts index e3ac2ae2c989..acfd7e4e6eb7 100644 --- a/code/lib/cli/src/add.ts +++ b/code/lib/cli/src/add.ts @@ -1,12 +1,16 @@ -import { getStorybookInfo, serverRequire } from '@storybook/core-common'; +import { + getStorybookInfo, + serverRequire, + getStorybookVersion, + isCorePackage, + JsPackageManagerFactory, + type PackageManagerName, +} from '@storybook/core-common'; import { readConfig, writeConfig } from '@storybook/csf-tools'; import { isAbsolute, join } from 'path'; import SemVer from 'semver'; import dedent from 'ts-dedent'; -import { JsPackageManagerFactory, type PackageManagerName } from './js-package-manager'; -import { getStorybookVersion, isCorePackage } from './utils'; - const logger = console; interface PostinstallOptions { diff --git a/code/lib/cli/src/automigrate/fixes/angular-builders-multiproject.test.ts b/code/lib/cli/src/automigrate/fixes/angular-builders-multiproject.test.ts index 50844068d13d..91d4f9ca1556 100644 --- a/code/lib/cli/src/automigrate/fixes/angular-builders-multiproject.test.ts +++ b/code/lib/cli/src/automigrate/fixes/angular-builders-multiproject.test.ts @@ -1,6 +1,6 @@ import { describe, beforeEach, afterEach, it, expect, vi } from 'vitest'; import type { StorybookConfig } from '@storybook/types'; -import type { JsPackageManager } from '../../js-package-manager'; +import type { JsPackageManager } from '@storybook/core-common'; import { angularBuildersMultiproject } from './angular-builders-multiproject'; import * as helpers from '../../helpers'; import * as angularHelpers from '../../generators/ANGULAR/helpers'; diff --git a/code/lib/cli/src/automigrate/fixes/angular-builders.test.ts b/code/lib/cli/src/automigrate/fixes/angular-builders.test.ts index c527b6434b3a..2b9623e26d93 100644 --- a/code/lib/cli/src/automigrate/fixes/angular-builders.test.ts +++ b/code/lib/cli/src/automigrate/fixes/angular-builders.test.ts @@ -3,7 +3,7 @@ import type { StorybookConfig } from '@storybook/types'; import { angularBuilders } from './angular-builders'; import * as helpers from '../../helpers'; import * as angularHelpers from '../../generators/ANGULAR/helpers'; -import type { JsPackageManager } from '../../js-package-manager'; +import type { JsPackageManager } from '@storybook/core-common'; const checkAngularBuilders = async ({ packageManager, diff --git a/code/lib/cli/src/automigrate/fixes/angular-builders.ts b/code/lib/cli/src/automigrate/fixes/angular-builders.ts index c360a4d55b35..b0c40a0f6991 100644 --- a/code/lib/cli/src/automigrate/fixes/angular-builders.ts +++ b/code/lib/cli/src/automigrate/fixes/angular-builders.ts @@ -6,7 +6,7 @@ import prompts from 'prompts'; import type { Fix } from '../types'; import { isNxProject } from '../../helpers'; import { AngularJSON } from '../../generators/ANGULAR/helpers'; -import type { JsPackageManager } from '../../js-package-manager'; +import type { JsPackageManager } from '@storybook/core-common'; import { getFrameworkPackageName } from '../helpers/mainConfigFile'; interface AngularBuildersRunOptions { diff --git a/code/lib/cli/src/automigrate/fixes/autodocs-true.test.ts b/code/lib/cli/src/automigrate/fixes/autodocs-true.test.ts index 9ef46cbd4559..af9a14a3e56e 100644 --- a/code/lib/cli/src/automigrate/fixes/autodocs-true.test.ts +++ b/code/lib/cli/src/automigrate/fixes/autodocs-true.test.ts @@ -1,6 +1,6 @@ import { describe, afterEach, it, expect, vi } from 'vitest'; import type { StorybookConfigRaw } from '@storybook/types'; -import type { PackageJson } from '../../js-package-manager'; +import type { PackageJson } from '@storybook/core-common'; import { makePackageManager } from '../helpers/testing-helpers'; import { autodocsTrue } from './autodocs-true'; diff --git a/code/lib/cli/src/automigrate/fixes/bare-mdx-stories-glob.test.ts b/code/lib/cli/src/automigrate/fixes/bare-mdx-stories-glob.test.ts index e24ae9b7860b..27154a2a63a2 100644 --- a/code/lib/cli/src/automigrate/fixes/bare-mdx-stories-glob.test.ts +++ b/code/lib/cli/src/automigrate/fixes/bare-mdx-stories-glob.test.ts @@ -1,7 +1,7 @@ import { describe, afterEach, it, expect, vi } from 'vitest'; import type { StorybookConfigRaw } from '@storybook/types'; -import type { PackageJson } from '../../js-package-manager'; +import type { PackageJson } from '@storybook/core-common'; import { ansiRegex } from '../helpers/cleanLog'; import { makePackageManager } from '../helpers/testing-helpers'; import type { BareMdxStoriesGlobRunOptions } from './bare-mdx-stories-glob'; diff --git a/code/lib/cli/src/automigrate/fixes/builder-vite.test.ts b/code/lib/cli/src/automigrate/fixes/builder-vite.test.ts index c8b9c70cf8e1..450e770635e1 100644 --- a/code/lib/cli/src/automigrate/fixes/builder-vite.test.ts +++ b/code/lib/cli/src/automigrate/fixes/builder-vite.test.ts @@ -1,7 +1,7 @@ import { describe, afterEach, it, expect, vi } from 'vitest'; import type { StorybookConfigRaw } from '@storybook/types'; import { makePackageManager } from '../helpers/testing-helpers'; -import type { PackageJson } from '../../js-package-manager'; +import type { PackageJson } from '@storybook/core-common'; import { builderVite } from './builder-vite'; const checkBuilderVite = async ({ diff --git a/code/lib/cli/src/automigrate/fixes/builder-vite.ts b/code/lib/cli/src/automigrate/fixes/builder-vite.ts index 6a46f16f15bd..8662bb6cc21b 100644 --- a/code/lib/cli/src/automigrate/fixes/builder-vite.ts +++ b/code/lib/cli/src/automigrate/fixes/builder-vite.ts @@ -4,7 +4,7 @@ import { dedent } from 'ts-dedent'; import { writeConfig } from '@storybook/csf-tools'; import type { Fix } from '../types'; -import type { PackageJson } from '../../js-package-manager'; +import type { PackageJson } from '@storybook/core-common'; import { updateMainConfig } from '../helpers/mainConfigFile'; import { getStorybookVersionSpecifier } from '../../helpers'; diff --git a/code/lib/cli/src/automigrate/fixes/cra5.test.ts b/code/lib/cli/src/automigrate/fixes/cra5.test.ts index 31eea811c41f..47a3afe13eb7 100644 --- a/code/lib/cli/src/automigrate/fixes/cra5.test.ts +++ b/code/lib/cli/src/automigrate/fixes/cra5.test.ts @@ -1,6 +1,6 @@ import { describe, afterEach, it, expect, vi } from 'vitest'; import type { StorybookConfigRaw } from '@storybook/types'; -import type { JsPackageManager } from '../../js-package-manager'; +import type { JsPackageManager } from '@storybook/core-common'; import { cra5 } from './cra5'; const checkCra5 = async ({ diff --git a/code/lib/cli/src/automigrate/fixes/eslint-plugin.test.ts b/code/lib/cli/src/automigrate/fixes/eslint-plugin.test.ts index 49ccd3d67442..735dc57c8cc4 100644 --- a/code/lib/cli/src/automigrate/fixes/eslint-plugin.test.ts +++ b/code/lib/cli/src/automigrate/fixes/eslint-plugin.test.ts @@ -2,7 +2,7 @@ import { describe, it, expect, vi } from 'vitest'; import { dedent } from 'ts-dedent'; import * as fsExtra from 'fs-extra'; -import type { PackageJson } from '../../js-package-manager'; +import type { PackageJson } from '@storybook/core-common'; import { eslintPlugin } from './eslint-plugin'; import { makePackageManager } from '../helpers/testing-helpers'; diff --git a/code/lib/cli/src/automigrate/fixes/incompatible-addons.test.ts b/code/lib/cli/src/automigrate/fixes/incompatible-addons.test.ts index 75b54bf014eb..d23f369da214 100644 --- a/code/lib/cli/src/automigrate/fixes/incompatible-addons.test.ts +++ b/code/lib/cli/src/automigrate/fixes/incompatible-addons.test.ts @@ -2,7 +2,7 @@ import { describe, afterEach, it, expect, vi } from 'vitest'; import type { StorybookConfig } from '@storybook/types'; import { incompatibleAddons } from './incompatible-addons'; -import type { JsPackageManager } from '../../js-package-manager'; +import type { JsPackageManager } from '@storybook/core-common'; const check = async ({ packageManager, diff --git a/code/lib/cli/src/automigrate/fixes/new-frameworks.test.ts b/code/lib/cli/src/automigrate/fixes/new-frameworks.test.ts index de0a7d41aea9..4cc2669b91ae 100644 --- a/code/lib/cli/src/automigrate/fixes/new-frameworks.test.ts +++ b/code/lib/cli/src/automigrate/fixes/new-frameworks.test.ts @@ -3,7 +3,7 @@ import type { StorybookConfig } from '@storybook/types'; import * as findUp from 'find-up'; import * as rendererHelpers from '../helpers/detectRenderer'; import { newFrameworks } from './new-frameworks'; -import type { JsPackageManager } from '../../js-package-manager'; +import type { JsPackageManager } from '@storybook/core-common'; vi.mock('find-up'); vi.mock('../helpers/detectRenderer', async (importOriginal) => ({ diff --git a/code/lib/cli/src/automigrate/fixes/prompt-remove-react.test.ts b/code/lib/cli/src/automigrate/fixes/prompt-remove-react.test.ts index 851fcd10f12c..7d33c0fe90aa 100644 --- a/code/lib/cli/src/automigrate/fixes/prompt-remove-react.test.ts +++ b/code/lib/cli/src/automigrate/fixes/prompt-remove-react.test.ts @@ -2,7 +2,7 @@ import { vi, describe, it, expect } from 'vitest'; import type { StorybookConfig } from '@storybook/types'; import { glob } from 'glob'; import { removeReactDependency } from './prompt-remove-react'; -import type { JsPackageManager } from '../../js-package-manager'; +import type { JsPackageManager } from '@storybook/core-common'; const check = async ({ packageManagerContent, diff --git a/code/lib/cli/src/automigrate/fixes/remove-global-client-apis.test.ts b/code/lib/cli/src/automigrate/fixes/remove-global-client-apis.test.ts index ac98844d4cc4..895a69ba3b90 100644 --- a/code/lib/cli/src/automigrate/fixes/remove-global-client-apis.test.ts +++ b/code/lib/cli/src/automigrate/fixes/remove-global-client-apis.test.ts @@ -3,7 +3,7 @@ import { describe, it, expect, vi } from 'vitest'; import path from 'path'; import * as fsExtra from 'fs-extra'; -import type { JsPackageManager } from '../../js-package-manager'; +import type { JsPackageManager } from '@storybook/core-common'; import { RemovedAPIs, removedGlobalClientAPIs as migration } from './remove-global-client-apis'; vi.mock('fs-extra', async () => import('../../../../../__mocks__/fs-extra')); diff --git a/code/lib/cli/src/automigrate/fixes/sb-binary.test.ts b/code/lib/cli/src/automigrate/fixes/sb-binary.test.ts index c709e448b746..b1f06e162c30 100644 --- a/code/lib/cli/src/automigrate/fixes/sb-binary.test.ts +++ b/code/lib/cli/src/automigrate/fixes/sb-binary.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect } from 'vitest'; -import type { JsPackageManager } from '../../js-package-manager'; +import type { JsPackageManager } from '@storybook/core-common'; import { sbBinary } from './sb-binary'; const checkStorybookBinary = async ({ diff --git a/code/lib/cli/src/automigrate/fixes/sb-binary.ts b/code/lib/cli/src/automigrate/fixes/sb-binary.ts index 276d10c6178a..4c84cf987d04 100644 --- a/code/lib/cli/src/automigrate/fixes/sb-binary.ts +++ b/code/lib/cli/src/automigrate/fixes/sb-binary.ts @@ -3,7 +3,7 @@ import { dedent } from 'ts-dedent'; import semver from 'semver'; import type { Fix } from '../types'; import { getStorybookVersionSpecifier } from '../../helpers'; -import type { PackageJsonWithDepsAndDevDeps } from '../../js-package-manager'; +import type { PackageJsonWithDepsAndDevDeps } from '@storybook/core-common'; interface SbBinaryRunOptions { storybookVersion: string; diff --git a/code/lib/cli/src/automigrate/fixes/sb-scripts.test.ts b/code/lib/cli/src/automigrate/fixes/sb-scripts.test.ts index 6e876329a94a..0c989b761d40 100644 --- a/code/lib/cli/src/automigrate/fixes/sb-scripts.test.ts +++ b/code/lib/cli/src/automigrate/fixes/sb-scripts.test.ts @@ -1,5 +1,5 @@ import { describe, afterEach, it, expect, vi } from 'vitest'; -import type { JsPackageManager } from '../../js-package-manager'; +import type { JsPackageManager } from '@storybook/core-common'; import { getStorybookScripts, sbScripts } from './sb-scripts'; const checkSbScripts = async ({ diff --git a/code/lib/cli/src/automigrate/fixes/sb-scripts.ts b/code/lib/cli/src/automigrate/fixes/sb-scripts.ts index 5192ccd4e187..7ff111920e27 100644 --- a/code/lib/cli/src/automigrate/fixes/sb-scripts.ts +++ b/code/lib/cli/src/automigrate/fixes/sb-scripts.ts @@ -3,7 +3,7 @@ import { dedent } from 'ts-dedent'; import semver from 'semver'; import type { PackageJson } from '@storybook/types'; import type { Fix } from '../types'; -import type { PackageJsonWithDepsAndDevDeps } from '../../js-package-manager'; +import type { PackageJsonWithDepsAndDevDeps } from '@storybook/core-common'; interface SbScriptsRunOptions { storybookScripts: Record; diff --git a/code/lib/cli/src/automigrate/fixes/storyshots-migration.test.ts b/code/lib/cli/src/automigrate/fixes/storyshots-migration.test.ts index 4a7ccc9d335a..66003509ee5d 100644 --- a/code/lib/cli/src/automigrate/fixes/storyshots-migration.test.ts +++ b/code/lib/cli/src/automigrate/fixes/storyshots-migration.test.ts @@ -2,7 +2,7 @@ import { describe, afterEach, it, expect, vi } from 'vitest'; import type { StorybookConfig } from '@storybook/types'; import { storyshotsMigration } from './storyshots-migration'; -import type { JsPackageManager } from '../../js-package-manager'; +import type { JsPackageManager } from '@storybook/core-common'; const check = async ({ packageManager, diff --git a/code/lib/cli/src/automigrate/fixes/vue3.test.ts b/code/lib/cli/src/automigrate/fixes/vue3.test.ts index dd9977a2bb6a..a578b707fa74 100644 --- a/code/lib/cli/src/automigrate/fixes/vue3.test.ts +++ b/code/lib/cli/src/automigrate/fixes/vue3.test.ts @@ -1,6 +1,6 @@ import { describe, afterEach, it, expect, vi } from 'vitest'; import type { StorybookConfig } from '@storybook/types'; -import type { JsPackageManager } from '../../js-package-manager'; +import type { JsPackageManager } from '@storybook/core-common'; import { vue3 } from './vue3'; const checkVue3 = async ({ diff --git a/code/lib/cli/src/automigrate/fixes/webpack5.test.ts b/code/lib/cli/src/automigrate/fixes/webpack5.test.ts index a8a9c965fc25..1076d89714d0 100644 --- a/code/lib/cli/src/automigrate/fixes/webpack5.test.ts +++ b/code/lib/cli/src/automigrate/fixes/webpack5.test.ts @@ -1,6 +1,6 @@ import { describe, afterEach, it, expect, vi } from 'vitest'; import type { StorybookConfig } from '@storybook/types'; -import type { JsPackageManager } from '../../js-package-manager'; +import type { JsPackageManager } from '@storybook/core-common'; import { webpack5 } from './webpack5'; const checkWebpack5 = async ({ diff --git a/code/lib/cli/src/automigrate/helpers/eslintPlugin.ts b/code/lib/cli/src/automigrate/helpers/eslintPlugin.ts index 992f54d69120..3090de3ef89c 100644 --- a/code/lib/cli/src/automigrate/helpers/eslintPlugin.ts +++ b/code/lib/cli/src/automigrate/helpers/eslintPlugin.ts @@ -1,12 +1,12 @@ import fse, { readFile, readJson, writeJson } from 'fs-extra'; - import { dedent } from 'ts-dedent'; import detectIndent from 'detect-indent'; -import { readConfig, writeConfig } from '@storybook/csf-tools'; import prompts from 'prompts'; import chalk from 'chalk'; -import type { JsPackageManager } from '../../js-package-manager'; -import { paddedLog } from '../../helpers'; + +import { readConfig, writeConfig } from '@storybook/csf-tools'; +import type { JsPackageManager } from '@storybook/core-common'; +import { paddedLog } from '@storybook/core-common'; export const SUPPORTED_ESLINT_EXTENSIONS = ['js', 'cjs', 'json']; const UNSUPPORTED_ESLINT_EXTENSIONS = ['yaml', 'yml']; diff --git a/code/lib/cli/src/automigrate/helpers/getMigrationSummary.test.ts b/code/lib/cli/src/automigrate/helpers/getMigrationSummary.test.ts index 27f2a2a5326b..85de99111c87 100644 --- a/code/lib/cli/src/automigrate/helpers/getMigrationSummary.test.ts +++ b/code/lib/cli/src/automigrate/helpers/getMigrationSummary.test.ts @@ -1,7 +1,7 @@ import { describe, expect, vi, it } from 'vitest'; import { getMigrationSummary } from './getMigrationSummary'; import { FixStatus } from '../types'; -import type { InstallationMetadata } from '../../js-package-manager/types'; +import type { InstallationMetadata } from '@storybook/core-common'; vi.mock('boxen', () => ({ default: vi.fn((str, { title = '' }) => `${title}\n\n${str.replace(/\x1b\[[0-9;]*[mG]/g, '')}`), diff --git a/code/lib/cli/src/automigrate/helpers/getMigrationSummary.ts b/code/lib/cli/src/automigrate/helpers/getMigrationSummary.ts index 12b576ee12f7..1dcd0a0658c7 100644 --- a/code/lib/cli/src/automigrate/helpers/getMigrationSummary.ts +++ b/code/lib/cli/src/automigrate/helpers/getMigrationSummary.ts @@ -1,9 +1,9 @@ import chalk from 'chalk'; import boxen from 'boxen'; import dedent from 'ts-dedent'; +import type { InstallationMetadata } from '@storybook/core-common'; import type { FixSummary } from '../types'; import { FixStatus } from '../types'; -import type { InstallationMetadata } from '../../js-package-manager/types'; import { getDuplicatedDepsWarnings } from '../../doctor/getDuplicatedDepsWarnings'; export const messageDivider = '\n\n'; diff --git a/code/lib/cli/src/automigrate/helpers/mainConfigFile.ts b/code/lib/cli/src/automigrate/helpers/mainConfigFile.ts index 2dd9de625704..c816ee90c5ec 100644 --- a/code/lib/cli/src/automigrate/helpers/mainConfigFile.ts +++ b/code/lib/cli/src/automigrate/helpers/mainConfigFile.ts @@ -11,8 +11,8 @@ import { readConfig, writeConfig as writeConfigFile } from '@storybook/csf-tools import chalk from 'chalk'; import dedent from 'ts-dedent'; import path from 'path'; -import type { JsPackageManager } from '../../js-package-manager'; -import { getStorybookVersion } from '../../utils'; +import type { JsPackageManager } from '@storybook/core-common'; +import { getStorybookVersion } from '@storybook/core-common'; const logger = console; diff --git a/code/lib/cli/src/automigrate/helpers/new-frameworks-utils.test.ts b/code/lib/cli/src/automigrate/helpers/new-frameworks-utils.test.ts index d4395cee5c0f..eda179500c26 100644 --- a/code/lib/cli/src/automigrate/helpers/new-frameworks-utils.test.ts +++ b/code/lib/cli/src/automigrate/helpers/new-frameworks-utils.test.ts @@ -4,7 +4,7 @@ import { detectBuilderInfo as _getBuilderInfo, getNextjsAddonOptions, } from './new-frameworks-utils'; -import type { JsPackageManager } from '../../js-package-manager'; +import type { JsPackageManager } from '@storybook/core-common'; vi.mock('find-up'); diff --git a/code/lib/cli/src/automigrate/helpers/new-frameworks-utils.ts b/code/lib/cli/src/automigrate/helpers/new-frameworks-utils.ts index 2232993d9aaf..a981d7c1ce61 100644 --- a/code/lib/cli/src/automigrate/helpers/new-frameworks-utils.ts +++ b/code/lib/cli/src/automigrate/helpers/new-frameworks-utils.ts @@ -1,7 +1,7 @@ import { frameworkPackages } from '@storybook/core-common'; import type { Preset, StorybookConfigRaw } from '@storybook/types'; import findUp from 'find-up'; -import type { JsPackageManager } from '../../js-package-manager'; +import type { JsPackageManager } from '@storybook/core-common'; import { getBuilderPackageName, getFrameworkPackageName } from './mainConfigFile'; const logger = console; diff --git a/code/lib/cli/src/automigrate/helpers/testing-helpers.ts b/code/lib/cli/src/automigrate/helpers/testing-helpers.ts index e6c01177f693..9e681b21f564 100644 --- a/code/lib/cli/src/automigrate/helpers/testing-helpers.ts +++ b/code/lib/cli/src/automigrate/helpers/testing-helpers.ts @@ -1,5 +1,5 @@ import { vi } from 'vitest'; -import type { JsPackageManager, PackageJson } from '../../js-package-manager'; +import type { JsPackageManager, PackageJson } from '@storybook/core-common'; vi.mock('./mainConfigFile', async (importOriginal) => ({ ...(await importOriginal()), diff --git a/code/lib/cli/src/automigrate/index.ts b/code/lib/cli/src/automigrate/index.ts index c1eabf8baa77..604393de492c 100644 --- a/code/lib/cli/src/automigrate/index.ts +++ b/code/lib/cli/src/automigrate/index.ts @@ -4,19 +4,22 @@ import boxen from 'boxen'; import { createWriteStream, move, remove } from 'fs-extra'; import tempy from 'tempy'; import dedent from 'ts-dedent'; - import { join } from 'path'; -import { getStorybookInfo, loadMainConfig } from '@storybook/core-common'; import invariant from 'tiny-invariant'; -import { JsPackageManagerFactory } from '../js-package-manager'; -import type { PackageManagerName } from '../js-package-manager'; + +import { + getStorybookInfo, + loadMainConfig, + getStorybookVersion, + JsPackageManagerFactory, +} from '@storybook/core-common'; +import type { PackageManagerName } from '@storybook/core-common'; import type { Fix, FixId, FixOptions, FixSummary } from './fixes'; import { FixStatus, PreCheckFailure, allFixes } from './fixes'; import { cleanLog } from './helpers/cleanLog'; import { getMigrationSummary } from './helpers/getMigrationSummary'; import { getStorybookData } from './helpers/mainConfigFile'; -import { getStorybookVersion } from '../utils'; const logger = console; const LOG_FILE_NAME = 'migration-storybook.log'; diff --git a/code/lib/cli/src/automigrate/types.ts b/code/lib/cli/src/automigrate/types.ts index 5949d70db98e..0fa57c1fdfcc 100644 --- a/code/lib/cli/src/automigrate/types.ts +++ b/code/lib/cli/src/automigrate/types.ts @@ -1,5 +1,5 @@ import type { StorybookConfigRaw } from '@storybook/types'; -import type { JsPackageManager, PackageManagerName } from '../js-package-manager'; +import type { JsPackageManager, PackageManagerName } from '@storybook/core-common'; export interface CheckOptions { packageManager: JsPackageManager; diff --git a/code/lib/cli/src/detect.test.ts b/code/lib/cli/src/detect.test.ts index f3e45d730d2f..c5140c46a1e3 100644 --- a/code/lib/cli/src/detect.test.ts +++ b/code/lib/cli/src/detect.test.ts @@ -3,7 +3,7 @@ import * as fs from 'fs'; import { logger } from '@storybook/node-logger'; import { detect, detectFrameworkPreset, detectLanguage } from './detect'; import { ProjectType, SupportedLanguage } from './project_types'; -import type { JsPackageManager, PackageJsonWithMaybeDeps } from './js-package-manager'; +import type { JsPackageManager, PackageJsonWithMaybeDeps } from '@storybook/core-common'; vi.mock('./helpers', () => ({ isNxProject: vi.fn(), diff --git a/code/lib/cli/src/detect.ts b/code/lib/cli/src/detect.ts index ca2399a14165..16c592236909 100644 --- a/code/lib/cli/src/detect.ts +++ b/code/lib/cli/src/detect.ts @@ -13,9 +13,9 @@ import { unsupportedTemplate, CoreBuilder, } from './project_types'; -import { commandLog, isNxProject } from './helpers'; -import type { JsPackageManager, PackageJsonWithMaybeDeps } from './js-package-manager'; -import { HandledError } from './HandledError'; +import { isNxProject } from './helpers'; +import type { JsPackageManager, PackageJsonWithMaybeDeps } from '@storybook/core-common'; +import { commandLog, HandledError } from '@storybook/core-common'; const viteConfigFiles = ['vite.config.ts', 'vite.config.js', 'vite.config.mjs']; const webpackConfigFiles = ['webpack.config.js']; diff --git a/code/lib/cli/src/dirs.ts b/code/lib/cli/src/dirs.ts index 554b003a97a3..213b1877aaee 100644 --- a/code/lib/cli/src/dirs.ts +++ b/code/lib/cli/src/dirs.ts @@ -7,8 +7,8 @@ import * as tempy from 'tempy'; import invariant from 'tiny-invariant'; import { externalFrameworks } from './project_types'; import type { SupportedFrameworks, SupportedRenderers } from './project_types'; -import type { JsPackageManager } from './js-package-manager'; -import versions from './versions'; +import type { JsPackageManager } from '@storybook/core-common'; +import { versions } from '@storybook/core-common'; export function getCliDir() { return dirname(require.resolve('@storybook/cli/package.json')); diff --git a/code/lib/cli/src/doctor/getDuplicatedDepsWarnings.ts b/code/lib/cli/src/doctor/getDuplicatedDepsWarnings.ts index 3c72d6c21bc0..d76c01d9ee97 100644 --- a/code/lib/cli/src/doctor/getDuplicatedDepsWarnings.ts +++ b/code/lib/cli/src/doctor/getDuplicatedDepsWarnings.ts @@ -1,7 +1,7 @@ import chalk from 'chalk'; import { frameworkPackages, rendererPackages } from '@storybook/core-common'; +import type { InstallationMetadata } from '@storybook/core-common'; import { hasMultipleVersions } from './hasMultipleVersions'; -import type { InstallationMetadata } from '../js-package-manager/types'; export const messageDivider = '\n\n'; diff --git a/code/lib/cli/src/doctor/getIncompatibleAddons.ts b/code/lib/cli/src/doctor/getIncompatibleAddons.ts index 0a0fc5dc2d19..e46297e94087 100644 --- a/code/lib/cli/src/doctor/getIncompatibleAddons.ts +++ b/code/lib/cli/src/doctor/getIncompatibleAddons.ts @@ -2,7 +2,7 @@ import type { StorybookConfig } from '@storybook/types'; import type { SemVer } from 'semver'; import semver from 'semver'; import { getAddonNames } from '../automigrate/helpers/mainConfigFile'; -import { JsPackageManagerFactory } from '../js-package-manager'; +import { JsPackageManagerFactory } from '@storybook/core-common'; export const getIncompatibleAddons = async ( mainConfig: StorybookConfig, diff --git a/code/lib/cli/src/doctor/getMismatchingVersionsWarning.ts b/code/lib/cli/src/doctor/getMismatchingVersionsWarning.ts index 68e93f491075..18f0008c537f 100644 --- a/code/lib/cli/src/doctor/getMismatchingVersionsWarning.ts +++ b/code/lib/cli/src/doctor/getMismatchingVersionsWarning.ts @@ -1,8 +1,7 @@ import chalk from 'chalk'; import semver from 'semver'; -import { frameworkPackages } from '@storybook/core-common'; -import type { InstallationMetadata } from '../js-package-manager/types'; -import storybookCorePackages from '../versions'; +import { frameworkPackages, versions as storybookCorePackages } from '@storybook/core-common'; +import type { InstallationMetadata } from '@storybook/core-common'; function getPrimaryVersion(name: string | undefined, installationMetadata?: InstallationMetadata) { if (!name) { diff --git a/code/lib/cli/src/doctor/index.ts b/code/lib/cli/src/doctor/index.ts index a6a304ce1ea6..9aaeede8bc0f 100644 --- a/code/lib/cli/src/doctor/index.ts +++ b/code/lib/cli/src/doctor/index.ts @@ -5,8 +5,8 @@ import tempy from 'tempy'; import dedent from 'ts-dedent'; import { join } from 'path'; -import { JsPackageManagerFactory } from '../js-package-manager'; -import type { PackageManagerName } from '../js-package-manager'; +import { JsPackageManagerFactory } from '@storybook/core-common'; +import type { PackageManagerName } from '@storybook/core-common'; import { getStorybookData } from '../automigrate/helpers/mainConfigFile'; import { cleanLog } from '../automigrate/helpers/cleanLog'; import { incompatibleAddons } from '../automigrate/fixes/incompatible-addons'; diff --git a/code/lib/cli/src/generate.ts b/code/lib/cli/src/generate.ts index 9e90f2f89f90..a3175e5d0b54 100644 --- a/code/lib/cli/src/generate.ts +++ b/code/lib/cli/src/generate.ts @@ -3,15 +3,16 @@ import chalk from 'chalk'; import envinfo from 'envinfo'; import leven from 'leven'; import { sync as readUpSync } from 'read-pkg-up'; +import invariant from 'tiny-invariant'; import { logger } from '@storybook/node-logger'; import { addToGlobalContext } from '@storybook/telemetry'; +import { parseList, getEnvConfig, JsPackageManagerFactory, versions } from '@storybook/core-common'; -import invariant from 'tiny-invariant'; import type { CommandOptions } from './generators/types'; import { initiate } from './initiate'; import { add } from './add'; -import { remove } from './remove'; +import { removeAddon as remove } from '@storybook/core-common'; import { migrate } from './migrate'; import { upgrade, type UpgradeOptions } from './upgrade'; import { sandbox } from './sandbox'; @@ -19,9 +20,6 @@ import { link } from './link'; import { automigrate } from './automigrate'; import { dev } from './dev'; import { build } from './build'; -import { parseList, getEnvConfig } from './utils'; -import versions from './versions'; -import { JsPackageManagerFactory } from './js-package-manager'; import { doctor } from './doctor'; addToGlobalContext('cliVersion', versions.storybook); diff --git a/code/lib/cli/src/generators/ANGULAR/index.ts b/code/lib/cli/src/generators/ANGULAR/index.ts index 27b10dfcc2ca..cedc43def3b4 100644 --- a/code/lib/cli/src/generators/ANGULAR/index.ts +++ b/code/lib/cli/src/generators/ANGULAR/index.ts @@ -1,10 +1,11 @@ import { join } from 'path'; +import { paddedLog } from '@storybook/core-common'; import { baseGenerator } from '../baseGenerator'; import type { Generator } from '../types'; import { CoreBuilder } from '../../project_types'; import { AngularJSON, compoDocPreviewPrefix, promptForCompoDocs } from './helpers'; import { getCliDir } from '../../dirs'; -import { paddedLog, copyTemplate } from '../../helpers'; +import { copyTemplate } from '../../helpers'; const generator: Generator<{ projectName: string }> = async ( packageManager, diff --git a/code/lib/cli/src/generators/REACT_NATIVE/index.ts b/code/lib/cli/src/generators/REACT_NATIVE/index.ts index d87cd187e55b..a77878dcc191 100644 --- a/code/lib/cli/src/generators/REACT_NATIVE/index.ts +++ b/code/lib/cli/src/generators/REACT_NATIVE/index.ts @@ -1,5 +1,5 @@ import { copyTemplateFiles, getBabelDependencies } from '../../helpers'; -import type { JsPackageManager } from '../../js-package-manager'; +import type { JsPackageManager } from '@storybook/core-common'; import type { NpmOptions } from '../../NpmOptions'; import { SupportedLanguage } from '../../project_types'; diff --git a/code/lib/cli/src/generators/REACT_SCRIPTS/index.ts b/code/lib/cli/src/generators/REACT_SCRIPTS/index.ts index 048fca45d327..97a8c906d526 100644 --- a/code/lib/cli/src/generators/REACT_SCRIPTS/index.ts +++ b/code/lib/cli/src/generators/REACT_SCRIPTS/index.ts @@ -1,12 +1,12 @@ import path from 'path'; import fs from 'fs'; import semver from 'semver'; - import dedent from 'ts-dedent'; +import { versions } from '@storybook/core-common'; + import { baseGenerator } from '../baseGenerator'; import type { Generator } from '../types'; import { CoreBuilder } from '../../project_types'; -import versions from '../../versions'; const generator: Generator = async (packageManager, npmOptions, options) => { const monorepoRootPath = path.join(__dirname, '..', '..', '..', '..', '..', '..'); diff --git a/code/lib/cli/src/generators/baseGenerator.ts b/code/lib/cli/src/generators/baseGenerator.ts index e28979f1e878..0cd4e6fb5041 100644 --- a/code/lib/cli/src/generators/baseGenerator.ts +++ b/code/lib/cli/src/generators/baseGenerator.ts @@ -3,14 +3,13 @@ import fse from 'fs-extra'; import { dedent } from 'ts-dedent'; import ora from 'ora'; import invariant from 'tiny-invariant'; +import type { JsPackageManager } from '@storybook/core-common'; +import { getPackageDetails, versions as packageVersions } from '@storybook/core-common'; import type { NpmOptions } from '../NpmOptions'; import type { SupportedRenderers, SupportedFrameworks, Builder } from '../project_types'; import { SupportedLanguage, externalFrameworks } from '../project_types'; import { copyTemplateFiles } from '../helpers'; import { configureMain, configurePreview } from './configure'; -import type { JsPackageManager } from '../js-package-manager'; -import { getPackageDetails } from '../js-package-manager'; -import packageVersions from '../versions'; import type { FrameworkOptions, GeneratorOptions } from './types'; import { configureEslintPlugin, extractEslintInfo } from '../automigrate/helpers/eslintPlugin'; import { detectBuilder } from '../detect'; diff --git a/code/lib/cli/src/generators/types.ts b/code/lib/cli/src/generators/types.ts index c2da3c3b32e8..763df2c4d1a6 100644 --- a/code/lib/cli/src/generators/types.ts +++ b/code/lib/cli/src/generators/types.ts @@ -1,6 +1,6 @@ import type { NpmOptions } from '../NpmOptions'; import type { SupportedLanguage, Builder, ProjectType } from '../project_types'; -import type { JsPackageManager, PackageManagerName } from '../js-package-manager/JsPackageManager'; +import type { JsPackageManager, PackageManagerName } from '@storybook/core-common'; import type { FrameworkPreviewParts } from './configure'; export type GeneratorOptions = { diff --git a/code/lib/cli/src/helpers.test.ts b/code/lib/cli/src/helpers.test.ts index 6628c7754ec3..73d7efa548bd 100644 --- a/code/lib/cli/src/helpers.test.ts +++ b/code/lib/cli/src/helpers.test.ts @@ -4,7 +4,7 @@ import fse from 'fs-extra'; import { sep } from 'path'; import * as helpers from './helpers'; import { IS_WINDOWS } from '../../../vitest.helpers'; -import type { JsPackageManager } from './js-package-manager'; +import type { JsPackageManager } from '@storybook/core-common'; import type { SupportedRenderers } from './project_types'; import { SupportedLanguage } from './project_types'; diff --git a/code/lib/cli/src/helpers.ts b/code/lib/cli/src/helpers.ts index c6be518ecf0d..95c22ee624f1 100644 --- a/code/lib/cli/src/helpers.ts +++ b/code/lib/cli/src/helpers.ts @@ -12,10 +12,10 @@ import type { JsPackageManager, PackageJson, PackageJsonWithDepsAndDevDeps, -} from './js-package-manager'; +} from '@storybook/core-common'; import type { SupportedFrameworks, SupportedRenderers } from './project_types'; import { SupportedLanguage } from './project_types'; -import storybookMonorepoPackages from './versions'; +import { versions as storybookMonorepoPackages } from '@storybook/core-common'; const logger = console; @@ -46,68 +46,6 @@ export const writeFileAsJson = (jsonPath: string, content: unknown) => { return true; }; -export const commandLog = (message: string) => { - process.stdout.write(chalk.cyan(' • ') + message); - - // Need `void` to be able to use this function in a then of a Promise - return (errorMessage?: string | void, errorInfo?: string) => { - if (errorMessage) { - process.stdout.write(`. ${chalk.red('✖')}\n`); - logger.error(`\n ${chalk.red(errorMessage)}`); - - if (!errorInfo) { - return; - } - - const newErrorInfo = errorInfo - .split('\n') - .map((line) => ` ${chalk.dim(line)}`) - .join('\n'); - logger.error(`${newErrorInfo}\n`); - return; - } - - process.stdout.write(`. ${chalk.green('✓')}\n`); - }; -}; - -export function paddedLog(message: string) { - const newMessage = message - .split('\n') - .map((line) => ` ${line}`) - .join('\n'); - - logger.log(newMessage); -} - -export function getChars(char: string, amount: number) { - let line = ''; - for (let lc = 0; lc < amount; lc += 1) { - line += char; - } - - return line; -} - -export function codeLog(codeLines: string[], leftPadAmount?: number) { - let maxLength = 0; - const newLines = codeLines.map((line) => { - maxLength = line.length > maxLength ? line.length : maxLength; - return line; - }); - - const finalResult = newLines - .map((line) => { - const rightPadAmount = maxLength - line.length; - let newLine = line + getChars(' ', rightPadAmount); - newLine = getChars(' ', leftPadAmount || 2) + chalk.inverse(` ${newLine} `); - return newLine; - }) - .join('\n'); - - logger.log(finalResult); -} - /** * Detect if any babel dependencies need to be added to the project * This is currently used by react-native generator diff --git a/code/lib/cli/src/index.ts b/code/lib/cli/src/index.ts index 4e4dd42c3a4f..5d093178b5b4 100644 --- a/code/lib/cli/src/index.ts +++ b/code/lib/cli/src/index.ts @@ -1,6 +1,5 @@ -import versions from './versions'; +import { deprecate } from '@storybook/node-logger'; -export { versions }; - -export * from './js-package-manager'; -export * from './utils'; +deprecate( + 'Exports from @storybook/cli are removed. Please import from @storybook/core-common instead.' +); diff --git a/code/lib/cli/src/initiate.ts b/code/lib/cli/src/initiate.ts index 6f753af9212a..54321ccb96a9 100644 --- a/code/lib/cli/src/initiate.ts +++ b/code/lib/cli/src/initiate.ts @@ -4,6 +4,15 @@ import prompts from 'prompts'; import { telemetry } from '@storybook/telemetry'; import { withTelemetry } from '@storybook/core-server'; import { NxProjectDetectedError } from '@storybook/core-events/server-errors'; +import { + versions, + HandledError, + JsPackageManagerFactory, + commandLog, + codeLog, + paddedLog, +} from '@storybook/core-common'; +import type { JsPackageManager } from '@storybook/core-common'; import dedent from 'ts-dedent'; import boxen from 'boxen'; @@ -11,7 +20,6 @@ import { lt, prerelease } from 'semver'; import type { Builder } from './project_types'; import { installableProjectTypes, ProjectType } from './project_types'; import { detect, isStorybookInstantiated, detectLanguage, detectPnp } from './detect'; -import { commandLog, codeLog, paddedLog } from './helpers'; import angularGenerator from './generators/ANGULAR'; import emberGenerator from './generators/EMBER'; import reactGenerator from './generators/REACT'; @@ -28,13 +36,9 @@ import qwikGenerator from './generators/QWIK'; import svelteKitGenerator from './generators/SVELTEKIT'; import solidGenerator from './generators/SOLID'; import serverGenerator from './generators/SERVER'; -import type { JsPackageManager } from './js-package-manager'; -import { JsPackageManagerFactory } from './js-package-manager'; import type { NpmOptions } from './NpmOptions'; import type { CommandOptions, GeneratorOptions } from './generators/types'; -import { HandledError } from './HandledError'; import { currentDirectoryIsEmpty, scaffoldNewProject } from './scaffold-new-project'; -import versions from './versions'; const logger = console; diff --git a/code/lib/cli/src/migrate.ts b/code/lib/cli/src/migrate.ts index 2d0a3b6af16d..5e0093d2480d 100644 --- a/code/lib/cli/src/migrate.ts +++ b/code/lib/cli/src/migrate.ts @@ -1,7 +1,7 @@ import { listCodemods, runCodemod } from '@storybook/codemod'; import { runFixes } from './automigrate'; import { bareMdxStoriesGlob } from './automigrate/fixes/bare-mdx-stories-glob'; -import { JsPackageManagerFactory } from './js-package-manager'; +import { JsPackageManagerFactory } from '@storybook/core-common'; import { getStorybookVersionSpecifier } from './helpers'; const logger = console; diff --git a/code/lib/cli/src/sandbox.ts b/code/lib/cli/src/sandbox.ts index 5cf2b09bb049..d7d5892abd5b 100644 --- a/code/lib/cli/src/sandbox.ts +++ b/code/lib/cli/src/sandbox.ts @@ -10,9 +10,9 @@ import invariant from 'tiny-invariant'; import { lt, prerelease } from 'semver'; import type { Template, TemplateKey } from './sandbox-templates'; import { allTemplates as TEMPLATES } from './sandbox-templates'; -import type { PackageJson, PackageManagerName } from './js-package-manager'; -import { JsPackageManagerFactory } from './js-package-manager'; -import versions from './versions'; +import type { PackageJson, PackageManagerName } from '@storybook/core-common'; +import { JsPackageManagerFactory } from '@storybook/core-common'; +import { versions } from '@storybook/core-common'; import { doInitiate } from './initiate'; const logger = console; diff --git a/code/lib/cli/src/scaffold-new-project.ts b/code/lib/cli/src/scaffold-new-project.ts index d33d82a32d7a..16ca8b7d444c 100644 --- a/code/lib/cli/src/scaffold-new-project.ts +++ b/code/lib/cli/src/scaffold-new-project.ts @@ -10,7 +10,7 @@ import { telemetry } from '@storybook/telemetry'; import { GenerateNewProjectOnInitError } from '@storybook/core-events/server-errors'; import { logger } from '@storybook/node-logger'; -import type { PackageManagerName } from './js-package-manager'; +import type { PackageManagerName } from '@storybook/core-common'; import type { CommandOptions } from './generators/types'; type CoercedPackageManagerName = 'npm' | 'yarn' | 'pnpm'; diff --git a/code/lib/cli/src/upgrade.test.ts b/code/lib/cli/src/upgrade.test.ts index be025028c495..3987b9b35789 100644 --- a/code/lib/cli/src/upgrade.test.ts +++ b/code/lib/cli/src/upgrade.test.ts @@ -5,13 +5,15 @@ import { UpgradeStorybookToSameVersionError, } from '@storybook/core-events/server-errors'; import { doUpgrade, getStorybookVersion } from './upgrade'; -import type versions from './versions'; + +import type * as sbcc from '@storybook/core-common'; vi.mock('@storybook/telemetry'); -vi.mock('./versions', async (importOriginal) => { - const originalVersions = ((await importOriginal()) as { default: typeof versions }).default; +vi.mock('@storybook/core-common', async (importOriginal) => { + const originalModule = (await importOriginal()) as typeof sbcc; return { - default: Object.keys(originalVersions).reduce( + ...originalModule, + versions: Object.keys(originalModule.versions).reduce( (acc, key) => { acc[key] = '8.0.0'; return acc; diff --git a/code/lib/cli/src/upgrade.ts b/code/lib/cli/src/upgrade.ts index 29115c4244cf..14135e44b2e7 100644 --- a/code/lib/cli/src/upgrade.ts +++ b/code/lib/cli/src/upgrade.ts @@ -11,12 +11,15 @@ import { import chalk from 'chalk'; import dedent from 'ts-dedent'; import boxen from 'boxen'; -import type { PackageManagerName } from './js-package-manager'; -import { JsPackageManagerFactory } from './js-package-manager'; -import { coerceSemver, commandLog } from './helpers'; +import type { PackageManagerName } from '@storybook/core-common'; +import { + JsPackageManagerFactory, + isCorePackage, + versions, + commandLog, +} from '@storybook/core-common'; +import { coerceSemver } from './helpers'; import { automigrate } from './automigrate'; -import { isCorePackage } from './utils'; -import versions from './versions'; type Package = { package: string; @@ -110,7 +113,8 @@ export const doUpgrade = async ({ }: UpgradeOptions) => { const packageManager = JsPackageManagerFactory.getPackageManager({ force: pkgMgr }); - const beforeVersion = await getStorybookCoreVersion(); + // If we can't determine the existing version (Yarn PnP), fallback to v0.0.0 to not block the upgrade + const beforeVersion = (await getStorybookCoreVersion()) ?? '0.0.0'; const currentVersion = versions['@storybook/cli']; const isCanary = currentVersion.startsWith('0.0.0'); diff --git a/code/lib/cli/tsconfig.json b/code/lib/cli/tsconfig.json index 9c5db279ca2d..e32ebe0f7b90 100644 --- a/code/lib/cli/tsconfig.json +++ b/code/lib/cli/tsconfig.json @@ -1,11 +1,15 @@ { "extends": "../../tsconfig.json", "compilerOptions": { - "types": ["node"], + "types": [ + "node" + ], "strict": true, "skipLibCheck": true, "resolveJsonModule": true, "noEmit": true }, - "include": ["src/**/*"] -} + "include": [ + "src/**/*" + ] +} \ No newline at end of file diff --git a/code/lib/core-common/package.json b/code/lib/core-common/package.json index 5867b044e06c..aa431de62d95 100644 --- a/code/lib/core-common/package.json +++ b/code/lib/core-common/package.json @@ -45,15 +45,20 @@ }, "dependencies": { "@storybook/core-events": "workspace:*", + "@storybook/csf-tools": "workspace:*", "@storybook/node-logger": "workspace:*", "@storybook/types": "workspace:*", "@types/find-cache-dir": "^3.2.1", "@types/node": "^18.0.0", "@types/node-fetch": "^2.6.4", "@types/pretty-hrtime": "^1.0.0", + "@yarnpkg/fslib": "2.10.3", + "@yarnpkg/libzip": "2.3.0", "chalk": "^4.1.0", + "cross-spawn": "^7.0.3", "esbuild": "^0.18.0", "esbuild-register": "^3.5.0", + "execa": "^5.0.0", "file-system-cache": "2.3.0", "find-cache-dir": "^3.0.0", "find-up": "^5.0.0", @@ -66,7 +71,11 @@ "pkg-dir": "^5.0.0", "pretty-hrtime": "^1.0.3", "resolve-from": "^5.0.0", - "ts-dedent": "^2.0.0" + "semver": "^7.3.7", + "tempy": "^1.0.1", + "tiny-invariant": "^1.3.1", + "ts-dedent": "^2.0.0", + "util": "^0.12.4" }, "devDependencies": { "@types/fs-extra": "^11.0.1", @@ -81,6 +90,7 @@ "access": "public" }, "bundler": { + "pre": "./scripts/generate-sb-packages-versions.js", "entries": [ "./src/index.ts" ], diff --git a/code/lib/cli/scripts/generate-sb-packages-versions.js b/code/lib/core-common/scripts/generate-sb-packages-versions.js similarity index 100% rename from code/lib/cli/scripts/generate-sb-packages-versions.js rename to code/lib/core-common/scripts/generate-sb-packages-versions.js diff --git a/code/lib/core-common/src/index.ts b/code/lib/core-common/src/index.ts index 2af1d1100798..e8f02195ba9d 100644 --- a/code/lib/core-common/src/index.ts +++ b/code/lib/core-common/src/index.ts @@ -3,6 +3,7 @@ export * from './presets'; export * from './utils/cache'; +export * from './utils/cli'; export * from './utils/check-addon-order'; export * from './utils/envs'; export * from './utils/common-glob-options'; @@ -13,6 +14,7 @@ export * from './utils/get-storybook-configuration'; export * from './utils/get-storybook-info'; export * from './utils/get-storybook-refs'; export * from './utils/glob-to-regexp'; +export * from './utils/HandledError'; export * from './utils/handlebars'; export * from './utils/interpolate'; export * from './utils/interpret-files'; @@ -21,10 +23,12 @@ export * from './utils/load-custom-presets'; export * from './utils/load-main-config'; export * from './utils/load-manager-or-addons-file'; export * from './utils/load-preview-or-config-file'; +export * from './utils/log'; export * from './utils/log-config'; export * from './utils/normalize-stories'; export * from './utils/paths'; export * from './utils/readTemplate'; +export * from './utils/remove'; export * from './utils/resolve-path-in-sb-cache'; export * from './utils/symlinks'; export * from './utils/template'; @@ -32,5 +36,9 @@ export * from './utils/validate-config'; export * from './utils/validate-configuration-files'; export * from './utils/satisfies'; export * from './utils/strip-abs-node-modules-path'; +export * from './js-package-manager'; + +import versions from './versions'; +export { versions }; export { createFileSystemCache } from './utils/file-cache'; diff --git a/code/lib/cli/src/js-package-manager/JsPackageManager.ts b/code/lib/core-common/src/js-package-manager/JsPackageManager.ts similarity index 99% rename from code/lib/cli/src/js-package-manager/JsPackageManager.ts rename to code/lib/core-common/src/js-package-manager/JsPackageManager.ts index f8cb289c837b..79dfe4a5fb29 100644 --- a/code/lib/cli/src/js-package-manager/JsPackageManager.ts +++ b/code/lib/core-common/src/js-package-manager/JsPackageManager.ts @@ -8,11 +8,11 @@ import fs from 'fs'; import dedent from 'ts-dedent'; import { readFile, writeFile, readFileSync } from 'fs-extra'; import invariant from 'tiny-invariant'; -import { commandLog } from '../helpers'; +import { commandLog } from '../utils/log'; import type { PackageJson, PackageJsonWithDepsAndDevDeps } from './PackageJson'; import storybookPackagesVersions from '../versions'; import type { InstallationMetadata } from './types'; -import { HandledError } from '../HandledError'; +import { HandledError } from '../utils/HandledError'; const logger = console; diff --git a/code/lib/cli/src/js-package-manager/JsPackageManagerFactory.test.ts b/code/lib/core-common/src/js-package-manager/JsPackageManagerFactory.test.ts similarity index 100% rename from code/lib/cli/src/js-package-manager/JsPackageManagerFactory.test.ts rename to code/lib/core-common/src/js-package-manager/JsPackageManagerFactory.test.ts diff --git a/code/lib/cli/src/js-package-manager/JsPackageManagerFactory.ts b/code/lib/core-common/src/js-package-manager/JsPackageManagerFactory.ts similarity index 100% rename from code/lib/cli/src/js-package-manager/JsPackageManagerFactory.ts rename to code/lib/core-common/src/js-package-manager/JsPackageManagerFactory.ts diff --git a/code/lib/cli/src/js-package-manager/NPMProxy.test.ts b/code/lib/core-common/src/js-package-manager/NPMProxy.test.ts similarity index 99% rename from code/lib/cli/src/js-package-manager/NPMProxy.test.ts rename to code/lib/core-common/src/js-package-manager/NPMProxy.test.ts index c73e5829a829..bd5a4372bccf 100644 --- a/code/lib/cli/src/js-package-manager/NPMProxy.test.ts +++ b/code/lib/core-common/src/js-package-manager/NPMProxy.test.ts @@ -2,7 +2,7 @@ import { describe, beforeEach, it, expect, vi } from 'vitest'; import { NPMProxy } from './NPMProxy'; // mock createLogStream -vi.mock('../utils', () => ({ +vi.mock('../utils/cli', () => ({ createLogStream: vi.fn(() => ({ logStream: '', readLogFile: vi.fn(), diff --git a/code/lib/cli/src/js-package-manager/NPMProxy.ts b/code/lib/core-common/src/js-package-manager/NPMProxy.ts similarity index 99% rename from code/lib/cli/src/js-package-manager/NPMProxy.ts rename to code/lib/core-common/src/js-package-manager/NPMProxy.ts index a8db9a836047..6e4e2939fca1 100644 --- a/code/lib/cli/src/js-package-manager/NPMProxy.ts +++ b/code/lib/core-common/src/js-package-manager/NPMProxy.ts @@ -9,7 +9,7 @@ import { logger } from '@storybook/node-logger'; import { JsPackageManager } from './JsPackageManager'; import type { PackageJson } from './PackageJson'; import type { InstallationMetadata, PackageMetadata } from './types'; -import { createLogStream } from '../utils'; +import { createLogStream } from '../utils/cli'; type NpmDependency = { version: string; diff --git a/code/lib/cli/src/js-package-manager/PNPMProxy.test.ts b/code/lib/core-common/src/js-package-manager/PNPMProxy.test.ts similarity index 100% rename from code/lib/cli/src/js-package-manager/PNPMProxy.test.ts rename to code/lib/core-common/src/js-package-manager/PNPMProxy.test.ts diff --git a/code/lib/cli/src/js-package-manager/PNPMProxy.ts b/code/lib/core-common/src/js-package-manager/PNPMProxy.ts similarity index 99% rename from code/lib/cli/src/js-package-manager/PNPMProxy.ts rename to code/lib/core-common/src/js-package-manager/PNPMProxy.ts index 10cc690bb652..d6cb6c99c175 100644 --- a/code/lib/cli/src/js-package-manager/PNPMProxy.ts +++ b/code/lib/core-common/src/js-package-manager/PNPMProxy.ts @@ -7,7 +7,7 @@ import semver from 'semver'; import { JsPackageManager } from './JsPackageManager'; import type { PackageJson } from './PackageJson'; import type { InstallationMetadata, PackageMetadata } from './types'; -import { createLogStream } from '../utils'; +import { createLogStream } from '../utils/cli'; type PnpmDependency = { from: string; diff --git a/code/lib/cli/src/js-package-manager/PackageJson.ts b/code/lib/core-common/src/js-package-manager/PackageJson.ts similarity index 100% rename from code/lib/cli/src/js-package-manager/PackageJson.ts rename to code/lib/core-common/src/js-package-manager/PackageJson.ts diff --git a/code/lib/cli/src/js-package-manager/Yarn1Proxy.test.ts b/code/lib/core-common/src/js-package-manager/Yarn1Proxy.test.ts similarity index 100% rename from code/lib/cli/src/js-package-manager/Yarn1Proxy.test.ts rename to code/lib/core-common/src/js-package-manager/Yarn1Proxy.test.ts diff --git a/code/lib/cli/src/js-package-manager/Yarn1Proxy.ts b/code/lib/core-common/src/js-package-manager/Yarn1Proxy.ts similarity index 99% rename from code/lib/cli/src/js-package-manager/Yarn1Proxy.ts rename to code/lib/core-common/src/js-package-manager/Yarn1Proxy.ts index c57de55e1d20..039a06f956cf 100644 --- a/code/lib/cli/src/js-package-manager/Yarn1Proxy.ts +++ b/code/lib/core-common/src/js-package-manager/Yarn1Proxy.ts @@ -3,7 +3,7 @@ import { sync as findUpSync } from 'find-up'; import { existsSync, readFileSync } from 'fs'; import path from 'path'; import semver from 'semver'; -import { createLogStream } from '../utils'; +import { createLogStream } from '../utils/cli'; import { JsPackageManager } from './JsPackageManager'; import type { PackageJson } from './PackageJson'; import type { InstallationMetadata, PackageMetadata } from './types'; diff --git a/code/lib/cli/src/js-package-manager/Yarn2Proxy.test.ts b/code/lib/core-common/src/js-package-manager/Yarn2Proxy.test.ts similarity index 100% rename from code/lib/cli/src/js-package-manager/Yarn2Proxy.test.ts rename to code/lib/core-common/src/js-package-manager/Yarn2Proxy.test.ts diff --git a/code/lib/cli/src/js-package-manager/Yarn2Proxy.ts b/code/lib/core-common/src/js-package-manager/Yarn2Proxy.ts similarity index 99% rename from code/lib/cli/src/js-package-manager/Yarn2Proxy.ts rename to code/lib/core-common/src/js-package-manager/Yarn2Proxy.ts index 987637ddda21..3dde844cba15 100644 --- a/code/lib/cli/src/js-package-manager/Yarn2Proxy.ts +++ b/code/lib/core-common/src/js-package-manager/Yarn2Proxy.ts @@ -5,7 +5,7 @@ import path from 'path'; import { PosixFS, VirtualFS, ZipOpenFS } from '@yarnpkg/fslib'; import { getLibzipSync } from '@yarnpkg/libzip'; import semver from 'semver'; -import { createLogStream } from '../utils'; +import { createLogStream } from '../utils/cli'; import { JsPackageManager } from './JsPackageManager'; import type { PackageJson } from './PackageJson'; import type { InstallationMetadata, PackageMetadata } from './types'; diff --git a/code/lib/cli/src/js-package-manager/fixtures/multiple-lockfiles/package.json b/code/lib/core-common/src/js-package-manager/fixtures/multiple-lockfiles/package.json similarity index 100% rename from code/lib/cli/src/js-package-manager/fixtures/multiple-lockfiles/package.json rename to code/lib/core-common/src/js-package-manager/fixtures/multiple-lockfiles/package.json diff --git a/code/lib/cli/src/js-package-manager/fixtures/multiple-lockfiles/pnpm-lock.yaml b/code/lib/core-common/src/js-package-manager/fixtures/multiple-lockfiles/pnpm-lock.yaml similarity index 100% rename from code/lib/cli/src/js-package-manager/fixtures/multiple-lockfiles/pnpm-lock.yaml rename to code/lib/core-common/src/js-package-manager/fixtures/multiple-lockfiles/pnpm-lock.yaml diff --git a/code/lib/cli/src/js-package-manager/fixtures/multiple-lockfiles/yarn.lock b/code/lib/core-common/src/js-package-manager/fixtures/multiple-lockfiles/yarn.lock similarity index 100% rename from code/lib/cli/src/js-package-manager/fixtures/multiple-lockfiles/yarn.lock rename to code/lib/core-common/src/js-package-manager/fixtures/multiple-lockfiles/yarn.lock diff --git a/code/lib/cli/src/js-package-manager/fixtures/pnpm-workspace/package.json b/code/lib/core-common/src/js-package-manager/fixtures/pnpm-workspace/package.json similarity index 100% rename from code/lib/cli/src/js-package-manager/fixtures/pnpm-workspace/package.json rename to code/lib/core-common/src/js-package-manager/fixtures/pnpm-workspace/package.json diff --git a/code/lib/cli/src/js-package-manager/fixtures/pnpm-workspace/package/package.json b/code/lib/core-common/src/js-package-manager/fixtures/pnpm-workspace/package/package.json similarity index 100% rename from code/lib/cli/src/js-package-manager/fixtures/pnpm-workspace/package/package.json rename to code/lib/core-common/src/js-package-manager/fixtures/pnpm-workspace/package/package.json diff --git a/code/lib/cli/src/js-package-manager/fixtures/pnpm-workspace/pnpm-lock.yaml b/code/lib/core-common/src/js-package-manager/fixtures/pnpm-workspace/pnpm-lock.yaml similarity index 100% rename from code/lib/cli/src/js-package-manager/fixtures/pnpm-workspace/pnpm-lock.yaml rename to code/lib/core-common/src/js-package-manager/fixtures/pnpm-workspace/pnpm-lock.yaml diff --git a/code/lib/cli/src/js-package-manager/fixtures/yarn.lock b/code/lib/core-common/src/js-package-manager/fixtures/yarn.lock similarity index 100% rename from code/lib/cli/src/js-package-manager/fixtures/yarn.lock rename to code/lib/core-common/src/js-package-manager/fixtures/yarn.lock diff --git a/code/lib/cli/src/js-package-manager/index.ts b/code/lib/core-common/src/js-package-manager/index.ts similarity index 81% rename from code/lib/cli/src/js-package-manager/index.ts rename to code/lib/core-common/src/js-package-manager/index.ts index f95d6f9659ba..58cfad6420a7 100644 --- a/code/lib/cli/src/js-package-manager/index.ts +++ b/code/lib/core-common/src/js-package-manager/index.ts @@ -1,4 +1,4 @@ export * from './JsPackageManagerFactory'; - export * from './JsPackageManager'; export * from './PackageJson'; +export * from './types'; diff --git a/code/lib/cli/src/js-package-manager/types.ts b/code/lib/core-common/src/js-package-manager/types.ts similarity index 100% rename from code/lib/cli/src/js-package-manager/types.ts rename to code/lib/core-common/src/js-package-manager/types.ts diff --git a/code/lib/cli/src/js-package-manager/util.ts b/code/lib/core-common/src/js-package-manager/util.ts similarity index 100% rename from code/lib/cli/src/js-package-manager/util.ts rename to code/lib/core-common/src/js-package-manager/util.ts diff --git a/code/lib/cli/src/HandledError.ts b/code/lib/core-common/src/utils/HandledError.ts similarity index 100% rename from code/lib/cli/src/HandledError.ts rename to code/lib/core-common/src/utils/HandledError.ts diff --git a/code/lib/cli/src/utils.test.ts b/code/lib/core-common/src/utils/cli.test.ts similarity index 94% rename from code/lib/cli/src/utils.test.ts rename to code/lib/core-common/src/utils/cli.test.ts index 41f65a83a7f1..f2444debb22a 100644 --- a/code/lib/cli/src/utils.test.ts +++ b/code/lib/core-common/src/utils/cli.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect } from 'vitest'; -import { isCorePackage } from './utils'; +import { isCorePackage } from './cli'; describe('UTILS', () => { describe.each([ diff --git a/code/lib/cli/src/utils.ts b/code/lib/core-common/src/utils/cli.ts similarity index 97% rename from code/lib/cli/src/utils.ts rename to code/lib/core-common/src/utils/cli.ts index fa5bfdc44b73..be022eac4460 100644 --- a/code/lib/cli/src/utils.ts +++ b/code/lib/core-common/src/utils/cli.ts @@ -2,8 +2,8 @@ import type { WriteStream } from 'fs-extra'; import { move, remove, writeFile, readFile, createWriteStream } from 'fs-extra'; import { join } from 'path'; import tempy from 'tempy'; -import { rendererPackages } from '@storybook/core-common'; -import type { JsPackageManager } from './js-package-manager'; +import { rendererPackages } from './get-storybook-info'; +import type { JsPackageManager } from '../js-package-manager'; export function parseList(str: string): string[] { return str diff --git a/code/lib/core-common/src/utils/log.ts b/code/lib/core-common/src/utils/log.ts new file mode 100644 index 000000000000..43119e616b24 --- /dev/null +++ b/code/lib/core-common/src/utils/log.ts @@ -0,0 +1,65 @@ +import chalk from 'chalk'; + +const logger = console; + +export const commandLog = (message: string) => { + process.stdout.write(chalk.cyan(' • ') + message); + + // Need `void` to be able to use this function in a then of a Promise + return (errorMessage?: string | void, errorInfo?: string) => { + if (errorMessage) { + process.stdout.write(`. ${chalk.red('✖')}\n`); + logger.error(`\n ${chalk.red(errorMessage)}`); + + if (!errorInfo) { + return; + } + + const newErrorInfo = errorInfo + .split('\n') + .map((line) => ` ${chalk.dim(line)}`) + .join('\n'); + logger.error(`${newErrorInfo}\n`); + return; + } + + process.stdout.write(`. ${chalk.green('✓')}\n`); + }; +}; + +export function paddedLog(message: string) { + const newMessage = message + .split('\n') + .map((line) => ` ${line}`) + .join('\n'); + + logger.log(newMessage); +} + +export function getChars(char: string, amount: number) { + let line = ''; + for (let lc = 0; lc < amount; lc += 1) { + line += char; + } + + return line; +} + +export function codeLog(codeLines: string[], leftPadAmount?: number) { + let maxLength = 0; + const newLines = codeLines.map((line) => { + maxLength = line.length > maxLength ? line.length : maxLength; + return line; + }); + + const finalResult = newLines + .map((line) => { + const rightPadAmount = maxLength - line.length; + let newLine = line + getChars(' ', rightPadAmount); + newLine = getChars(' ', leftPadAmount || 2) + chalk.inverse(` ${newLine} `); + return newLine; + }) + .join('\n'); + + logger.log(finalResult); +} diff --git a/code/lib/cli/src/remove.ts b/code/lib/core-common/src/utils/remove.ts similarity index 80% rename from code/lib/cli/src/remove.ts rename to code/lib/core-common/src/utils/remove.ts index 47c556eb578f..0c9af135aa39 100644 --- a/code/lib/cli/src/remove.ts +++ b/code/lib/core-common/src/utils/remove.ts @@ -1,8 +1,8 @@ -import { getStorybookInfo } from '@storybook/core-common'; -import { readConfig, writeConfig } from '@storybook/csf-tools'; import dedent from 'ts-dedent'; - -import { JsPackageManagerFactory, type PackageManagerName } from './js-package-manager'; +import { readConfig, writeConfig } from '@storybook/csf-tools'; +import type { PackageManagerName } from '../js-package-manager'; +import { JsPackageManagerFactory } from '../js-package-manager'; +import { getStorybookInfo } from './get-storybook-info'; const logger = console; @@ -12,7 +12,10 @@ const logger = console; * Usage: * - sb remove @storybook/addon-links */ -export async function remove(addon: string, options: { packageManager: PackageManagerName }) { +export async function removeAddon( + addon: string, + options: { packageManager?: PackageManagerName } = {} +) { const { packageManager: pkgMgr } = options; const packageManager = JsPackageManagerFactory.getPackageManager({ force: pkgMgr }); diff --git a/code/lib/cli/src/versions.ts b/code/lib/core-common/src/versions.ts similarity index 100% rename from code/lib/cli/src/versions.ts rename to code/lib/core-common/src/versions.ts diff --git a/code/lib/core-server/src/presets/common-preset.ts b/code/lib/core-server/src/presets/common-preset.ts index db67ca08bb47..4e0c183ea7c1 100644 --- a/code/lib/core-server/src/presets/common-preset.ts +++ b/code/lib/core-server/src/presets/common-preset.ts @@ -7,6 +7,7 @@ import { getPreviewBodyTemplate, getPreviewHeadTemplate, loadEnvs, + removeAddon, } from '@storybook/core-common'; import type { CLIOptions, @@ -160,6 +161,12 @@ const optionalEnvToBoolean = (input: string | undefined): boolean | undefined => return undefined; }; +// eslint-disable-next-line @typescript-eslint/naming-convention +export const experimental_serverAPI = (extension: Record) => ({ + ...extension, + removeAddon, +}); + /** * If for some reason this config is not applied, the reason is that * likely there is an addon that does `export core = () => ({ someConfig })`, diff --git a/code/lib/manager-api/src/modules/shortcuts.ts b/code/lib/manager-api/src/modules/shortcuts.ts index d050028f8b8b..892afa652ab7 100644 --- a/code/lib/manager-api/src/modules/shortcuts.ts +++ b/code/lib/manager-api/src/modules/shortcuts.ts @@ -125,12 +125,12 @@ type API_AddonShortcutLabels = Record; type API_AddonShortcutDefaults = Record; export const defaultShortcuts: API_Shortcuts = Object.freeze({ - fullScreen: ['F'], - togglePanel: ['A'], - panelPosition: ['D'], - toggleNav: ['S'], - toolbar: ['T'], - search: ['/'], + fullScreen: ['alt', 'F'], + togglePanel: ['alt', 'A'], + panelPosition: ['alt', 'D'], + toggleNav: ['alt', 'S'], + toolbar: ['alt', 'T'], + search: [controlOrMetaKey(), 'K'], focusNav: ['1'], focusIframe: ['2'], focusPanel: ['3'], @@ -139,7 +139,7 @@ export const defaultShortcuts: API_Shortcuts = Object.freeze({ prevStory: ['alt', 'ArrowLeft'], nextStory: ['alt', 'ArrowRight'], shortcutsPage: [controlOrMetaKey(), 'shift', ','], - aboutPage: [','], + aboutPage: [controlOrMetaKey(), ','], escape: ['escape'], // This one is not customizable collapseAll: [controlOrMetaKey(), 'shift', 'ArrowUp'], expandAll: [controlOrMetaKey(), 'shift', 'ArrowDown'], diff --git a/code/lib/manager-api/src/modules/versions.ts b/code/lib/manager-api/src/modules/versions.ts index a230da3a501f..a414c07fe073 100644 --- a/code/lib/manager-api/src/modules/versions.ts +++ b/code/lib/manager-api/src/modules/versions.ts @@ -23,6 +23,14 @@ const getVersionCheckData = memoize(1)((): API_Versions => { } }); +const normalizeRendererName = (renderer: string) => { + if (renderer.includes('vue')) { + return 'vue'; + } + + return renderer; +}; + export interface SubAPI { /** * Returns the current version of the Storybook Manager. @@ -36,6 +44,12 @@ export interface SubAPI { * @returns {API_Version} The latest version of the Storybook Manager. */ getLatestVersion: () => API_Version; + /** + * Returns the URL of the Storybook documentation for the current version. + * + * @returns {string} The URL of the Storybook Manager documentation. + */ + getDocsUrl: (options: { subpath?: string; versioned?: boolean; renderer?: boolean }) => string; /** * Checks if an update is available for the Storybook Manager. * @@ -73,6 +87,37 @@ export const init: ModuleFn = ({ store }) => { } return latest as API_Version; }, + // TODO: Move this to it's own "info" module later + getDocsUrl: ({ subpath, versioned, renderer }) => { + const { + versions: { latest, current }, + } = store.getState(); + + let url = 'https://storybook.js.org/docs/'; + + if (versioned && current?.version && latest?.version) { + const versionDiff = semver.diff(latest.version, current.version); + const isLatestDocs = versionDiff === 'patch' || versionDiff === null; + + if (!isLatestDocs) { + url += `${semver.major(current.version)}.${semver.minor(current.version)}/`; + } + } + + if (subpath) { + url += `${subpath}/`; + } + + if (renderer && typeof global.STORYBOOK_RENDERER !== 'undefined') { + const rendererName = (global.STORYBOOK_RENDERER as string).split('/').pop()?.toLowerCase(); + + if (rendererName) { + url += `?renderer=${normalizeRendererName(rendererName)}`; + } + } + + return url; + }, versionUpdateAvailable: () => { const latest = api.getLatestVersion(); const current = api.getCurrentVersion(); diff --git a/code/lib/manager-api/src/tests/shortcuts.test.js b/code/lib/manager-api/src/tests/shortcuts.test.js index d5604a47f020..9aba57c756f7 100644 --- a/code/lib/manager-api/src/tests/shortcuts.test.js +++ b/code/lib/manager-api/src/tests/shortcuts.test.js @@ -54,7 +54,7 @@ describe('shortcuts api', () => { const { api, state } = initShortcuts({ store }); store.setState(state); - expect(api.getDefaultShortcuts()).toHaveProperty('fullScreen', ['F']); + expect(api.getDefaultShortcuts()).toHaveProperty('fullScreen', ['alt', 'F']); }); it('gets defaults including addon ones', async () => { @@ -67,7 +67,7 @@ describe('shortcuts api', () => { await api.setAddonShortcut(mockAddonSecondShortcut.addon, mockAddonSecondShortcut.shortcut); await api.setAddonShortcut(mockSecondAddonShortcut.addon, mockSecondAddonShortcut.shortcut); - expect(api.getDefaultShortcuts()).toHaveProperty('fullScreen', ['F']); + expect(api.getDefaultShortcuts()).toHaveProperty('fullScreen', ['alt', 'F']); expect(api.getDefaultShortcuts()).toHaveProperty( `${mockAddonShortcut.addon}-${mockAddonShortcut.shortcut.actionName}`, mockAddonShortcut.shortcut.defaultShortcut @@ -148,7 +148,7 @@ describe('shortcuts api', () => { const { api, state } = initShortcuts({ store }); store.setState(state); - expect(api.getShortcutKeys().fullScreen).toEqual(['F']); + expect(api.getShortcutKeys().fullScreen).toEqual(['alt', 'F']); }); it('sets addon shortcut with default value', async () => { @@ -161,7 +161,7 @@ describe('shortcuts api', () => { await api.setAddonShortcut(mockAddonSecondShortcut.addon, mockAddonSecondShortcut.shortcut); await api.setAddonShortcut(mockSecondAddonShortcut.addon, mockSecondAddonShortcut.shortcut); - expect(api.getDefaultShortcuts()).toHaveProperty('fullScreen', ['F']); + expect(api.getDefaultShortcuts()).toHaveProperty('fullScreen', ['alt', 'F']); expect(api.getDefaultShortcuts()).toHaveProperty( `${mockAddonShortcut.addon}-${mockAddonShortcut.shortcut.actionName}`, mockAddonShortcut.shortcut.defaultShortcut @@ -184,7 +184,7 @@ describe('shortcuts api', () => { store.setState(state); expect(api.getShortcutKeys().fullScreen).toEqual(['Z']); - expect(api.getShortcutKeys().togglePanel).toEqual(['A']); + expect(api.getShortcutKeys().togglePanel).toEqual(['alt', 'A']); }); it('sets defaults, ignoring anything persisted that is out of date', () => { @@ -234,8 +234,8 @@ describe('shortcuts api', () => { await api.setShortcut(`${addon}-${shortcut.actionName}`, ['I']); await api.restoreAllDefaultShortcuts(); - expect(api.getShortcutKeys().fullScreen).toEqual(['F']); - expect(api.getShortcutKeys().togglePanel).toEqual(['A']); + expect(api.getShortcutKeys().fullScreen).toEqual(['alt', 'F']); + expect(api.getShortcutKeys().togglePanel).toEqual(['alt', 'A']); expect(api.getShortcutKeys()[`${addon}-${shortcut.actionName}`]).toEqual( shortcut.defaultShortcut ); @@ -269,7 +269,7 @@ describe('shortcuts api', () => { `${mockAddonShortcut.addon}-${mockAddonShortcut.shortcut.actionName}` ); - expect(api.getShortcutKeys().fullScreen).toEqual(['F']); + expect(api.getShortcutKeys().fullScreen).toEqual(['alt', 'F']); expect(api.getShortcutKeys().togglePanel).toEqual(['B']); expect( api.getShortcutKeys()[`${mockAddonShortcut.addon}-${mockAddonShortcut.shortcut.actionName}`] diff --git a/code/lib/manager-api/src/tests/versions.test.js b/code/lib/manager-api/src/tests/versions.test.js index 4f3e1cf93d7f..fbd29142b8d0 100644 --- a/code/lib/manager-api/src/tests/versions.test.js +++ b/code/lib/manager-api/src/tests/versions.test.js @@ -1,4 +1,6 @@ -import { describe, it, expect, vi } from 'vitest'; +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { global } from '@storybook/global'; + import { init as initVersions } from '../modules/versions'; vi.mock('../version', () => ({ @@ -122,6 +124,135 @@ describe('versions API', () => { }); }); + describe('METHOD: getDocsUrl()', () => { + beforeEach(() => { + global.STORYBOOK_RENDERER = undefined; + }); + + it('returns the latest url when current version is latest', async () => { + const store = createMockStore(); + const { + init, + api, + state: initialState, + } = initVersions({ + store, + }); + + await init(); + + store.setState({ + ...initialState, + versions: { + ...initialState.versions, + current: { version: '7.6.1' }, + latest: { version: '7.6.1' }, + }, + }); + + expect(api.getDocsUrl({ versioned: true })).toEqual('https://storybook.js.org/docs/'); + }); + + it('returns the latest url when version has patch diff with latest', async () => { + const store = createMockStore(); + const { + init, + api, + state: initialState, + } = initVersions({ + store, + }); + + await init(); + + store.setState({ + ...initialState, + versions: { + ...initialState.versions, + current: { version: '7.6.1' }, + latest: { version: '7.6.10' }, + }, + }); + + expect(api.getDocsUrl({ versioned: true })).toEqual('https://storybook.js.org/docs/'); + }); + + it('returns the versioned url when current has different docs to latest', async () => { + const store = createMockStore(); + const { + init, + api, + state: initialState, + } = initVersions({ + store, + }); + + await init(); + + store.setState({ + ...initialState, + versions: { + ...initialState.versions, + current: { version: '7.2.5' }, + latest: { version: '7.6.10' }, + }, + }); + + expect(api.getDocsUrl({ versioned: true })).toEqual('https://storybook.js.org/docs/7.2/'); + }); + + it('returns the versioned url when current is a prerelease', async () => { + const store = createMockStore(); + const { + init, + api, + state: initialState, + } = initVersions({ + store, + }); + + await init(); + + store.setState({ + ...initialState, + versions: { + ...initialState.versions, + current: { version: '8.0.0-beta' }, + latest: { version: '7.6.10' }, + }, + }); + + expect(api.getDocsUrl({ versioned: true })).toEqual('https://storybook.js.org/docs/8.0/'); + }); + + it('returns a Url with a renderer query param when "renderer" is true', async () => { + const store = createMockStore(); + const { + init, + api, + state: initialState, + } = initVersions({ + store, + }); + store.setState({ + ...initialState, + versions: { + ...initialState.versions, + current: { version: '5.2.1' }, + latest: { version: '5.2.1' }, + }, + }); + + await init(); + + global.STORYBOOK_RENDERER = 'vue'; + + expect(api.getDocsUrl({ renderer: true })).toEqual( + 'https://storybook.js.org/docs/?renderer=vue' + ); + }); + }); + describe('versionUpdateAvailable', () => { it('matching version', async () => { const store = createMockStore(); diff --git a/code/lib/manager-api/src/typings.d.ts b/code/lib/manager-api/src/typings.d.ts index 63edef9413da..afbe02b0417f 100644 --- a/code/lib/manager-api/src/typings.d.ts +++ b/code/lib/manager-api/src/typings.d.ts @@ -8,3 +8,6 @@ declare var REFS: any; declare var VERSIONCHECK: any; declare var LOGLEVEL: 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'silent' | undefined; declare var STORYBOOK_ADDON_STATE: Record; +declare var STORYBOOK_RENDERER: string | undefined; +declare var STORYBOOK_BUILDER: string | undefined; +declare var STORYBOOK_FRAMEWORK: string | undefined; diff --git a/code/lib/preview-api/src/modules/store/csf/processCSFFile.ts b/code/lib/preview-api/src/modules/store/csf/processCSFFile.ts index 55fc8895eecd..c29322a25277 100644 --- a/code/lib/preview-api/src/modules/store/csf/processCSFFile.ts +++ b/code/lib/preview-api/src/modules/store/csf/processCSFFile.ts @@ -8,9 +8,8 @@ import type { NormalizedComponentAnnotations, } from '@storybook/types'; import { isExportStory } from '@storybook/csf'; -import { deprecate, logger } from '@storybook/client-logger'; +import { logger } from '@storybook/client-logger'; -import dedent from 'ts-dedent'; import { normalizeStory } from './normalizeStory'; import { normalizeComponentAnnotations } from './normalizeComponentAnnotations'; @@ -39,15 +38,6 @@ const checkDisallowedParameters = (parameters?: Parameters) => { checkStorySort(parameters); }; -const checkSubcomponents = (meta: ModuleExports) => { - if (meta.subcomponents) { - deprecate(dedent`The \`subcomponents\` annotation is deprecated. - - Please refer to the migration guide: https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#argstable-block' - `); - } -}; - // Given the raw exports of a CSF file, check and normalize it. export function processCSFFile( moduleExports: ModuleExports, @@ -63,7 +53,6 @@ export function processCSFFile( importPath ); checkDisallowedParameters(meta.parameters); - checkSubcomponents(meta); const csfFile: CSFFile = { meta, stories: {}, moduleExports }; diff --git a/code/lib/telemetry/src/package-json.ts b/code/lib/telemetry/src/package-json.ts index 8163cc2fa167..56e6823b5920 100644 --- a/code/lib/telemetry/src/package-json.ts +++ b/code/lib/telemetry/src/package-json.ts @@ -28,10 +28,8 @@ export const getActualPackageJson = async (packageName: string) => { return packageJson; }; -// Note that this probably doesn't work in PNPM mode +// Note that this probably doesn't work in Yarn PNP mode because @storybook/telemetry doesn't depend on @storybook/cli export const getStorybookCoreVersion = async () => { - const coreVersions = await Promise.all( - ['@storybook/core-common', '@storybook/core-server'].map(getActualPackageVersion) - ); - return coreVersions.find((v) => v.version)?.version; + const { version } = await getActualPackageVersion('@storybook/cli'); + return version; }; diff --git a/code/lib/theming/src/index.ts b/code/lib/theming/src/index.ts index fe048aa0f2dd..9c964ea8a25e 100644 --- a/code/lib/theming/src/index.ts +++ b/code/lib/theming/src/index.ts @@ -4,8 +4,6 @@ import type { FunctionInterpolation, Interpolation } from '@emotion/react'; import type { StorybookTheme } from './types'; -/// - export { default as styled } from '@emotion/styled'; export type { StyledComponent } from '@emotion/styled'; @@ -32,6 +30,7 @@ export type { export * from './base'; export * from './types'; +export * from './emotionAugmentation.d'; export { default as createCache } from '@emotion/cache'; export { default as isPropValid } from '@emotion/is-prop-valid'; diff --git a/code/package.json b/code/package.json index 9899cbae023e..747f43193e51 100644 --- a/code/package.json +++ b/code/package.json @@ -292,5 +292,6 @@ "Dependency Upgrades" ] ] - } + }, + "deferredNextVersion": "8.0.0-alpha.12" } diff --git a/code/renderers/react/src/docs/lib/inspection/acornParser.ts b/code/renderers/react/src/docs/lib/inspection/acornParser.ts index 75be3aafb34c..ce62510430b8 100644 --- a/code/renderers/react/src/docs/lib/inspection/acornParser.ts +++ b/code/renderers/react/src/docs/lib/inspection/acornParser.ts @@ -206,7 +206,7 @@ function parseExpression(expression: any): ParsingResult } export function parse(value: string): ParsingResult { - const ast = acornParser.parse(`(${value})`) as unknown as estree.Program; + const ast = acornParser.parse(`(${value})`, { ecmaVersion: 2020 }) as unknown as estree.Program; let parsingResult: ParsingResult = { inferredType: { type: InspectionType.UNKNOWN }, diff --git a/code/ui/.storybook/main.ts b/code/ui/.storybook/main.ts index a15c3a4fd988..3defc0046331 100644 --- a/code/ui/.storybook/main.ts +++ b/code/ui/.storybook/main.ts @@ -57,7 +57,10 @@ const config: StorybookConfig = { ], build: { test: { + // we have stories for the blocks here, we can't exclude them disableBlocks: false, + // some stories in blocks (ArgTypes, Controls) depends on argTypes inference + disableDocgen: false, }, }, framework: { diff --git a/code/ui/blocks/src/blocks/ArgTypes.stories.tsx b/code/ui/blocks/src/blocks/ArgTypes.stories.tsx index a177868ba92a..509497af2be1 100644 --- a/code/ui/blocks/src/blocks/ArgTypes.stories.tsx +++ b/code/ui/blocks/src/blocks/ArgTypes.stories.tsx @@ -3,12 +3,18 @@ import type { Meta, StoryObj } from '@storybook/react'; import { ArgTypes } from './ArgTypes'; import * as ExampleStories from '../examples/ArgTypesParameters.stories'; +import * as SubcomponentsExampleStories from '../examples/ArgTypesWithSubcomponentsParameters.stories'; +import { within } from '@storybook/test'; +import type { PlayFunctionContext } from '@storybook/csf'; const meta: Meta = { title: 'Blocks/ArgTypes', component: ArgTypes, parameters: { - relativeCsfPaths: ['../examples/ArgTypesParameters.stories'], + relativeCsfPaths: [ + '../examples/ArgTypesParameters.stories', + '../examples/ArgTypesWithSubcomponentsParameters.stories', + ], docsStyles: true, }, }; @@ -46,7 +52,6 @@ export const OfUndefined: Story = { decorators: [(s) => (window?.navigator.userAgent.match(/StorybookTestRunner/) ?
: s())], }; -// NOTE: this will throw with no of prop export const OfStoryUnattached: Story = { parameters: { attached: false }, args: { @@ -92,3 +97,67 @@ export const SortParameter: Story = { of: ExampleStories.Sort, }, }; + +export const Categories: Story = { + args: { + of: ExampleStories.Categories, + }, +}; + +const findSubcomponentTabs = async ( + canvas: ReturnType, + step: PlayFunctionContext['step'] +) => { + let subcomponentATab: HTMLElement; + let subcomponentBTab: HTMLElement; + await step('should have tabs for the subcomponents', async () => { + subcomponentATab = await canvas.findByText('SubcomponentA'); + subcomponentBTab = await canvas.findByText('SubcomponentB'); + }); + return { subcomponentATab, subcomponentBTab }; +}; + +export const SubcomponentsOfMeta: Story = { + args: { + of: SubcomponentsExampleStories.default, + }, + play: async ({ canvasElement, step }) => { + const canvas = within(canvasElement); + await findSubcomponentTabs(canvas, step); + }, +}; + +export const SubcomponentsOfStory: Story = { + ...SubcomponentsOfMeta, + args: { + of: SubcomponentsExampleStories.NoParameters, + }, +}; + +export const SubcomponentsIncludeProp: Story = { + args: { + of: SubcomponentsExampleStories.NoParameters, + include: ['a', 'f'], + }, + play: async ({ canvasElement, step }) => { + const canvas = within(canvasElement); + const { subcomponentBTab } = await findSubcomponentTabs(canvas, step); + await subcomponentBTab.click(); + }, +}; + +export const SubcomponentsExcludeProp: Story = { + ...SubcomponentsIncludeProp, + args: { + of: SubcomponentsExampleStories.NoParameters, + exclude: ['a', 'c', 'f', 'g'], + }, +}; + +export const SubcomponentsSortProp: Story = { + ...SubcomponentsIncludeProp, + args: { + of: SubcomponentsExampleStories.NoParameters, + sort: 'alpha', + }, +}; diff --git a/code/ui/blocks/src/blocks/ArgTypes.tsx b/code/ui/blocks/src/blocks/ArgTypes.tsx index 3515af129bbc..7fe3cf8a66c9 100644 --- a/code/ui/blocks/src/blocks/ArgTypes.tsx +++ b/code/ui/blocks/src/blocks/ArgTypes.tsx @@ -7,9 +7,11 @@ import { filterArgTypes } from '@storybook/preview-api'; import type { ArgTypesExtractor } from '@storybook/docs-tools'; import React from 'react'; +import { mapValues } from 'lodash'; import type { SortType } from '../components'; -import { ArgsTable as PureArgsTable, ArgsTableError } from '../components'; +import { ArgsTable as PureArgsTable, ArgsTableError, TabbedArgsTable } from '../components'; import { useOf } from './useOf'; +import { getComponentName } from './utils'; type ArgTypesParameters = { include?: PropDescriptor; @@ -31,7 +33,7 @@ function extractComponentArgTypes( return extractArgTypes(component); } -function getArgTypesFromResolved(resolved: ReturnType, props: ArgTypesProps) { +function getArgTypesFromResolved(resolved: ReturnType) { if (resolved.type === 'component') { const { component, @@ -40,22 +42,23 @@ function getArgTypesFromResolved(resolved: ReturnType, props: ArgT return { argTypes: extractComponentArgTypes(component, parameters), parameters, + component, }; } if (resolved.type === 'meta') { const { - preparedMeta: { argTypes, parameters }, + preparedMeta: { argTypes, parameters, component, subcomponents }, } = resolved; - return { argTypes, parameters }; + return { argTypes, parameters, component, subcomponents }; } // In the case of the story, the enhanceArgs argTypeEnhancer has already added the extracted // arg types from the component to the prepared story. const { - story: { argTypes, parameters }, + story: { argTypes, parameters, component, subcomponents }, } = resolved; - return { argTypes, parameters }; + return { argTypes, parameters, component, subcomponents }; } export const ArgTypes: FC = (props) => { @@ -64,7 +67,7 @@ export const ArgTypes: FC = (props) => { throw new Error('Unexpected `of={undefined}`, did you mistype a CSF file reference?'); } const resolved = useOf(of || 'meta'); - const { argTypes, parameters } = getArgTypesFromResolved(resolved, props); + const { argTypes, parameters, component, subcomponents } = getArgTypesFromResolved(resolved); const argTypesParameters = parameters.docs?.argTypes || ({} as ArgTypesParameters); const include = props.include ?? argTypesParameters.include; @@ -73,5 +76,20 @@ export const ArgTypes: FC = (props) => { const filteredArgTypes = filterArgTypes(argTypes, include, exclude); - return ; + const hasSubcomponents = Boolean(subcomponents) && Object.keys(subcomponents).length > 0; + + if (!hasSubcomponents) { + return ; + } + + const mainComponentName = getComponentName(component); + const subcomponentTabs = mapValues(subcomponents, (comp) => ({ + rows: filterArgTypes(extractComponentArgTypes(comp, parameters), include, exclude), + sort, + })); + const tabs = { + [mainComponentName]: { rows: filteredArgTypes, sort }, + ...subcomponentTabs, + }; + return ; }; diff --git a/code/ui/blocks/src/blocks/ArgsTable.tsx b/code/ui/blocks/src/blocks/ArgsTable.tsx deleted file mode 100644 index aa0ff827ed34..000000000000 --- a/code/ui/blocks/src/blocks/ArgsTable.tsx +++ /dev/null @@ -1,277 +0,0 @@ -import type { FC } from 'react'; -import React, { useContext, useEffect, useState, useCallback } from 'react'; -import mapValues from 'lodash/mapValues.js'; -import type { ArgTypesExtractor } from '@storybook/docs-tools'; -import type { PropDescriptor } from '@storybook/preview-api'; -import { filterArgTypes } from '@storybook/preview-api'; -import type { StrictArgTypes, Args, Globals, Parameters } from '@storybook/types'; -import { - STORY_ARGS_UPDATED, - UPDATE_STORY_ARGS, - RESET_STORY_ARGS, - GLOBALS_UPDATED, -} from '@storybook/core-events'; -import { deprecate } from '@storybook/client-logger'; -import dedent from 'ts-dedent'; -import type { ArgsTableProps as PureArgsTableProps, SortType } from '../components'; -import { ArgsTable as PureArgsTable, ArgsTableError, TabbedArgsTable } from '../components'; - -import type { DocsContextProps } from './DocsContext'; -import { DocsContext } from './DocsContext'; -import type { Component } from './types'; -import { PRIMARY_STORY } from './types'; -import { getComponentName } from './utils'; -import { useStory } from './useStory'; - -interface BaseProps { - include?: PropDescriptor; - exclude?: PropDescriptor; - sort?: SortType; -} - -type OfProps = BaseProps & { - of: '^' | Component; -}; - -type ComponentsProps = BaseProps & { - parameters: Parameters; - components: { - [label: string]: Component; - }; -}; - -type StoryProps = BaseProps & { - story: '.' | '^' | string; - showComponent?: boolean; -}; - -type ArgsTableProps = BaseProps | OfProps | ComponentsProps | StoryProps; - -const useArgs = ( - storyId: string, - context: DocsContextProps -): [Args, (args: Args) => void, (argNames?: string[]) => void] => { - const storyContext = context.getStoryContext(context.storyById()); - - const [args, setArgs] = useState(storyContext.args); - useEffect(() => { - const cb = (changed: { storyId: string; args: Args }) => { - if (changed.storyId === storyId) { - setArgs(changed.args); - } - }; - context.channel.on(STORY_ARGS_UPDATED, cb); - return () => context.channel.off(STORY_ARGS_UPDATED, cb); - }, [storyId]); - const updateArgs = useCallback( - (updatedArgs: any) => context.channel.emit(UPDATE_STORY_ARGS, { storyId, updatedArgs }), - [storyId] - ); - const resetArgs = useCallback( - (argNames?: string[]) => context.channel.emit(RESET_STORY_ARGS, { storyId, argNames }), - [storyId] - ); - return [args, updateArgs, resetArgs]; -}; - -const useGlobals = (context: DocsContextProps): [Globals] => { - const storyContext = context.getStoryContext(context.storyById()); - const [globals, setGlobals] = useState(storyContext.globals); - - useEffect(() => { - const cb = (changed: { globals: Globals }) => { - setGlobals(changed.globals); - }; - context.channel.on(GLOBALS_UPDATED, cb); - return () => context.channel.off(GLOBALS_UPDATED, cb); - }, []); - - return [globals]; -}; - -export const extractComponentArgTypes = ( - component: Component, - parameters: Parameters, - include?: PropDescriptor, - exclude?: PropDescriptor -): StrictArgTypes => { - const { extractArgTypes }: { extractArgTypes: ArgTypesExtractor } = parameters.docs || {}; - if (!extractArgTypes) { - throw new Error(ArgsTableError.ARGS_UNSUPPORTED); - } - let argTypes = extractArgTypes(component); - argTypes = filterArgTypes(argTypes, include, exclude); - - return argTypes; -}; - -const isShortcut = (value?: string) => { - return value && [PRIMARY_STORY].includes(value); -}; - -export const getComponent = (props: ArgsTableProps, component: Component): Component => { - const { of } = props as OfProps; - const { story } = props as StoryProps; - if (isShortcut(of) || isShortcut(story)) { - return component || null; - } - if (!of) { - throw new Error(ArgsTableError.NO_COMPONENT); - } - return of; -}; - -const addComponentTabs = ( - tabs: Record, - components: Record, - parameters: Parameters, - include?: PropDescriptor, - exclude?: PropDescriptor, - sort?: SortType -) => ({ - ...tabs, - ...mapValues(components, (comp) => ({ - rows: extractComponentArgTypes(comp, parameters, include, exclude), - sort, - })), -}); - -export const StoryTable: FC< - StoryProps & { component: Component; subcomponents: Record } -> = (props) => { - const context = useContext(DocsContext); - const { - story: storyName, - component, - subcomponents, - showComponent, - include, - exclude, - sort, - } = props; - try { - let storyId; - switch (storyName) { - case PRIMARY_STORY: { - const primaryStory = context.storyById(); - storyId = primaryStory.id; - break; - } - default: { - storyId = context.storyIdByName(storyName); - } - } - - const story = useStory(storyId, context); - // eslint-disable-next-line prefer-const - let [args, updateArgs, resetArgs] = useArgs(storyId, context); - - const [globals] = useGlobals(context); - if (!story) return ; - - const argTypes = filterArgTypes(story.argTypes, include, exclude); - - const mainLabel = getComponentName(component) || 'Story'; - - let tabs = { [mainLabel]: { rows: argTypes, args, globals, updateArgs, resetArgs } } as Record< - string, - PureArgsTableProps - >; - - // Use the dynamically generated component tabs if there are no controls - const storyHasArgsWithControls = argTypes && Object.values(argTypes).find((v) => !!v?.control); - - if (!storyHasArgsWithControls) { - updateArgs = null; - resetArgs = null; - tabs = {}; - } - - if (component && (!storyHasArgsWithControls || showComponent)) { - tabs = addComponentTabs(tabs, { [mainLabel]: component }, story.parameters, include, exclude); - } - - if (subcomponents) { - if (Array.isArray(subcomponents)) { - throw new Error( - `Unexpected subcomponents array. Expected an object whose keys are tab labels and whose values are components.` - ); - } - tabs = addComponentTabs(tabs, subcomponents, story.parameters, include, exclude); - } - return ; - } catch (err) { - return ; - } -}; - -export const ComponentsTable: FC = (props) => { - const { components, include, exclude, sort, parameters } = props; - - const tabs = addComponentTabs({}, components, parameters, include, exclude); - return ; -}; - -export const ArgsTable: FC = (props) => { - deprecate(dedent`The ArgsTable doc block is deprecated. Instead use the ArgTypes doc block for static tables or the Controls doc block for tables with controls. - - Please refer to the migration guide: https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#argstable-block - `); - const context = useContext(DocsContext); - - let parameters: Parameters; - let component: any; - let subcomponents: Record; - try { - ({ parameters, component, subcomponents } = context.storyById()); - } catch (err) { - const { of } = props as OfProps; - if ('of' in props && of === undefined) { - throw new Error('Unexpected `of={undefined}`, did you mistype a CSF file reference?'); - } - ({ - projectAnnotations: { parameters }, - } = context.resolveOf(of, ['component'])); - } - - const { include, exclude, components, sort: sortProp } = props as ComponentsProps; - const { story: storyName } = props as StoryProps; - - const sort = sortProp || parameters.controls?.sort; - - const main = getComponent(props, component); - if (storyName) { - return ; - } - - if (!components && !subcomponents) { - let mainProps; - try { - mainProps = { rows: extractComponentArgTypes(main, parameters, include, exclude) }; - } catch (err) { - mainProps = { error: err.message }; - } - - return ; - } - - if (components) { - return ( - - ); - } - - const mainLabel = getComponentName(main); - return ( - - ); -}; - -ArgsTable.defaultProps = { - of: PRIMARY_STORY, -}; diff --git a/code/ui/blocks/src/blocks/Controls.stories.tsx b/code/ui/blocks/src/blocks/Controls.stories.tsx index 7bde517b7bfa..9d32d9fe12f1 100644 --- a/code/ui/blocks/src/blocks/Controls.stories.tsx +++ b/code/ui/blocks/src/blocks/Controls.stories.tsx @@ -3,11 +3,17 @@ import type { Meta, StoryObj } from '@storybook/react'; import { Controls } from './Controls'; import * as ExampleStories from '../examples/ControlsParameters.stories'; +import * as SubcomponentsExampleStories from '../examples/ControlsWithSubcomponentsParameters.stories'; +import { within } from '@storybook/test'; +import type { PlayFunctionContext } from '@storybook/csf'; const meta: Meta = { component: Controls, parameters: { - relativeCsfPaths: ['../examples/ControlsParameters.stories'], + relativeCsfPaths: [ + '../examples/ControlsParameters.stories', + '../examples/ControlsWithSubcomponentsParameters.stories', + ], docsStyles: true, }, }; @@ -79,3 +85,60 @@ export const SortParameter: Story = { of: ExampleStories.Sort, }, }; + +export const Categories: Story = { + args: { + of: ExampleStories.Categories, + }, +}; + +const findSubcomponentTabs = async ( + canvas: ReturnType, + step: PlayFunctionContext['step'] +) => { + let subcomponentATab: HTMLElement; + let subcomponentBTab: HTMLElement; + await step('should have tabs for the subcomponents', async () => { + subcomponentATab = await canvas.findByText('SubcomponentA'); + subcomponentBTab = await canvas.findByText('SubcomponentB'); + }); + return { subcomponentATab, subcomponentBTab }; +}; + +export const SubcomponentsOfStory: Story = { + args: { + of: SubcomponentsExampleStories.NoParameters, + }, + play: async ({ canvasElement, step }) => { + const canvas = within(canvasElement); + await findSubcomponentTabs(canvas, step); + }, +}; + +export const SubcomponentsIncludeProp: Story = { + args: { + of: SubcomponentsExampleStories.NoParameters, + include: ['a', 'f'], + }, + play: async ({ canvasElement, step }) => { + const canvas = within(canvasElement); + const { subcomponentBTab } = await findSubcomponentTabs(canvas, step); + await subcomponentBTab.click(); + }, +}; + +export const SubcomponentsExcludeProp: Story = { + ...SubcomponentsIncludeProp, + args: { + of: SubcomponentsExampleStories.NoParameters, + exclude: ['a', 'c', 'f', 'g'], + }, +}; + +export const SubcomponentsSortProp: Story = { + ...SubcomponentsIncludeProp, + args: { + of: SubcomponentsExampleStories.NoParameters, + sort: 'alpha', + }, +}; diff --git a/code/ui/blocks/src/blocks/Controls.tsx b/code/ui/blocks/src/blocks/Controls.tsx index 9712aeda130e..2adef888e66a 100644 --- a/code/ui/blocks/src/blocks/Controls.tsx +++ b/code/ui/blocks/src/blocks/Controls.tsx @@ -1,16 +1,19 @@ /* eslint-disable react/destructuring-assignment */ -import type { Renderer } from '@storybook/csf'; +import type { Renderer, Parameters, StrictArgTypes } from '@storybook/csf'; import type { ModuleExports } from '@storybook/types'; import type { FC } from 'react'; import React, { useContext } from 'react'; import { filterArgTypes } from '@storybook/preview-api'; import type { PropDescriptor } from '@storybook/preview-api'; +import type { ArgTypesExtractor } from '@storybook/docs-tools'; +import { mapValues } from 'lodash'; import type { SortType } from '../components'; -import { ArgsTable as PureArgsTable } from '../components'; +import { ArgsTable as PureArgsTable, ArgsTableError, TabbedArgsTable } from '../components'; import { DocsContext } from './DocsContext'; import { useGlobals } from './useGlobals'; import { useArgs } from './useArgs'; +import { getComponentName } from './utils'; type ControlsParameters = { include?: PropDescriptor; @@ -22,6 +25,17 @@ type ControlsProps = ControlsParameters & { of?: Renderer['component'] | ModuleExports; }; +function extractComponentArgTypes( + component: Renderer['component'], + parameters: Parameters +): StrictArgTypes { + const { extractArgTypes }: { extractArgTypes: ArgTypesExtractor } = parameters.docs || {}; + if (!extractArgTypes) { + throw new Error(ArgsTableError.ARGS_UNSUPPORTED); + } + return extractArgTypes(component); +} + export const Controls: FC = (props) => { const { of } = props; if ('of' in props && of === undefined) { @@ -30,7 +44,7 @@ export const Controls: FC = (props) => { const context = useContext(DocsContext); const { story } = context.resolveOf(of || 'story', ['story']); - const { parameters, argTypes } = story; + const { parameters, argTypes, component, subcomponents } = story; const controlsParameters = parameters.docs?.controls || ({} as ControlsParameters); const include = props.include ?? controlsParameters.include; @@ -42,14 +56,38 @@ export const Controls: FC = (props) => { const filteredArgTypes = filterArgTypes(argTypes, include, exclude); + const hasSubcomponents = Boolean(subcomponents) && Object.keys(subcomponents).length > 0; + + if (!hasSubcomponents) { + return ( + + ); + } + + const mainComponentName = getComponentName(component); + const subcomponentTabs = mapValues(subcomponents, (comp) => ({ + rows: filterArgTypes(extractComponentArgTypes(comp, parameters), include, exclude), + sort, + })); + const tabs = { + [mainComponentName]: { rows: filteredArgTypes, sort }, + ...subcomponentTabs, + }; return ( - ); }; diff --git a/code/ui/blocks/src/blocks/index.ts b/code/ui/blocks/src/blocks/index.ts index 464ddc8c0b76..c6d3565b6869 100644 --- a/code/ui/blocks/src/blocks/index.ts +++ b/code/ui/blocks/src/blocks/index.ts @@ -2,7 +2,6 @@ export { ColorPalette, ColorItem, IconGallery, IconItem, Typeset } from '../comp export * from './Anchor'; export * from './ArgTypes'; -export * from './ArgsTable'; export * from './Canvas'; export * from './Controls'; export * from './Description'; diff --git a/code/ui/blocks/src/components/ArgsTable/ArgsTable.tsx b/code/ui/blocks/src/components/ArgsTable/ArgsTable.tsx index c0a9e8b0d0bc..70e4e1f0edd7 100644 --- a/code/ui/blocks/src/components/ArgsTable/ArgsTable.tsx +++ b/code/ui/blocks/src/components/ArgsTable/ArgsTable.tsx @@ -7,7 +7,7 @@ import { includeConditionalArg } from '@storybook/csf'; import { once } from '@storybook/client-logger'; import { IconButton, ResetWrapper, Link } from '@storybook/components'; -import { UndoIcon } from '@storybook/icons'; +import { DocumentIcon, UndoIcon } from '@storybook/icons'; import { ArgRow } from './ArgRow'; import { SectionRow } from './SectionRow'; import type { ArgType, ArgTypes, Args, Globals } from './types'; @@ -323,7 +323,7 @@ export const ArgsTable: FC = (props) => { {error}  - Read the docs + Read the docs ); diff --git a/code/ui/blocks/src/components/ArgsTable/Empty.tsx b/code/ui/blocks/src/components/ArgsTable/Empty.tsx index 796a3dcfba39..c4269a605f95 100644 --- a/code/ui/blocks/src/components/ArgsTable/Empty.tsx +++ b/code/ui/blocks/src/components/ArgsTable/Empty.tsx @@ -2,7 +2,7 @@ import type { FC } from 'react'; import React, { useEffect, useState } from 'react'; import { styled } from '@storybook/theming'; import { Link } from '@storybook/components'; -import { VideoIcon } from '@storybook/icons'; +import { DocumentIcon, SupportIcon, VideoIcon } from '@storybook/icons'; interface EmptyProps { inAddonPanel?: boolean; @@ -92,21 +92,17 @@ export const Empty: FC = ({ inAddonPanel }) => { - Read docs + Read docs )} {!inAddonPanel && ( - - Learn how to set that up + + Learn how to set that up )} diff --git a/code/ui/blocks/src/components/ArgsTable/TabbedArgsTable.tsx b/code/ui/blocks/src/components/ArgsTable/TabbedArgsTable.tsx index e493848fedae..d50b20509d3a 100644 --- a/code/ui/blocks/src/components/ArgsTable/TabbedArgsTable.tsx +++ b/code/ui/blocks/src/components/ArgsTable/TabbedArgsTable.tsx @@ -2,15 +2,14 @@ import type { FC } from 'react'; import React from 'react'; import { TabsState } from '@storybook/components'; -import type { ArgsTableProps, SortType } from './ArgsTable'; - +import type { ArgsTableProps } from './ArgsTable'; import { ArgsTable } from './ArgsTable'; -export interface TabbedArgsTableProps { - children?: React.ReactNode; +type DistributiveOmit = T extends any ? Omit : never; + +export type TabbedArgsTableProps = DistributiveOmit & { tabs: Record; - sort?: SortType; -} +}; export const TabbedArgsTable: FC = ({ tabs, ...props }) => { const entries = Object.entries(tabs); @@ -21,7 +20,7 @@ export const TabbedArgsTable: FC = ({ tabs, ...props }) => return ( - {entries.map((entry) => { + {entries.map((entry, index) => { const [label, table] = entry; const id = `prop_table_div_${label}`; const Component = 'div' as unknown as React.ElementType< @@ -29,10 +28,20 @@ export const TabbedArgsTable: FC = ({ tabs, ...props }) => children: ({ active }: { active: boolean }) => React.ReactNode; } >; + + /** + * The first tab is the main component, controllable if in the Controls block + * All other tabs are subcomponents, never controllable, so we filter out the props indicating controllability + * Essentially all subcomponents always behave like ArgTypes, never Controls + */ + const argsTableProps = index === 0 ? props : { sort: props.sort }; + return ( {({ active }) => - active ? : null + active ? ( + + ) : null } ); diff --git a/code/ui/blocks/src/examples/ArgTypesParameters.stories.tsx b/code/ui/blocks/src/examples/ArgTypesParameters.stories.tsx index 94df6f2b374d..e79c0c1bf0e3 100644 --- a/code/ui/blocks/src/examples/ArgTypesParameters.stories.tsx +++ b/code/ui/blocks/src/examples/ArgTypesParameters.stories.tsx @@ -34,17 +34,45 @@ export const NoParameters: Story = { }, }; -export const Include = { +export const Include: Story = { ...NoParameters, parameters: { docs: { argTypes: { include: ['a'] } } }, }; -export const Exclude = { +export const Exclude: Story = { ...NoParameters, parameters: { docs: { argTypes: { exclude: ['a'] } } }, }; -export const Sort = { +export const Sort: Story = { ...NoParameters, parameters: { docs: { argTypes: { sort: 'alpha' } } }, }; + +export const Categories: Story = { + ...NoParameters, + argTypes: { + c: { + description: 'a description', + table: { + category: 'the first category', + }, + }, + d: { + table: { + category: 'the first category', + subcategory: 'a subcategory', + }, + }, + e: { + table: { + subcategory: 'a subcategory without a category', + }, + }, + f: { + table: { + category: 'the second category', + }, + }, + } as any, +}; diff --git a/code/ui/blocks/src/examples/ArgTypesParameters.tsx b/code/ui/blocks/src/examples/ArgTypesParameters.tsx index 5e2522a21a1f..038b3ac85f18 100644 --- a/code/ui/blocks/src/examples/ArgTypesParameters.tsx +++ b/code/ui/blocks/src/examples/ArgTypesParameters.tsx @@ -3,3 +3,15 @@ import React from 'react'; type PropTypes = { a?: string; b: string }; export const ArgTypesParameters = ({ a = 'a', b }: PropTypes) =>
Example story
; + +type SubcomponentAPropTypes = { e: boolean; c: boolean; d?: boolean }; + +export const SubcomponentA = ({ d = false }: SubcomponentAPropTypes) => ( +
Example subcomponent A
+); + +type SubcomponentBPropTypes = { g: number; h: number; f?: number }; + +export const SubcomponentB = ({ f = 42 }: SubcomponentBPropTypes) => ( +
Example subcomponent B
+); diff --git a/code/ui/blocks/src/examples/ArgTypesWithSubcomponentsParameters.stories.tsx b/code/ui/blocks/src/examples/ArgTypesWithSubcomponentsParameters.stories.tsx new file mode 100644 index 000000000000..79b6c14f882b --- /dev/null +++ b/code/ui/blocks/src/examples/ArgTypesWithSubcomponentsParameters.stories.tsx @@ -0,0 +1,26 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import { ArgTypesParameters, SubcomponentA, SubcomponentB } from './ArgTypesParameters'; + +/** + * Reference stories to be used by the ArgTypes stories + */ +const meta = { + title: 'examples/Stories for the ArgTypes Block with Subcomponents', + component: ArgTypesParameters, + subcomponents: { SubcomponentA, SubcomponentB }, + args: { b: 'b' }, + argTypes: { + // @ts-expect-error Meta type is trying to force us to use real props as args + extraMetaArgType: { + type: { name: 'string' }, + name: 'Extra Meta', + description: 'An extra argtype added at the meta level', + table: { defaultValue: { summary: "'a default value'" } }, + }, + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const NoParameters: Story = {}; diff --git a/code/ui/blocks/src/examples/ControlsParameters.stories.tsx b/code/ui/blocks/src/examples/ControlsParameters.stories.tsx index 603f188a30de..402e49c601f3 100644 --- a/code/ui/blocks/src/examples/ControlsParameters.stories.tsx +++ b/code/ui/blocks/src/examples/ControlsParameters.stories.tsx @@ -34,17 +34,54 @@ export const NoParameters: Story = { }, }; -export const Include = { +export const Include: Story = { ...NoParameters, parameters: { docs: { controls: { include: ['a'] } } }, }; -export const Exclude = { +export const Exclude: Story = { ...NoParameters, parameters: { docs: { controls: { exclude: ['a'] } } }, }; -export const Sort = { +export const Sort: Story = { ...NoParameters, parameters: { docs: { controls: { sort: 'alpha' } } }, }; + +export const Categories: Story = { + ...NoParameters, + argTypes: { + c: { + description: 'a description', + control: { + type: 'text', + }, + table: { + category: 'the first category', + }, + }, + d: { + control: { + type: 'number', + }, + table: { + category: 'the first category', + subcategory: 'a subcategory', + }, + }, + e: { + control: { + type: 'color', + }, + table: { + subcategory: 'a subcategory without a category', + }, + }, + f: { + table: { + category: 'the second category', + }, + }, + } as any, +}; diff --git a/code/ui/blocks/src/examples/ControlsParameters.tsx b/code/ui/blocks/src/examples/ControlsParameters.tsx index 8d32189898d6..ceaa0801eb76 100644 --- a/code/ui/blocks/src/examples/ControlsParameters.tsx +++ b/code/ui/blocks/src/examples/ControlsParameters.tsx @@ -3,3 +3,15 @@ import React from 'react'; type PropTypes = { a?: string; b: string }; export const ControlsParameters = ({ a = 'a', b }: PropTypes) =>
Example story
; + +type SubcomponentAPropTypes = { e: boolean; c: boolean; d?: boolean }; + +export const SubcomponentA = ({ d = false }: SubcomponentAPropTypes) => ( +
Example subcomponent A
+); + +type SubcomponentBPropTypes = { g: number; h: number; f?: number }; + +export const SubcomponentB = ({ f = 42 }: SubcomponentBPropTypes) => ( +
Example subcomponent B
+); diff --git a/code/ui/blocks/src/examples/ControlsWithSubcomponentsParameters.stories.tsx b/code/ui/blocks/src/examples/ControlsWithSubcomponentsParameters.stories.tsx new file mode 100644 index 000000000000..65d350564c1a --- /dev/null +++ b/code/ui/blocks/src/examples/ControlsWithSubcomponentsParameters.stories.tsx @@ -0,0 +1,36 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import { ControlsParameters, SubcomponentA, SubcomponentB } from './ControlsParameters'; + +/** + * Reference stories to be used by the Controls stories + */ +const meta = { + title: 'examples/Stories for the Controls Block with Subcomponents', + component: ControlsParameters, + subcomponents: { SubcomponentA, SubcomponentB }, + args: { b: 'b' }, + argTypes: { + // @ts-expect-error Meta type is trying to force us to use real props as args + extraMetaArgType: { + type: { name: 'string' }, + name: 'Extra Meta', + description: 'An extra argtype added at the meta level', + table: { defaultValue: { summary: "'a default value'" } }, + }, + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const NoParameters: Story = { + argTypes: { + // @ts-expect-error Story type is trying to force us to use real props as args + extraStoryArgType: { + type: { name: 'string' }, + name: 'Extra Story', + description: 'An extra argtype added at the story level', + table: { defaultValue: { summary: "'a default value'" } }, + }, + }, +}; diff --git a/code/ui/manager/src/components/sidebar/Menu.stories.tsx b/code/ui/manager/src/components/sidebar/Menu.stories.tsx index ca89361f179e..98788518db4b 100644 --- a/code/ui/manager/src/components/sidebar/Menu.stories.tsx +++ b/code/ui/manager/src/components/sidebar/Menu.stories.tsx @@ -56,6 +56,7 @@ export const Expanded: Story = { getAddonsShortcuts: () => ({}), versionUpdateAvailable: () => false, isWhatsNewUnread: () => true, + getDocsUrl: () => 'https://storybook.js.org/docs/', }, false, false, @@ -99,6 +100,7 @@ export const ExpandedWithoutWhatsNew: Story = { getAddonsShortcuts: () => ({}), versionUpdateAvailable: () => false, isWhatsNewUnread: () => false, + getDocsUrl: () => 'https://storybook.js.org/docs/', }, false, false, diff --git a/code/ui/manager/src/components/sidebar/Search.tsx b/code/ui/manager/src/components/sidebar/Search.tsx index 32415c056110..525337b8b2aa 100644 --- a/code/ui/manager/src/components/sidebar/Search.tsx +++ b/code/ui/manager/src/components/sidebar/Search.tsx @@ -122,8 +122,15 @@ const FocusKey = styled.code(({ theme }) => ({ color: theme.base === 'light' ? theme.color.dark : theme.textMutedColor, userSelect: 'none', pointerEvents: 'none', + display: 'flex', + alignItems: 'center', + gap: 4, })); +const FocusKeyCmd = styled.span({ + fontSize: '14px', +}); + const ClearIcon = styled.div(({ theme }) => ({ position: 'absolute', top: 0, @@ -352,7 +359,17 @@ export const Search = React.memo<{ {/* @ts-expect-error (TODO) */} - {enableShortcuts && !isOpen && {searchShortcut}} + {enableShortcuts && !isOpen && ( + + {searchShortcut === '⌘ K' ? ( + <> + K + + ) : ( + searchShortcut + )} + + )} {isOpen && ( clearSelection()}> diff --git a/code/ui/manager/src/container/Menu.tsx b/code/ui/manager/src/container/Menu.tsx index 2c8fb64c01a3..77df6aecdd01 100644 --- a/code/ui/manager/src/container/Menu.tsx +++ b/code/ui/manager/src/container/Menu.tsx @@ -5,7 +5,7 @@ import { Badge } from '@storybook/components'; import type { API, State } from '@storybook/manager-api'; import { shortcutToHumanString } from '@storybook/manager-api'; import { styled, useTheme } from '@storybook/theming'; -import { CheckIcon } from '@storybook/icons'; +import { CheckIcon, InfoIcon, ShareAltIcon, WandIcon } from '@storybook/icons'; const focusableUIElements = { storySearchField: 'storybook-explorer-searchfield', @@ -65,10 +65,22 @@ export const useMenu = ( id: 'about', title: 'About your Storybook', onClick: () => api.changeSettingsTab('about'), + icon: , }), [api] ); + const documentation = useMemo(() => { + const docsUrl = api.getDocsUrl({ versioned: true, renderer: true }); + + return { + id: 'documentation', + title: 'Documentation', + href: docsUrl, + icon: , + }; + }, [api]); + const whatsNewNotificationsEnabled = state.whatsNewData?.status === 'SUCCESS' && !state.disableWhatsNewNotifications; const isWhatsNewUnread = api.isWhatsNewUnread(); @@ -80,6 +92,7 @@ export const useMenu = ( right: whatsNewNotificationsEnabled && isWhatsNewUnread && ( Check it out ), + icon: , }), [api, whatsNewNotificationsEnabled, isWhatsNewUnread] ); @@ -104,7 +117,7 @@ export const useMenu = ( onClick: () => api.toggleNav(), active: isNavShown, right: enableShortcuts ? : null, - left: isNavShown ? : null, + icon: isNavShown ? : null, }), [api, enableShortcuts, shortcutKeys, isNavShown] ); @@ -116,7 +129,7 @@ export const useMenu = ( onClick: () => api.toggleToolbar(), active: showToolbar, right: enableShortcuts ? : null, - left: showToolbar ? : null, + icon: showToolbar ? : null, }), [api, enableShortcuts, shortcutKeys, showToolbar] ); @@ -128,7 +141,7 @@ export const useMenu = ( onClick: () => api.togglePanel(), active: isPanelShown, right: enableShortcuts ? : null, - left: isPanelShown ? : null, + icon: isPanelShown ? : null, }), [api, enableShortcuts, shortcutKeys, isPanelShown] ); @@ -150,7 +163,7 @@ export const useMenu = ( onClick: () => api.toggleFullscreen(), active: isFullscreen, right: enableShortcuts ? : null, - left: isFullscreen ? : null, + icon: isFullscreen ? : null, }), [api, enableShortcuts, shortcutKeys, isFullscreen] ); @@ -232,6 +245,7 @@ export const useMenu = ( () => [ about, ...(state.whatsNewData?.status === 'SUCCESS' ? [whatsNew] : []), + documentation, shortcuts, sidebarToggle, toolbarToogle, @@ -250,6 +264,7 @@ export const useMenu = ( about, state, whatsNew, + documentation, shortcuts, sidebarToggle, toolbarToogle, diff --git a/code/yarn.lock b/code/yarn.lock index c589f2a7e3c1..658e3dd2ca3c 100644 --- a/code/yarn.lock +++ b/code/yarn.lock @@ -4915,8 +4915,8 @@ __metadata: fs-extra: "npm:^11.1.0" react: "npm:^18.2.0" react-dom: "npm:^18.2.0" - remark-external-links: "npm:^8.0.0" - remark-slug: "npm:^6.0.0" + rehype-external-links: "npm:^3.0.0" + rehype-slug: "npm:^6.0.0" ts-dedent: "npm:^2.0.0" typescript: "npm:^5.3.2" vite: "npm:^4.0.4" @@ -5169,7 +5169,6 @@ __metadata: "@angular/platform-browser": "npm:^17.0.5" "@angular/platform-browser-dynamic": "npm:^17.0.5" "@storybook/builder-webpack5": "workspace:*" - "@storybook/cli": "workspace:*" "@storybook/client-logger": "workspace:*" "@storybook/core-common": "workspace:*" "@storybook/core-events": "workspace:*" @@ -5439,11 +5438,9 @@ __metadata: detect-indent: "npm:^6.1.0" envinfo: "npm:^7.7.3" execa: "npm:^5.0.0" - express: "npm:^4.17.3" find-up: "npm:^5.0.0" fs-extra: "npm:^11.1.0" get-npm-tarball-url: "npm:^2.0.3" - get-port: "npm:^5.1.1" giget: "npm:^1.0.0" globby: "npm:^11.0.2" jscodeshift: "npm:^0.15.1" @@ -5459,7 +5456,6 @@ __metadata: tiny-invariant: "npm:^1.3.1" ts-dedent: "npm:^2.0.0" typescript: "npm:^5.3.2" - util-deprecate: "npm:^1.0.2" bin: getstorybook: ./bin/index.js sb: ./bin/index.js @@ -5548,6 +5544,7 @@ __metadata: resolution: "@storybook/core-common@workspace:lib/core-common" dependencies: "@storybook/core-events": "workspace:*" + "@storybook/csf-tools": "workspace:*" "@storybook/node-logger": "workspace:*" "@storybook/types": "workspace:*" "@types/find-cache-dir": "npm:^3.2.1" @@ -5557,9 +5554,13 @@ __metadata: "@types/node-fetch": "npm:^2.6.4" "@types/picomatch": "npm:^2.3.0" "@types/pretty-hrtime": "npm:^1.0.0" + "@yarnpkg/fslib": "npm:2.10.3" + "@yarnpkg/libzip": "npm:2.3.0" chalk: "npm:^4.1.0" + cross-spawn: "npm:^7.0.3" esbuild: "npm:^0.18.0" esbuild-register: "npm:^3.5.0" + execa: "npm:^5.0.0" file-system-cache: "npm:2.3.0" find-cache-dir: "npm:^3.0.0" find-up: "npm:^5.0.0" @@ -5573,10 +5574,14 @@ __metadata: pkg-dir: "npm:^5.0.0" pretty-hrtime: "npm:^1.0.3" resolve-from: "npm:^5.0.0" + semver: "npm:^7.3.7" slash: "npm:^5.0.0" + tempy: "npm:^1.0.1" + tiny-invariant: "npm:^1.3.1" ts-dedent: "npm:^2.0.0" type-fest: "npm:~2.19" typescript: "npm:^5.3.2" + util: "npm:^0.12.4" languageName: unknown linkType: soft @@ -15801,13 +15806,6 @@ __metadata: languageName: node linkType: hard -"get-port@npm:^5.1.1": - version: 5.1.1 - resolution: "get-port@npm:5.1.1" - checksum: 2873877a469b24e6d5e0be490724a17edb39fafc795d1d662e7bea951ca649713b4a50117a473f9d162312cb0e946597bd0e049ed2f866e79e576e8e213d3d1c - languageName: node - linkType: hard - "get-stdin@npm:^6.0.0": version: 6.0.0 resolution: "get-stdin@npm:6.0.0" @@ -15927,10 +15925,10 @@ __metadata: languageName: node linkType: hard -"github-slugger@npm:^1.0.0": - version: 1.5.0 - resolution: "github-slugger@npm:1.5.0" - checksum: 116f99732925f939cbfd6f2e57db1aa7e111a460db0d103e3b3f2fce6909d44311663d4542350706cad806345b9892358cc3b153674f88eeae77f43380b3bfca +"github-slugger@npm:^2.0.0": + version: 2.0.0 + resolution: "github-slugger@npm:2.0.0" + checksum: 21b912b6b1e48f1e5a50b2292b48df0ff6abeeb0691b161b3d93d84f4ae6b1acd6ae23702e914af7ea5d441c096453cf0f621b72d57893946618d21dd1a1c486 languageName: node linkType: hard @@ -16439,6 +16437,24 @@ __metadata: languageName: node linkType: hard +"hast-util-heading-rank@npm:^3.0.0": + version: 3.0.0 + resolution: "hast-util-heading-rank@npm:3.0.0" + dependencies: + "@types/hast": "npm:^3.0.0" + checksum: 1879c84f629e73f1f13247ab349324355cd801363b44e3d46f763aa5c0ea3b42dcd47b46e5643a0502cf01a6b1fdb9208fd12852e44ca6c671b3e4bccf9369a1 + languageName: node + linkType: hard + +"hast-util-is-element@npm:^3.0.0": + version: 3.0.0 + resolution: "hast-util-is-element@npm:3.0.0" + dependencies: + "@types/hast": "npm:^3.0.0" + checksum: f5361e4c9859c587ca8eb0d8343492f3077ccaa0f58a44cd09f35d5038f94d65152288dcd0c19336ef2c9491ec4d4e45fde2176b05293437021570aa0bc3613b + languageName: node + linkType: hard + "hast-util-parse-selector@npm:^2.0.0": version: 2.2.5 resolution: "hast-util-parse-selector@npm:2.2.5" @@ -16493,6 +16509,15 @@ __metadata: languageName: node linkType: hard +"hast-util-to-string@npm:^3.0.0": + version: 3.0.0 + resolution: "hast-util-to-string@npm:3.0.0" + dependencies: + "@types/hast": "npm:^3.0.0" + checksum: 649edd993cf244563ad86d861aa0863759a4fbec49c43b3d92240e42aa4b69f0c3332ddff9e80954bbd8756c86b0fddc20e97d281c6da59d00427f45da8dab68 + languageName: node + linkType: hard + "hast-util-whitespace@npm:^3.0.0": version: 3.0.0 resolution: "hast-util-whitespace@npm:3.0.0" @@ -17268,10 +17293,10 @@ __metadata: languageName: node linkType: hard -"is-absolute-url@npm:^3.0.0": - version: 3.0.3 - resolution: "is-absolute-url@npm:3.0.3" - checksum: 04c415974c32e73a83d3a21a9bea18fc4e2c14fbe6bbd64832cf1e67a75ade2af0e900f552f0b8a447f1305f5ffc9d143ccd8d005dbe715d198c359d342b86f0 +"is-absolute-url@npm:^4.0.0": + version: 4.0.1 + resolution: "is-absolute-url@npm:4.0.1" + checksum: 6f8f603945bd9f2c6031758bbc12352fc647bd5d807cad10d96cc6300fd0e15240cc091521a61db767e4ec0bacff257b4f1015fd5249c147bbb4a4497356c72e languageName: node linkType: hard @@ -19627,15 +19652,6 @@ __metadata: languageName: node linkType: hard -"mdast-util-definitions@npm:^4.0.0": - version: 4.0.0 - resolution: "mdast-util-definitions@npm:4.0.0" - dependencies: - unist-util-visit: "npm:^2.0.0" - checksum: d81bb0b702f99878c8e8e4f66dd7f6f673ab341f061b3d9487ba47dad28b584e02f16b4c42df23714eaac8a7dd8544ba7d77308fad8d4a9fd0ac92e2a7f56be9 - languageName: node - linkType: hard - "mdast-util-find-and-replace@npm:^3.0.0": version: 3.0.1 resolution: "mdast-util-find-and-replace@npm:3.0.1" @@ -19917,13 +19933,6 @@ __metadata: languageName: node linkType: hard -"mdast-util-to-string@npm:^1.0.0": - version: 1.1.0 - resolution: "mdast-util-to-string@npm:1.1.0" - checksum: 5dad9746ec0839792a8a35f504564e8d2b8c30013652410306c111963d33f1ee7b5477aa64ed77b64e13216363a29395809875ffd80e2031a08614657628a121 - languageName: node - linkType: hard - "mdast-util-to-string@npm:^3.0.0, mdast-util-to-string@npm:^3.1.0": version: 3.2.0 resolution: "mdast-util-to-string@npm:3.2.0" @@ -24526,6 +24535,33 @@ __metadata: languageName: node linkType: hard +"rehype-external-links@npm:^3.0.0": + version: 3.0.0 + resolution: "rehype-external-links@npm:3.0.0" + dependencies: + "@types/hast": "npm:^3.0.0" + "@ungap/structured-clone": "npm:^1.0.0" + hast-util-is-element: "npm:^3.0.0" + is-absolute-url: "npm:^4.0.0" + space-separated-tokens: "npm:^2.0.0" + unist-util-visit: "npm:^5.0.0" + checksum: 486b5db73d8fe72611d62b4eb0b56ec71025ea32bba764ad54473f714ca627be75e057ac29243763f85a77c3810f31727ce3e03c975b3803c1c98643d038e9ae + languageName: node + linkType: hard + +"rehype-slug@npm:^6.0.0": + version: 6.0.0 + resolution: "rehype-slug@npm:6.0.0" + dependencies: + "@types/hast": "npm:^3.0.0" + github-slugger: "npm:^2.0.0" + hast-util-heading-rank: "npm:^3.0.0" + hast-util-to-string: "npm:^3.0.0" + unist-util-visit: "npm:^5.0.0" + checksum: 51303c33d039c271cabe62161b49fa737be488f70ced62f00c165e47a089a99de2060050385e5c00d0df83ed30c7fa1c79a51b78508702836aefa51f7e7a6760 + languageName: node + linkType: hard + "relateurl@npm:^0.2.7": version: 0.2.7 resolution: "relateurl@npm:0.2.7" @@ -24547,19 +24583,6 @@ __metadata: languageName: node linkType: hard -"remark-external-links@npm:^8.0.0": - version: 8.0.0 - resolution: "remark-external-links@npm:8.0.0" - dependencies: - extend: "npm:^3.0.0" - is-absolute-url: "npm:^3.0.0" - mdast-util-definitions: "npm:^4.0.0" - space-separated-tokens: "npm:^1.0.0" - unist-util-visit: "npm:^2.0.0" - checksum: 5f0affc97e18ad3247e3b29449f4df98be5a75950cf0f0f13dd1755c4ef1065f9ab44626bba34d913d32bb92afd6f06a8e2f8068e83b48337f0b7a5d1f0cecfe - languageName: node - linkType: hard - "remark-gfm@npm:^4.0.0": version: 4.0.0 resolution: "remark-gfm@npm:4.0.0" @@ -24855,17 +24878,6 @@ __metadata: languageName: node linkType: hard -"remark-slug@npm:^6.0.0": - version: 6.1.0 - resolution: "remark-slug@npm:6.1.0" - dependencies: - github-slugger: "npm:^1.0.0" - mdast-util-to-string: "npm:^1.0.0" - unist-util-visit: "npm:^2.0.0" - checksum: 7cc2857936fce9c9c00b9c7d70de46d594cedf93bd8560fd006164dee7aacccdf472654ee35b33f4fb4bd0af882d89998c6d0c9088c2e95702a9fc15ebae002a - languageName: node - linkType: hard - "remark-stringify@npm:^11.0.0": version: 11.0.0 resolution: "remark-stringify@npm:11.0.0" @@ -28215,13 +28227,6 @@ __metadata: languageName: node linkType: hard -"unist-util-is@npm:^4.0.0": - version: 4.1.0 - resolution: "unist-util-is@npm:4.1.0" - checksum: 21ca3d7bacc88853b880b19cb1b133a056c501617d7f9b8cce969cd8b430ed7e1bc416a3a11b02540d5de6fb86807e169d00596108a459d034cf5faec97c055e - languageName: node - linkType: hard - "unist-util-is@npm:^5.0.0": version: 5.2.1 resolution: "unist-util-is@npm:5.2.1" @@ -28308,16 +28313,6 @@ __metadata: languageName: node linkType: hard -"unist-util-visit-parents@npm:^3.0.0": - version: 3.1.1 - resolution: "unist-util-visit-parents@npm:3.1.1" - dependencies: - "@types/unist": "npm:^2.0.0" - unist-util-is: "npm:^4.0.0" - checksum: 231c80c5ba8e79263956fcaa25ed2a11ad7fe77ac5ba0d322e9d51bbc4238501e3bb52f405e518bcdc5471e27b33eff520db0aa4a3b1feb9fb6e2de6ae385d49 - languageName: node - linkType: hard - "unist-util-visit-parents@npm:^4.0.0": version: 4.1.1 resolution: "unist-util-visit-parents@npm:4.1.1" @@ -28348,17 +28343,6 @@ __metadata: languageName: node linkType: hard -"unist-util-visit@npm:^2.0.0": - version: 2.0.3 - resolution: "unist-util-visit@npm:2.0.3" - dependencies: - "@types/unist": "npm:^2.0.0" - unist-util-is: "npm:^4.0.0" - unist-util-visit-parents: "npm:^3.0.0" - checksum: 7b11303d82271ca53a2ced2d56c87a689dd518596c99ff4a11cdff750f5cc5c0e4b64b146bd2363557cb29443c98713bfd1e8dc6d1c3f9d474b9eb1f23a60888 - languageName: node - linkType: hard - "unist-util-visit@npm:^3.0.0": version: 3.1.0 resolution: "unist-util-visit@npm:3.1.0" diff --git a/docs/addons/addon-types.md b/docs/addons/addon-types.md index c189c090d815..f46e4f41ff03 100644 --- a/docs/addons/addon-types.md +++ b/docs/addons/addon-types.md @@ -90,7 +90,7 @@ Use this boilerplate code while writing your own preset addon. -## Learn more about the Storybook addon ecosystem +**Learn more about the Storybook addon ecosystem** - Types of addons for other types of addons - [Writing addons](./writing-addons.md) for the basics of addon development diff --git a/docs/addons/addons-api.md b/docs/addons/addons-api.md index 5a0464170067..8bdc7d62367e 100644 --- a/docs/addons/addons-api.md +++ b/docs/addons/addons-api.md @@ -378,7 +378,7 @@ Hook that allows you to retrieve or update a story's [`args`](../writing-stories -## Learn more about the Storybook addon ecosystem +**Learn more about the Storybook addon ecosystem** - [Types of addons](./addon-types.md) for other types of addons - [Writing addons](./writing-addons.md) for the basics of addon development diff --git a/docs/addons/integration-catalog.md b/docs/addons/integration-catalog.md index fba4fd76fe2f..e5620b1a7c50 100644 --- a/docs/addons/integration-catalog.md +++ b/docs/addons/integration-catalog.md @@ -104,7 +104,7 @@ Not finding the recipe that you want? If it's popular in the community, our docs If you'd like to request a recipe, open a [new discussion](https://github.com/storybookjs/storybook/discussions/new?category=ideas) in our GitHub repo. We'll review your request, and if it's popular, we'll add it to our backlog and prioritize it. -## Learn more about the Storybook addon ecosystem +**Learn more about the Storybook addon ecosystem** - [Types of addons](./addon-types.md) for other types of addons - [Writing addons](./writing-addons.md) for the basics of addon development diff --git a/docs/addons/writing-addons.md b/docs/addons/writing-addons.md index ca963041014c..dd99775dbdd5 100644 --- a/docs/addons/writing-addons.md +++ b/docs/addons/writing-addons.md @@ -315,7 +315,7 @@ By default, the Addon Kit comes pre-configured with a GitHub Actions workflow, e Then, click the **New repository secret**, name it `NPM_TOKEN`, and paste the token you generated earlier. Whenever you merge a pull request to the default branch, the workflow will run and publish a new release, automatically incrementing the version number and updating the changelog. -## Learn more about the Storybook addon ecosystem +**Learn more about the Storybook addon ecosystem** - [Types of addons](./addon-types.md) for other types of addons - Writing addons for the basics of addon development diff --git a/docs/addons/writing-presets.md b/docs/addons/writing-presets.md index 4c7ae3be3843..eaa3da082eb5 100644 --- a/docs/addons/writing-presets.md +++ b/docs/addons/writing-presets.md @@ -57,7 +57,9 @@ To customize Storybook's Babel configuration and add support for additional feat + The Babel configuration is only applied to frameworks that use Babel internally. If you enable it for a framework that uses a different compiler, like [SWC](https://swc.rs/) or [esbuild](https://esbuild.github.io/), it will be ignored. + ### Builders @@ -199,6 +201,8 @@ However, if you need, you can also customize the template used by Storybook to r As Storybook relies on [esbuild](https://esbuild.github.io/) instead of Webpack to build the UI, presets that depend on the `managerWebpack` API to configure the manager or load additional files other than CSS or images will no longer work. We recommend removing it from your preset and adjusting your configuration to convert any additional files to JavaScript. +**Learn more about the Storybook addon ecosystem** + - [Types of addons](./addon-types.md) for other types of addons - [Writing addons](./writing-addons.md) for the basics of addon development - Presets for preset development diff --git a/docs/api/main-config-babel-default.md b/docs/api/main-config-babel-default.md index b79867e1dc34..94f34878ac63 100644 --- a/docs/api/main-config-babel-default.md +++ b/docs/api/main-config-babel-default.md @@ -26,7 +26,7 @@ To adjust your Storybook's Babel setup directly—not via an addon—use [`babel ## `Babel.Config` -See [Babel docs](https://babeljs.io/docs/options). +The options provided by [Babel](https://babeljs.io/docs/options) are only applicable if you've enabled the [`@storybook/addon-webpack5-compiler-babel`](https://storybook.js.org/addons/@storybook/addon-webpack5-compiler-babel) addon. ## `Options` diff --git a/docs/api/main-config-babel.md b/docs/api/main-config-babel.md index a84a938b5a35..d7e9c865a0e0 100644 --- a/docs/api/main-config-babel.md +++ b/docs/api/main-config-babel.md @@ -27,7 +27,13 @@ Customize Storybook's [Babel](https://babeljs.io/) setup. ## `Babel.Config` -See [Babel docs](https://babeljs.io/docs/options). +The options provided by [Babel](https://babeljs.io/docs/options) are only applicable if you've enabled the [`@storybook/addon-webpack5-compiler-babel`](https://storybook.js.org/addons/@storybook/addon-webpack5-compiler-babel) addon. + + + +If you have an existing Babel configuration file (e.g., `.babelrc`), it will be automatically detected and used by Storybook without any additional configuration required. + + ## `Options` diff --git a/docs/api/main-config-stories.md b/docs/api/main-config-stories.md index b59d0cb93bf0..89b7051588ba 100644 --- a/docs/api/main-config-stories.md +++ b/docs/api/main-config-stories.md @@ -101,7 +101,7 @@ Where to start looking for story files, relative to the root of your project. Type: `string` -Default: `'**/*.@(mdx|stories.@(mdx|js|jsx|mjs|ts|tsx))'` +Default: `'**/*.@(mdx|stories.@(js|jsx|mjs|ts|tsx))'` A glob, relative to `StoriesSpecifier.directory` (with no leading `./`), that matches the filenames to load. diff --git a/docs/api/main-config-swc.md b/docs/api/main-config-swc.md index ffd91ddbc7fb..0a102bf0cf13 100644 --- a/docs/api/main-config-swc.md +++ b/docs/api/main-config-swc.md @@ -21,7 +21,7 @@ Customize Storybook's [SWC](https://swc.rs/) setup. ## `SWC.Options` -See SWC's [documentation](https://swc.rs/) for more information. +The options provided by [SWC](https://swc.rs/) are only applicable if you've enabled the [`@storybook/addon-webpack5-compiler-swc`](https://storybook.js.org/addons/@storybook/addon-webpack5-compiler-swc) addon. ## Options diff --git a/docs/builders/builder-api.md b/docs/builders/builder-api.md index 82ecfcf27fbf..f06c82b95e27 100644 --- a/docs/builders/builder-api.md +++ b/docs/builders/builder-api.md @@ -157,7 +157,7 @@ While running in development mode, the builder's development server must be able This area is under rapid development, and the associated documentation is still in progress and subject to change. If you are interested in creating a builder, you can learn more about implementing a builder in Storybook by checking the source code for [Vite](https://github.com/storybookjs/storybook/tree/next/code/builders/builder-vite), [Webpack](https://github.com/storybookjs/storybook/tree/next/code/builders/builder-webpack5), or Modern Web's [dev-server-storybook](https://github.com/modernweb-dev/web/blob/master/packages/dev-server-storybook/src/serve/storybookPlugin.ts). When you're ready, open an [RFC](../contribute/RFC.md) to discuss your proposal with the Storybook community and maintainers. -#### Learn more about builders +**Learn more about builders** - [Vite builder](./vite.md) for bundling with Vite - [Webpack builder](./webpack.md) for bundling with Webpack diff --git a/docs/builders/vite.md b/docs/builders/vite.md index 5a2025ed0a51..e8d722777e7d 100644 --- a/docs/builders/vite.md +++ b/docs/builders/vite.md @@ -140,7 +140,7 @@ If you are migrating from a Webpack-based project, such as [CRA](https://create- -#### Learn more about builders +**Learn more about builders** - Vite builder for bundling with Vite - [Webpack builder](./webpack.md) for bundling with Webpack diff --git a/docs/builders/webpack.md b/docs/builders/webpack.md index dcf545175b56..b4f13fa46aec 100644 --- a/docs/builders/webpack.md +++ b/docs/builders/webpack.md @@ -107,9 +107,57 @@ Additionally, if you're generating a [static build](../api/cli-options.md#build) -## What about Webpack 4 support? +## Compiler support -Support for Webpack 4 has been removed and is no longer being maintained. If you're upgrading your Storybook, it will automatically use Webpack 5 and attempt to migrate your configuration. However, if you're working with a custom Webpack configuration, you may need to update it to work with Webpack 5. The migration process is necessary to ensure that your project runs smoothly with the latest version of Storybook. You can follow the instructions provided on the Webpack [website](https://webpack.js.org/migrate/5/) to update your configuration. +Storybook takes a compiler-agnostic approach to bundling. This allows you to bring your own application bundler (e.g., [Babel](https://babeljs.io/), [SWC](https://swc.rs/)) and ensures compatibility within the vast ecosystem of Webpack 5-based projects. + +### SWC + +If your project is built using [SWC](https://swc.rs/), use the [`@storybook/addon-webpack5-compiler-swc`](https://storybook.js.org/addons/@storybook/addon-webpack5-compiler-swc) addon. This addon increases ecosystem compatibility with Webpack 5 projects while maintaining high performance. Run the following command to set up the addon automatically: + + + + + + + + + +Additional options can be provided to customize the SWC configuration. See the [SWC API documentation](../api/main-config-swc.md) for more information. + + + +When enabled, this addon adjusts the Webpack configuration to use the [`swc-loader`](https://swc.rs/docs/usage/swc-loader) for JavaScript and TypeScript files. Additionally, it will detect and use your project's SWC configuration. + +### Babel + +If you're working with a project that relies on Babel's tooling to provide support for specific features, including TypeScript or other modern JavaScript features, you can use the [`@storybook/addon-webpack5-compiler-babel`](https://storybook.js.org/addons/@storybook/addon-webpack5-compiler-babel) addon to allow you to include them in your Storybook to ensure compatibility with your project. Run the following command to set up the addon automatically: + + + + + + + + + +Additional options can be provided to customize the Babel configuration. See the [`babel` API documentation](../api/main-config-babel.md) for more information, or if you're working on an addon, the [`babelDefault` documentation](../api/main-config-babel-default.md) for more information. + + + +When enabled, the addon will adjust the Webpack configuration to use the [`babel-loader`](https://webpack.js.org/loaders/babel-loader/) as the default loader for JavaScript and TypeScript files. Additionally, it will detect and use your project's Babel configuration. ## Troubleshooting @@ -145,7 +193,11 @@ However, if you're working with a framework that provides a default aliasing con As Storybook relies on [esbuild](https://esbuild.github.io/) to build its internal manager, support for bundling assets with the `managerWebpack` will no longer have an impact on the Storybook UI. We recommend removing existing `managerWebpack` configuration elements from your Storybook configuration file and bundling assets other than images or CSS into JavaScript beforehand. -#### Learn more about builders +### Storybook doesn't run with Webpack 4 + +Support for Webpack 4 has been removed and is no longer being maintained. If you're upgrading your Storybook, it will automatically use Webpack 5 and attempt to migrate your configuration. However, if you're working with a custom Webpack configuration, you may need to update it to work with Webpack 5. The migration process is necessary to ensure that your project runs smoothly with the latest version of Storybook. You can follow the instructions provided on the Webpack [website](https://webpack.js.org/migrate/5/) to update your configuration. + +**Learn more about builders** - [Vite builder](./vite.md) for bundling with Vite - Webpack builder for bundling with Webpack diff --git a/docs/contribute/RFC.md b/docs/contribute/RFC.md index e920e6b6bead..96b93158d07d 100644 --- a/docs/contribute/RFC.md +++ b/docs/contribute/RFC.md @@ -46,7 +46,7 @@ If you are interested in implementing an "active" RFC, but cannot determine if s This RFC process took heavy inspiration from the RFC processes from [Rust](https://github.com/rust-lang/rfcs) and [Gatsby](https://www.gatsbyjs.com/contributing/rfc-process/). -## Learn more about contributing to Storybook +**Learn more about contributing to Storybook** - RFC process for authoring feature requests - [Code](./code.md) for features and bug fixes diff --git a/docs/contribute/code.md b/docs/contribute/code.md index f386fa2f2be9..b9d246e2119a 100644 --- a/docs/contribute/code.md +++ b/docs/contribute/code.md @@ -261,7 +261,7 @@ It's troublesome to know which packages you'll change ahead of time, and watchin -## Learn more about contributing to Storybook +**Learn more about contributing to Storybook** - [RFC process](./RFC.md) for authoring feature requests - Code for features and bug fixes diff --git a/docs/contribute/documentation-updates.md b/docs/contribute/documentation-updates.md index 76808e2c786b..d73d9d8f3070 100644 --- a/docs/contribute/documentation-updates.md +++ b/docs/contribute/documentation-updates.md @@ -22,7 +22,7 @@ Scroll down to the bottom of the document page on GitHub and describe what you c In the Storybook repository, create a pull request that describes changes and includes additional context that would help maintainers review. Once you submit the PR, a maintainer will guide you through the triage and merge process. -## Learn more about contributing to Storybook +**Learn more about contributing to Storybook** - [RFC process](./RFC.md) for authoring feature requests - [Code](./code.md) for features and bug fixes diff --git a/docs/contribute/framework.md b/docs/contribute/framework.md index e568b0d54fb4..f72a7eae9500 100644 --- a/docs/contribute/framework.md +++ b/docs/contribute/framework.md @@ -143,7 +143,7 @@ Test it in a fresh project using a Storybook set up as close as possible to your Once it's fully tested and released, please let us know about your framework by either announcing it in the [`#showcase`](https://discord.com/channels/486522875931656193/1048740936953376859) Discord channel or tweeting it and mentioning `@storybookjs`. It's our hope that well-made community frameworks can eventually move into the Storybook codebase and be considered "officially" supported. -## Learn more about contributing to Storybook +**Learn more about contributing to Storybook** - [RFC process](./RFC.md) for authoring feature requests - [Code](./code.md) for features and bug fixes diff --git a/docs/contribute/new-snippets.md b/docs/contribute/new-snippets.md index 0ba9c72067f0..ca07ff4bfa79 100644 --- a/docs/contribute/new-snippets.md +++ b/docs/contribute/new-snippets.md @@ -131,7 +131,7 @@ Go through the documentation and check your work. Finally, commit, push and open a pull request in the Storybook monorepo. Add a clear description of the work you've done, and one of the maintainers will guide you through the merge process. -## Learn more about contributing to Storybook +**Learn more about contributing to Storybook** - [RFC process](./RFC.md) for authoring feature requests - [Code](./code.md) for features and bug fixes diff --git a/docs/snippets/common/component-story-mdx-embed.mdx.mdx b/docs/snippets/common/component-story-mdx-embed.mdx.mdx deleted file mode 100644 index a87ba2dda8d1..000000000000 --- a/docs/snippets/common/component-story-mdx-embed.mdx.mdx +++ /dev/null @@ -1,11 +0,0 @@ -```md - - -import { Story } from '@storybook/addon-docs'; - -# Some header - -And Markdown here - - -``` \ No newline at end of file diff --git a/docs/snippets/common/component-story-mdx-reference-storyid.mdx.mdx b/docs/snippets/common/component-story-mdx-reference-storyid.mdx.mdx deleted file mode 100644 index d79599c9cecb..000000000000 --- a/docs/snippets/common/component-story-mdx-reference-storyid.mdx.mdx +++ /dev/null @@ -1,7 +0,0 @@ -```md - - -import { Story } from '@storybook/addon-docs'; - - -``` \ No newline at end of file diff --git a/docs/snippets/common/main-config-features-legacy-mdx-1.js.mdx b/docs/snippets/common/main-config-features-legacy-mdx-1.js.mdx deleted file mode 100644 index e961bf074cb3..000000000000 --- a/docs/snippets/common/main-config-features-legacy-mdx-1.js.mdx +++ /dev/null @@ -1,12 +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)'], - features: { - legacyMdx1: true, // 👈 Enables MDX v1 support - }, -}; -``` diff --git a/docs/snippets/common/main-config-features-legacy-mdx-1.ts.mdx b/docs/snippets/common/main-config-features-legacy-mdx-1.ts.mdx deleted file mode 100644 index c983311700f4..000000000000 --- a/docs/snippets/common/main-config-features-legacy-mdx-1.ts.mdx +++ /dev/null @@ -1,16 +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)'], - features: { - legacyMdx1: true, // 👈 Enables MDX v1 support - }, -}; - -export default config; -``` diff --git a/docs/snippets/common/mdx-canvas-doc-block.mdx.mdx b/docs/snippets/common/mdx-canvas-doc-block.mdx.mdx deleted file mode 100644 index dbd2297f3da2..000000000000 --- a/docs/snippets/common/mdx-canvas-doc-block.mdx.mdx +++ /dev/null @@ -1,13 +0,0 @@ -```md - - -import { Canvas, Meta } from '@storybook/addon-docs'; - -import { ExampleComponent} from './ExampleComponent'; - - - - - - -``` \ No newline at end of file diff --git a/docs/snippets/common/mdx-changelog-stories.mdx.mdx b/docs/snippets/common/mdx-changelog-stories.mdx.mdx deleted file mode 100644 index bc0aeb2980a7..000000000000 --- a/docs/snippets/common/mdx-changelog-stories.mdx.mdx +++ /dev/null @@ -1,12 +0,0 @@ -```md - - - -import { Meta } from '@storybook/addon-docs'; - -import Changelog from '../CHANGELOG.md'; - - - - -``` \ No newline at end of file diff --git a/docs/snippets/common/my-component-with-story-content.mdx.mdx b/docs/snippets/common/my-component-with-story-content.mdx.mdx deleted file mode 100644 index 66451f49bbbf..000000000000 --- a/docs/snippets/common/my-component-with-story-content.mdx.mdx +++ /dev/null @@ -1,12 +0,0 @@ -```md - - -import { Canvas } from '@storybook/addon-docs'; - -import { MyComponent } from './MyComponent'; - - -

Some here

- -
-``` diff --git a/docs/snippets/common/storybook-addon-compiler-babel-auto-install.npm.js.mdx b/docs/snippets/common/storybook-addon-compiler-babel-auto-install.npm.js.mdx new file mode 100644 index 000000000000..334013ebdfba --- /dev/null +++ b/docs/snippets/common/storybook-addon-compiler-babel-auto-install.npm.js.mdx @@ -0,0 +1,3 @@ +```sh +npx storybook@latest add @storybook/addon-webpack5-compiler-babel +``` diff --git a/docs/snippets/common/storybook-addon-compiler-babel-auto-install.pnpm.js.mdx b/docs/snippets/common/storybook-addon-compiler-babel-auto-install.pnpm.js.mdx new file mode 100644 index 000000000000..bb5e796ab2f1 --- /dev/null +++ b/docs/snippets/common/storybook-addon-compiler-babel-auto-install.pnpm.js.mdx @@ -0,0 +1,3 @@ +```sh +pnpm dlx storybook@latest add @storybook/addon-webpack5-compiler-babel +``` diff --git a/docs/snippets/common/storybook-addon-compiler-babel-auto-install.yarn.js.mdx b/docs/snippets/common/storybook-addon-compiler-babel-auto-install.yarn.js.mdx new file mode 100644 index 000000000000..c29afff2cedd --- /dev/null +++ b/docs/snippets/common/storybook-addon-compiler-babel-auto-install.yarn.js.mdx @@ -0,0 +1,3 @@ +```sh +yarn dlx storybook@latest add @storybook/addon-webpack5-compiler-babel +``` diff --git a/docs/snippets/common/storybook-addon-compiler-swc-auto-install.npm.js.mdx b/docs/snippets/common/storybook-addon-compiler-swc-auto-install.npm.js.mdx new file mode 100644 index 000000000000..8161ee59d97d --- /dev/null +++ b/docs/snippets/common/storybook-addon-compiler-swc-auto-install.npm.js.mdx @@ -0,0 +1,3 @@ +```sh +npx storybook@latest add @storybook/addon-webpack5-compiler-swc +``` diff --git a/docs/snippets/common/storybook-addon-compiler-swc-auto-install.pnpm.js.mdx b/docs/snippets/common/storybook-addon-compiler-swc-auto-install.pnpm.js.mdx new file mode 100644 index 000000000000..5f70d2b8d773 --- /dev/null +++ b/docs/snippets/common/storybook-addon-compiler-swc-auto-install.pnpm.js.mdx @@ -0,0 +1,3 @@ +```sh +pnpm dlx storybook@latest add @storybook/addon-webpack5-compiler-swc +``` diff --git a/docs/snippets/common/storybook-addon-compiler-swc-auto-install.yarn.js.mdx b/docs/snippets/common/storybook-addon-compiler-swc-auto-install.yarn.js.mdx new file mode 100644 index 000000000000..ceb0554462a0 --- /dev/null +++ b/docs/snippets/common/storybook-addon-compiler-swc-auto-install.yarn.js.mdx @@ -0,0 +1,3 @@ +```sh +yarn dlx storybook@latest add @storybook/addon-webpack5-compiler-swc +``` diff --git a/docs/snippets/common/storybook-automigration-mdx-legacy.npm.mdx b/docs/snippets/common/storybook-automigration-mdx-legacy.npm.mdx new file mode 100644 index 000000000000..b54577026015 --- /dev/null +++ b/docs/snippets/common/storybook-automigration-mdx-legacy.npm.mdx @@ -0,0 +1,3 @@ +```shell +npx storybook@latest automigrate mdx1to2 +``` diff --git a/docs/snippets/common/storybook-automigration-mdx-legacy.pnpm.mdx b/docs/snippets/common/storybook-automigration-mdx-legacy.pnpm.mdx new file mode 100644 index 000000000000..4c17825bf1a8 --- /dev/null +++ b/docs/snippets/common/storybook-automigration-mdx-legacy.pnpm.mdx @@ -0,0 +1,3 @@ +```shell +pnpm dlx storybook@latest automigrate mdx1to2 +``` diff --git a/docs/snippets/common/storybook-automigration-mdx-legacy.yarn.mdx b/docs/snippets/common/storybook-automigration-mdx-legacy.yarn.mdx new file mode 100644 index 000000000000..277d34f73467 --- /dev/null +++ b/docs/snippets/common/storybook-automigration-mdx-legacy.yarn.mdx @@ -0,0 +1,3 @@ +```shell +yarn dlx storybook@latest automigrate mdx1to2 +``` diff --git a/docs/snippets/common/storybook-main-register-essentials-addon.js.mdx b/docs/snippets/common/storybook-main-register-essentials-addon.js.mdx index 6e7e2fca09fe..ee4ecfbd706c 100644 --- a/docs/snippets/common/storybook-main-register-essentials-addon.js.mdx +++ b/docs/snippets/common/storybook-main-register-essentials-addon.js.mdx @@ -3,7 +3,7 @@ export default { framework: '@storybook/your-framework', - stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'], + stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'], addons: ['@storybook/addon-essentials'], // 👈 Register addon-essentials }; ``` diff --git a/docs/snippets/common/storybook-main-register-essentials-addon.ts.mdx b/docs/snippets/common/storybook-main-register-essentials-addon.ts.mdx index d4e42fd31129..88f505b62b7b 100644 --- a/docs/snippets/common/storybook-main-register-essentials-addon.ts.mdx +++ b/docs/snippets/common/storybook-main-register-essentials-addon.ts.mdx @@ -6,7 +6,7 @@ import type { StorybookConfig } from '@storybook/your-framework'; const config: StorybookConfig = { framework: '@storybook/your-framework', - stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'], + stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'], addons: ['@storybook/addon-essentials'], // 👈 Register addon-essentials }; diff --git a/docs/snippets/common/storybook-migration-mdx-stories.npm.mdx b/docs/snippets/common/storybook-migration-mdx-stories.npm.mdx new file mode 100644 index 000000000000..f2f2c30f2fac --- /dev/null +++ b/docs/snippets/common/storybook-migration-mdx-stories.npm.mdx @@ -0,0 +1,3 @@ +```shell +npx storybook@latest migrate mdx-to-csf --glob "src/**/*.stories.mdx” +``` diff --git a/docs/snippets/common/storybook-migration-mdx-stories.pnpm.mdx b/docs/snippets/common/storybook-migration-mdx-stories.pnpm.mdx new file mode 100644 index 000000000000..5f9ac39d6e7c --- /dev/null +++ b/docs/snippets/common/storybook-migration-mdx-stories.pnpm.mdx @@ -0,0 +1,3 @@ +```shell +pnpm dlx storybook@latest migrate mdx-to-csf --glob "src/**/*.stories.mdx” +``` diff --git a/docs/snippets/common/storybook-migration-mdx-stories.yarn.mdx b/docs/snippets/common/storybook-migration-mdx-stories.yarn.mdx new file mode 100644 index 000000000000..499212af80b1 --- /dev/null +++ b/docs/snippets/common/storybook-migration-mdx-stories.yarn.mdx @@ -0,0 +1,3 @@ +```shell +yarn dlx storybook@latest migrate mdx-to-csf --glob "src/**/*.stories.mdx” +``` diff --git a/docs/snippets/html/checkbox-story.mdx.mdx b/docs/snippets/html/checkbox-story.mdx.mdx deleted file mode 100644 index e81434c68848..000000000000 --- a/docs/snippets/html/checkbox-story.mdx.mdx +++ /dev/null @@ -1,45 +0,0 @@ -```md - - -import { Canvas, Meta, Story } from '@storybook/addon-docs'; - -export const Checkbox = (args) => ``; - - - -# Checkbox - -With `MDX`, we can define a story for `Checkbox` right in the middle of our -Markdown documentation. - - - - - - -``` diff --git a/docs/snippets/preact/histogram-story.mdx.mdx b/docs/snippets/preact/histogram-story.mdx.mdx deleted file mode 100644 index e0843151cc72..000000000000 --- a/docs/snippets/preact/histogram-story.mdx.mdx +++ /dev/null @@ -1,27 +0,0 @@ -```md - - -import { Canvas, Meta, Story } from '@storybook/addon-docs/'; - -import { Histogram } from './Histogram'; - - - - - - - } /> - -``` \ No newline at end of file diff --git a/docs/versions/next.json b/docs/versions/next.json index 690b644cd675..fefad810da35 100644 --- a/docs/versions/next.json +++ b/docs/versions/next.json @@ -1 +1 @@ -{"version":"8.0.0-alpha.11","info":{"plain":"- Angular: Remove cached NgModules and introduce a global queue during bootstrapping - [#24982](https://github.com/storybookjs/storybook/pull/24982), thanks [@Marklb](https://github.com/Marklb)!\n- CLI: Fix sandbox command versioning - [#25600](https://github.com/storybookjs/storybook/pull/25600), thanks [@ndelangen](https://github.com/ndelangen)!\n- CLI: Support upgrading to canary versions - [#25596](https://github.com/storybookjs/storybook/pull/25596), thanks [@JReinhold](https://github.com/JReinhold)!\n- ConfigFile: Fix export specifiers - [#25590](https://github.com/storybookjs/storybook/pull/25590), thanks [@shilman](https://github.com/shilman)!\n- Interaction: Replace @storybook/jest by @storybook/test - [#25584](https://github.com/storybookjs/storybook/pull/25584), thanks [@valentinpalkovic](https://github.com/valentinpalkovic)!\n- Next.js: Pass jsConfig to SWC Loader and load config with defaults - [#25203](https://github.com/storybookjs/storybook/pull/25203), thanks [@valentinpalkovic](https://github.com/valentinpalkovic)!\n- Parameters: Remove passArgsFirst flag - [#25585](https://github.com/storybookjs/storybook/pull/25585), thanks [@valentinpalkovic](https://github.com/valentinpalkovic)!\n- Preset: Remove deprecated config preset - [#25607](https://github.com/storybookjs/storybook/pull/25607), thanks [@valentinpalkovic](https://github.com/valentinpalkovic)!\n- React: Refactor RSC out of Next - [#25591](https://github.com/storybookjs/storybook/pull/25591), thanks [@shilman](https://github.com/shilman)!\n- Sandboxes: Update wait-on command to use TCP instead of HTTP - [#25541](https://github.com/storybookjs/storybook/pull/25541), thanks [@valentinpalkovic](https://github.com/valentinpalkovic)!\n- Telejson: Update stringify options in codebase - [#25564](https://github.com/storybookjs/storybook/pull/25564), thanks [@valentinpalkovic](https://github.com/valentinpalkovic)!\n- UI: Fix menu icon on the sidebar - [#25587](https://github.com/storybookjs/storybook/pull/25587), thanks [@cdedreuille](https://github.com/cdedreuille)!\n- Webpack5: Make export-order-loader compatible with both esm and cjs - [#25540](https://github.com/storybookjs/storybook/pull/25540), thanks [@mlazari](https://github.com/mlazari)!"}} +{"version":"8.0.0-alpha.12","info":{"plain":"- Blocks: Fix Controls block not having controls - [#25663](https://github.com/storybookjs/storybook/pull/25663), thanks [@JReinhold](https://github.com/JReinhold)!\n- Blocks: Support `subcomponents` in `ArgTypes` and `Controls`, remove `ArgsTable` block - [#25614](https://github.com/storybookjs/storybook/pull/25614), thanks [@JReinhold](https://github.com/JReinhold)!\n- CLI: Fix existing version detection in `upgrade` - [#25642](https://github.com/storybookjs/storybook/pull/25642), thanks [@JReinhold](https://github.com/JReinhold)!\n- Core: Add preset with experimental server API - [#25664](https://github.com/storybookjs/storybook/pull/25664), thanks [@shilman](https://github.com/shilman)!\n- MDX: Replace remark by rehype plugins - [#25615](https://github.com/storybookjs/storybook/pull/25615), thanks [@valentinpalkovic](https://github.com/valentinpalkovic)!\n- React: Fix acorn ecma version warning - [#25634](https://github.com/storybookjs/storybook/pull/25634), thanks [@dannyhw](https://github.com/dannyhw)!\n- Shortcuts: Require modifier key to trigger shortcuts (`F`,`A`,`D`,`S`,`T`,`/`) - [#25625](https://github.com/storybookjs/storybook/pull/25625), thanks [@cdedreuille](https://github.com/cdedreuille)!\n- Theming: Fix export of module augmentation - [#25643](https://github.com/storybookjs/storybook/pull/25643), thanks [@valentinpalkovic](https://github.com/valentinpalkovic)!\n- UI: Add links to documentation and videos in UI - [#25565](https://github.com/storybookjs/storybook/pull/25565), thanks [@Integrayshaun](https://github.com/Integrayshaun)!\n- Webpack: Use `node:assert` used in `export-order-loader` - [#25622](https://github.com/storybookjs/storybook/pull/25622), thanks [@JReinhold](https://github.com/JReinhold)!"}} diff --git a/docs/writing-docs/autodocs.md b/docs/writing-docs/autodocs.md index 4e066de59f64..e902c99eb942 100644 --- a/docs/writing-docs/autodocs.md +++ b/docs/writing-docs/autodocs.md @@ -290,7 +290,7 @@ If you're still encountering issues, we recommend reaching out to the community If you turned off inline rendering for your stories via the [`inline`](../api/doc-block-story.md#inline) configuration option, you would run into a situation where the associated controls are not updating the story within the documentation page. This is a known limitation of the current implementation and will be addressed in a future release. -#### Learn more about Storybook documentation +**Learn more about Storybook documentation** - Autodocs for creating documentation for your stories - [MDX](./mdx.md) for customizing your documentation diff --git a/docs/writing-docs/build-documentation.md b/docs/writing-docs/build-documentation.md index fffb922a5c9b..aeaec7bf5ae9 100644 --- a/docs/writing-docs/build-documentation.md +++ b/docs/writing-docs/build-documentation.md @@ -50,7 +50,7 @@ You can use any hosting provider to deploy your documentation, for instance: - [Netlify](https://www.netlify.com/) - [S3](https://aws.amazon.com/en/s3/) -#### Learn more about Storybook documentation +**Learn more about Storybook documentation** - [Autodocs](./autodocs.md) for creating documentation for your stories - [MDX](./mdx.md) for customizing your documentation diff --git a/docs/writing-docs/doc-blocks.md b/docs/writing-docs/doc-blocks.md index 12cfaee8317b..d42b5e8749fe 100644 --- a/docs/writing-docs/doc-blocks.md +++ b/docs/writing-docs/doc-blocks.md @@ -278,7 +278,7 @@ Storybook's Doc Blocks are highly customizable and helpful building blocks to as -#### Learn more about Storybook documentation +**Learn more about Storybook documentation** - [Autodocs](./autodocs.md) for creating documentation for your stories - [MDX](./mdx.md) for customizing your documentation diff --git a/docs/writing-docs/mdx.md b/docs/writing-docs/mdx.md index cfe98b9bc4a7..afc4c11a86b4 100644 --- a/docs/writing-docs/mdx.md +++ b/docs/writing-docs/mdx.md @@ -12,7 +12,7 @@ In addition, you can write pure documentation pages in MDX and add them to Story -Writing stories directly in MDX was deprecated in Storybook 7. Please reference the [previous documentation](../../../release-6-5/docs/writing-docs/mdx.md) for guidance on that feature. +Writing stories directly in MDX was removed in Storybook 8, and we're no longer supporting it. Please reference the [previous documentation](../../../release-6-5/docs/writing-docs/mdx.md) for guidance on that feature or [migrate](../migration-guide.md#storiesmdx-to-mdxcsf) to the new format. @@ -63,12 +63,6 @@ The first thing you'll notice is that the component documentation is divided int - **CSF** is great for succinctly defining stories (component examples). If you use TypeScript, it also provides type safety and auto-completion. - **MDX** is great for writing structured documentation and composing it with interactive JSX elements. - - -If you’re coming from a previous version of Storybook, you might be accustomed to using MDX both for **documentation** and for defining **stories** in the same `.stories.mdx` file. We’ve deprecated this functionality and plan to remove it in a future version of Storybook. We provide [migration](#automigration) scripts to help you onto the new format. - - - ### Anatomy of MDX Assuming you’re already familiar with writing stories with [CSF](../writing-stories/index.md), we can dissect the MDX side of things in greater detail. @@ -210,9 +204,17 @@ The [`remark-gfm`](https://github.com/remarkjs/remark-gfm) package isn't provide To help you transition to the new version, we've created a migration helper in our CLI. We recommend using it and reaching out using the default communication channels (e.g., [GitHub discussions](https://github.com/storybookjs/storybook/discussions/new?category=help)) for problems you encounter. -```shell -npx storybook@latest automigrate mdx1to2 -``` + + + + + ## Setup custom documentation @@ -386,47 +388,12 @@ By applying this pattern with the Controls addon, all anchors will be ignored in ### The MDX documentation doesn't render in my environment -As Storybook relies on MDX 2 to render documentation, some technical limitations may prevent you from migrating to this version. If that's the case, we've prepared a set of instructions to help you transition to this new version. +As Storybook relies on the latest version of [MDX](https://mdxjs.com/) to render documentation, some technical limitations may prevent you from migrating to this version. If that's the case, we've prepared a set of instructions to help you transition to this new version. #### Storybook doesn't create documentation for my component stories If you run into a situation where Storybook is not able to detect and render the documentation for your component stories, it may be due to a misconfiguration in your Storybook. Check your configuration file (i.e., `.storybook/main.js|ts`) and ensure the `stories` configuration element provides the correct path to your stories location(e.g., `../src/**/*.stories.@(js|jsx|mjs|ts|tsx)`). -#### The documentation doesn't render using `stories.mdx` - -Starting with Storybook 7.0, we've deprecated documenting stories with the `.stories.mdx` file extension. If you're still using the `stories.mdx` extension, we recommend [migrating](#automigration) as soon as possible to avoid any issues, as the majority of APIs and [Doc Blocks](./doc-blocks.md) used by Storybook were overhauled to support MDX 2 and the new MDX compiler (e.g., the [`Meta`](../api/doc-block-meta.md) block). - -#### MDX 1 fallback - -If you're still having issues with MDX documentation, you can enable MDX 1 as a fallback. To do so, you'll need to take some additional steps. - -Run the following command to install the required dependency. - - - - - - - -Update your Storybook configuration (in `.storybook/main.js|ts`), and provide the `legacyMdx1` feature flag to enable MDX 1 support. - - - - - - - ### The migration seems flaky and keeps failing By default, running the [migration command](#automigration) will try and migrate all existing MDX files in your project according to the MDX 2 specification. However, this might not always be possible, and you might run into issues during the migration. To help you troubleshoot those issues, we've prepared some recommendations that might help you. @@ -459,7 +426,16 @@ If you're still encountering issues, we recommend reaching out to the community If you turned off inline rendering for your stories via the [`inline`](../api/doc-block-story.md#inline) configuration option, you would run into a situation where the associated controls are not updating the story within the documentation page. This is a known limitation of the current implementation and will be addressed in a future release. -#### Learn more about Storybook documentation +### The React version used is unexpected + +For most projects, Storybook's addon-docs uses the React version listed in your project's dependencies. If it does not find one, it will use React 18.2.0. There are two exceptions to this: + +- Preact projects will always use React 17 +- Next.js projects will always use the canary version that comes with the Next.js version installed, regardless of which React version is listed in the project’s dependencies. + +If you're having issues with the React version used, you may need to re-create your project's `node_modules` folder to ensure the correct version is used. + +**Learn more about Storybook documentation** - [Autodocs](./autodocs.md) for creating documentation for your stories - MDX for customizing your documentation diff --git a/scripts/event-log-checker.ts b/scripts/event-log-checker.ts index 3a835f6ddf22..849c5d0cf140 100644 --- a/scripts/event-log-checker.ts +++ b/scripts/event-log-checker.ts @@ -4,7 +4,7 @@ import assert from 'assert'; import fetch from 'node-fetch'; import { esMain } from './utils/esmain'; import { allTemplates } from '../code/lib/cli/src/sandbox-templates'; -import versions from '../code/lib/cli/src/versions'; +import versions from '../code/lib/core-common/src/versions'; import { oneWayHash } from '../code/lib/telemetry/src/one-way-hash'; const PORT = process.env.PORT || 6007; diff --git a/scripts/release/__tests__/version.test.ts b/scripts/release/__tests__/version.test.ts index 2f5ffb8ea9b0..87f998222188 100644 --- a/scripts/release/__tests__/version.test.ts +++ b/scripts/release/__tests__/version.test.ts @@ -11,7 +11,7 @@ import type * as MockedFSToExtra from '../../../code/__mocks__/fs-extra'; vi.mock('fs-extra', async () => import('../../../code/__mocks__/fs-extra')); const fsExtra = fsExtraImp as unknown as typeof MockedFSToExtra; -vi.mock('../../../code/lib/cli/src/versions', () => ({ +vi.mock('../../../code/lib/core-common/src/versions', () => ({ '@storybook/addon-a11y': '7.1.0-alpha.29', })); @@ -40,7 +40,7 @@ describe('Version', () => { 'src', 'version.ts' ); - const VERSIONS_PATH = path.join(CODE_DIR_PATH, 'lib', 'cli', 'src', 'versions.ts'); + const VERSIONS_PATH = path.join(CODE_DIR_PATH, 'lib', 'core-common', 'src', 'versions.ts'); const A11Y_PACKAGE_JSON_PATH = path.join(CODE_DIR_PATH, 'addons', 'a11y', 'package.json'); it('should throw when release type is invalid', async () => { diff --git a/scripts/release/version.ts b/scripts/release/version.ts index c56d7afa5b54..53365fd2d499 100644 --- a/scripts/release/version.ts +++ b/scripts/release/version.ts @@ -125,7 +125,7 @@ const bumpCodeVersion = async (nextVersion: string) => { const bumpVersionSources = async (currentVersion: string, nextVersion: string) => { const filesToUpdate = [ path.join(CODE_DIR_PATH, 'lib', 'manager-api', 'src', 'version.ts'), - path.join(CODE_DIR_PATH, 'lib', 'cli', 'src', 'versions.ts'), + path.join(CODE_DIR_PATH, 'lib', 'core-common', 'src', 'versions.ts'), ]; console.log(`🤜 Bumping versions in...:\n ${chalk.cyan(filesToUpdate.join('\n '))}`); diff --git a/scripts/sandbox/generate.ts b/scripts/sandbox/generate.ts index f6260ada4875..27f17f8c7ad0 100755 --- a/scripts/sandbox/generate.ts +++ b/scripts/sandbox/generate.ts @@ -12,14 +12,14 @@ import { esMain } from '../utils/esmain'; import type { OptionValues } from '../utils/options'; import { createOptions } from '../utils/options'; import { allTemplates as sandboxTemplates } from '../../code/lib/cli/src/sandbox-templates'; -import storybookVersions from '../../code/lib/cli/src/versions'; -import { JsPackageManagerFactory } from '../../code/lib/cli/src/js-package-manager/JsPackageManagerFactory'; +import storybookVersions from '../../code/lib/core-common/src/versions'; +import { JsPackageManagerFactory } from '../../code/lib/core-common/src/js-package-manager/JsPackageManagerFactory'; // eslint-disable-next-line import/no-cycle import { localizeYarnConfigFiles, setupYarn } from './utils/yarn'; import type { GeneratorConfig } from './utils/types'; import { getStackblitzUrl, renderTemplate } from './utils/template'; -import type { JsPackageManager } from '../../code/lib/cli/src/js-package-manager'; +import type { JsPackageManager } from '../../code/lib/core-common/src/js-package-manager'; import { BEFORE_DIR_NAME, AFTER_DIR_NAME, diff --git a/scripts/tasks/sandbox-parts.ts b/scripts/tasks/sandbox-parts.ts index 22047cf6b69e..cd7dc643a185 100644 --- a/scripts/tasks/sandbox-parts.ts +++ b/scripts/tasks/sandbox-parts.ts @@ -25,21 +25,23 @@ import { addWorkaroundResolutions, } from '../utils/yarn'; import { exec } from '../utils/exec'; -import type { ConfigFile } from '../../code/lib/csf-tools'; -import storybookPackages from '../../code/lib/cli/src/versions'; -import { writeConfig } from '../../code/lib/csf-tools'; +import type { ConfigFile } from '../../code/lib/csf-tools/src'; +import { writeConfig } from '../../code/lib/csf-tools/src'; import { filterExistsInCodeDir } from '../utils/filterExistsInCodeDir'; import { findFirstPath } from '../utils/paths'; import { detectLanguage } from '../../code/lib/cli/src/detect'; import { SupportedLanguage } from '../../code/lib/cli/src/project_types'; import { updatePackageScripts } from '../utils/package-json'; import { addPreviewAnnotations, readMainConfig } from '../utils/main-js'; -import { JsPackageManagerFactory } from '../../code/lib/cli/src/js-package-manager/JsPackageManagerFactory'; +import { + type JsPackageManager, + versions as storybookPackages, + JsPackageManagerFactory, +} from '../../code/lib/core-common/src'; import { workspacePath } from '../utils/workspace'; import { babelParse } from '../../code/lib/csf-tools/src/babelParse'; import { CODE_DIRECTORY, REPROS_DIRECTORY } from '../utils/constants'; import type { TemplateKey } from '../../code/lib/cli/src/sandbox-templates'; -import type { JsPackageManager } from '../../code/lib/cli/src/js-package-manager'; const logger = console; @@ -378,7 +380,8 @@ export const addStories: Task['run'] = async ( // Ensure that we match the right stories in the stories directory updateStoriesField( mainConfig, - (await detectLanguage(packageManager)) === SupportedLanguage.JAVASCRIPT + (await detectLanguage(packageManager as any as Parameters[0])) === + SupportedLanguage.JAVASCRIPT ); const isCoreRenderer = diff --git a/scripts/utils/yarn.ts b/scripts/utils/yarn.ts index 006aac905f80..a2fb82e1fa6a 100644 --- a/scripts/utils/yarn.ts +++ b/scripts/utils/yarn.ts @@ -4,7 +4,7 @@ import path from 'path'; import type { TemplateKey } from 'get-template'; import { exec } from './exec'; // TODO -- should we generate this file a second time outside of CLI? -import storybookVersions from '../../code/lib/cli/src/versions'; +import storybookVersions from '../../code/lib/core-common/src/versions'; import touch from './touch'; export type YarnOptions = { diff --git a/yarn.lock b/yarn.lock index 41331a2f1000..14231d72f18e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9,4 +9,4 @@ __metadata: version: 0.0.0-use.local resolution: "@storybook/root@workspace:." languageName: unknown - linkType: soft \ No newline at end of file + linkType: soft