diff --git a/CHANGELOG.prerelease.md b/CHANGELOG.prerelease.md index 89b57f4aec60..b6021fc61dc1 100644 --- a/CHANGELOG.prerelease.md +++ b/CHANGELOG.prerelease.md @@ -1,3 +1,15 @@ +## 7.2.0-rc.0 + +- Addon: Create @storybook/addon-themes - [#23524](https://github.com/storybookjs/storybook/pull/23524), thanks [@Integrayshaun](https://github.com/Integrayshaun)! +- Angular: Fix initialization of Storybook in Angular 16.1 - [#23598](https://github.com/storybookjs/storybook/pull/23598), thanks [@valentinpalkovic](https://github.com/valentinpalkovic)! +- CLI: Gracefully shutdown and cleanup execa child processes - [#23538](https://github.com/storybookjs/storybook/pull/23538), thanks [@valentinpalkovic](https://github.com/valentinpalkovic)! +- Dependencies: Downgrade `jest-mock` - [#23597](https://github.com/storybookjs/storybook/pull/23597), thanks [@ndelangen](https://github.com/ndelangen)! +- Dependencies: Upgrade simple-update-notifier - [#23396](https://github.com/storybookjs/storybook/pull/23396), thanks [@dartess](https://github.com/dartess)! +- Storyshots: fix broken storyshots with angular - [#23555](https://github.com/storybookjs/storybook/pull/23555), thanks [@mattlewis92](https://github.com/mattlewis92)! +- TypeScript: Added `expanded` to `CoreCommon_StorybookRefs` to fix typescript errors - [#23488](https://github.com/storybookjs/storybook/pull/23488), thanks [@DotwoodMedia](https://github.com/DotwoodMedia)! +- TypeScript: Downgrade to the last version of type-fest that doesn't need typescript 5.0 - [#23574](https://github.com/storybookjs/storybook/pull/23574), thanks [@ndelangen](https://github.com/ndelangen)! +- Vue2: Source Decorator reactivity - [#23149](https://github.com/storybookjs/storybook/pull/23149), thanks [@chakAs3](https://github.com/chakAs3)! + ## 7.2.0-alpha.0 - Angular: Make enableProdMode optional - [#23489](https://github.com/storybookjs/storybook/pull/23489), thanks [@valentinpalkovic](https://github.com/valentinpalkovic)! diff --git a/CODEOWNERS b/CODEOWNERS index 447c21101fb2..de427497aba2 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -6,7 +6,7 @@ .yarnrc.yml @ndelangen @JReinhold # Docs -docs/ @kylegach @jonniebigodes +/docs/ @kylegach @jonniebigodes # Scripts /scripts/ @ndelangen @kasperpeulen @@ -39,8 +39,8 @@ docs/ @kylegach @jonniebigodes # Frameworks /code/frameworks/angular/ @valentinpalkovic @yannbf -/code/frameworks/html-vite/ @kasperpeulen @JReinhold -/code/frameworks/html-webpack5/ @kasperpeulen @JReinhold +/code/frameworks/html-vite/ @kasperpeulen @JReinhold +/code/frameworks/html-webpack5/ @kasperpeulen @JReinhold /code/frameworks/nextjs/ @valentinpalkovic @kasperpeulen @yannbf /code/frameworks/react-vite/ @valentinpalkovic @kasperpeulen /code/frameworks/react-webpack5/ @valentinpalkovic @kasperpeulen @@ -61,7 +61,7 @@ docs/ @kylegach @jonniebigodes /code/lib/codemod/ @kasperpeulen @ndelangen /code/lib/core-common/ @ndelangen @yannbf /code/lib/core-events/ @ndelangen @kasperpeulen -/code/lib/core-server/ @ndelangen @JReinhold @tmeasday @shilman +/code/lib/core-server/ @ndelangen @JReinhold @tmeasday @shilman /code/lib/core-webpack/ @valentinpalkovic @ndelangen /code/lib/csf-plugin/ @ndelangen @valentinpalkovic /code/lib/csf-tools/ @kasperpeulen @shilman @@ -71,7 +71,7 @@ docs/ @kylegach @jonniebigodes /code/lib/node-logger/ @yannbf @ndelangen /code/lib/preview/ @ndelangen @kasperpeulen /code/lib/preview-api/ @yannbf @ndelangen @tmeasday -/code/lib/react-dom-shim/ @ndelangen @valentinpalkovic @tmeasday +/code/lib/react-dom-shim/ @ndelangen @valentinpalkovic @tmeasday /code/lib/router/ @ndelangen @JReinhold /code/lib/telemetry/ @shilman @yannbf @ndelangen /code/lib/theming/ @cdedreuille @ndelangen @JReinhold diff --git a/code/addons/a11y/package.json b/code/addons/a11y/package.json index 3d9a0f6c4b4b..8619df1b6e36 100644 --- a/code/addons/a11y/package.json +++ b/code/addons/a11y/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/addon-a11y", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Test component compliance with web accessibility standards", "keywords": [ "a11y", diff --git a/code/addons/actions/package.json b/code/addons/actions/package.json index 5971b5305b8e..27ff35307bbf 100644 --- a/code/addons/actions/package.json +++ b/code/addons/actions/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/addon-actions", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Get UI feedback when an action is performed on an interactive element", "keywords": [ "storybook", diff --git a/code/addons/backgrounds/package.json b/code/addons/backgrounds/package.json index 4d506dc26d1e..b594041c2b6d 100644 --- a/code/addons/backgrounds/package.json +++ b/code/addons/backgrounds/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/addon-backgrounds", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Switch backgrounds to view components in different settings", "keywords": [ "addon", diff --git a/code/addons/controls/package.json b/code/addons/controls/package.json index 8f8b58cab34f..f250d87e0e1e 100644 --- a/code/addons/controls/package.json +++ b/code/addons/controls/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/addon-controls", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Interact with component inputs dynamically in the Storybook UI", "keywords": [ "addon", diff --git a/code/addons/docs/package.json b/code/addons/docs/package.json index 4861727d62f6..410bc930ca60 100644 --- a/code/addons/docs/package.json +++ b/code/addons/docs/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/addon-docs", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Document component usage and properties in Markdown", "keywords": [ "addon", diff --git a/code/addons/essentials/package.json b/code/addons/essentials/package.json index 0ebb763c7a4d..130d70a2fde0 100644 --- a/code/addons/essentials/package.json +++ b/code/addons/essentials/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/addon-essentials", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Curated addons to bring out the best of Storybook", "keywords": [ "addon", @@ -93,6 +93,16 @@ "require": "./dist/outline/manager.js", "import": "./dist/outline/manager.mjs" }, + "./themes/manager": { + "types": "./dist/themes/manager.d.ts", + "require": "./dist/themes/manager.js", + "import": "./dist/themes/manager.mjs" + }, + "./themes/preview": { + "types": "./dist/themes/preview.d.ts", + "require": "./dist/themes/preview.js", + "import": "./dist/themes/preview.mjs" + }, "./toolbars/manager": { "types": "./dist/toolbars/manager.d.ts", "require": "./dist/toolbars/manager.js", @@ -162,7 +172,9 @@ "./src/outline/preview.ts", "./src/outline/manager.ts", "./src/toolbars/manager.ts", - "./src/viewport/manager.ts" + "./src/viewport/manager.ts", + "./src/themes/manager.ts", + "./src/themes/preview.ts" ], "platform": "node" }, diff --git a/code/addons/essentials/src/index.ts b/code/addons/essentials/src/index.ts index 96fd171146bb..d7762ff88033 100644 --- a/code/addons/essentials/src/index.ts +++ b/code/addons/essentials/src/index.ts @@ -3,15 +3,16 @@ import { logger } from '@storybook/node-logger'; import { serverRequire } from '@storybook/core-common'; interface PresetOptions { - configDir: string; - docs?: boolean; - controls?: boolean; actions?: boolean; backgrounds?: boolean; - viewport?: boolean; - toolbars?: boolean; + configDir: string; + controls?: boolean; + docs?: boolean; measure?: boolean; outline?: boolean; + themes?: boolean; + toolbars?: boolean; + viewport?: boolean; } const requireMain = (configDir: string) => { @@ -37,6 +38,8 @@ export function addons(options: PresetOptions) { }; const main = requireMain(options.configDir); + + // NOTE: The order of these addons is important. return [ 'docs', 'controls', @@ -47,6 +50,7 @@ export function addons(options: PresetOptions) { 'measure', 'outline', 'highlight', + 'themes', ] .filter((key) => (options as any)[key] !== false) .filter((addon) => !checkInstalled(addon, main)) diff --git a/code/addons/essentials/src/themes/manager.ts b/code/addons/essentials/src/themes/manager.ts new file mode 100644 index 000000000000..84a500ab3a74 --- /dev/null +++ b/code/addons/essentials/src/themes/manager.ts @@ -0,0 +1 @@ +export * from '@storybook/addon-themes/manager'; diff --git a/code/addons/essentials/src/themes/preview.ts b/code/addons/essentials/src/themes/preview.ts new file mode 100644 index 000000000000..08db0c7bf907 --- /dev/null +++ b/code/addons/essentials/src/themes/preview.ts @@ -0,0 +1,2 @@ +// eslint-disable-next-line import/export +export * from '@storybook/addon-themes/preview'; diff --git a/code/addons/gfm/package.json b/code/addons/gfm/package.json index 868704c8d68a..4be2753d5cc0 100644 --- a/code/addons/gfm/package.json +++ b/code/addons/gfm/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/addon-mdx-gfm", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "GitHub Flavored Markdown in Storybook", "keywords": [ "addon", diff --git a/code/addons/highlight/package.json b/code/addons/highlight/package.json index 3d5c74e7e66a..7bb187a97962 100644 --- a/code/addons/highlight/package.json +++ b/code/addons/highlight/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/addon-highlight", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Highlight DOM nodes within your stories", "keywords": [ "storybook-addons", diff --git a/code/addons/interactions/package.json b/code/addons/interactions/package.json index 3b2bdc7da042..4347956a6aad 100644 --- a/code/addons/interactions/package.json +++ b/code/addons/interactions/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/addon-interactions", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Automate, test and debug user interactions", "keywords": [ "storybook-addons", diff --git a/code/addons/jest/package.json b/code/addons/jest/package.json index 1413f7e8b0a3..16eb582b39fc 100644 --- a/code/addons/jest/package.json +++ b/code/addons/jest/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/addon-jest", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "React storybook addon that show component jest report", "keywords": [ "addon", diff --git a/code/addons/links/package.json b/code/addons/links/package.json index 953bac36b818..a8e22e57b067 100644 --- a/code/addons/links/package.json +++ b/code/addons/links/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/addon-links", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Link stories together to build demos and prototypes with your UI components", "keywords": [ "addon", diff --git a/code/addons/measure/package.json b/code/addons/measure/package.json index 6c931b8086c9..5ef448a09bb9 100644 --- a/code/addons/measure/package.json +++ b/code/addons/measure/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/addon-measure", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Inspect layouts by visualizing the box model", "keywords": [ "storybook-addons", diff --git a/code/addons/outline/package.json b/code/addons/outline/package.json index ecea3f5f7943..522b2107157d 100644 --- a/code/addons/outline/package.json +++ b/code/addons/outline/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/addon-outline", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Outline all elements with CSS to help with layout placement and alignment", "keywords": [ "storybook-addons", diff --git a/code/addons/storyshots-core/package.json b/code/addons/storyshots-core/package.json index 973ef77afba8..d6d6cd731dbd 100644 --- a/code/addons/storyshots-core/package.json +++ b/code/addons/storyshots-core/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/addon-storyshots", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Take a code snapshot of every story automatically with Jest", "keywords": [ "addon", diff --git a/code/addons/storyshots-puppeteer/package.json b/code/addons/storyshots-puppeteer/package.json index a2a34aab5817..103231c8c9f8 100644 --- a/code/addons/storyshots-puppeteer/package.json +++ b/code/addons/storyshots-puppeteer/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/addon-storyshots-puppeteer", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Image snapshots addition to StoryShots based on puppeteer", "keywords": [ "addon", diff --git a/code/addons/storysource/package.json b/code/addons/storysource/package.json index 237dc234615f..97a60772dbdf 100644 --- a/code/addons/storysource/package.json +++ b/code/addons/storysource/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/addon-storysource", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "View a story’s source code to see how it works and paste into your app", "keywords": [ "addon", diff --git a/code/addons/themes/README.md b/code/addons/themes/README.md new file mode 100644 index 000000000000..bbc5506c4d97 --- /dev/null +++ b/code/addons/themes/README.md @@ -0,0 +1,72 @@ +# `@storybook/addon-themes + +Storybook Addon Themes can be used which between multiple themes for components inside the preview in [Storybook](https://storybook.js.org). + +![React Storybook Screenshot](https://user-images.githubusercontent.com/42671/98158421-dada2300-1ea8-11eb-8619-af1e7018e1ec.png) + +## Usage + +Requires Storybook 7.0 or later. Themes is part of [essentials](https://storybook.js.org/docs/react/essentials/introduction) and so is installed in all new Storybooks by default. If you need to add it to your Storybook, you can run: + +```sh +npm i -D @storybook/addon-themes +``` + +Then, add following content to [`.storybook/main.js`](https://storybook.js.org/docs/react/configure/overview#configure-your-storybook-project): + +```js +export default { + addons: ['@storybook/addon-themes'], +}; +``` + +### πŸ‘‡ Tool specific configuration + +For tool-specific setup, check out the recipes below + +- [`@emotion/styled`](https://github.com/storybookjs/storybook/tree/next/code/addons/themes/docs/getting-started/emotion.md) +- [`@mui/material`](https://github.com/storybookjs/storybook/tree/next/code/addons/themes/docs/getting-started/material-ui.md) +- [`bootstrap`](https://github.com/storybookjs/storybook/tree/next/code/addons/themes/docs/getting-started/bootstrap.md) +- [`styled-components`](https://github.com/storybookjs/storybook/tree/next/code/addons/themes/docs/getting-started/styled-components.md) +- [`tailwind`](https://github.com/storybookjs/storybook/tree/next/code/addons/themes/docs/getting-started/tailwind.md) +- [`vuetify@3.x`](https://github.com/storybookjs/storybook/blob/next/code/addons/themes/docs/api.md#writing-a-custom-decorator) + +Don't see your favorite tool listed? Don't worry! That doesn't mean this addon isn't for you. Check out the ["Writing a custom decorator"](https://github.com/storybookjs/storybook/blob/next/code/addons/themes/docs/api.md#writing-a-custom-decorator) section of the [api reference](https://github.com/storybookjs/storybook/blob/next/code/addons/themes/docs/api.md). + +### ❗️ Overriding theme + +If you want to override your theme for a particular component or story, you can use the `themes.themeOverride` parameter. + +```js +import React from 'react'; +import { Button } from './Button'; + +export default { + title: 'Example/Button', + component: Button, + parameters: { + themes: { + themeOverride: 'light', // component level override + }, + }, +}; + +export const Primary = { + args: { + primary: true, + label: 'Button', + }, +}; + +export const PrimaryDark = { + args: { + primary: true, + label: 'Button', + }, + parameters: { + themes: { + themeOverride: 'dark', // Story level override + }, + }, +}; +``` diff --git a/code/addons/themes/docs/api.md b/code/addons/themes/docs/api.md new file mode 100644 index 000000000000..36138394d93b --- /dev/null +++ b/code/addons/themes/docs/api.md @@ -0,0 +1,206 @@ +# API + +## Decorators + +### `withThemeFromJSXProvider` + +Takes your provider component, global styles, and theme(s)to wrap your stories in. + +```js +import { withThemeFromJSXProvider } from '@storybook/addon-styling'; + +export const decorators = [ + withThemeFromJSXProvider({ + themes: { + light: lightTheme, + dark: darkTheme, + }, + defaultTheme: 'light', + Provider: ThemeProvider, + GlobalStyles: CssBaseline, + }), +]; +``` + +Available options: + +| option | type | required? | Description | +| ------------ | --------------------- | :-------: | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| themes | `Record` | | An object of theme configurations where the key is the name of the theme and the value is the theme object. If multiple themes are provided, a toolbar item will be added to switch between themes. | +| defaultTheme | `string` | | The name of the default theme to use | +| Provider | | | The JSX component to provide themes | +| GlobalStyles | | | A JSX component containing global css styles. | + +### `withThemeByClassName` + +Takes your theme class names to apply your parent element to enable your theme(s). + +```js +import { withThemeByClassName } from '@storybook/addon-styling'; + +export const decorators = [ + withThemeByClassName({ + themes: { + light: 'light-theme', + dark: 'dark-theme', + }, + defaultTheme: 'light', + }), +]; +``` + +Available options: + +| option | type | required? | Description | +| -------------- | ------------------------ | :-------: | --------------------------------------------------------------------------------------------------------------- | +| themes | `Record` | βœ… | An object of theme configurations where the key is the name of the theme and the value is the theme class name. | +| defaultTheme | `string` | βœ… | The name of the default theme to use | +| parentSelector | `string` | | The selector for the parent element that you want to apply your theme class to. Defaults to "html" | + +### `withThemeByDataAttribute` + +Takes your theme names and data attribute to apply your parent element to enable your theme(s). + +```js +import { withThemeByDataAttribute } from '@storybook/addon-styling'; + +export const decorators = [ + withThemeByDataAttribute({ + themes: { + light: 'light', + dark: 'dark', + }, + defaultTheme: 'light', + attributeName: 'data-bs-theme', + }), +]; +``` + +available options: + +| option | type | required? | Description | +| -------------- | ------------------------ | :-------: | ------------------------------------------------------------------------------------------------------------------- | +| themes | `Record` | βœ… | An object of theme configurations where the key is the name of the theme and the value is the data attribute value. | +| defaultTheme | `string` | βœ… | The name of the default theme to use | +| parentSelector | `string` | | The selector for the parent element that you want to apply your theme class to. Defaults to "html" | +| attributeName | `string` | | The name of the data attribute to set on the parent element for your theme(s). Defaults to "data-theme" | + +## Writing a custom decorator + +If none of these decorators work for your library there is still hope. We've provided a collection of helper functions to get access to the theme toggling state so that you can create a decorator of your own. + +### `pluckThemeFromContext` + +Pulls the selected theme from storybook's global state. + +```js +import { DecoratorHelpers } from '@storybook/addon-styling'; +const { pluckThemeFromContext } = DecoratorHelpers; + +export const myCustomDecorator = + ({ themes, defaultState, ...rest }) => + (storyFn, context) => { + const selectedTheme = pluckThemeFromContext(context); + + // Snipped + }; +``` + +### `useThemeParameters` + +Returns the theme parameters for this addon. + +```js +import { DecoratorHelpers } from '@storybook/addon-styling'; +const { useThemeParameters } = DecoratorHelpers; + +export const myCustomDecorator = + ({ themes, defaultState, ...rest }) => + (storyFn, context) => { + const { themeOverride } = useThemeParameters(); + + // Snipped + }; +``` + +### `initializeThemeState` + +Used to register the themes and defaultTheme with the addon state. + +```js +import { DecoratorHelpers } from '@storybook/addon-styling'; +const { initializeThemeState } = DecoratorHelpers; + +export const myCustomDecorator = ({ themes, defaultState, ...rest }) => { + initializeThemeState(Object.keys(themes), defaultTheme); + + return (storyFn, context) => { + // Snipped + }; +}; +``` + +### Putting it all together + +Let's use Vuetify as an example. Vuetify uses it's own global state to know which theme to render. To build a custom decorator to accommodate this method we'll need to do the following + +```js +// .storybook/withVeutifyTheme.decorator.js + +import { DecoratorHelpers } from '@storybook/addon-styling'; +import { useTheme } from 'vuetify'; + +const { initializeThemeState, pluckThemeFromContext, useThemeParameters } = DecoratorHelpers; + +export const withVuetifyTheme = ({ themes, defaultTheme }) => { + initializeThemeState(Object.keys(themes), defaultTheme); + + return (story, context) => { + const selectedTheme = pluckThemeFromContext(context); + const { themeOverride } = useThemeParameters(); + + const selected = themeOverride || selectedTheme || defaultTheme; + + return { + components: { story }, + setup() { + const theme = useTheme(); + + theme.global.name.value = selected; + + return { + theme, + }; + }, + template: ``, + }; + }; +}; +``` + +This can then be provided to Storybook in `.storybook/preview.js`: + +```js +// .storybook/preview.js + +import { setup } from '@storybook/vue3'; +import { registerPlugins } from '../src/plugins'; +import { withVuetifyTheme } from './withVuetifyTheme.decorator'; + +setup((app) => { + registerPlugins(app); +}); + +/* snipped for brevity */ + +export const decorators = [ + withVuetifyTheme({ + themes: { + light: 'light', + dark: 'dark', + customTheme: 'myCustomTheme', + }, + defaultTheme: 'customTheme', // The key of your default theme + }), +]; +``` diff --git a/code/addons/themes/docs/getting-started/bootstrap.md b/code/addons/themes/docs/getting-started/bootstrap.md new file mode 100644 index 000000000000..cfdba14a73b0 --- /dev/null +++ b/code/addons/themes/docs/getting-started/bootstrap.md @@ -0,0 +1,90 @@ +# 🏁 Getting started with `bootstrap` + +## πŸ“¦ Install addon + +**NOTE:** As of Storybook 7.2, `@storybook/addon-themes` ships in `@storybook/addon-essentials`. If you're using Storybook >= 7.2, skip to ["Import Bootstrap"](#πŸ₯Ύ-import-bootstrap). + +To get started, **install the package** as a dev dependency + +yarn: + +```zsh +yarn add -D @storybook/addon-themes +``` + +npm: + +```zsh +npm install -D @storybook/addon-themes +``` + +pnpm: + +```zsh +pnpm add -D @storybook/addon-themes +``` + +## 🧩 Register Addon + +Now, **include the addon** in your `.storybook/main.js` file. + +```diff +module.exports = { + stories: [ + "../stories/**/*.stories.mdx", + "../stories/**/*.stories.@(js|jsx|ts|tsx)", + ], + addons: [ + "@storybook/addon-essentials", ++ "@storybook/addon-themes" + ], +}; +``` + +## πŸ₯Ύ Import Bootstrap + +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 "bootstrap/dist/css/bootstrap.min.css"; ++import "bootstrap/dist/js/bootstrap.bundle"; + +const preview: Preview = { + parameters: { /* ... */ }, +}; + +export default preview; +``` + +## 🎨 Provide your theme(s) + +Bootstrap now supports light and dark color modes out of the box as well as the ability to make your own custom modes. These modes can be activated by setting a `data-bs-theme` attribute on a parent element. + +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 "bootstrap/dist/css/bootstrap.min.css"; +import "bootstrap/dist/js/bootstrap.bundle"; + +const preview: Preview = { + parameters: { /* ... */ }, ++ decorators: [ ++ withThemeByDataAttribute({ ++ themes: { ++ light: "light", ++ dark: "dark", ++ }, ++ defaultTheme: "light", ++ attributeName: "data-bs-theme", ++ }), ++ ] +}; + +export default preview; +``` diff --git a/code/addons/themes/docs/getting-started/emotion.md b/code/addons/themes/docs/getting-started/emotion.md new file mode 100644 index 000000000000..6914e61e3e47 --- /dev/null +++ b/code/addons/themes/docs/getting-started/emotion.md @@ -0,0 +1,74 @@ +# 🏁 Getting started with `@emotion/styled` + +## πŸ“¦ Install addon + +**NOTE:** As of Storybook 7.2, `@storybook/addon-themes` ships in `@storybook/addon-essentials`. If you're using Storybook >= 7.2, skip to ["Provide your themes"](#🎨-provide-your-themes). + +To get started, **install the package** as a dev dependency + +yarn: + +```zsh +yarn add -D @storybook/addon-themes +``` + +npm: + +```zsh +npm install -D @storybook/addon-themes +``` + +pnpm: + +```zsh +pnpm add -D @storybook/addon-themes +``` + +## 🧩 Register Addon + +Now, **include the addon** in your `.storybook/main.js` file + +```diff +module.exports = { + stories: [ + "../stories/**/*.stories.mdx", + "../stories/**/*.stories.@(js|jsx|ts|tsx)", + ], + addons: [ + "@storybook/addon-essentials", ++ "@storybook/addon-themes" + ], +}; +``` + +## 🎨 Provide your theme(s) + +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` + +```diff +-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 + + +const preview: Preview = { + parameters: { /* ... */ }, ++ decorators: [ ++ withThemeFromJSXProvider({ ++ themes: { ++ light: lightTheme, ++ dark: darkTheme, ++ }, ++ defaultTheme: "light", ++ Provider: ThemeProvider, ++ GlobalStyles: GlobalStyles, ++ }), ++ ] +}; + +export default preview; +``` diff --git a/code/addons/themes/docs/getting-started/material-ui.md b/code/addons/themes/docs/getting-started/material-ui.md new file mode 100644 index 000000000000..d33249be1edd --- /dev/null +++ b/code/addons/themes/docs/getting-started/material-ui.md @@ -0,0 +1,103 @@ +# 🏁 Getting started with `@mui/material` + +## πŸ“¦ Install addon + +**NOTE:** As of Storybook 7.2, `@storybook/addon-themes` ships in `@storybook/addon-essentials`. If you're using Storybook >= 7.2, skip to ["Import fonts"](#πŸ”€-import-fonts). + +To get started, **install the package** as a dev dependency + +yarn: + +```zsh +yarn add -D @storybook/addon-themes +``` + +npm: + +```zsh +npm install -D @storybook/addon-themes +``` + +pnpm: + +```zsh +pnpm add -D @storybook/addon-themes +``` + +## 🧩 Register Addon + +Now, **include the addon** in your `.storybook/main.js` file + +```diff +module.exports = { + stories: [ + "../stories/**/*.stories.mdx", + "../stories/**/*.stories.@(js|jsx|ts|tsx)", + ], + addons: [ + "@storybook/addon-essentials", ++ "@storybook/addon-themes", + ], +}; +``` + +## πŸ”€ Import fonts + +`@mui/material` requires Google's Roboto and Material Icon fonts to render everything as intended. I'd recommend getting them from [fontsource](https://github.com/fontsource/fontsource) so that they are version locked dependencies that doesn't require a CDN. + +These can be imported into your `.storybook/preview.js` file. + +```diff +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"; + +const preview: Preview = { + parameters: { /* ... */ }, +}; + +export default preview; +``` + +## 🎨 Provide your theme(s) + +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` + +```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 + +// 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"; + +const preview: Preview = { + parameters: { /* ... */ }, ++ decorators: [ ++ withThemeFromJSXProvider({ ++ themes: { ++ light: lightTheme, ++ dark: darkTheme, ++ }, ++ defaultTheme: "light", ++ Provider: ThemeProvider, ++ GlobalStyles: CssBaseline, ++ }), ++ ], +}; + +export default preview; +``` diff --git a/code/addons/themes/docs/getting-started/styled-components.md b/code/addons/themes/docs/getting-started/styled-components.md new file mode 100644 index 000000000000..d6cae156d8e1 --- /dev/null +++ b/code/addons/themes/docs/getting-started/styled-components.md @@ -0,0 +1,73 @@ +# 🏁 Getting started with `styled-components` + +## πŸ“¦ Install addon + +**NOTE:** As of Storybook 7.2, `@storybook/addon-themes` ships in `@storybook/addon-essentials`. If you're using Storybook >= 7.2, skip to ["Provide your themes"](#🎨-provide-your-themes). + +To get started, **install the package** as a dev dependency + +yarn: + +```zsh +yarn add -D @storybook/addon-themes +``` + +npm: + +```zsh +npm install -D @storybook/addon-themes +``` + +pnpm: + +```zsh +pnpm add -D @storybook/addon-themes +``` + +## 🧩 Register Addon + +Now, **include the addon** in your `.storybook/main.js` file + +```diff +module.exports = { + stories: [ + "../stories/**/*.stories.mdx", + "../stories/**/*.stories.@(js|jsx|ts|tsx)", + ], + addons: [ + "@storybook/addon-essentials", ++ "@storybook/addon-themes" + ], +}; +``` + +## 🎨 Provide your theme(s) + +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` + +```diff +-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 + +const preview: Preview = { + parameters: { /* ... */ }, ++ decorators: [ ++ withThemeFromJSXProvider({ ++ themes: { ++ light: lightTheme, ++ dark: darkTheme, ++ }, ++ defaultTheme: "light", ++ Provider: ThemeProvider, ++ GlobalStyles: GlobalStyles, ++ }), ++ ], +}; + +export default preview; +``` diff --git a/code/addons/themes/docs/getting-started/tailwind.md b/code/addons/themes/docs/getting-started/tailwind.md new file mode 100644 index 000000000000..2225a993af00 --- /dev/null +++ b/code/addons/themes/docs/getting-started/tailwind.md @@ -0,0 +1,117 @@ +# 🏁 Getting started with `tailwind.css` + +## πŸ“¦ Install addon + +**NOTE:** As of Storybook 7.2, `@storybook/addon-themes` ships in `@storybook/addon-essentials`. If you're using Storybook >= 7.2, skip to ["Import your css"](#πŸ₯Ύ-import-your-css). + +To get started, **install the package** as a dev dependency + +yarn: + +```zsh +yarn add -D @storybook/addon-themes +``` + +npm: + +```zsh +npm install -D @storybook/addon-themes +``` + +pnpm: + +```zsh +pnpm add -D @storybook/addon-themes +``` + +## 🧩 Register Addon + +Now, **include the addon** in your `.storybook/main.js` file. + +```diff +module.exports = { + stories: [ + "../stories/**/*.stories.mdx", + "../stories/**/*.stories.@(js|jsx|ts|tsx)", + ], + addons: [ + "@storybook/addon-essentials", ++ "@storybook/addon-themes" + ], +}; +``` + +## πŸ₯Ύ Import your CSS + +To give your stories access to Tailwind styles, import them into your `.storybook/preview.js` file. + +```diff +import { Preview } from "@storybook/your-renderer"; + ++import "../src/index.css"; + +const preview: Preview = { + parameters: { /* ... */ }, +}; + +export default preview; +``` + +## 🎨 Provide your theme(s) + +Tailwind supports light and dark color modes out of the box. These modes can be activated by setting a `.dark` class on a parent element. + +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 "../src/index.css"; + + +const preview: Preview = { + parameters: { /* ... */ }, ++ decorators: [ ++ withThemeByClassName({ ++ themes: { ++ light: "", ++ dark: "dark", ++ }, ++ defaultTheme: "light", ++ }), ++ ] +}; + +export default preview; +``` + +## 🏷️ Using a data-attribute for theme? + +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 "../src/index.css"; + + +const preview: Preview = { + parameters: { /* ... */ }, ++ decorators: [ ++ withThemeByDataAttribute({ ++ themes: { ++ light: "light", ++ dark: "dark", ++ }, ++ defaultTheme: "light", ++ attributeName: "data-theme", ++ }), ++ ] +}; + +export default preview; +``` diff --git a/code/addons/themes/jest.config.js b/code/addons/themes/jest.config.js new file mode 100644 index 000000000000..4396fbc7010d --- /dev/null +++ b/code/addons/themes/jest.config.js @@ -0,0 +1,7 @@ +const path = require('path'); +const baseConfig = require('../../jest.config.browser'); + +module.exports = { + ...baseConfig, + displayName: __dirname.split(path.sep).slice(-2).join(path.posix.sep), +}; diff --git a/code/addons/themes/manager.js b/code/addons/themes/manager.js new file mode 100644 index 000000000000..8a2eae4ffce1 --- /dev/null +++ b/code/addons/themes/manager.js @@ -0,0 +1 @@ +import './dist/manager'; diff --git a/code/addons/themes/package.json b/code/addons/themes/package.json new file mode 100644 index 000000000000..543b9fcbf745 --- /dev/null +++ b/code/addons/themes/package.json @@ -0,0 +1,117 @@ +{ + "name": "@storybook/addon-themes", + "version": "7.2.0-rc.0", + "description": "Switch between multiple themes for you components in Storybook", + "keywords": [ + "css", + "essentials", + "storybook-addon", + "storybook-addons", + "style", + "themes", + "toggle" + ], + "homepage": "https://github.com/storybookjs/storybook/tree/next/code/addons/themes", + "bugs": { + "url": "https://github.com/storybookjs/storybook/issues" + }, + "repository": { + "type": "git", + "url": "https://github.com/storybookjs/storybook.git", + "directory": "code/addons/themes" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "license": "MIT", + "author": "Shaun Evening", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "node": "./dist/index.js", + "require": "./dist/index.js", + "import": "./dist/index.mjs" + }, + "./manager": { + "types": "./dist/manager.d.ts", + "require": "./dist/manager.js", + "import": "./dist/manager.mjs" + }, + "./preview": { + "types": "./dist/preview.d.ts", + "require": "./dist/preview.js", + "import": "./dist/preview.mjs" + }, + "./package.json": "./package.json" + }, + "main": "dist/index.js", + "module": "dist/index.mjs", + "types": "dist/index.d.ts", + "typesVersions": { + "*": { + "*": [ + "dist/index.d.ts" + ], + "manager": [ + "dist/manager.d.ts" + ], + "preview": [ + "dist/preview.d.ts" + ] + } + }, + "files": [ + "dist/**/*", + "README.md", + "*.js", + "*.d.ts" + ], + "scripts": { + "check": "../../../scripts/prepare/check.ts", + "prep": "../../../scripts/prepare/bundle.ts" + }, + "dependencies": { + "@storybook/client-logger": "7.2.0-rc.0", + "@storybook/components": "7.2.0-rc.0", + "@storybook/core-events": "7.2.0-rc.0", + "@storybook/manager-api": "7.2.0-rc.0", + "@storybook/preview-api": "7.2.0-rc.0", + "@storybook/theming": "7.2.0-rc.0", + "@storybook/types": "7.2.0-rc.0", + "ts-dedent": "^2.0.0" + }, + "devDependencies": { + "typescript": "~4.9.3" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + }, + "publishConfig": { + "access": "public" + }, + "bundler": { + "entries": [ + "./src/index.ts", + "./src/manager.tsx", + "./src/preview.tsx" + ] + }, + "gitHead": "e6a7fd8a655c69780bc20b9749c2699e44beae17", + "storybook": { + "displayName": "Themes", + "unsupportedFrameworks": [ + "react-native" + ], + "icon": "" + } +} diff --git a/code/addons/themes/preview.js b/code/addons/themes/preview.js new file mode 100644 index 000000000000..49ad602f79f4 --- /dev/null +++ b/code/addons/themes/preview.js @@ -0,0 +1 @@ +export * from './dist/preview'; diff --git a/code/addons/themes/project.json b/code/addons/themes/project.json new file mode 100644 index 000000000000..73d07c1e71a6 --- /dev/null +++ b/code/addons/themes/project.json @@ -0,0 +1,6 @@ +{ + "name": "@storybook/addon-themes", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "implicitDependencies": [], + "type": "library" +} diff --git a/code/addons/themes/src/constants.ts b/code/addons/themes/src/constants.ts new file mode 100644 index 000000000000..a1872de9e88e --- /dev/null +++ b/code/addons/themes/src/constants.ts @@ -0,0 +1,24 @@ +export const PARAM_KEY = 'themes' as const; +export const ADDON_ID = `storybook/${PARAM_KEY}}` as const; +export const GLOBAL_KEY = 'theme' as const; +export const THEME_SWITCHER_ID = `${ADDON_ID}/theme-switcher` as const; + +export interface ThemeAddonState { + themesList: string[]; + themeDefault?: string; +} + +export const DEFAULT_ADDON_STATE: ThemeAddonState = { + themesList: [], + themeDefault: undefined, +}; + +export interface ThemeParameters { + themeOverride?: string; +} + +export const DEFAULT_THEME_PARAMETERS: ThemeParameters = {}; + +export const THEMING_EVENTS = { + REGISTER_THEMES: `${ADDON_ID}/REGISTER_THEMES`, +} as const; diff --git a/code/addons/themes/src/decorators/class-name.decorator.tsx b/code/addons/themes/src/decorators/class-name.decorator.tsx new file mode 100644 index 000000000000..ccbe4fbf7f31 --- /dev/null +++ b/code/addons/themes/src/decorators/class-name.decorator.tsx @@ -0,0 +1,53 @@ +import { useEffect } from '@storybook/preview-api'; +import type { DecoratorFunction, Renderer } from '@storybook/types'; + +import { initializeThemeState, pluckThemeFromContext, useThemeParameters } from './helpers'; + +export interface ClassNameStrategyConfiguration { + themes: Record; + defaultTheme: string; + parentSelector?: string; +} + +const DEFAULT_ELEMENT_SELECTOR = 'html'; + +const classStringToArray = (classString: string) => classString.split(' ').filter(Boolean); + +export const withThemeByClassName = ({ + themes, + defaultTheme, + parentSelector = DEFAULT_ELEMENT_SELECTOR, +}: ClassNameStrategyConfiguration): DecoratorFunction => { + initializeThemeState(Object.keys(themes), defaultTheme); + + return (storyFn, context) => { + const { themeOverride } = useThemeParameters(); + const selected = pluckThemeFromContext(context); + + useEffect(() => { + const selectedThemeName = themeOverride || selected || defaultTheme; + const parentElement = document.querySelector(parentSelector); + + if (!parentElement) { + return; + } + + Object.entries(themes) + .filter(([themeName]) => themeName !== selectedThemeName) + .forEach(([themeName, className]) => { + const classes = classStringToArray(className); + if (classes.length > 0) { + parentElement.classList.remove(...classes); + } + }); + + const newThemeClasses = classStringToArray(themes[selectedThemeName]); + + if (newThemeClasses.length > 0) { + parentElement.classList.add(...newThemeClasses); + } + }, [themeOverride, selected, parentSelector]); + + return storyFn(); + }; +}; diff --git a/code/addons/themes/src/decorators/data-attribute.decorator.tsx b/code/addons/themes/src/decorators/data-attribute.decorator.tsx new file mode 100644 index 000000000000..546885db8d62 --- /dev/null +++ b/code/addons/themes/src/decorators/data-attribute.decorator.tsx @@ -0,0 +1,37 @@ +import { useEffect } from '@storybook/preview-api'; +import type { DecoratorFunction, Renderer } from '@storybook/types'; +import { initializeThemeState, pluckThemeFromContext, useThemeParameters } from './helpers'; + +export interface DataAttributeStrategyConfiguration { + themes: Record; + defaultTheme: string; + parentSelector?: string; + attributeName?: string; +} + +const DEFAULT_ELEMENT_SELECTOR = 'html'; +const DEFAULT_DATA_ATTRIBUTE = 'data-theme'; + +export const withThemeByDataAttribute = ({ + themes, + defaultTheme, + parentSelector = DEFAULT_ELEMENT_SELECTOR, + attributeName = DEFAULT_DATA_ATTRIBUTE, +}: DataAttributeStrategyConfiguration): DecoratorFunction => { + initializeThemeState(Object.keys(themes), defaultTheme); + return (storyFn, context) => { + const { themeOverride } = useThemeParameters(); + const selected = pluckThemeFromContext(context); + + useEffect(() => { + const parentElement = document.querySelector(parentSelector); + const themeKey = themeOverride || selected || defaultTheme; + + if (parentElement) { + parentElement.setAttribute(attributeName, themes[themeKey]); + } + }, [themeOverride, selected, parentSelector, attributeName]); + + return storyFn(); + }; +}; diff --git a/code/addons/themes/src/decorators/helpers.ts b/code/addons/themes/src/decorators/helpers.ts new file mode 100644 index 000000000000..98f9c89c4e46 --- /dev/null +++ b/code/addons/themes/src/decorators/helpers.ts @@ -0,0 +1,24 @@ +import { addons, useParameter } from '@storybook/preview-api'; +import type { StoryContext } from '@storybook/types'; +import type { ThemeParameters } from '../constants'; +import { GLOBAL_KEY, PARAM_KEY, THEMING_EVENTS, DEFAULT_THEME_PARAMETERS } from '../constants'; + +/** + * + * @param StoryContext + * @returns The global theme name set for your stories + */ +export function pluckThemeFromContext({ globals }: StoryContext): string { + return globals[GLOBAL_KEY] || ''; +} + +export function useThemeParameters(): ThemeParameters { + return useParameter(PARAM_KEY, DEFAULT_THEME_PARAMETERS) as ThemeParameters; +} + +export function initializeThemeState(themeNames: string[], defaultTheme: string) { + addons.getChannel().emit(THEMING_EVENTS.REGISTER_THEMES, { + defaultTheme, + themes: themeNames, + }); +} diff --git a/code/addons/themes/src/decorators/index.ts b/code/addons/themes/src/decorators/index.ts new file mode 100644 index 000000000000..ce07c553332e --- /dev/null +++ b/code/addons/themes/src/decorators/index.ts @@ -0,0 +1,5 @@ +export * from './class-name.decorator'; +export * from './data-attribute.decorator'; +export * from './provider.decorator'; + +export * as DecoratorHelpers from './helpers'; diff --git a/code/addons/themes/src/decorators/provider.decorator.tsx b/code/addons/themes/src/decorators/provider.decorator.tsx new file mode 100644 index 000000000000..6063034eb859 --- /dev/null +++ b/code/addons/themes/src/decorators/provider.decorator.tsx @@ -0,0 +1,58 @@ +import React from 'react'; +import { useMemo } from '@storybook/preview-api'; +import type { DecoratorFunction, Renderer } from '@storybook/types'; + +import { initializeThemeState, pluckThemeFromContext, useThemeParameters } from './helpers'; + +type Theme = Record; +type ThemeMap = Record; + +export interface ProviderStrategyConfiguration { + Provider?: any; + GlobalStyles?: any; + defaultTheme?: string; + themes?: ThemeMap; +} + +const pluckThemeFromKeyPairTuple = ([_, themeConfig]: [string, Theme]): Theme => themeConfig; + +export const withThemeFromJSXProvider = ({ + Provider, + GlobalStyles, + defaultTheme, + themes = {}, +}: ProviderStrategyConfiguration): DecoratorFunction => { + const themeNames = Object.keys(themes); + const initialTheme = defaultTheme || themeNames[0]; + + initializeThemeState(themeNames, initialTheme); + + // eslint-disable-next-line react/display-name + return (storyFn, context) => { + const { themeOverride } = useThemeParameters(); + const selected = pluckThemeFromContext(context); + + const theme = useMemo(() => { + const selectedThemeName = themeOverride || selected || initialTheme; + const pairs = Object.entries(themes); + + return pairs.length === 1 ? pluckThemeFromKeyPairTuple(pairs[0]) : themes[selectedThemeName]; + }, [themes, selected, themeOverride]); + + if (!Provider) { + return ( + <> + {GlobalStyles && } + {storyFn()} + + ); + } + + return ( + + {GlobalStyles && } + {storyFn()} + + ); + }; +}; diff --git a/code/addons/themes/src/index.ts b/code/addons/themes/src/index.ts new file mode 100644 index 000000000000..b89aa12deba9 --- /dev/null +++ b/code/addons/themes/src/index.ts @@ -0,0 +1,3 @@ +// make it work with --isolatedModules +export default {}; +export * from './decorators'; diff --git a/code/addons/themes/src/manager.tsx b/code/addons/themes/src/manager.tsx new file mode 100644 index 000000000000..2fba5bd29a4b --- /dev/null +++ b/code/addons/themes/src/manager.tsx @@ -0,0 +1,14 @@ +import { addons, types } from '@storybook/manager-api'; + +import { ADDON_ID, PARAM_KEY, THEME_SWITCHER_ID } from './constants'; +import { ThemeSwitcher } from './theme-switcher'; + +addons.register(ADDON_ID, () => { + addons.add(THEME_SWITCHER_ID, { + title: 'Themes', + type: types.TOOL, + match: ({ viewMode }) => !!(viewMode && viewMode.match(/^(story|docs)$/)), + render: ThemeSwitcher, + paramKey: PARAM_KEY, + }); +}); diff --git a/code/addons/themes/src/preview.tsx b/code/addons/themes/src/preview.tsx new file mode 100644 index 000000000000..88b41773e9db --- /dev/null +++ b/code/addons/themes/src/preview.tsx @@ -0,0 +1,11 @@ +import type { Renderer, ProjectAnnotations } from '@storybook/types'; +import { GLOBAL_KEY } from './constants'; + +const preview: ProjectAnnotations = { + globals: { + // Required to make sure SB picks this up from URL params + [GLOBAL_KEY]: '', + }, +}; + +export default preview; diff --git a/code/addons/themes/src/theme-switcher.tsx b/code/addons/themes/src/theme-switcher.tsx new file mode 100644 index 000000000000..3658a3ef78cc --- /dev/null +++ b/code/addons/themes/src/theme-switcher.tsx @@ -0,0 +1,83 @@ +import React, { Fragment, useMemo } from 'react'; +import { useAddonState, useChannel, useGlobals, useParameter } from '@storybook/manager-api'; +import { styled } from '@storybook/theming'; +import { Icons, IconButton, WithTooltip, TooltipLinkList } from '@storybook/components'; + +import type { ThemeAddonState, ThemeParameters } from './constants'; +import { + PARAM_KEY, + THEME_SWITCHER_ID, + THEMING_EVENTS, + DEFAULT_ADDON_STATE, + DEFAULT_THEME_PARAMETERS, +} from './constants'; + +const IconButtonLabel = styled.div(({ theme }) => ({ + fontSize: theme.typography.size.s2 - 1, + marginLeft: 10, +})); + +const hasMultipleThemes = (themesList: ThemeAddonState['themesList']) => themesList.length > 1; + +export const ThemeSwitcher = () => { + const { themeOverride } = useParameter( + PARAM_KEY, + DEFAULT_THEME_PARAMETERS + ) as ThemeParameters; + const [{ theme: selected }, updateGlobals] = useGlobals(); + + const [{ themesList, themeDefault }, updateState] = useAddonState( + THEME_SWITCHER_ID, + DEFAULT_ADDON_STATE + ); + + useChannel({ + [THEMING_EVENTS.REGISTER_THEMES]: ({ themes, defaultTheme }) => { + updateState((state) => ({ + ...state, + themesList: themes, + themeDefault: defaultTheme, + })); + }, + }); + + const label = useMemo(() => { + if (themeOverride) { + return <>Story override; + } + + const themeName = selected || themeDefault; + + return themeName && <>{`${themeName} theme`}; + }, [themeOverride, themeDefault, selected]); + + return hasMultipleThemes(themesList) ? ( + + { + return ( + ({ + id: theme, + title: theme, + active: selected === theme, + onClick: () => { + updateGlobals({ theme }); + onHide(); + }, + }))} + /> + ); + }} + > + + + {label && {label}} + + + + ) : null; +}; diff --git a/code/addons/themes/tsconfig.json b/code/addons/themes/tsconfig.json new file mode 100644 index 000000000000..b5a2f9a70918 --- /dev/null +++ b/code/addons/themes/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "include": ["src/**/*"], + "compilerOptions": { + "strict": true + } +} diff --git a/code/addons/toolbars/package.json b/code/addons/toolbars/package.json index 2f255801d3f8..6d855fbfbea7 100644 --- a/code/addons/toolbars/package.json +++ b/code/addons/toolbars/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/addon-toolbars", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Create your own toolbar items that control story rendering", "keywords": [ "addon", diff --git a/code/addons/viewport/package.json b/code/addons/viewport/package.json index 987f1513c498..88a24756e3aa 100644 --- a/code/addons/viewport/package.json +++ b/code/addons/viewport/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/addon-viewport", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Build responsive components by adjusting Storybook’s viewport size and orientation", "keywords": [ "addon", diff --git a/code/builders/builder-manager/package.json b/code/builders/builder-manager/package.json index 72ea64d0ca48..b763563e8f9b 100644 --- a/code/builders/builder-manager/package.json +++ b/code/builders/builder-manager/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/builder-manager", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Storybook manager builder", "keywords": [ "storybook" diff --git a/code/builders/builder-vite/package.json b/code/builders/builder-vite/package.json index e4c0aaa5443e..7f081abb8d54 100644 --- a/code/builders/builder-vite/package.json +++ b/code/builders/builder-vite/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/builder-vite", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "A plugin to run and build Storybooks with Vite", "homepage": "https://github.com/storybookjs/storybook/tree/next/code/builders/builder-vite/#readme", "bugs": { diff --git a/code/builders/builder-webpack5/package.json b/code/builders/builder-webpack5/package.json index cf21ac606e91..6d34d3399ede 100644 --- a/code/builders/builder-webpack5/package.json +++ b/code/builders/builder-webpack5/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/builder-webpack5", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Storybook framework-agnostic API", "keywords": [ "storybook" diff --git a/code/deprecated/addons/package.json b/code/deprecated/addons/package.json index 75b6b5df12e8..8142517597e5 100644 --- a/code/deprecated/addons/package.json +++ b/code/deprecated/addons/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/addons", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Storybook addons store", "keywords": [ "storybook" diff --git a/code/deprecated/channel-postmessage/package.json b/code/deprecated/channel-postmessage/package.json index 6a071407cb03..303150fa6a87 100644 --- a/code/deprecated/channel-postmessage/package.json +++ b/code/deprecated/channel-postmessage/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/channel-postmessage", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "", "keywords": [ "storybook" diff --git a/code/deprecated/channel-websocket/package.json b/code/deprecated/channel-websocket/package.json index b2a5010ff578..1a8baabd3ff8 100644 --- a/code/deprecated/channel-websocket/package.json +++ b/code/deprecated/channel-websocket/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/channel-websocket", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "", "keywords": [ "storybook" diff --git a/code/deprecated/client-api/package.json b/code/deprecated/client-api/package.json index e163b0f06e10..5c5b043c8bdb 100644 --- a/code/deprecated/client-api/package.json +++ b/code/deprecated/client-api/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/client-api", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Storybook Client API", "keywords": [ "storybook" diff --git a/code/deprecated/core-client/package.json b/code/deprecated/core-client/package.json index 894754b749ac..a6c521fa7143 100644 --- a/code/deprecated/core-client/package.json +++ b/code/deprecated/core-client/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/core-client", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Storybook framework-agnostic API", "keywords": [ "storybook" diff --git a/code/deprecated/manager-api-shim/package.json b/code/deprecated/manager-api-shim/package.json index 9bd206e90a07..b6d204f7e331 100644 --- a/code/deprecated/manager-api-shim/package.json +++ b/code/deprecated/manager-api-shim/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/api", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Storybook Manager API (facade)", "keywords": [ "storybook" diff --git a/code/deprecated/preview-web/package.json b/code/deprecated/preview-web/package.json index acb4f48b4847..6c04ed9efaf9 100644 --- a/code/deprecated/preview-web/package.json +++ b/code/deprecated/preview-web/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/preview-web", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "", "keywords": [ "storybook" diff --git a/code/deprecated/store/package.json b/code/deprecated/store/package.json index 5d23c114f6ad..b77674dfb3cc 100644 --- a/code/deprecated/store/package.json +++ b/code/deprecated/store/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/store", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "", "keywords": [ "storybook" diff --git a/code/frameworks/angular/package.json b/code/frameworks/angular/package.json index 2ff211e8f214..e1b9f6bcf4f0 100644 --- a/code/frameworks/angular/package.json +++ b/code/frameworks/angular/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/angular", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Storybook for Angular: Develop Angular components in isolation with hot reloading.", "keywords": [ "storybook", diff --git a/code/frameworks/ember/package.json b/code/frameworks/ember/package.json index 62f15de56494..6a07358a06fe 100644 --- a/code/frameworks/ember/package.json +++ b/code/frameworks/ember/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/ember", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Storybook for Ember: Develop Ember Component in isolation with Hot Reloading.", "homepage": "https://github.com/storybookjs/storybook/tree/next/code/frameworks/ember", "bugs": { diff --git a/code/frameworks/html-vite/package.json b/code/frameworks/html-vite/package.json index 6a300ebb0524..7871886e20f0 100644 --- a/code/frameworks/html-vite/package.json +++ b/code/frameworks/html-vite/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/html-vite", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Storybook for HTML and Vite: Develop HTML in isolation with Hot Reloading.", "keywords": [ "storybook" diff --git a/code/frameworks/html-webpack5/package.json b/code/frameworks/html-webpack5/package.json index 299b80217410..1a33ba1fee71 100644 --- a/code/frameworks/html-webpack5/package.json +++ b/code/frameworks/html-webpack5/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/html-webpack5", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Storybook for HTML: View HTML snippets in isolation with Hot Reloading.", "keywords": [ "storybook" diff --git a/code/frameworks/nextjs/package.json b/code/frameworks/nextjs/package.json index b450f976627b..723a6995a74b 100644 --- a/code/frameworks/nextjs/package.json +++ b/code/frameworks/nextjs/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/nextjs", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Storybook for Next.js", "keywords": [ "storybook", diff --git a/code/frameworks/preact-vite/package.json b/code/frameworks/preact-vite/package.json index 93d3acb5c508..62e47df58264 100644 --- a/code/frameworks/preact-vite/package.json +++ b/code/frameworks/preact-vite/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/preact-vite", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Storybook for Preact and Vite: Develop Preact components in isolation with Hot Reloading.", "keywords": [ "storybook" diff --git a/code/frameworks/preact-webpack5/package.json b/code/frameworks/preact-webpack5/package.json index 4f9d47e5ce29..03b7adcf0dc9 100644 --- a/code/frameworks/preact-webpack5/package.json +++ b/code/frameworks/preact-webpack5/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/preact-webpack5", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Storybook for Preact: Develop Preact Component in isolation.", "keywords": [ "storybook" diff --git a/code/frameworks/react-vite/package.json b/code/frameworks/react-vite/package.json index 274e2ff922c6..6f62e7027d48 100644 --- a/code/frameworks/react-vite/package.json +++ b/code/frameworks/react-vite/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/react-vite", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Storybook for React and Vite: Develop React components in isolation with Hot Reloading.", "keywords": [ "storybook" diff --git a/code/frameworks/react-webpack5/package.json b/code/frameworks/react-webpack5/package.json index e01c56b7b22d..cc09604bc00e 100644 --- a/code/frameworks/react-webpack5/package.json +++ b/code/frameworks/react-webpack5/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/react-webpack5", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Storybook for React: Develop React Component in isolation with Hot Reloading.", "keywords": [ "storybook" diff --git a/code/frameworks/server-webpack5/package.json b/code/frameworks/server-webpack5/package.json index 40e27faaeb1a..9deffe741416 100644 --- a/code/frameworks/server-webpack5/package.json +++ b/code/frameworks/server-webpack5/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/server-webpack5", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Storybook for Server: View HTML snippets from a server in isolation with Hot Reloading.", "keywords": [ "storybook" diff --git a/code/frameworks/svelte-vite/package.json b/code/frameworks/svelte-vite/package.json index 0200d6e75011..5646c649a86a 100644 --- a/code/frameworks/svelte-vite/package.json +++ b/code/frameworks/svelte-vite/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/svelte-vite", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Storybook for Svelte and Vite: Develop Svelte components in isolation with Hot Reloading.", "keywords": [ "storybook" diff --git a/code/frameworks/svelte-webpack5/package.json b/code/frameworks/svelte-webpack5/package.json index 279bb509e1ad..cbb4cf360311 100644 --- a/code/frameworks/svelte-webpack5/package.json +++ b/code/frameworks/svelte-webpack5/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/svelte-webpack5", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Storybook for Svelte: Develop Svelte Component in isolation with Hot Reloading.", "keywords": [ "storybook" diff --git a/code/frameworks/sveltekit/package.json b/code/frameworks/sveltekit/package.json index 95a120561296..dad6f68106c4 100644 --- a/code/frameworks/sveltekit/package.json +++ b/code/frameworks/sveltekit/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/sveltekit", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Storybook for SvelteKit", "keywords": [ "storybook", diff --git a/code/frameworks/vue-vite/package.json b/code/frameworks/vue-vite/package.json index 1e89429ef785..6dd94c7d3e19 100644 --- a/code/frameworks/vue-vite/package.json +++ b/code/frameworks/vue-vite/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/vue-vite", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Storybook for Vue2 and Vite: Develop Vue2 Components in isolation with Hot Reloading.", "keywords": [ "storybook" diff --git a/code/frameworks/vue-webpack5/package.json b/code/frameworks/vue-webpack5/package.json index ff052c15d6fb..0d7f57f6f410 100644 --- a/code/frameworks/vue-webpack5/package.json +++ b/code/frameworks/vue-webpack5/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/vue-webpack5", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Storybook for Vue: Develop Vue Component in isolation with Hot Reloading.", "keywords": [ "storybook" diff --git a/code/frameworks/vue3-vite/package.json b/code/frameworks/vue3-vite/package.json index 59e7569c65a5..d8b65fdd5759 100644 --- a/code/frameworks/vue3-vite/package.json +++ b/code/frameworks/vue3-vite/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/vue3-vite", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Storybook for Vue3 and Vite: Develop Vue3 components in isolation with Hot Reloading.", "keywords": [ "storybook" diff --git a/code/frameworks/vue3-webpack5/package.json b/code/frameworks/vue3-webpack5/package.json index 9f72ae532966..11299ef17689 100644 --- a/code/frameworks/vue3-webpack5/package.json +++ b/code/frameworks/vue3-webpack5/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/vue3-webpack5", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Storybook for Vue 3: Develop Vue 3 Components in isolation with Hot Reloading.", "keywords": [ "storybook" diff --git a/code/frameworks/web-components-vite/package.json b/code/frameworks/web-components-vite/package.json index 07b9a2322ed2..d5650076f000 100644 --- a/code/frameworks/web-components-vite/package.json +++ b/code/frameworks/web-components-vite/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/web-components-vite", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Storybook for web-components and Vite: Develop Web Components in isolation with Hot Reloading.", "keywords": [ "storybook" diff --git a/code/frameworks/web-components-webpack5/package.json b/code/frameworks/web-components-webpack5/package.json index 4e2137692781..9fc59464381f 100644 --- a/code/frameworks/web-components-webpack5/package.json +++ b/code/frameworks/web-components-webpack5/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/web-components-webpack5", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Storybook for web-components: View web components snippets in isolation with Hot Reloading.", "keywords": [ "lit", diff --git a/code/lib/channels/package.json b/code/lib/channels/package.json index 0e6269f7fd55..e2bbf9045561 100644 --- a/code/lib/channels/package.json +++ b/code/lib/channels/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/channels", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "", "keywords": [ "storybook" diff --git a/code/lib/cli-sb/package.json b/code/lib/cli-sb/package.json index 40793d9f1b7b..9815cf8c1164 100644 --- a/code/lib/cli-sb/package.json +++ b/code/lib/cli-sb/package.json @@ -1,6 +1,6 @@ { "name": "sb", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Storybook CLI", "keywords": [ "storybook" diff --git a/code/lib/cli-storybook/package.json b/code/lib/cli-storybook/package.json index b3dd18c52654..257c3ead3814 100644 --- a/code/lib/cli-storybook/package.json +++ b/code/lib/cli-storybook/package.json @@ -1,6 +1,6 @@ { "name": "storybook", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Storybook CLI", "keywords": [ "storybook" diff --git a/code/lib/cli/package.json b/code/lib/cli/package.json index e625bd282f06..2846cdc7763f 100644 --- a/code/lib/cli/package.json +++ b/code/lib/cli/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/cli", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Storybook's CLI - easiest method of adding storybook to your projects", "keywords": [ "cli", @@ -96,7 +96,7 @@ "util-deprecate": "^1.0.2" }, "devDependencies": { - "@storybook/client-api": "7.2.0-alpha.0", + "@storybook/client-api": "7.2.0-rc.0", "@types/cross-spawn": "^6.0.2", "@types/prompts": "^2.0.9", "@types/puppeteer-core": "^2.1.0", diff --git a/code/lib/cli/src/versions.ts b/code/lib/cli/src/versions.ts index 3c655591f8dc..fcccffaf87a7 100644 --- a/code/lib/cli/src/versions.ts +++ b/code/lib/cli/src/versions.ts @@ -1,96 +1,96 @@ // auto generated file, do not edit export default { - '@storybook/addon-a11y': '7.2.0-alpha.0', - '@storybook/addon-actions': '7.2.0-alpha.0', - '@storybook/addon-backgrounds': '7.2.0-alpha.0', - '@storybook/addon-controls': '7.2.0-alpha.0', - '@storybook/addon-docs': '7.2.0-alpha.0', - '@storybook/addon-essentials': '7.2.0-alpha.0', - '@storybook/addon-highlight': '7.2.0-alpha.0', - '@storybook/addon-interactions': '7.2.0-alpha.0', - '@storybook/addon-jest': '7.2.0-alpha.0', - '@storybook/addon-links': '7.2.0-alpha.0', - '@storybook/addon-mdx-gfm': '7.2.0-alpha.0', - '@storybook/addon-measure': '7.2.0-alpha.0', - '@storybook/addon-outline': '7.2.0-alpha.0', - '@storybook/addon-storyshots': '7.2.0-alpha.0', - '@storybook/addon-storyshots-puppeteer': '7.2.0-alpha.0', - '@storybook/addon-storysource': '7.2.0-alpha.0', - '@storybook/addon-toolbars': '7.2.0-alpha.0', - '@storybook/addon-viewport': '7.2.0-alpha.0', - '@storybook/addons': '7.2.0-alpha.0', - '@storybook/angular': '7.2.0-alpha.0', - '@storybook/api': '7.2.0-alpha.0', - '@storybook/blocks': '7.2.0-alpha.0', - '@storybook/builder-manager': '7.2.0-alpha.0', - '@storybook/builder-vite': '7.2.0-alpha.0', - '@storybook/builder-webpack5': '7.2.0-alpha.0', - '@storybook/channel-postmessage': '7.2.0-alpha.0', - '@storybook/channel-websocket': '7.2.0-alpha.0', - '@storybook/channels': '7.2.0-alpha.0', - '@storybook/cli': '7.2.0-alpha.0', - '@storybook/client-api': '7.2.0-alpha.0', - '@storybook/client-logger': '7.2.0-alpha.0', - '@storybook/codemod': '7.2.0-alpha.0', - '@storybook/components': '7.2.0-alpha.0', - '@storybook/core-client': '7.2.0-alpha.0', - '@storybook/core-common': '7.2.0-alpha.0', - '@storybook/core-events': '7.2.0-alpha.0', - '@storybook/core-server': '7.2.0-alpha.0', - '@storybook/core-webpack': '7.2.0-alpha.0', - '@storybook/csf-plugin': '7.2.0-alpha.0', - '@storybook/csf-tools': '7.2.0-alpha.0', - '@storybook/docs-tools': '7.2.0-alpha.0', - '@storybook/ember': '7.2.0-alpha.0', - '@storybook/html': '7.2.0-alpha.0', - '@storybook/html-vite': '7.2.0-alpha.0', - '@storybook/html-webpack5': '7.2.0-alpha.0', - '@storybook/instrumenter': '7.2.0-alpha.0', - '@storybook/manager': '7.2.0-alpha.0', - '@storybook/manager-api': '7.2.0-alpha.0', - '@storybook/nextjs': '7.2.0-alpha.0', - '@storybook/node-logger': '7.2.0-alpha.0', - '@storybook/postinstall': '7.2.0-alpha.0', - '@storybook/preact': '7.2.0-alpha.0', - '@storybook/preact-vite': '7.2.0-alpha.0', - '@storybook/preact-webpack5': '7.2.0-alpha.0', - '@storybook/preset-create-react-app': '7.2.0-alpha.0', - '@storybook/preset-html-webpack': '7.2.0-alpha.0', - '@storybook/preset-preact-webpack': '7.2.0-alpha.0', - '@storybook/preset-react-webpack': '7.2.0-alpha.0', - '@storybook/preset-server-webpack': '7.2.0-alpha.0', - '@storybook/preset-svelte-webpack': '7.2.0-alpha.0', - '@storybook/preset-vue-webpack': '7.2.0-alpha.0', - '@storybook/preset-vue3-webpack': '7.2.0-alpha.0', - '@storybook/preset-web-components-webpack': '7.2.0-alpha.0', - '@storybook/preview': '7.2.0-alpha.0', - '@storybook/preview-api': '7.2.0-alpha.0', - '@storybook/preview-web': '7.2.0-alpha.0', - '@storybook/react': '7.2.0-alpha.0', - '@storybook/react-dom-shim': '7.2.0-alpha.0', - '@storybook/react-vite': '7.2.0-alpha.0', - '@storybook/react-webpack5': '7.2.0-alpha.0', - '@storybook/router': '7.2.0-alpha.0', - '@storybook/server': '7.2.0-alpha.0', - '@storybook/server-webpack5': '7.2.0-alpha.0', - '@storybook/source-loader': '7.2.0-alpha.0', - '@storybook/store': '7.2.0-alpha.0', - '@storybook/svelte': '7.2.0-alpha.0', - '@storybook/svelte-vite': '7.2.0-alpha.0', - '@storybook/svelte-webpack5': '7.2.0-alpha.0', - '@storybook/sveltekit': '7.2.0-alpha.0', - '@storybook/telemetry': '7.2.0-alpha.0', - '@storybook/theming': '7.2.0-alpha.0', - '@storybook/types': '7.2.0-alpha.0', - '@storybook/vue': '7.2.0-alpha.0', - '@storybook/vue-vite': '7.2.0-alpha.0', - '@storybook/vue-webpack5': '7.2.0-alpha.0', - '@storybook/vue3': '7.2.0-alpha.0', - '@storybook/vue3-vite': '7.2.0-alpha.0', - '@storybook/vue3-webpack5': '7.2.0-alpha.0', - '@storybook/web-components': '7.2.0-alpha.0', - '@storybook/web-components-vite': '7.2.0-alpha.0', - '@storybook/web-components-webpack5': '7.2.0-alpha.0', - sb: '7.2.0-alpha.0', - storybook: '7.2.0-alpha.0', + '@storybook/addon-a11y': '7.2.0-rc.0', + '@storybook/addon-actions': '7.2.0-rc.0', + '@storybook/addon-backgrounds': '7.2.0-rc.0', + '@storybook/addon-controls': '7.2.0-rc.0', + '@storybook/addon-docs': '7.2.0-rc.0', + '@storybook/addon-essentials': '7.2.0-rc.0', + '@storybook/addon-highlight': '7.2.0-rc.0', + '@storybook/addon-interactions': '7.2.0-rc.0', + '@storybook/addon-jest': '7.2.0-rc.0', + '@storybook/addon-links': '7.2.0-rc.0', + '@storybook/addon-mdx-gfm': '7.2.0-rc.0', + '@storybook/addon-measure': '7.2.0-rc.0', + '@storybook/addon-outline': '7.2.0-rc.0', + '@storybook/addon-storyshots': '7.2.0-rc.0', + '@storybook/addon-storyshots-puppeteer': '7.2.0-rc.0', + '@storybook/addon-storysource': '7.2.0-rc.0', + '@storybook/addon-toolbars': '7.2.0-rc.0', + '@storybook/addon-viewport': '7.2.0-rc.0', + '@storybook/addons': '7.2.0-rc.0', + '@storybook/angular': '7.2.0-rc.0', + '@storybook/api': '7.2.0-rc.0', + '@storybook/blocks': '7.2.0-rc.0', + '@storybook/builder-manager': '7.2.0-rc.0', + '@storybook/builder-vite': '7.2.0-rc.0', + '@storybook/builder-webpack5': '7.2.0-rc.0', + '@storybook/channel-postmessage': '7.2.0-rc.0', + '@storybook/channel-websocket': '7.2.0-rc.0', + '@storybook/channels': '7.2.0-rc.0', + '@storybook/cli': '7.2.0-rc.0', + '@storybook/client-api': '7.2.0-rc.0', + '@storybook/client-logger': '7.2.0-rc.0', + '@storybook/codemod': '7.2.0-rc.0', + '@storybook/components': '7.2.0-rc.0', + '@storybook/core-client': '7.2.0-rc.0', + '@storybook/core-common': '7.2.0-rc.0', + '@storybook/core-events': '7.2.0-rc.0', + '@storybook/core-server': '7.2.0-rc.0', + '@storybook/core-webpack': '7.2.0-rc.0', + '@storybook/csf-plugin': '7.2.0-rc.0', + '@storybook/csf-tools': '7.2.0-rc.0', + '@storybook/docs-tools': '7.2.0-rc.0', + '@storybook/ember': '7.2.0-rc.0', + '@storybook/html': '7.2.0-rc.0', + '@storybook/html-vite': '7.2.0-rc.0', + '@storybook/html-webpack5': '7.2.0-rc.0', + '@storybook/instrumenter': '7.2.0-rc.0', + '@storybook/manager': '7.2.0-rc.0', + '@storybook/manager-api': '7.2.0-rc.0', + '@storybook/nextjs': '7.2.0-rc.0', + '@storybook/node-logger': '7.2.0-rc.0', + '@storybook/postinstall': '7.2.0-rc.0', + '@storybook/preact': '7.2.0-rc.0', + '@storybook/preact-vite': '7.2.0-rc.0', + '@storybook/preact-webpack5': '7.2.0-rc.0', + '@storybook/preset-create-react-app': '7.2.0-rc.0', + '@storybook/preset-html-webpack': '7.2.0-rc.0', + '@storybook/preset-preact-webpack': '7.2.0-rc.0', + '@storybook/preset-react-webpack': '7.2.0-rc.0', + '@storybook/preset-server-webpack': '7.2.0-rc.0', + '@storybook/preset-svelte-webpack': '7.2.0-rc.0', + '@storybook/preset-vue-webpack': '7.2.0-rc.0', + '@storybook/preset-vue3-webpack': '7.2.0-rc.0', + '@storybook/preset-web-components-webpack': '7.2.0-rc.0', + '@storybook/preview': '7.2.0-rc.0', + '@storybook/preview-api': '7.2.0-rc.0', + '@storybook/preview-web': '7.2.0-rc.0', + '@storybook/react': '7.2.0-rc.0', + '@storybook/react-dom-shim': '7.2.0-rc.0', + '@storybook/react-vite': '7.2.0-rc.0', + '@storybook/react-webpack5': '7.2.0-rc.0', + '@storybook/router': '7.2.0-rc.0', + '@storybook/server': '7.2.0-rc.0', + '@storybook/server-webpack5': '7.2.0-rc.0', + '@storybook/source-loader': '7.2.0-rc.0', + '@storybook/store': '7.2.0-rc.0', + '@storybook/svelte': '7.2.0-rc.0', + '@storybook/svelte-vite': '7.2.0-rc.0', + '@storybook/svelte-webpack5': '7.2.0-rc.0', + '@storybook/sveltekit': '7.2.0-rc.0', + '@storybook/telemetry': '7.2.0-rc.0', + '@storybook/theming': '7.2.0-rc.0', + '@storybook/types': '7.2.0-rc.0', + '@storybook/vue': '7.2.0-rc.0', + '@storybook/vue-vite': '7.2.0-rc.0', + '@storybook/vue-webpack5': '7.2.0-rc.0', + '@storybook/vue3': '7.2.0-rc.0', + '@storybook/vue3-vite': '7.2.0-rc.0', + '@storybook/vue3-webpack5': '7.2.0-rc.0', + '@storybook/web-components': '7.2.0-rc.0', + '@storybook/web-components-vite': '7.2.0-rc.0', + '@storybook/web-components-webpack5': '7.2.0-rc.0', + sb: '7.2.0-rc.0', + storybook: '7.2.0-rc.0', }; diff --git a/code/lib/client-logger/package.json b/code/lib/client-logger/package.json index 98aea1ffd3f4..6c04681a9538 100644 --- a/code/lib/client-logger/package.json +++ b/code/lib/client-logger/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/client-logger", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "", "keywords": [ "storybook" diff --git a/code/lib/codemod/package.json b/code/lib/codemod/package.json index e455c2a50146..f5180d506bb8 100644 --- a/code/lib/codemod/package.json +++ b/code/lib/codemod/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/codemod", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "A collection of codemod scripts written with JSCodeshift", "keywords": [ "storybook" diff --git a/code/lib/core-common/package.json b/code/lib/core-common/package.json index 57e5fa6322d0..86bd319897a6 100644 --- a/code/lib/core-common/package.json +++ b/code/lib/core-common/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/core-common", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Storybook framework-agnostic API", "keywords": [ "storybook" diff --git a/code/lib/core-events/package.json b/code/lib/core-events/package.json index bb0becbb8a8f..adc51e3c6565 100644 --- a/code/lib/core-events/package.json +++ b/code/lib/core-events/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/core-events", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Event names used in storybook core", "keywords": [ "storybook" diff --git a/code/lib/core-server/package.json b/code/lib/core-server/package.json index 9b71fd5d5f22..cebd63028db3 100644 --- a/code/lib/core-server/package.json +++ b/code/lib/core-server/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/core-server", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Storybook framework-agnostic API", "keywords": [ "storybook" diff --git a/code/lib/core-server/src/build-dev.ts b/code/lib/core-server/src/build-dev.ts index ef3d7e3cc3f8..7d1a223e1703 100644 --- a/code/lib/core-server/src/build-dev.ts +++ b/code/lib/core-server/src/build-dev.ts @@ -14,6 +14,7 @@ import { validateFrameworkName, } from '@storybook/core-common'; import prompts from 'prompts'; +import invariant from 'tiny-invariant'; import { global } from '@storybook/global'; import { telemetry } from '@storybook/telemetry'; @@ -31,7 +32,7 @@ export async function buildDevStandalone( ): Promise<{ port: number; address: string; networkAddress: string }> { const { packageJson, versionUpdates } = options; const { version } = packageJson; - + invariant(version !== undefined, 'Expected package.json version to be defined.'); // updateInfo are cached, so this is typically pretty fast const [port, versionCheck] = await Promise.all([ getServerPort(options.port), @@ -63,9 +64,10 @@ export async function buildDevStandalone( const config = await loadMainConfig(options); const { framework } = config; + invariant(framework, 'framework is required in Storybook v7'); const corePresets = []; - const frameworkName = typeof framework === 'string' ? framework : framework?.name; + const frameworkName = typeof framework === 'string' ? framework : framework.name; validateFrameworkName(frameworkName); corePresets.push(join(frameworkName, 'preset')); @@ -82,6 +84,7 @@ export async function buildDevStandalone( }); const { renderer, builder, disableTelemetry } = await presets.apply('core', {}); + invariant(builder, 'no builder configured!'); if (!options.disableTelemetry && !disableTelemetry) { if (versionCheck.success && !versionCheck.cached) { @@ -89,23 +92,26 @@ export async function buildDevStandalone( } } - const builderName = typeof builder === 'string' ? builder : builder?.name; + const builderName = typeof builder === 'string' ? builder : builder.name; const [previewBuilder, managerBuilder] = await Promise.all([ getPreviewBuilder(builderName, options.configDir), getManagerBuilder(), ]); + const resolvedRenderer = renderer + ? resolveAddonName(options.configDir, renderer, options) + : undefined; // Load second pass: all presets are applied in order presets = await loadAllPresets({ corePresets: [ require.resolve('@storybook/core-server/dist/presets/common-preset'), ...(managerBuilder.corePresets || []), ...(previewBuilder.corePresets || []), - ...(renderer ? [resolveAddonName(options.configDir, renderer, options)] : []), + ...(resolvedRenderer ? [resolvedRenderer] : []), ...corePresets, require.resolve('@storybook/core-server/dist/presets/babel-cache-preset'), ], - overridePresets: previewBuilder.overridePresets, + overridePresets: previewBuilder.overridePresets ?? [], ...options, }); @@ -123,8 +129,7 @@ export async function buildDevStandalone( ); const previewTotalTime = previewResult && previewResult.totalTime; - const managerTotalTime = managerResult && managerResult.totalTime; - + const managerTotalTime = managerResult ? managerResult.totalTime : undefined; const previewStats = previewResult && previewResult.stats; const managerStats = managerResult && managerResult.stats; diff --git a/code/lib/core-server/src/build-static.ts b/code/lib/core-server/src/build-static.ts index 8bbfff506316..6682046662aa 100644 --- a/code/lib/core-server/src/build-static.ts +++ b/code/lib/core-server/src/build-static.ts @@ -91,13 +91,15 @@ export async function buildStaticStandalone(options: BuildStaticStandaloneOption const [previewBuilder, managerBuilder] = await getBuilders({ ...options, presets }); const { renderer } = await presets.apply('core', {}); - + const resolvedRenderer = renderer + ? resolveAddonName(options.configDir, renderer, options) + : undefined; presets = await loadAllPresets({ corePresets: [ require.resolve('@storybook/core-server/dist/presets/common-preset'), ...(managerBuilder.corePresets || []), ...(previewBuilder.corePresets || []), - ...(renderer ? [resolveAddonName(options.configDir, renderer, options)] : []), + ...(resolvedRenderer ? [resolvedRenderer] : []), ...corePresets, require.resolve('@storybook/core-server/dist/presets/babel-cache-preset'), ], @@ -151,7 +153,8 @@ export async function buildStaticStandalone(options: BuildStaticStandaloneOption ); effects.push(copy(coreServerPublicDir, options.outputDir)); - let initializedStoryIndexGenerator: Promise = Promise.resolve(undefined); + let initializedStoryIndexGenerator: Promise = + Promise.resolve(undefined); if ((features?.buildStoriesJson || features?.storyStoreV7) && !options.ignorePreview) { const workingDir = process.cwd(); const directories = { @@ -167,17 +170,21 @@ export async function buildStaticStandalone(options: BuildStaticStandaloneOption storyStoreV7: !!features?.storyStoreV7, }); - initializedStoryIndexGenerator = generator.initialize().then(() => generator); + const initializedStoryIndexGeneratorPromise = generator.initialize().then(() => generator); effects.push( extractStoriesJson( join(options.outputDir, 'stories.json'), - initializedStoryIndexGenerator, + initializedStoryIndexGeneratorPromise, convertToIndexV3 ) ); effects.push( - extractStoriesJson(join(options.outputDir, 'index.json'), initializedStoryIndexGenerator) + extractStoriesJson( + join(options.outputDir, 'index.json'), + initializedStoryIndexGeneratorPromise + ) ); + initializedStoryIndexGenerator = initializedStoryIndexGeneratorPromise; } if (!core?.disableProjectJson) { diff --git a/code/lib/core-server/src/dev-server.ts b/code/lib/core-server/src/dev-server.ts index 6843c194e180..2eb1ea420cda 100644 --- a/code/lib/core-server/src/dev-server.ts +++ b/code/lib/core-server/src/dev-server.ts @@ -1,5 +1,6 @@ import express from 'express'; import compression from 'compression'; +import invariant from 'tiny-invariant'; import type { CoreConfig, Options, StorybookConfig } from '@storybook/types'; @@ -34,16 +35,13 @@ export async function storybookDevServer(options: Options) { getServerChannel(server) ); - let indexError: Error; + let indexError: Error | undefined; // try get index generator, if failed, send telemetry without storyCount, then rethrow the error - const initializedStoryIndexGenerator: Promise = getStoryIndexGenerator( - features, - options, - serverChannel - ).catch((err) => { - indexError = err; - return undefined; - }); + const initializedStoryIndexGenerator: Promise = + getStoryIndexGenerator(features ?? {}, options, serverChannel).catch((err) => { + indexError = err; + return undefined; + }); app.use(compression({ level: 1 })); @@ -51,7 +49,7 @@ export async function storybookDevServer(options: Options) { options.extendServer(server); } - app.use(getAccessControlMiddleware(core?.crossOriginIsolated)); + app.use(getAccessControlMiddleware(core?.crossOriginIsolated ?? false)); app.use(getCachingMiddleware()); getMiddleware(options.configDir)(router); @@ -59,6 +57,7 @@ export async function storybookDevServer(options: Options) { app.use(router); const { port, host, initialPath } = options; + invariant(port, 'expected options to have a port'); const proto = options.https ? 'https' : 'http'; const { address, networkAddress } = getServerAddresses(port, host, proto, initialPath); @@ -67,6 +66,7 @@ export async function storybookDevServer(options: Options) { server.listen({ port, host }, (error: Error) => (error ? reject(error) : resolve())); }); + invariant(core?.builder, 'no builder configured!'); const builderName = typeof core?.builder === 'string' ? core.builder : core?.builder?.name; const [previewBuilder, managerBuilder] = await Promise.all([ diff --git a/code/lib/core-server/src/presets/common-preset.ts b/code/lib/core-server/src/presets/common-preset.ts index 800f7ccaf0f7..76240e469f4e 100644 --- a/code/lib/core-server/src/presets/common-preset.ts +++ b/code/lib/core-server/src/presets/common-preset.ts @@ -44,7 +44,7 @@ export const staticDirs: PresetPropertyFn<'staticDirs'> = async (values = []) => ]; export const favicon = async ( - value: string, + value: string | undefined, options: Pick ) => { if (value) { @@ -257,8 +257,13 @@ type WhatsNewResponse = { excerpt: string; }; +type OptionsWithRequiredCache = Exclude & Required>; + // eslint-disable-next-line @typescript-eslint/naming-convention -export const experimental_serverChannel = async (channel: Channel, options: Options) => { +export const experimental_serverChannel = async ( + channel: Channel, + options: OptionsWithRequiredCache +) => { const coreOptions = await options.presets.apply('core'); channel.on(SET_WHATS_NEW_CACHE, async (data: WhatsNewCache) => { @@ -277,7 +282,10 @@ export const experimental_serverChannel = async (channel: Channel, options: Opti throw response; })) as WhatsNewResponse; - const main = await readConfig(findConfigFile('main', options.configDir)); + const configFileName = findConfigFile('main', options.configDir); + if (!configFileName) + throw new Error(`unable to find storybook main file in ${options.configDir}`); + const main = await readConfig(configFileName); const disableWhatsNewNotifications = main.getFieldValue([ 'core', 'disableWhatsNewNotifications', @@ -293,7 +301,7 @@ export const experimental_serverChannel = async (channel: Channel, options: Opti } satisfies WhatsNewData; channel.emit(RESULT_WHATS_NEW_DATA, { data }); } catch (e) { - logger.verbose(e); + logger.verbose(e instanceof Error ? e.message : String(e)); channel.emit(RESULT_WHATS_NEW_DATA, { data: { status: 'ERROR' } satisfies WhatsNewData, }); @@ -305,7 +313,10 @@ export const experimental_serverChannel = async (channel: Channel, options: Opti async ({ disableWhatsNewNotifications }: { disableWhatsNewNotifications: boolean }) => { const isTelemetryEnabled = coreOptions.disableTelemetry !== true; try { - const main = await readConfig(findConfigFile('main', options.configDir)); + const configFileName = findConfigFile('main', options.configDir); + if (!configFileName) + throw new Error(`unable to find storybook main file in ${options.configDir}`); + const main = await readConfig(configFileName); main.setFieldValue(['core', 'disableWhatsNewNotifications'], disableWhatsNewNotifications); await writeConfig(main); @@ -314,7 +325,7 @@ export const experimental_serverChannel = async (channel: Channel, options: Opti } } catch (error) { if (isTelemetryEnabled) { - await sendTelemetryError(error, 'core-config', { + await sendTelemetryError(error as Error, 'core-config', { cliOptions: options, presetOptions: { ...options, corePresets: [], overridePresets: [] }, skipPrompt: true, diff --git a/code/lib/core-server/src/standalone.ts b/code/lib/core-server/src/standalone.ts index 392f9027a000..bef85d55023d 100644 --- a/code/lib/core-server/src/standalone.ts +++ b/code/lib/core-server/src/standalone.ts @@ -4,7 +4,7 @@ import { buildDevStandalone } from './build-dev'; async function build(options: any = {}, frameworkOptions: any = {}) { const { mode = 'dev' } = options; - const { packageJson } = readUpSync({ cwd: __dirname }); + const packageJson = readUpSync({ cwd: __dirname })?.packageJson; const commonOptions = { ...options, diff --git a/code/lib/core-server/src/utils/StoryIndexGenerator.ts b/code/lib/core-server/src/utils/StoryIndexGenerator.ts index 221fa7665189..cceef09459fc 100644 --- a/code/lib/core-server/src/utils/StoryIndexGenerator.ts +++ b/code/lib/core-server/src/utils/StoryIndexGenerator.ts @@ -94,10 +94,10 @@ export class StoryIndexGenerator { // Cache the last value of `getStoryIndex`. We invalidate (by unsetting) when: // - any file changes, including deletions // - the preview changes [not yet implemented] - private lastIndex?: StoryIndex; + private lastIndex?: StoryIndex | null; // Same as the above but for the error case - private lastError?: Error; + private lastError?: Error | null; constructor( public readonly specifiers: NormalizedStoriesSpecifier[], @@ -171,6 +171,10 @@ export class StoryIndexGenerator { await Promise.all( this.specifiers.map(async (specifier) => { const entry = this.specifierToCache.get(specifier); + if (!entry) + throw new Error( + `specifier ${specifier} does not have a matching cache entry in specifierToCache` + ); return Promise.all( Object.keys(entry).map(async (absolutePath) => { if (entry[absolutePath] && !overwrite) return; @@ -185,7 +189,11 @@ export class StoryIndexGenerator { entry[absolutePath] = { type: 'error', - err: new IndexingError(err.message, [relativePath], err.stack), + err: new IndexingError( + err instanceof Error ? err.message : String(err), + [relativePath], + err instanceof Error ? err.stack : undefined + ), }; } }) @@ -213,6 +221,10 @@ export class StoryIndexGenerator { return this.specifiers.flatMap((specifier) => { const cache = this.specifierToCache.get(specifier); + if (!cache) + throw new Error( + `specifier ${specifier} does not have a matching cache entry in specifierToCache` + ); return Object.values(cache).flatMap((entry): (IndexEntry | ErrorEntry)[] => { if (!entry) return []; if (entry.type === 'docs') return [entry]; @@ -245,7 +257,12 @@ export class StoryIndexGenerator { const entries = [] as IndexEntry[]; const importPath = slash(normalizeStoryPath(relativePath)); const makeTitle = (userTitle?: string) => { - return userOrAutoTitleFromSpecifier(importPath, specifier, userTitle); + const title = userOrAutoTitleFromSpecifier(importPath, specifier, userTitle); + if (!title) + throw new Error( + "makeTitle created an undefined title. This happens when a specifier's doesn't have any matches in its fileName" + ); + return title; }; const storyIndexer = this.options.storyIndexers.find((indexer) => @@ -255,11 +272,11 @@ export class StoryIndexGenerator { throw new Error(`No matching story indexer found for ${absolutePath}`); } const csf = await storyIndexer.indexer(absolutePath, { makeTitle }); - const componentTags = csf.meta.tags || []; csf.stories.forEach(({ id, name, tags: storyTags, parameters }) => { if (!parameters?.docsOnly) { const tags = [...(storyTags || componentTags), 'story']; + invariant(csf.meta.title); entries.push({ id, title: csf.meta.title, name, importPath, tags, type: 'story' }); } }); @@ -273,6 +290,8 @@ export class StoryIndexGenerator { // b) we have docs page enabled for this file if (componentTags.includes(STORIES_MDX_TAG) || autodocsOptedIn) { const name = this.options.docs.defaultName; + if (!name) throw new Error('expected a defaultName property in options.docs'); + invariant(csf.meta.title); const id = toId(csf.meta.title, name); entries.unshift({ id, @@ -332,7 +351,7 @@ export class StoryIndexGenerator { // Also, if `result.of` is set, it means that we're using the `` syntax, // so find the `title` defined the file that `meta` points to. - let csfEntry: StoryIndexEntry; + let csfEntry: StoryIndexEntry | undefined; if (result.of) { const absoluteOf = makeAbsolute(result.of, normalizedPath, this.options.workingDir); dependencies.forEach((dep) => { @@ -369,7 +388,13 @@ export class StoryIndexGenerator { const title = csfEntry?.title || userOrAutoTitleFromSpecifier(importPath, specifier, result.title); + if (!title) + throw new Error( + "makeTitle created an undefined title. This happens when a specifier's doesn't have any matches in its fileName" + ); const { defaultName } = this.options.docs; + if (!defaultName) throw new Error('expected a defaultName property in options.docs'); + const name = result.name || (csfEntry ? autoName(importPath, csfEntry.importPath, defaultName) : defaultName); @@ -386,7 +411,7 @@ export class StoryIndexGenerator { }; return docsEntry; } catch (err) { - if (err.source?.match(/mdast-util-mdx-jsx/g)) { + if (err && (err as { source: any }).source?.match(/mdast-util-mdx-jsx/g)) { logger.warn( `πŸ’‘ This seems to be an MDX2 syntax error. Please refer to the MDX section in the following resource for assistance on how to fix this: ${chalk.yellow( 'https://storybook.js.org/migration-guides/7.0' @@ -512,7 +537,7 @@ export class StoryIndexGenerator { indexEntries[entry.id] = entry; } } catch (err) { - duplicateErrors.push(err); + if (err instanceof IndexingError) duplicateErrors.push(err); } }); if (duplicateErrors.length) throw new MultipleIndexingError(duplicateErrors); @@ -552,9 +577,9 @@ export class StoryIndexGenerator { return this.lastIndex; } catch (err) { - this.lastError = err; - logger.warn(`🚨 ${this.lastError.toString()}`); + this.lastError = err == null || err instanceof Error ? err : undefined; invariant(this.lastError); + logger.warn(`🚨 ${this.lastError.toString()}`); throw this.lastError; } } @@ -562,7 +587,7 @@ export class StoryIndexGenerator { invalidate(specifier: NormalizedStoriesSpecifier, importPath: Path, removed: boolean) { const absolutePath = slash(path.resolve(this.options.workingDir, importPath)); const cache = this.specifierToCache.get(specifier); - + if (!cache) throw new Error(`no `); const cacheEntry = cache[absolutePath]; if (cacheEntry && cacheEntry.type === 'stories') { const { dependents } = cacheEntry; diff --git a/code/lib/core-server/src/utils/copy-all-static-files.ts b/code/lib/core-server/src/utils/copy-all-static-files.ts index 2e6475acc8c2..44f7bf12d31f 100644 --- a/code/lib/core-server/src/utils/copy-all-static-files.ts +++ b/code/lib/core-server/src/utils/copy-all-static-files.ts @@ -22,7 +22,7 @@ export async function copyAllStaticFiles(staticDirs: any[] | undefined, outputDi filter: (_, dest) => !skipPaths.includes(dest), }); } catch (e) { - logger.error(e.message); + if (e instanceof Error) logger.error(e.message); process.exit(-1); } }) @@ -37,7 +37,7 @@ export async function copyAllStaticFilesRelativeToMain( ) { const workingDir = process.cwd(); - return staticDirs.reduce(async (acc, dir) => { + return staticDirs?.reduce(async (acc, dir) => { await acc; const staticDirAndTarget = typeof dir === 'string' ? dir : `${dir.from}:${dir.to}`; diff --git a/code/lib/core-server/src/utils/doTelemetry.ts b/code/lib/core-server/src/utils/doTelemetry.ts index 1a5b62d6ce92..ecb494fc45f5 100644 --- a/code/lib/core-server/src/utils/doTelemetry.ts +++ b/code/lib/core-server/src/utils/doTelemetry.ts @@ -1,3 +1,4 @@ +import invariant from 'tiny-invariant'; import type { CoreConfig, Options, StoryIndex } from '@storybook/types'; import { telemetry, getPrecedingUpgrade } from '@storybook/telemetry'; import { useStorybookMetadata } from './metadata'; @@ -9,17 +10,18 @@ import { sendTelemetryError } from '../withTelemetry'; export async function doTelemetry( core: CoreConfig, - initializedStoryIndexGenerator: Promise, + initializedStoryIndexGenerator: Promise, options: Options ) { if (!core?.disableTelemetry) { initializedStoryIndexGenerator.then(async (generator) => { - let storyIndex: StoryIndex; + let storyIndex: StoryIndex | undefined; try { storyIndex = await generator?.getIndex(); } catch (err) { // If we fail to get the index, treat it as a recoverable error, but send it up to telemetry // as if we crashed. In the future we will revisit this to send a distinct error + if (!(err instanceof Error)) throw new Error('encountered a non-recoverable error'); sendTelemetryError(err, 'dev', { cliOptions: options, presetOptions: { ...options, corePresets: [], overridePresets: [] }, @@ -27,12 +29,16 @@ export async function doTelemetry( return; } const { versionCheck, versionUpdates } = options; + invariant( + !versionUpdates || (versionUpdates && versionCheck), + 'versionCheck should be defined when versionUpdates is true' + ); const payload = { precedingUpgrade: await getPrecedingUpgrade(), }; if (storyIndex) { Object.assign(payload, { - versionStatus: versionUpdates ? versionStatus(versionCheck) : 'disabled', + versionStatus: versionUpdates && versionCheck ? versionStatus(versionCheck) : 'disabled', storyIndex: summarizeIndex(storyIndex), }); } diff --git a/code/lib/core-server/src/utils/get-builders.ts b/code/lib/core-server/src/utils/get-builders.ts index 1b43381c02cc..7ed83e293613 100644 --- a/code/lib/core-server/src/utils/get-builders.ts +++ b/code/lib/core-server/src/utils/get-builders.ts @@ -1,5 +1,6 @@ import type { Builder, CoreConfig, Options } from '@storybook/types'; import { pathToFileURL } from 'node:url'; +import invariant from 'tiny-invariant'; export async function getManagerBuilder(): Promise> { return import('@storybook/builder-manager'); @@ -24,7 +25,8 @@ export async function getPreviewBuilder( export async function getBuilders({ presets, configDir }: Options): Promise[]> { const { builder } = await presets.apply('core', {}); - const builderName = typeof builder === 'string' ? builder : builder?.name; + invariant(builder, 'no builder configured!'); + const builderName = typeof builder === 'string' ? builder : builder.name; return Promise.all([getPreviewBuilder(builderName, configDir), getManagerBuilder()]); } diff --git a/code/lib/core-server/src/utils/get-server-channel.ts b/code/lib/core-server/src/utils/get-server-channel.ts index f43f0dcf5b0e..c44c51b36307 100644 --- a/code/lib/core-server/src/utils/get-server-channel.ts +++ b/code/lib/core-server/src/utils/get-server-channel.ts @@ -3,7 +3,7 @@ import { isJSON, parse, stringify } from 'telejson'; import type { ChannelHandler } from '@storybook/channels'; import { Channel } from '@storybook/channels'; -type Server = ConstructorParameters[0]['server']; +type Server = NonNullable[0]>['server']>; /** * This class represents a channel transport that allows for a one-to-many relationship between the server and clients. @@ -28,7 +28,7 @@ export class ServerChannelTransport { wss.on('message', (raw) => { const data = raw.toString(); const event = typeof data === 'string' && isJSON(data) ? parse(data) : data; - this.handler(event); + this.handler?.(event); }); }); } diff --git a/code/lib/core-server/src/utils/getStoryIndexGenerator.ts b/code/lib/core-server/src/utils/getStoryIndexGenerator.ts index c1f75831806c..f7de41266822 100644 --- a/code/lib/core-server/src/utils/getStoryIndexGenerator.ts +++ b/code/lib/core-server/src/utils/getStoryIndexGenerator.ts @@ -15,7 +15,6 @@ export async function getStoryIndexGenerator( options: Options, serverChannel: ServerChannel ) { - let initializedStoryIndexGenerator: Promise = Promise.resolve(undefined); if (features?.buildStoriesJson || features?.storyStoreV7) { const workingDir = process.cwd(); const directories = { @@ -33,10 +32,10 @@ export async function getStoryIndexGenerator( docs: await docsOptions, workingDir, storiesV2Compatibility: !features?.storyStoreV7, - storyStoreV7: features?.storyStoreV7, + storyStoreV7: features.storyStoreV7 ?? false, }); - initializedStoryIndexGenerator = generator.initialize().then(() => generator); + const initializedStoryIndexGenerator = generator.initialize().then(() => generator); useStoriesJson({ router, @@ -45,6 +44,8 @@ export async function getStoryIndexGenerator( serverChannel, workingDir, }); + + return initializedStoryIndexGenerator; } - return initializedStoryIndexGenerator; + return Promise.resolve(undefined); } diff --git a/code/lib/core-server/src/utils/server-address.ts b/code/lib/core-server/src/utils/server-address.ts index 3332fa53b0d3..5e4ae1b18b72 100644 --- a/code/lib/core-server/src/utils/server-address.ts +++ b/code/lib/core-server/src/utils/server-address.ts @@ -5,7 +5,7 @@ import detectFreePort from 'detect-port'; export function getServerAddresses( port: number, - host: string, + host: string | undefined, proto: string, initialPath?: string ) { @@ -26,7 +26,7 @@ export function getServerAddresses( }; } -export const getServerPort = (port: number) => +export const getServerPort = (port?: number) => detectFreePort(port).catch((error) => { logger.error(error); process.exit(-1); diff --git a/code/lib/core-server/src/utils/server-statics.ts b/code/lib/core-server/src/utils/server-statics.ts index 96386df1d05a..a5ccc59fbd21 100644 --- a/code/lib/core-server/src/utils/server-statics.ts +++ b/code/lib/core-server/src/utils/server-statics.ts @@ -26,7 +26,7 @@ export async function useStatics(router: any, options: Options) { } const statics = [ - ...staticDirs.map((dir) => (typeof dir === 'string' ? dir : `${dir.from}:${dir.to}`)), + ...(staticDirs ?? []).map((dir) => (typeof dir === 'string' ? dir : `${dir.from}:${dir.to}`)), ...(options.staticDir || []), ]; @@ -52,7 +52,7 @@ export async function useStatics(router: any, options: Options) { router.use(targetEndpoint, express.static(staticPath, { index: false })); } catch (e) { - logger.warn(e.message); + if (e instanceof Error) logger.warn(e.message); } }) ); diff --git a/code/lib/core-server/src/utils/stories-json.ts b/code/lib/core-server/src/utils/stories-json.ts index 6ab44e1dcf5b..7070245a6043 100644 --- a/code/lib/core-server/src/utils/stories-json.ts +++ b/code/lib/core-server/src/utils/stories-json.ts @@ -51,7 +51,7 @@ export function useStoriesJson({ res.send(JSON.stringify(index)); } catch (err) { res.status(500); - res.send(err.toString()); + res.send(err instanceof Error ? err.toString() : String(err)); } }); @@ -63,7 +63,7 @@ export function useStoriesJson({ res.send(JSON.stringify(index)); } catch (err) { res.status(500); - res.send(err.toString()); + res.send(err instanceof Error ? err.toString() : String(err)); } }); } diff --git a/code/lib/core-server/src/withTelemetry.ts b/code/lib/core-server/src/withTelemetry.ts index 82bd7488dd21..1888f733cd04 100644 --- a/code/lib/core-server/src/withTelemetry.ts +++ b/code/lib/core-server/src/withTelemetry.ts @@ -42,11 +42,7 @@ async function getErrorLevel({ if (!presetOptions) return 'full'; // should we load the preset? - const presets = await loadAllPresets({ - corePresets: [require.resolve('@storybook/core-server/dist/presets/common-preset')], - overridePresets: [], - ...presetOptions, - }); + const presets = await loadAllPresets(presetOptions); // If the user has chosen to enable/disable crash reports in main.js // or disabled telemetry, we can return that @@ -108,7 +104,7 @@ export async function withTelemetry( eventType: EventType, options: TelemetryOptions, run: () => Promise -): Promise { +): Promise { let canceled = false; async function cancelTelemetry() { @@ -136,8 +132,8 @@ export async function withTelemetry( } const { printError = logger.error } = options; - printError(error); - await sendTelemetryError(error, eventType, options); + printError(error instanceof Error ? error.message : String(error)); + if (error instanceof Error) await sendTelemetryError(error, eventType, options); throw error; } finally { diff --git a/code/lib/core-server/tsconfig.json b/code/lib/core-server/tsconfig.json index a6f65038a17b..1dc5a72190bd 100644 --- a/code/lib/core-server/tsconfig.json +++ b/code/lib/core-server/tsconfig.json @@ -2,7 +2,7 @@ "extends": "../../tsconfig.json", "compilerOptions": { "skipLibCheck": true, - "strict": false + "strict": true }, "include": ["src/**/*"] } diff --git a/code/lib/core-webpack/package.json b/code/lib/core-webpack/package.json index 6618a7f237f5..f7e34a3f84c2 100644 --- a/code/lib/core-webpack/package.json +++ b/code/lib/core-webpack/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/core-webpack", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Storybook framework-agnostic API", "keywords": [ "storybook" diff --git a/code/lib/csf-plugin/package.json b/code/lib/csf-plugin/package.json index 3c82d210e3ae..78237d24e3c4 100644 --- a/code/lib/csf-plugin/package.json +++ b/code/lib/csf-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/csf-plugin", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Enrich CSF files via static analysis", "keywords": [ "storybook" diff --git a/code/lib/csf-tools/package.json b/code/lib/csf-tools/package.json index bf7e3336e8c8..78f16fa1443a 100644 --- a/code/lib/csf-tools/package.json +++ b/code/lib/csf-tools/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/csf-tools", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Parse and manipulate CSF and Storybook config files", "keywords": [ "storybook" diff --git a/code/lib/docs-tools/package.json b/code/lib/docs-tools/package.json index 2bb34f2b1c2c..99e728c70164 100644 --- a/code/lib/docs-tools/package.json +++ b/code/lib/docs-tools/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/docs-tools", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Shared utility functions for frameworks to implement docs", "keywords": [ "storybook" diff --git a/code/lib/instrumenter/package.json b/code/lib/instrumenter/package.json index 1864fa58014a..1df872a3556c 100644 --- a/code/lib/instrumenter/package.json +++ b/code/lib/instrumenter/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/instrumenter", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "", "keywords": [ "storybook" diff --git a/code/lib/manager-api/package.json b/code/lib/manager-api/package.json index 6ad30c18be6b..6d45047e51c9 100644 --- a/code/lib/manager-api/package.json +++ b/code/lib/manager-api/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/manager-api", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Core Storybook Manager API & Context", "keywords": [ "storybook" diff --git a/code/lib/manager-api/src/version.ts b/code/lib/manager-api/src/version.ts index 94dd7a797158..a796c945ee46 100644 --- a/code/lib/manager-api/src/version.ts +++ b/code/lib/manager-api/src/version.ts @@ -1 +1 @@ -export const version = '7.2.0-alpha.0'; +export const version = '7.2.0-rc.0'; diff --git a/code/lib/node-logger/package.json b/code/lib/node-logger/package.json index ee3dda8ef665..c69e1a09ea3f 100644 --- a/code/lib/node-logger/package.json +++ b/code/lib/node-logger/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/node-logger", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "", "keywords": [ "storybook" diff --git a/code/lib/postinstall/package.json b/code/lib/postinstall/package.json index 524821ca96df..05cab6d5b26b 100644 --- a/code/lib/postinstall/package.json +++ b/code/lib/postinstall/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/postinstall", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Storybook addons postinstall utilities", "keywords": [ "api", diff --git a/code/lib/preview-api/package.json b/code/lib/preview-api/package.json index e5e016151030..f1ccae48da26 100644 --- a/code/lib/preview-api/package.json +++ b/code/lib/preview-api/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/preview-api", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "", "keywords": [ "storybook" diff --git a/code/lib/preview-api/src/modules/store/csf/normalizeInputTypes.test.ts b/code/lib/preview-api/src/modules/store/csf/normalizeInputTypes.test.ts index c06939e9cc59..5cdb504e94eb 100644 --- a/code/lib/preview-api/src/modules/store/csf/normalizeInputTypes.test.ts +++ b/code/lib/preview-api/src/modules/store/csf/normalizeInputTypes.test.ts @@ -9,7 +9,7 @@ describe('normalizeInputType', () => { { name: 'name', type: { name: 'string' }, - control: { type: 'string' }, + control: { type: 'text' }, description: 'description', defaultValue: 'defaultValue', }, @@ -18,7 +18,7 @@ describe('normalizeInputType', () => { ).toEqual({ name: 'name', type: { name: 'string' }, - control: { type: 'string' }, + control: { type: 'text' }, description: 'description', defaultValue: 'defaultValue', }); @@ -29,7 +29,7 @@ describe('normalizeInputType', () => { normalizeInputType( { type: 'string', - control: 'string', + control: 'text', description: 'description', defaultValue: 'defaultValue', }, @@ -38,7 +38,7 @@ describe('normalizeInputType', () => { ).toEqual({ name: 'arg', type: { name: 'string' }, - control: { type: 'string' }, + control: { type: 'text' }, description: 'description', defaultValue: 'defaultValue', }); diff --git a/code/lib/preview/package.json b/code/lib/preview/package.json index 44ee06e86934..da835dbb23bd 100644 --- a/code/lib/preview/package.json +++ b/code/lib/preview/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/preview", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "", "keywords": [ "storybook" diff --git a/code/lib/react-dom-shim/package.json b/code/lib/react-dom-shim/package.json index bca093e657fb..255b19dfd6e4 100644 --- a/code/lib/react-dom-shim/package.json +++ b/code/lib/react-dom-shim/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/react-dom-shim", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "", "keywords": [ "storybook" diff --git a/code/lib/router/package.json b/code/lib/router/package.json index 948e3950f518..9e222d136d22 100644 --- a/code/lib/router/package.json +++ b/code/lib/router/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/router", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Core Storybook Router", "keywords": [ "storybook" @@ -48,7 +48,7 @@ "prep": "../../../scripts/prepare/bundle.ts" }, "dependencies": { - "@storybook/client-logger": "7.2.0-alpha.0", + "@storybook/client-logger": "7.2.0-rc.0", "memoizerific": "^1.11.3", "qs": "^6.10.0" }, diff --git a/code/lib/source-loader/package.json b/code/lib/source-loader/package.json index d68e4b55ce14..92253f3bc92c 100644 --- a/code/lib/source-loader/package.json +++ b/code/lib/source-loader/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/source-loader", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Source loader", "keywords": [ "lib", diff --git a/code/lib/telemetry/package.json b/code/lib/telemetry/package.json index 5f3bc134b0f9..104e6b1f296b 100644 --- a/code/lib/telemetry/package.json +++ b/code/lib/telemetry/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/telemetry", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Telemetry logging for crash reports and usage statistics", "keywords": [ "storybook" diff --git a/code/lib/theming/package.json b/code/lib/theming/package.json index 7119ef18b1e6..2e1339470e41 100644 --- a/code/lib/theming/package.json +++ b/code/lib/theming/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/theming", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Core Storybook Components", "keywords": [ "storybook" diff --git a/code/lib/types/package.json b/code/lib/types/package.json index 0f72d896a606..f6d8288bed06 100644 --- a/code/lib/types/package.json +++ b/code/lib/types/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/types", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Core Storybook TS Types", "keywords": [ "storybook" diff --git a/code/package.json b/code/package.json index cbe436f7b86d..8c83b2a41028 100644 --- a/code/package.json +++ b/code/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/root", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "private": true, "description": "Storybook root", "homepage": "https://storybook.js.org/", diff --git a/code/presets/create-react-app/package.json b/code/presets/create-react-app/package.json index cea14cf6b907..f057a7550441 100644 --- a/code/presets/create-react-app/package.json +++ b/code/presets/create-react-app/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/preset-create-react-app", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Storybook for Create React App preset", "keywords": [ "storybook" diff --git a/code/presets/html-webpack/package.json b/code/presets/html-webpack/package.json index f43a1bd1d59b..a5b50b6319dd 100644 --- a/code/presets/html-webpack/package.json +++ b/code/presets/html-webpack/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/preset-html-webpack", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Storybook for HTML: View HTML snippets in isolation with Hot Reloading.", "keywords": [ "storybook" diff --git a/code/presets/preact-webpack/package.json b/code/presets/preact-webpack/package.json index 013e73b1d1b4..432b36e35328 100644 --- a/code/presets/preact-webpack/package.json +++ b/code/presets/preact-webpack/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/preset-preact-webpack", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Storybook for Preact: Develop Preact Component in isolation.", "keywords": [ "storybook" diff --git a/code/presets/react-webpack/package.json b/code/presets/react-webpack/package.json index 98cd5000deee..0df436fa5921 100644 --- a/code/presets/react-webpack/package.json +++ b/code/presets/react-webpack/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/preset-react-webpack", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Storybook for React: Develop React Component in isolation with Hot Reloading", "keywords": [ "storybook" diff --git a/code/presets/server-webpack/package.json b/code/presets/server-webpack/package.json index d42f973ccc41..6c601bc1872c 100644 --- a/code/presets/server-webpack/package.json +++ b/code/presets/server-webpack/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/preset-server-webpack", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Storybook for Server: View HTML snippets from a server in isolation with Hot Reloading.", "keywords": [ "storybook" diff --git a/code/presets/svelte-webpack/package.json b/code/presets/svelte-webpack/package.json index 942fb67a8613..17cd2607a1b2 100644 --- a/code/presets/svelte-webpack/package.json +++ b/code/presets/svelte-webpack/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/preset-svelte-webpack", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Storybook for Svelte: Develop Svelte Component in isolation with Hot Reloading.", "keywords": [ "storybook" diff --git a/code/presets/vue-webpack/package.json b/code/presets/vue-webpack/package.json index 4c9dda823f05..693c01be891a 100644 --- a/code/presets/vue-webpack/package.json +++ b/code/presets/vue-webpack/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/preset-vue-webpack", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Storybook for Vue: Develop Vue Component in isolation with Hot Reloading.", "keywords": [ "storybook" diff --git a/code/presets/vue3-webpack/package.json b/code/presets/vue3-webpack/package.json index e621566a00ac..ccc7ee1766ce 100644 --- a/code/presets/vue3-webpack/package.json +++ b/code/presets/vue3-webpack/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/preset-vue3-webpack", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Storybook for Vue 3: Develop Vue 3 Components in isolation with Hot Reloading.", "keywords": [ "storybook" diff --git a/code/presets/vue3-webpack/src/framework-preset-vue3-docs.ts b/code/presets/vue3-webpack/src/framework-preset-vue3-docs.ts index 5bdbd97ff75d..b3fd1c028be7 100644 --- a/code/presets/vue3-webpack/src/framework-preset-vue3-docs.ts +++ b/code/presets/vue3-webpack/src/framework-preset-vue3-docs.ts @@ -16,7 +16,7 @@ export const webpackFinal: StorybookConfig['webpackFinal'] = (config, options) = } }); - config.module.rules.push({ + config.module?.rules?.push({ test: /\.vue$/, loader: require.resolve('vue-docgen-loader', { paths: [require.resolve('@storybook/preset-vue3-webpack')], @@ -24,7 +24,7 @@ export const webpackFinal: StorybookConfig['webpackFinal'] = (config, options) = enforce: 'post', options: { docgenOptions: { - alias: config.resolve.alias, + alias: config.resolve?.alias, ...vueDocgenOptions, }, }, diff --git a/code/presets/vue3-webpack/src/framework-preset-vue3.ts b/code/presets/vue3-webpack/src/framework-preset-vue3.ts index 8ce312123208..df7f684be456 100644 --- a/code/presets/vue3-webpack/src/framework-preset-vue3.ts +++ b/code/presets/vue3-webpack/src/framework-preset-vue3.ts @@ -6,7 +6,7 @@ export const webpack: StorybookConfig['webpack'] = (config) => { return { ...config, plugins: [ - ...config.plugins, + ...(config.plugins ?? []), new VueLoaderPlugin(), new DefinePlugin({ __VUE_OPTIONS_API__: JSON.stringify(true), @@ -16,7 +16,7 @@ export const webpack: StorybookConfig['webpack'] = (config) => { module: { ...config.module, rules: [ - ...config.module.rules, + ...(config.module?.rules ?? []), { test: /\.vue$/, loader: require.resolve('vue-loader'), @@ -51,9 +51,9 @@ export const webpack: StorybookConfig['webpack'] = (config) => { }, resolve: { ...config.resolve, - extensions: [...config.resolve.extensions, '.vue'], + extensions: [...(config.resolve?.extensions ?? []), '.vue'], alias: { - ...config.resolve.alias, + ...config.resolve?.alias, vue$: require.resolve('vue/dist/vue.esm-bundler.js'), }, }, diff --git a/code/presets/vue3-webpack/tsconfig.json b/code/presets/vue3-webpack/tsconfig.json index 40813306fd49..a7475d986d4d 100644 --- a/code/presets/vue3-webpack/tsconfig.json +++ b/code/presets/vue3-webpack/tsconfig.json @@ -3,7 +3,7 @@ "compilerOptions": { "resolveJsonModule": true, "skipLibCheck": true, - "strict": false + "strict": true }, "include": ["src/**/*"] } diff --git a/code/presets/web-components-webpack/package.json b/code/presets/web-components-webpack/package.json index 8a1e8076836a..4d61d2a277e6 100644 --- a/code/presets/web-components-webpack/package.json +++ b/code/presets/web-components-webpack/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/preset-web-components-webpack", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Storybook for web-components: View web components snippets in isolation with Hot Reloading.", "keywords": [ "lit", diff --git a/code/renderers/html/package.json b/code/renderers/html/package.json index 1cd253928910..e878999bdc67 100644 --- a/code/renderers/html/package.json +++ b/code/renderers/html/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/html", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Storybook HTML renderer", "keywords": [ "storybook" diff --git a/code/renderers/preact/package.json b/code/renderers/preact/package.json index 6d84788fdff9..1ebbb5263f6c 100644 --- a/code/renderers/preact/package.json +++ b/code/renderers/preact/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/preact", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Storybook Preact renderer", "keywords": [ "storybook" diff --git a/code/renderers/react/package.json b/code/renderers/react/package.json index 9832726dc303..d5b0298d15c3 100644 --- a/code/renderers/react/package.json +++ b/code/renderers/react/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/react", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Storybook React renderer", "keywords": [ "storybook" diff --git a/code/renderers/server/package.json b/code/renderers/server/package.json index 0553de69f926..0dad68ed165a 100644 --- a/code/renderers/server/package.json +++ b/code/renderers/server/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/server", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Storybook Server renderer", "keywords": [ "storybook" diff --git a/code/renderers/svelte/package.json b/code/renderers/svelte/package.json index 8239cd178da9..bba9807e5835 100644 --- a/code/renderers/svelte/package.json +++ b/code/renderers/svelte/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/svelte", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Storybook Svelte renderer", "keywords": [ "storybook" diff --git a/code/renderers/vue/package.json b/code/renderers/vue/package.json index 3a17532e7d8c..964948484842 100644 --- a/code/renderers/vue/package.json +++ b/code/renderers/vue/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/vue", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Storybook Vue renderer", "keywords": [ "storybook" diff --git a/code/renderers/vue3/package.json b/code/renderers/vue3/package.json index 67214bebe11a..f9ef5e8ca59d 100644 --- a/code/renderers/vue3/package.json +++ b/code/renderers/vue3/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/vue3", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Storybook Vue 3 renderer", "keywords": [ "storybook" diff --git a/code/renderers/web-components/package.json b/code/renderers/web-components/package.json index fcac4d5390ad..c8de5369ea7b 100644 --- a/code/renderers/web-components/package.json +++ b/code/renderers/web-components/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/web-components", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Storybook web-components renderer", "keywords": [ "lit", diff --git a/code/ui/blocks/package.json b/code/ui/blocks/package.json index 5b194fa4b3cb..718f6b186486 100644 --- a/code/ui/blocks/package.json +++ b/code/ui/blocks/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/blocks", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Storybook Doc Blocks", "keywords": [ "storybook" diff --git a/code/ui/components/package.json b/code/ui/components/package.json index aacf71efd415..7bb130e5f7c2 100644 --- a/code/ui/components/package.json +++ b/code/ui/components/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/components", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Core Storybook Components", "keywords": [ "storybook" diff --git a/code/ui/manager/package.json b/code/ui/manager/package.json index dc2d88f853e2..76a87bbce49f 100644 --- a/code/ui/manager/package.json +++ b/code/ui/manager/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/manager", - "version": "7.2.0-alpha.0", + "version": "7.2.0-rc.0", "description": "Core Storybook UI", "keywords": [ "storybook" diff --git a/code/yarn.lock b/code/yarn.lock index 01de3b0d72e2..b32b13944fa3 100644 --- a/code/yarn.lock +++ b/code/yarn.lock @@ -6027,6 +6027,30 @@ __metadata: languageName: unknown linkType: soft +"@storybook/addon-themes@workspace:addons/themes": + version: 0.0.0-use.local + resolution: "@storybook/addon-themes@workspace:addons/themes" + dependencies: + "@storybook/client-logger": 7.2.0-rc.0 + "@storybook/components": 7.2.0-rc.0 + "@storybook/core-events": 7.2.0-rc.0 + "@storybook/manager-api": 7.2.0-rc.0 + "@storybook/preview-api": 7.2.0-rc.0 + "@storybook/theming": 7.2.0-rc.0 + "@storybook/types": 7.2.0-rc.0 + ts-dedent: ^2.0.0 + typescript: ~4.9.3 + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + react: + optional: true + react-dom: + optional: true + languageName: unknown + linkType: soft + "@storybook/addon-toolbars@workspace:*, @storybook/addon-toolbars@workspace:addons/toolbars": version: 0.0.0-use.local resolution: "@storybook/addon-toolbars@workspace:addons/toolbars" @@ -6422,7 +6446,7 @@ __metadata: "@babel/preset-env": ^7.22.9 "@babel/types": ^7.22.5 "@ndelangen/get-tarball": ^3.0.7 - "@storybook/client-api": 7.2.0-alpha.0 + "@storybook/client-api": 7.2.0-rc.0 "@storybook/codemod": "workspace:*" "@storybook/core-common": "workspace:*" "@storybook/core-server": "workspace:*" @@ -6472,7 +6496,7 @@ __metadata: languageName: unknown linkType: soft -"@storybook/client-api@7.2.0-alpha.0, @storybook/client-api@workspace:*, @storybook/client-api@workspace:deprecated/client-api": +"@storybook/client-api@7.2.0-rc.0, @storybook/client-api@workspace:*, @storybook/client-api@workspace:deprecated/client-api": version: 0.0.0-use.local resolution: "@storybook/client-api@workspace:deprecated/client-api" dependencies: @@ -6481,7 +6505,7 @@ __metadata: languageName: unknown linkType: soft -"@storybook/client-logger@7.2.0-alpha.0, @storybook/client-logger@workspace:*, @storybook/client-logger@workspace:lib/client-logger": +"@storybook/client-logger@7.2.0-rc.0, @storybook/client-logger@workspace:*, @storybook/client-logger@workspace:lib/client-logger": version: 0.0.0-use.local resolution: "@storybook/client-logger@workspace:lib/client-logger" dependencies: @@ -6526,7 +6550,7 @@ __metadata: languageName: unknown linkType: soft -"@storybook/components@workspace:*, @storybook/components@workspace:ui/components": +"@storybook/components@7.2.0-rc.0, @storybook/components@workspace:*, @storybook/components@workspace:ui/components": version: 0.0.0-use.local resolution: "@storybook/components@workspace:ui/components" dependencies: @@ -6602,7 +6626,7 @@ __metadata: languageName: unknown linkType: soft -"@storybook/core-events@workspace:*, @storybook/core-events@workspace:lib/core-events": +"@storybook/core-events@7.2.0-rc.0, @storybook/core-events@workspace:*, @storybook/core-events@workspace:lib/core-events": version: 0.0.0-use.local resolution: "@storybook/core-events@workspace:lib/core-events" dependencies: @@ -6907,7 +6931,7 @@ __metadata: languageName: node linkType: hard -"@storybook/manager-api@workspace:*, @storybook/manager-api@workspace:lib/manager-api": +"@storybook/manager-api@7.2.0-rc.0, @storybook/manager-api@workspace:*, @storybook/manager-api@workspace:lib/manager-api": version: 0.0.0-use.local resolution: "@storybook/manager-api@workspace:lib/manager-api" dependencies: @@ -7316,7 +7340,7 @@ __metadata: languageName: unknown linkType: soft -"@storybook/preview-api@workspace:*, @storybook/preview-api@workspace:lib/preview-api": +"@storybook/preview-api@7.2.0-rc.0, @storybook/preview-api@workspace:*, @storybook/preview-api@workspace:lib/preview-api": version: 0.0.0-use.local resolution: "@storybook/preview-api@workspace:lib/preview-api" dependencies: @@ -7672,7 +7696,7 @@ __metadata: version: 0.0.0-use.local resolution: "@storybook/router@workspace:lib/router" dependencies: - "@storybook/client-logger": 7.2.0-alpha.0 + "@storybook/client-logger": 7.2.0-rc.0 "@storybook/global": ^5.0.0 dequal: ^2.0.2 lodash: ^4.17.21 @@ -7855,7 +7879,7 @@ __metadata: languageName: node linkType: hard -"@storybook/theming@workspace:*, @storybook/theming@workspace:lib/theming": +"@storybook/theming@7.2.0-rc.0, @storybook/theming@workspace:*, @storybook/theming@workspace:lib/theming": version: 0.0.0-use.local resolution: "@storybook/theming@workspace:lib/theming" dependencies: @@ -7880,7 +7904,7 @@ __metadata: languageName: unknown linkType: soft -"@storybook/types@workspace:*, @storybook/types@workspace:lib/types": +"@storybook/types@7.2.0-rc.0, @storybook/types@workspace:*, @storybook/types@workspace:lib/types": version: 0.0.0-use.local resolution: "@storybook/types@workspace:lib/types" dependencies: diff --git a/docs/configure/environment-variables.md b/docs/configure/environment-variables.md index 8cafa0e0eeae..4f32cac6c51b 100644 --- a/docs/configure/environment-variables.md +++ b/docs/configure/environment-variables.md @@ -3,7 +3,7 @@ title: 'Environment variables' --- You can use environment variables in Storybook to change its behavior in different β€œmodes”. -If you supply an environment variable prefixed with `STORYBOOK_`, it will be available in `process.env` when using webpack, or `import.meta.env` when using the vite builder: +If you supply an environment variable prefixed with `STORYBOOK_`, it will be available in `process.env` when using Webpack, or `import.meta.env` when using the Vite builder: ```shell STORYBOOK_THEME=red STORYBOOK_DATA_KEY=12345 npm run storybook @@ -17,14 +17,33 @@ STORYBOOK_THEME=red STORYBOOK_DATA_KEY=12345 npm run storybook Then we can access these environment variables anywhere inside our preview JavaScript code like below: + + + + + + + + + + + + + + You can also access these variables in your custom ``/`` using the substitution `%STORYBOOK_X%`, for example: `%STORYBOOK_THEME%` will become `red`. @@ -49,25 +68,41 @@ Then you can access this environment variable anywhere, even within your stories + + + + + +#### With Vite + +Out of the box, Storybook provides a [Vite builder](../builders/vite.md), which does not output Node.js globals like `process.env`. To access environment variables in Storybook (e.g., `STORYBOOK_`, `VITE_`), you can use `import.meta.env`. For example: + + + + + +
-You can also use specific files for specific modes. Add a .env.development or .env.production to apply different values to your environment variables. + +You can also use specific files for specific modes. Add a `.env.development` or `.env.production` to apply different values to your environment variables. +
You can also pass these environment variables when you are [building your Storybook](../sharing/publish-storybook.md) with `build-storybook`. @@ -76,7 +111,7 @@ Then they'll be hardcoded to the static version of your Storybook. ### Using Storybook configuration -Additionally, you can extend your Storybook configuration file (i.e., [`.storybook/main.js`](../configure/overview.md#configure-story-rendering)) and provide a configuration field that you can use to define specific variables (e.g., API URLs). For example: +Additionally, you can extend your Storybook configuration file (i.e., [`.storybook/main.js|.ts`](../configure/overview.md#configure-story-rendering)) and provide a configuration field that you can use to define specific variables (e.g., API URLs). For example: @@ -122,3 +157,9 @@ The table below lists the available options:
πŸ’‘ By default, Storybook will open a new Chrome window as part of its startup process. If you don't have Chrome installed, make sure to include one of the following options, or set your default browser accordingly.
+ +## Troubleshooting + +### Environment variables are not working + +If you're trying to use framework-specific environment variables (e.g.,`VUE_APP_`), you may run into issues primarily due to the fact that Storybook and your framework may have specific configurations and may not be able to recognize and use those environment variables. If you run into a similar situation, you may need to adjust your framework configuration to make sure that it can recognize and use those environment variables. For example, if you're working with a Vite-based framework, you can extend the configuration file and enable the [`envPrefix`](https://vitejs.dev/config/shared-options.html#envprefix) option. Other frameworks may require a similar approach. diff --git a/docs/essentials/addon-themes-example.gif b/docs/essentials/addon-themes-example.gif new file mode 100644 index 000000000000..f4fc95ef5919 Binary files /dev/null and b/docs/essentials/addon-themes-example.gif differ diff --git a/docs/essentials/introduction.md b/docs/essentials/introduction.md index 7b3f9d1d9524..73cdf81dcb4b 100644 --- a/docs/essentials/introduction.md +++ b/docs/essentials/introduction.md @@ -4,14 +4,15 @@ title: 'Essential addons' A major strength of Storybook are [addons](https://storybook.js.org/addons) that extend Storybook’s UI and behavior. Storybook ships by default with a set of β€œessential” addons that add to the initial user experience. There are many third-party addons as well as β€œofficial” addons developed by the Storybook core team. -- [Docs](../writing-docs/introduction.md) -- [Controls](./controls.md) - [Actions](./actions.md) -- [Viewport](./viewport.md) - [Backgrounds](./backgrounds.md) -- [Toolbars & globals](./toolbars-and-globals.md) -- [Measure & outline](./measure-and-outline.md) +- [Controls](./controls.md) +- [Docs](../writing-docs/introduction.md) - [Highlight](./highlight.md) +- [Measure & outline](./measure-and-outline.md) +- [Themes](./themes.md) +- [Toolbars & globals](./toolbars-and-globals.md) +- [Viewport](./viewport.md) ### Installation @@ -97,6 +98,7 @@ Below is an abridged configuration and table with all the available options for | `@storybook/addon-backgrounds` | N/A | N/A | | `@storybook/addon-toolbars` | N/A | N/A | | `@storybook/addon-measure` | N/A | N/A | +| `@storybook/addon-themes` | N/A | Provide and switch between multiple themes for components inside the preview in [Storybook](https://storybook.js.org). | When you start Storybook, your custom configuration will override the default. @@ -119,6 +121,6 @@ For example, if you wanted to disable the [backgrounds addon](./backgrounds.md),
-πŸ’‘ You can use the following keys for each individual addon: `actions`, `backgrounds`, `controls`, `docs`, `viewport`, `toolbars`, `measure`, `outline`, `highlight`. +πŸ’‘ You can use the following keys for each individual addon: `actions`, `backgrounds`, `controls`, `docs`, `viewport`, `toolbars`, `measure`, `outline`, `highlight`, `themes`.
diff --git a/docs/essentials/themes.md b/docs/essentials/themes.md new file mode 100644 index 000000000000..cdb199452ef0 --- /dev/null +++ b/docs/essentials/themes.md @@ -0,0 +1,56 @@ +--- +title: 'Themes' +--- + +Storybook's [Themes](https://github.com/storybookjs/storybook/tree/next/code/addons/themes) addon allows you to switch between multiple themes for your components inside of the preview in [Storybook](https://storybook.js.org). + +![Switching between themes in Storybook](./addon-themes-example.gif) + +## Theme decorators + +To make your themes accessible to your stories, `@storybook/addon-themes` exposes three [decorators](https://storybook.js.org/docs/react/writing-stories/decorators) for different methods of theming. + +### JSX providers + +For libraries that expose themes to components through providers, such as [Material UI](https://storybook.js.org/recipes/@mui/material/), [Styled-components](https://storybook.js.org/recipes/styled-components/), and [Emotion](https://storybook.js.org/recipes/@emotion/styled/), use the `withThemeFromJSXProvider`. + + + + + + + +### CSS classes + +For libraries that rely on CSS classes on a parent element to determine the theme, you can use the `withThemeByClassName` decorator. + + + + + + + +### Data attributes + +For libraries that rely on data attributes on a parent element to determine the theme, you can use the `withThemeByDataAttribute` decorator. + + + + + + diff --git a/docs/snippets/angular/arg-types-in-meta.ts.mdx b/docs/snippets/angular/arg-types-in-meta.ts.mdx index 1cc7933b518a..1bc7fd43f2f5 100644 --- a/docs/snippets/angular/arg-types-in-meta.ts.mdx +++ b/docs/snippets/angular/arg-types-in-meta.ts.mdx @@ -10,7 +10,7 @@ const meta: Meta