diff --git a/CHANGELOG.md b/CHANGELOG.md index 26e490d7c6d2..966bf5ce9a52 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## 7.4.3 + +- CLI: Fix `sb add` adding duplicative entries - [#24229](https://github.com/storybookjs/storybook/pull/24229), thanks [@ndelangen](https://github.com/ndelangen)! +- NextJS: Add compatibility with nextjs `13.5` - [#24239](https://github.com/storybookjs/storybook/pull/24239), thanks [@ndelangen](https://github.com/ndelangen)! +- NextJS: Aliases `react` and `react-dom` like `next.js` does - [#23671](https://github.com/storybookjs/storybook/pull/23671), thanks [@sookmax](https://github.com/sookmax)! +- Types: Allow `null` in value of `experimental_updateStatus` to clear status - [#24206](https://github.com/storybookjs/storybook/pull/24206), thanks [@ndelangen](https://github.com/ndelangen)! + ## 7.4.2 - Addon API: Improve the updateStatus API - [#24007](https://github.com/storybookjs/storybook/pull/24007), thanks [@ndelangen](https://github.com/ndelangen)! diff --git a/code/frameworks/nextjs/src/config/webpack.ts b/code/frameworks/nextjs/src/config/webpack.ts index 13c1a251cebe..f5e72bc360d8 100644 --- a/code/frameworks/nextjs/src/config/webpack.ts +++ b/code/frameworks/nextjs/src/config/webpack.ts @@ -5,6 +5,14 @@ import type { NextConfig } from 'next'; import { DefinePlugin } from 'webpack'; import { addScopedAlias, getNextjsVersion, resolveNextConfig } from '../utils'; +const tryResolve = (path: string) => { + try { + return require.resolve(path); + } catch (err) { + return false; + } +}; + export const configureConfig = async ({ baseConfig, nextConfigPath, @@ -17,8 +25,12 @@ export const configureConfig = async ({ const nextConfig = await resolveNextConfig({ baseConfig, nextConfigPath, configDir }); addScopedAlias(baseConfig, 'next/config'); - addScopedAlias(baseConfig, 'react', 'next/dist/compiled/react'); - addScopedAlias(baseConfig, 'react-dom', 'next/dist/compiled/react-dom'); + if (tryResolve('next/dist/compiled/react')) { + addScopedAlias(baseConfig, 'react', 'next/dist/compiled/react'); + } + if (tryResolve('next/dist/compiled/react-dom')) { + addScopedAlias(baseConfig, 'react-dom', 'next/dist/compiled/react-dom'); + } setupRuntimeConfig(baseConfig, nextConfig); return nextConfig; diff --git a/code/frameworks/nextjs/src/dependency-map.ts b/code/frameworks/nextjs/src/dependency-map.ts new file mode 100644 index 000000000000..70ad2ece94e0 --- /dev/null +++ b/code/frameworks/nextjs/src/dependency-map.ts @@ -0,0 +1,36 @@ +import type { Configuration as WebpackConfig } from 'webpack'; +import semver from 'semver'; +import { getNextjsVersion, addScopedAlias } from './utils'; + +const mapping: Record> = { + '<11.1.0': { + 'next/dist/next-server/lib/router-context': 'next/dist/next-server/lib/router-context', + }, + '>=11.1.0': { + 'next/dist/shared/lib/router-context': 'next/dist/shared/lib/router-context', + }, + '>=13.5.0': { + 'next/dist/shared/lib/router-context': 'next/dist/shared/lib/router-context.shared-runtime', + 'next/dist/shared/lib/head-manager-context': + 'next/dist/shared/lib/head-manager-context.shared-runtime', + 'next/dist/shared/lib/app-router-context': + 'next/dist/shared/lib/app-router-context.shared-runtime', + 'next/dist/shared/lib/hooks-client-context': + 'next/dist/shared/lib/hooks-client-context.shared-runtime', + }, +}; + +export const configureAliasing = (baseConfig: WebpackConfig): void => { + const version = getNextjsVersion(); + const result: Record = {}; + + Object.keys(mapping).forEach((key) => { + if (semver.intersects(version, key)) { + Object.assign(result, mapping[key]); + } + }); + + Object.entries(result).forEach(([name, alias]) => { + addScopedAlias(baseConfig, name, alias); + }); +}; diff --git a/code/frameworks/nextjs/src/preset.ts b/code/frameworks/nextjs/src/preset.ts index 4aa06d54c4d0..db1c276da9b4 100644 --- a/code/frameworks/nextjs/src/preset.ts +++ b/code/frameworks/nextjs/src/preset.ts @@ -7,7 +7,6 @@ import { getProjectRoot } from '@storybook/core-common'; import { configureConfig } from './config/webpack'; import { configureCss } from './css/webpack'; import { configureImports } from './imports/webpack'; -import { configureRouting } from './routing/webpack'; import { configureStyledJsx } from './styledJsx/webpack'; import { configureImages } from './images/webpack'; import { configureRuntimeNextjsVersionResolution } from './utils'; @@ -17,6 +16,7 @@ import TransformFontImports from './font/babel'; import { configureNextFont } from './font/webpack/configureNextFont'; import nextBabelPreset from './babel/preset'; import { configureNodePolyfills } from './nodePolyfills/webpack'; +import { configureAliasing } from './dependency-map'; export const addons: PresetProperty<'addons', StorybookConfig> = [ dirname(require.resolve(join('@storybook/preset-react-webpack', 'package.json'))), @@ -143,13 +143,13 @@ export const webpackFinal: StorybookConfig['webpackFinal'] = async (baseConfig, configDir: options.configDir, }); + configureAliasing(baseConfig); configureNextFont(baseConfig); configureNextImport(baseConfig); configureRuntimeNextjsVersionResolution(baseConfig); configureImports({ baseConfig, configDir: options.configDir }); configureCss(baseConfig, nextConfig); configureImages(baseConfig, nextConfig); - configureRouting(baseConfig); configureStyledJsx(baseConfig); configureNodePolyfills(baseConfig); diff --git a/code/frameworks/nextjs/src/routing/webpack.tsx b/code/frameworks/nextjs/src/routing/webpack.tsx deleted file mode 100644 index c0d245219742..000000000000 --- a/code/frameworks/nextjs/src/routing/webpack.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import type { Configuration as WebpackConfig } from 'webpack'; -import semver from 'semver'; -import { addScopedAlias, getNextjsVersion } from '../utils'; - -export const configureRouting = (baseConfig: WebpackConfig): void => { - // here we resolve the router context path with the installed version of Next.js - const routerContextPath = getRouterContextPath(); - addScopedAlias(baseConfig, routerContextPath); -}; - -const getRouterContextPath = () => { - const version = getNextjsVersion(); - if (semver.gte(version, '11.1.0')) { - return 'next/dist/shared/lib/router-context'; - } - - return 'next/dist/next-server/lib/router-context'; -}; diff --git a/code/lib/cli/src/add.ts b/code/lib/cli/src/add.ts index 71437b314b1c..8728da80ad5f 100644 --- a/code/lib/cli/src/add.ts +++ b/code/lib/cli/src/add.ts @@ -1,6 +1,8 @@ -import { getStorybookInfo } from '@storybook/core-common'; +import { getStorybookInfo, serverRequire } from '@storybook/core-common'; import { readConfig, writeConfig } from '@storybook/csf-tools'; +import { isAbsolute, join } from 'path'; import SemVer from 'semver'; +import dedent from 'ts-dedent'; import { JsPackageManagerFactory, @@ -38,6 +40,21 @@ const getVersionSpecifier = (addon: string) => { return groups ? [groups[1], groups[2]] : [addon, undefined]; }; +const requireMain = (configDir: string) => { + const absoluteConfigDir = isAbsolute(configDir) ? configDir : join(process.cwd(), configDir); + const mainFile = join(absoluteConfigDir, 'main'); + + return serverRequire(mainFile) ?? {}; +}; + +const checkInstalled = (addonName: string, main: any) => { + const existingAddon = main.addons?.find((entry: string | { name: string }) => { + const name = typeof entry === 'string' ? entry : entry.name; + return name?.endsWith(addonName); + }); + return !!existingAddon; +}; + /** * Install the given addon package and add it to main.js * @@ -60,9 +77,16 @@ export async function add( } const packageManager = JsPackageManagerFactory.getPackageManager({ force: pkgMgr }); const packageJson = await packageManager.retrievePackageJson(); + const { mainConfig, configDir } = getStorybookInfo(packageJson); + + if (checkInstalled(addon, requireMain(configDir))) { + throw new Error(dedent` + Addon ${addon} is already installed; we skipped adding it to your ${mainConfig}. + `); + } + const [addonName, versionSpecifier] = getVersionSpecifier(addon); - const { mainConfig } = getStorybookInfo(packageJson); if (!mainConfig) { logger.error('Unable to find storybook main.js config'); return; diff --git a/code/lib/core-common/src/utils/get-storybook-info.ts b/code/lib/core-common/src/utils/get-storybook-info.ts index fe183d566b7c..8d97fed4d3ed 100644 --- a/code/lib/core-common/src/utils/get-storybook-info.ts +++ b/code/lib/core-common/src/utils/get-storybook-info.ts @@ -100,7 +100,7 @@ const getConfigInfo = (packageJson: PackageJson, configDir?: string) => { } return { - configDir, + configDir: storybookConfigDir, mainConfig: findConfigFile('main', storybookConfigDir), previewConfig: findConfigFile('preview', storybookConfigDir), managerConfig: findConfigFile('manager', storybookConfigDir), diff --git a/code/lib/manager-api/src/index.tsx b/code/lib/manager-api/src/index.tsx index ab247114e440..bb7fe0e5ec6c 100644 --- a/code/lib/manager-api/src/index.tsx +++ b/code/lib/manager-api/src/index.tsx @@ -386,30 +386,34 @@ export function useParameter(parameterKey: string, defaultValue?: S) { } // cache for taking care of HMR -const addonStateCache: { - [key: string]: any; -} = {}; +globalThis.STORYBOOK_ADDON_STATE = {}; +const { STORYBOOK_ADDON_STATE } = globalThis; // shared state export function useSharedState(stateId: string, defaultState?: S) { const api = useStorybookApi(); - const existingState = api.getAddonState(stateId) || addonStateCache[stateId]; + const existingState = api.getAddonState(stateId) || STORYBOOK_ADDON_STATE[stateId]; const state = orDefault( existingState, - addonStateCache[stateId] ? addonStateCache[stateId] : defaultState + STORYBOOK_ADDON_STATE[stateId] ? STORYBOOK_ADDON_STATE[stateId] : defaultState ); + let quicksync = false; + + if (state === defaultState && defaultState !== undefined) { + STORYBOOK_ADDON_STATE[stateId] = defaultState; + quicksync = true; + } + useEffect(() => { - if (api.getAddonState(stateId) === undefined && api.getAddonState(stateId) !== state) { - api.setAddonState(stateId, state).then((s) => { - addonStateCache[stateId] = s; - }); + if (quicksync) { + api.setAddonState(stateId, defaultState); } - }, [api]); + }); const setState = (s: S | API_StateMerger, options?: Options) => { const result = api.setAddonState(stateId, s, options); - addonStateCache[stateId] = result; + STORYBOOK_ADDON_STATE[stateId] = result; return result; }; const allListeners = useMemo(() => { @@ -421,17 +425,17 @@ export function useSharedState(stateId: string, defaultState?: S) { [SET_STORIES]: async () => { const currentState = api.getAddonState(stateId); if (currentState) { - addonStateCache[stateId] = currentState; + STORYBOOK_ADDON_STATE[stateId] = currentState; api.emit(`${SHARED_STATE_SET}-manager-${stateId}`, currentState); - } else if (addonStateCache[stateId]) { + } else if (STORYBOOK_ADDON_STATE[stateId]) { // this happens when HMR - await setState(addonStateCache[stateId]); - api.emit(`${SHARED_STATE_SET}-manager-${stateId}`, addonStateCache[stateId]); + await setState(STORYBOOK_ADDON_STATE[stateId]); + api.emit(`${SHARED_STATE_SET}-manager-${stateId}`, STORYBOOK_ADDON_STATE[stateId]); } else if (defaultState !== undefined) { // if not HMR, yet the defaults are from the manager await setState(defaultState); - // initialize addonStateCache after first load, so its available for subsequent HMR - addonStateCache[stateId] = defaultState; + // initialize STORYBOOK_ADDON_STATE after first load, so its available for subsequent HMR + STORYBOOK_ADDON_STATE[stateId] = defaultState; api.emit(`${SHARED_STATE_SET}-manager-${stateId}`, defaultState); } }, diff --git a/code/lib/manager-api/src/modules/addons.ts b/code/lib/manager-api/src/modules/addons.ts index 0e1d69ca39e5..4f618a23b7ff 100644 --- a/code/lib/manager-api/src/modules/addons.ts +++ b/code/lib/manager-api/src/modules/addons.ts @@ -146,7 +146,7 @@ export const init: ModuleFn = ({ provider, store, fullAPI }) = .then(() => api.getAddonState(addonId)); }, getAddonState: (addonId) => { - return store.getState().addons[addonId]; + return store.getState().addons[addonId] || globalThis?.STORYBOOK_ADDON_STATE[addonId]; }, }; diff --git a/code/lib/manager-api/src/typings.d.ts b/code/lib/manager-api/src/typings.d.ts index 1d0d8d33ff0e..b7de89a44b4e 100644 --- a/code/lib/manager-api/src/typings.d.ts +++ b/code/lib/manager-api/src/typings.d.ts @@ -6,3 +6,4 @@ declare var FEATURES: import('@storybook/types').StorybookConfig['features']; declare var REFS: any; declare var VERSIONCHECK: any; declare var LOGLEVEL: 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'silent' | undefined; +declare var STORYBOOK_ADDON_STATE: Record; diff --git a/code/lib/preview-api/src/modules/preview-web/PreviewWeb.test.ts b/code/lib/preview-api/src/modules/preview-web/PreviewWeb.test.ts index 41e735ebfddb..8123020b86bb 100644 --- a/code/lib/preview-api/src/modules/preview-web/PreviewWeb.test.ts +++ b/code/lib/preview-api/src/modules/preview-web/PreviewWeb.test.ts @@ -3502,6 +3502,19 @@ describe('PreviewWeb', () => { document.location.search = '?id=component-one--docs&viewMode=docs'; const preview = await createAndRenderPreview(); + preview.onKeydown({ + composedPath: jest + .fn() + .mockReturnValue([{ tagName: 'div', getAttribute: jest.fn().mockReturnValue(null) }]), + } as any); + + expect(mockChannel.emit).toHaveBeenCalledWith(PREVIEW_KEYDOWN, expect.objectContaining({})); + }); + + it('emits PREVIEW_KEYDOWN for regular elements, fallback to event.target', async () => { + document.location.search = '?id=component-one--docs&viewMode=docs'; + const preview = await createAndRenderPreview(); + preview.onKeydown({ target: { tagName: 'div', getAttribute: jest.fn().mockReturnValue(null) }, } as any); @@ -3514,7 +3527,9 @@ describe('PreviewWeb', () => { const preview = await createAndRenderPreview(); preview.onKeydown({ - target: { tagName: 'input', getAttribute: jest.fn().mockReturnValue(null) }, + composedPath: jest + .fn() + .mockReturnValue([{ tagName: 'input', getAttribute: jest.fn().mockReturnValue(null) }]), } as any); expect(mockChannel.emit).not.toHaveBeenCalledWith( diff --git a/code/lib/preview-api/src/modules/preview-web/PreviewWithSelection.tsx b/code/lib/preview-api/src/modules/preview-web/PreviewWithSelection.tsx index ca30544d7c49..73b58cc70ead 100644 --- a/code/lib/preview-api/src/modules/preview-web/PreviewWithSelection.tsx +++ b/code/lib/preview-api/src/modules/preview-web/PreviewWithSelection.tsx @@ -45,7 +45,7 @@ import type { StorySpecifier } from '../store/StoryIndexStore'; const globalWindow = globalThis; function focusInInput(event: Event) { - const target = event.target as Element; + const target = ((event.composedPath && event.composedPath()[0]) || event.target) as Element; return /input|textarea/i.test(target.tagName) || target.getAttribute('contenteditable') !== null; } diff --git a/docs/api/main-config-indexers-arch-build-plugin.jpg b/docs/api/main-config-indexers-arch-build-plugin.jpg new file mode 100644 index 000000000000..73e2e5a54806 Binary files /dev/null and b/docs/api/main-config-indexers-arch-build-plugin.jpg differ diff --git a/docs/api/main-config-indexers-arch-indexer.jpg b/docs/api/main-config-indexers-arch-indexer.jpg new file mode 100644 index 000000000000..a7176864e366 Binary files /dev/null and b/docs/api/main-config-indexers-arch-indexer.jpg differ diff --git a/docs/api/main-config-indexers.md b/docs/api/main-config-indexers.md new file mode 100644 index 000000000000..d5efd754787a --- /dev/null +++ b/docs/api/main-config-indexers.md @@ -0,0 +1,366 @@ +--- +title: 'indexers' +--- + +(⚠️ **Experimental**) + +
+ +πŸ§ͺ While this feature is experimental, it must be specified by the `experimental_indexers` property of [`StorybookConfig`](./main-config.md). + +
+ +Parent: [main.js|ts configuration](./main-config.md) + +Type: `(existingIndexers: Indexer[]) => Promise` + +Indexers are responsible for building Storybook's index of storiesβ€”the list of all stories and a subset of their metadata like `id`, `title`, `tags`, and more. The index can be read at the `/index.json` route of your Storybook. + +The indexers API is an advanced feature that allows you to customize Storybook's indexers, which dictate how Storybook indexes and parses files into story entries. This adds more flexibility to how you can write stories, including which language stories are defined in or where to get stories from. + +They are defined as a function that returns the full list of indexers, including the existing ones. This allows you to add your own indexer to the list, or to replace an existing one: + + + + + + + +Unless your indexer is doing something relatively trivial (e.g. [indexing stories with a different naming convention](../configure/sidebar-and-urls#story-indexers)), in addition to indexing the file, you will likely need to [transpile it to CSF](#transpiling-to-csf) so that Storybook can read them in the browser. + +## `Indexer` + +Type: + +```ts +{ + test: RegExp; + createIndex: (fileName: string, options: IndexerOptions) => Promise; +} +``` + +Specifies which files to index and how to index them as stories. + +### `test` + +(Required) + +Type: `RegExp` + +A regular expression run against file names included in the [`stories`](./main-config-stories.md) configuration that should match all files to be handled by this indexer. + +### `createIndex` + +(Required) + +Type: `(fileName: string, options: IndexerOptions) => Promise` + +Function that accepts a single CSF file and returns a list of entries to index. + +#### `fileName` + +Type: `string` + +The name of the CSF file used to create entries to index. + +#### `IndexerOptions` + +Type: + +```ts +{ + makeTitle: (userTitle?: string) => string; +} +``` + +Options for indexing the file. + +##### `makeTitle` + +Type: `(userTitle?: string) => string` + +A function that takes a user-provided title and returns a formatted title for the index entry, which is used in the sidebar. If no user title is provided, one is automatically generated based on the file name and path. + +See [`IndexInput.title`](#title) for example usage. + +#### `IndexInput` + +Type: + +```ts +{ + exportName: string; + importPath: string; + type: 'story'; + metaId?: string; + name?: string; + tags?: string[]; + title?: string; + __id?: string; +} +``` + +An object representing a story to be added to the stories index. + +##### `exportName` + +(Required) + +Type: `string` + +For each `IndexInput`, the indexer will add this export (from the file found at `importPath`) as an entry in the index. + +##### `importPath` + +(Required) + +Type: `string` + +The file to import from, e.g. the [CSF](./csf.md) file. + +It is likely that the [`fileName`](#filename) being indexed is not CSF, in which you will need to [transpile it to CSF](#transpiling-to-csf) so that Storybook can read it in the browser. + +##### `type` + +(Required) + +Type: `'story'` + +The type of entry. + +##### `metaId` + +Type: `string` + +Default: Auto-generated from [`title`](#title) + +Define the custom id for meta of the entry. + +If specified, the export default (meta) in the CSF file _must_ have a corresponding `id` property, to be correctly matched. + +##### `name` + +Type: `string` + +Default: Auto-generated from [`exportName`](#exportname) + +The name of the entry. + +##### `tags` + +Type: `string[]` + +Tags for filtering entries in Storybook and its tools. + +##### `title` + +Type: `string` + +Default: Auto-generated from default export of [`importPath`](#importpath) + +Determines the location of the entry in the sidebar. + +Most of the time, you should **not** specify a title, so that your indexer will use the default naming behavior. When specifying a title, you **must** use the [`makeTitle`](#maketitle) function provided in [`IndexerOptions`](#indexeroptions) to also use this behavior. For example, here's an indexer that merely appends a "Custom" prefix to the title derived from the file name: + + + + + + + +##### `__id` + +Type: `string` + +Default: Auto-generated from [`title`](#title)/[`metaId`](#metaid) and [`exportName`](#exportname) + +Define the custom id for the story of the entry. + +If specified, the story in the CSF file **must** have a corresponding `__id` property, to be correctly matched. + +Only use this if you need to override the auto-generated id. + +## Transpiling to CSF + +The value of [`importPath`](#importpath) in an [`IndexInput`](#indexinput) must resolve to a [CSF](./csf.md) file. Most custom indexers, however, are only necessary because the input is _not_ CSF. Therefore, you will likely need to transpile the input to CSF, so that Storybook can read it in the browser and render your stories. + +Transpiling the custom source format to CSF is beyond the scope of this documentation. This transpilation is often done at the builder level ([Vite](../builders/vite.md) and/or [Webpack](../builders/webpack.md)), and we recommend using [unplugin](https://github.com/unjs/unplugin) to create plugins for multiple builders. + +The general architecture looks something like this: + +![Architecture diagram showing how a custom indexer indexes stories from a source file](./main-config-indexers-arch-indexer.jpg) + +1. Using the [`stories`](./main-config-stories.md) configuration, Storybook finds all files that match the [`test`](#test) property of your indexer +2. Storybook passes each matching file to your indexer's [`createIndex` function](#createindex), which uses the file contents to generate and return a list of index entries (stories) to add to the index +3. The index populates the sidebar in the Storybook UI + +![Architecture diagram showing how a build plugin transforms a source file into CSF](./main-config-indexers-arch-build-plugin.jpg) + +4. In the Storybook UI, the user navigates to a URL matching the story id and the browser requests the CSF file specified by the [`importPath`](#importpath) property of the index entry +5. Back on the server, your builder plugin transpiles the source file to CSF, and serves it to the client +6. The Storybook UI reads the CSF file, imports the story specified by [`exportName`](#exportname), and renders it + +Let's look at an example of how this might work. + +First, here's an example of a non-CSF source file: + +```ts +// Button.variants.js|ts + +import { variantsFromComponent, createStoryFromVariant } from '../utils'; +import { Button } from './Button'; + +/** + * Returns raw strings representing stories via component props, eg. + * 'export const PrimaryVariant = { + * args: { + * primary: true + * }, + * };' + */ +export const generateStories = () => { + const variants = variantsFromComponent(Button); + return variants.map((variant) => createStoryFromVariant(variant)); +}; +``` + +The builder plugin would then: + +1. Receive and read the source file +2. Import the exported `generateStories` function +3. Run the function to generate the stories +4. Write the stories to a CSF file + +That resulting CSF file would then be indexed by Storybook. It would look something like this: + +```js +// virtual:Button.variants.js|ts + +import { Button } from './Button'; + +export default { + component: Button, +}; + +export const Primary = { + args: { + primary: true, + }, +}; +``` + +### Examples + +Some example usages of custom indexers include: + +
+ +Generating stories dynamically from fixture data or API endpoints + +This indexer generates stories for components based on JSON fixture data. It looks for `*.stories.json` files in the project, adds them to the index and separately converts their content to CSF. + + + + + + + +An example input JSON file could look like this: + +```json +{ + "Button": { + "componentPath": "./button/Button.jsx", + "stories": { + "Primary": { + "args": { + "primary": true + }, + "Secondary": { + "args": { + "primary": false + } + } + } + }, + "Dialog": { + "componentPath": "./dialog/Dialog.jsx", + "stories": { + "Closed": {}, + "Open": { + "args": { + "isOpen": true + } + }, + } + } +} +``` + +A builder plugin will then need to transform the JSON file into a regular CSF file. This transformation could be done with a Vite plugin similar to this: + +```ts +// vite-plugin-storybook-json-stories.ts + +import type { PluginOption } from 'vite'; +import fs from 'fs/promises'; + +function JsonStoriesPlugin(): PluginOption { + return { + name: 'vite-plugin-storybook-json-stories', + load(id) { + if (!id.startsWith('virtual:jsonstories')) { + return; + } + + const [, fileName, componentName] = id.split('--'); + const content = JSON.parse(fs.readFileSync(fileName)); + + const { componentPath, stories } = getComponentStoriesFromJson(content, componentName); + + return ` + import ${componentName} from '${componentPath}'; + + export default { component: ${componentName} }; + + ${stories.map((story) => `export const ${story.name} = ${story.config};\n`)} + `; + }, + }; +} +``` + +
+ +
+ +Generating stories with an alternative API + +You can use a custom indexer and builder plugin to create your own API for defining stories, such as imperatively defining stories similar to the legacy [`storiesOf`](https://github.com/storybookjs/storybook/blob/main/code/lib/preview-api/docs/storiesOf.md) format. + +The [dynamic stories proof of concept](https://stackblitz.com/edit/github-h2rgfk?file=README.md) is an elaborate, functional example of doing just that. It contains everything needed to support such a feature, including the indexer, a Vite plugin and a Webpack loader. + +
+ +
+ +Defining stories in non-JavaScript language + +Custom indexers can be used for an advanced purpose: defining stories in any language, including template languages, and converting the files to CSF. To see examples of this in action, you can refer to [`@storybook/addon-svelte-csf`](https://github.com/storybookjs/addon-svelte-csf) for Svelte template syntax and [`storybook-vue-addon`](https://github.com/tobiasdiez/storybook-vue-addon) for Vue template syntax. + +
diff --git a/docs/api/main-config.md b/docs/api/main-config.md index ed4b1b387319..69372067bdf8 100644 --- a/docs/api/main-config.md +++ b/docs/api/main-config.md @@ -34,6 +34,7 @@ An object to configure Storybook containing the following properties: - [`docs`](./main-config-docs.md) - [`env`](./main-config-env.md) - [`features`](./main-config-features.md) +- [`indexers`](./main-config-indexers.md) (⚠️ Experimental) - [`logLevel`](./main-config-log-level.md) - [`managerHead`](./main-config-manager-head.md) - [`previewAnnotations`](./main-config-preview-annotations.md) diff --git a/docs/configure/babel.md b/docs/configure/babel.md index b497a28d4a31..331add4d5e6e 100644 --- a/docs/configure/babel.md +++ b/docs/configure/babel.md @@ -133,7 +133,9 @@ When the command finishes running, it will display the available Babel configura For more info, please refer to the [Babel documentation](https://babeljs.io/docs/en/configuration#print-effective-configs). -### SWC alternative (experimental) +### SWC alternative + +(⚠️ **Experimental**) If you're working with a Webpack-based project, you can opt into replacing Babel with the [SWC](https://swc.rs/) compiler, which can be faster for some projects. To do so, update your [Storybook configuration file](../api/main-config.md) (e.g., `.storybook/main.js|ts`) to enable the experimental `useSWC` option: diff --git a/docs/configure/sidebar-and-urls.md b/docs/configure/sidebar-and-urls.md index 0d3bc106cb9b..966d928b3fe5 100644 --- a/docs/configure/sidebar-and-urls.md +++ b/docs/configure/sidebar-and-urls.md @@ -94,6 +94,19 @@ Storybook 6.4 introduced [CSF 3.0](https://storybook.js.org/blog/component-story When Storybook loads, the story can show up in the sidebar as `components/My Component`. +Auto-titles work with explicit titling options like the component's `title` and the story's `name`: + + + + + + + ### Auto-title filename case Starting with Storybook 6.5, story titles generated automatically no longer rely on Lodash's [startCase](https://lodash.com/docs/#startCase). @@ -149,55 +162,6 @@ When Storybook generates the titles for all matching stories, they'll retain the ### Story Indexers -Story Indexers are a set of heuristics used by Storybook to crawl your filesystem based on a given glob pattern searching for matching stories, which is then used to generate an `index.json` (formerly `stories.json`) file responsible for populating the sidebar with the necessary information. By default, this heuristic will look for files that contain the following scheme \*.stories.@(js|jsx|mjs|ts|tsx). However, if you need, you can create your custom story indexer that you can use to include stories that have a different naming convention. For example: - - - - - - - -### Processing custom titles - -Out of the box, Storybook automatically infers the story's title based on a set of patterns, including, for example, the file's physical location. If you're working on creating a custom story indexer and you want to handle the titles based on your set of rules, you can adjust it and provide a `makeTitle` function inside the `loadCsf` function. Below is a condensed table and examples of how these patterns work and how they relate to stories. - -| Pattern | Description | -| -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | -| `titlePrefix` | Prefixes the indexed story title based on the [configuration](./overview.md#with-a-configuration-object). | -| `title` | Requires CSF and MDX.
Infers the story title based on the information defined in the metadata. | -| `name` | Requires CSF and MDX.
Overrides the story title based on the provided [name](../writing-stories/introduction.md#rename-stories) property. | -| `component` | Requires CSF.
Generates the title based on the component property defined in the metadata. | -| `of` | Requires MDX.
Retrieves the title based on the referenced story file. | -| `story export` | Requires CSF.
Defines the title based on the [named story export](../api/csf.md#named-story-exports). | -| `filename` | Enabled with custom indexers.
Defines the story title based on the filename parsed with the indexer. | - - - - - - - - - - - - - -
- -πŸ’‘ Story indexers are a great way to get non-developers playing with your components and stories. They can do much more than documented here; we encourage you to check out the [`@storybook-extras/markdown` ](https://storybook.js.org/addons/@storybook-extras/markdown/) to learn more about these techniques. +[Story Indexers](./main-config-indexers.md) are a set of heuristics used by Storybook to crawl your filesystem based on a given glob pattern searching for matching stories, which is then used to generate an `index.json` (formerly `stories.json`) file responsible for populating the sidebar with the necessary information. By default, this heuristic will look for files that contain the following scheme `*.stories.@(js|jsx|mjs|ts|tsx)`. -
+You can provide your own indexer to include stories with a different naming convention, adjust the automatic title generation beyond a prefix, and many other use cases. For more information, see the [Story Indexers API reference](./main-config-indexers.md). diff --git a/docs/faq.md b/docs/faq.md index a936a3dbc8ed..b26eb9ec12fe 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -228,6 +228,7 @@ Introduce a single `.mdx` docs page (addon-essentials or addon-docs required), t ```mdx + # Welcome Some description here @@ -244,9 +245,9 @@ const config = { refs: { firstProject: { title: 'First', url: 'some-url' }, secondProject: { title: 'Second', url: 'other-url' }, - } + }, // ... -} +}; export default config; ``` @@ -264,83 +265,83 @@ With the release of version 6.0, we updated our documentation as well. That does We're only covering versions 5.3 and 5.0 as they were important milestones for Storybook. If you want to go back in time a little more, you'll have to check the specific release in the monorepo. -| Section | Page | Current Location | Version 5.3 location | Version 5.0 location | -| ---------------- | -------------------------------------------- | ---------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | -| N/A | Why Storybook | [See current documentation](./why-storybook.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| Get started | Install | [See current documentation](./get-started/install.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/guides/quick-start-guide) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/guides/quick-start-guide) | -| | What's a story | [See current documentation](./get-started/whats-a-story.md) | [See versioned documentation for your framework](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/guides) | [See versioned documentation for your framework](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/guides) | -| | Browse Stories | [See current documentation](./get-started/browse-stories.md) | [See versioned documentation for your framework](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/guides) | [See versioned documentation for your framework](https://github.com/storybookjs/storybook/blob/release/5.0/docs/src/pages/guides) | -| | Setup | [See current documentation](./get-started/setup.md) | [See versioned documentation for your framework](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/guides) | [See versioned documentation for your framework](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/guides) | -| Write stories | Introduction | [See current documentation](./writing-stories/introduction.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/basics/writing-stories) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/basics/writing-stories) | -| | Parameters | [See current documentation](./writing-stories/parameters.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/basics/writing-stories/index.md#parameters) | Non existing feature or undocumented | -| | Decorators | [See current documentation](./writing-stories/decorators.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/basics/writing-stories/index.md#decorators) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/basics/writing-stories/index.md#using-decorators) | -| | Naming components and hierarchy | [See current documentation](./writing-stories/naming-components-and-hierarchy.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/basics/writing-stories) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/basics/writing-stories) | -| | Build pages and screens | [See current documentation](./writing-stories/build-pages-with-storybook.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | Stories for multiple components | [See current documentation](./writing-stories/stories-for-multiple-components.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| Write docs | Autodocs | [See current documentation](./writing-docs/autodocs.md) | See versioned addon documentation | Non existing feature or undocumented | -| | MDX | [See current documentation](./writing-docs/mdx.md) | See versioned addon documentation | Non existing feature or undocumented | -| | Doc Blocks | [See current documentation](./writing-docs/doc-blocks.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | Preview and build docs | [See current documentation](./writing-docs/build-documentation.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| Testing | Visual tests | [See current documentation](./writing-tests/visual-testing.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/testing/automated-visual-testing) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/testing/automated-visual-testing) | -| | Accessibility tests | [See current documentation](./writing-tests/accessibility-testing.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | Interaction tests | [See current documentation](./writing-tests/interaction-testing.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/testing/interaction-testing) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/testing/interaction-testing) | -| | Snapshot tests | [See current documentation](./writing-tests/snapshot-testing.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/testing/structural-testing) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/testing/structural-testing) | -| | Import stories in tests/Unit tests | [See current documentation](./writing-tests/stories-in-unit-tests.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/testing/react-ui-testing) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/testing/react-ui-testing) | -| | Import stories in tests/End-to-end testing | [See current documentation](./writing-tests/stories-in-end-to-end-tests.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/testing/react-ui-testing) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/testing/react-ui-testing) | -| Sharing | Publish Storybook | [See current documentation](./sharing/publish-storybook.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/basics/exporting-storybook) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/basics/exporting-storybook) | -| | Embed | [See current documentation](./sharing/embed.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | Composition | [See current documentation](./sharing/storybook-composition.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | Package Composition | [See current documentation](./sharing/package-composition.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| Essential addons | Controls | [See current documentation](./essentials/controls.md) | Controls are specific to version 6.0 see [Knobs versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/addons/knobs) | Controls are specific to version 6.0 see [Knobs versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/addons/knobs) | -| | Actions | [See current documentation](./essentials/actions.md) | [See addon versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/addons/actions) | [See addon versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/addons/actions) | -| | Viewport | [See current documentation](./essentials/viewport.md) | [See addon versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/addons/viewport) | [See addon versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/addons/viewport) | -| | Backgrounds | [See current documentation](./essentials/backgrounds.md) | [See addon versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/addons/backgrounds) | [See addon versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/addons/backgrounds) | -| | Toolbars and globals | [See current documentation](./essentials/toolbars-and-globals.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/basics/toolbar-guide) | Non existing feature or undocumented | -| Configure | Overview | [See current documentation](./configure/overview.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/configurations/overview) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/basics/writing-stories) | -| | Integration/Frameworks | [See current documentation](./configure/frameworks.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | Integration/Framework support for frameworks | [See current documentation](./configure/frameworks-feature-support.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | Integration/Babel | [See current documentation](./configure/babel.md) | See versioned documentation [here](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/configurations/custom-babel-config) | See versioned documentation [here](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/configurations/custom-babel-config) | -| | Integration/Typescript | [See current documentation](./configure/typescript.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/configurations/typescript-config) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/configurations/typescript-config) | -| | Integration/Styling and CSS | [See current documentation](./configure/styling-and-css.md) | See versioned documentation | See versioned documentation | -| | Integration/Images and assets | [See current documentation](./configure/images-and-assets.md) | See versioned documentation | See versioned documentation | -| | Story rendering | [See current documentation](./configure/story-rendering.md) | See versioned documentation [here](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/configurations/add-custom-head-tags) and [here](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/configurations/add-custom-body) | See versioned documentation [here](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/configurations/add-custom-head-tags) | -| | Story Layout | [See current documentation](./configure/story-layout.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | User Interface/Features and behavior | [See current documentation](./configure/features-and-behavior.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/configurations/options-parameter) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/configurations/options-parameter) | -| | User Interface/Theming | [See current documentation](./configure/theming.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/configurations/theming) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/configurations/theming) | -| | User Interface/Sidebar & URLS | [See current documentation](./configure/sidebar-and-urls.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/configurations/options-parameter) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/configurations/options-parameter) | -| | Environment variables | [See current documentation](./configure/environment-variables.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/configurations/env-vars) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/configurations/env-vars) | -| Builders | Introduction | [See current documentation](./builders/overview.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | Vite | [See current documentation](./builders/vite.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | Webpack | [See current documentation](./builders/webpack.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/configurations/custom-webpack-config/index.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/configurations/custom-webpack-config/index.md) | -| | Builder API | [See current documentation](./builders/builder-api.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| Addons | Introduction | [See current documentation](./addons/introduction.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/addons/writing-addons) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/addons/writing-addons) | -| | Install addons | [See current documentation](./addons/install-addons.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/addons/using-addons/) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/addons/using-addons/) | -| | Writing Addons | [See current documentation](./addons/writing-addons.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/addons/writing-addons) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/addons/writing-addons) | -| | Writing Presets | [See current documentation](./addons/writing-presets.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/presets/writing-presets) | Non existing feature or undocumented | -| | Addons Knowledge Base | [See current documentation](./addons/addon-knowledge-base.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/addons/writing-addons) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/addons/writing-addons) | -| | Types of addons | [See current documentation](./addons/addon-types.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | Addons API | [See current documentation](./addons/addons-api.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/addons/api) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/addons/api) | -| API | @storybook/blocks/ArgTypes | [See current documentation](./api/doc-block-argtypes.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | @storybook/blocks/Canvas | [See current documentation](./api/doc-block-canvas.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | @storybook/blocks/ColorPalette | [See current documentation](./api/doc-block-colorpalette.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | @storybook/blocks/Controls | [See current documentation](./api/doc-block-controls.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | @storybook/blocks/Description | [See current documentation](./api/doc-description.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | @storybook/blocks/IconGallery | [See current documentation](./api/doc-block-icongallery.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | @storybook/blocks/Markdown | [See current documentation](./api/doc-block-markdown.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | @storybook/blocks/Meta | [See current documentation](./api/doc-block-meta.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | @storybook/blocks/Primary | [See current documentation](./api/doc-block-primary.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | @storybook/blocks/Source | [See current documentation](./api/doc-block-source.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | @storybook/blocks/Stories | [See current documentation](./api/doc-block-stories.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | @storybook/blocks/Story | [See current documentation](./api/doc-block-story.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | @storybook/blocks/Subtitle | [See current documentation](./api/doc-block-subtitle.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | @storybook/blocks/Title | [See current documentation](./api/doc-block-title.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | @storybook/blocks/Typeset | [See current documentation](./api/doc-block-typeset.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | @storybook/blocks/Unstyled | [See current documentation](./api/doc-block-unstyled.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | @storybook/blocks/useOf | [See current documentation](./api/doc-block-useof.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | Stories/Component Story Format | [See current documentation](./api/csf.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/formats/component-story-format) | Non existing feature or undocumented | -| | Stories/StoriesOF format (see note below) | [See current documentation](https://github.com/storybookjs/storybook/blob/main/lib/core/docs/storiesOf.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/formats/storiesof-api) | Non existing feature or undocumented | -| | Frameworks | [See current documentation](./api/new-frameworks.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | CLI options | [See current documentation](./api/cli-options.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/configurations/cli-options) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/configurations/cli-options) | +| Section | Page | Current Location | Version 5.3 location | Version 5.0 location | +| ---------------- | -------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | +| N/A | Why Storybook | [See current documentation](./why-storybook.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| Get started | Install | [See current documentation](./get-started/install.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/guides/quick-start-guide) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/guides/quick-start-guide) | +| | What's a story | [See current documentation](./get-started/whats-a-story.md) | [See versioned documentation for your framework](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/guides) | [See versioned documentation for your framework](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/guides) | +| | Browse Stories | [See current documentation](./get-started/browse-stories.md) | [See versioned documentation for your framework](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/guides) | [See versioned documentation for your framework](https://github.com/storybookjs/storybook/blob/release/5.0/docs/src/pages/guides) | +| | Setup | [See current documentation](./get-started/setup.md) | [See versioned documentation for your framework](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/guides) | [See versioned documentation for your framework](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/guides) | +| Write stories | Introduction | [See current documentation](./writing-stories/introduction.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/basics/writing-stories) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/basics/writing-stories) | +| | Parameters | [See current documentation](./writing-stories/parameters.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/basics/writing-stories/index.md#parameters) | Non existing feature or undocumented | +| | Decorators | [See current documentation](./writing-stories/decorators.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/basics/writing-stories/index.md#decorators) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/basics/writing-stories/index.md#using-decorators) | +| | Naming components and hierarchy | [See current documentation](./writing-stories/naming-components-and-hierarchy.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/basics/writing-stories) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/basics/writing-stories) | +| | Build pages and screens | [See current documentation](./writing-stories/build-pages-with-storybook.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | Stories for multiple components | [See current documentation](./writing-stories/stories-for-multiple-components.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| Write docs | Autodocs | [See current documentation](./writing-docs/autodocs.md) | See versioned addon documentation | Non existing feature or undocumented | +| | MDX | [See current documentation](./writing-docs/mdx.md) | See versioned addon documentation | Non existing feature or undocumented | +| | Doc Blocks | [See current documentation](./writing-docs/doc-blocks.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | Preview and build docs | [See current documentation](./writing-docs/build-documentation.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| Testing | Visual tests | [See current documentation](./writing-tests/visual-testing.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/testing/automated-visual-testing) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/testing/automated-visual-testing) | +| | Accessibility tests | [See current documentation](./writing-tests/accessibility-testing.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | Interaction tests | [See current documentation](./writing-tests/interaction-testing.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/testing/interaction-testing) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/testing/interaction-testing) | +| | Snapshot tests | [See current documentation](./writing-tests/snapshot-testing.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/testing/structural-testing) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/testing/structural-testing) | +| | Import stories in tests/Unit tests | [See current documentation](./writing-tests/stories-in-unit-tests.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/testing/react-ui-testing) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/testing/react-ui-testing) | +| | Import stories in tests/End-to-end testing | [See current documentation](./writing-tests/stories-in-end-to-end-tests.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/testing/react-ui-testing) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/testing/react-ui-testing) | +| Sharing | Publish Storybook | [See current documentation](./sharing/publish-storybook.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/basics/exporting-storybook) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/basics/exporting-storybook) | +| | Embed | [See current documentation](./sharing/embed.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | Composition | [See current documentation](./sharing/storybook-composition.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | Package Composition | [See current documentation](./sharing/package-composition.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| Essential addons | Controls | [See current documentation](./essentials/controls.md) | Controls are specific to version 6.0 see [Knobs versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/addons/knobs) | Controls are specific to version 6.0 see [Knobs versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/addons/knobs) | +| | Actions | [See current documentation](./essentials/actions.md) | [See addon versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/addons/actions) | [See addon versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/addons/actions) | +| | Viewport | [See current documentation](./essentials/viewport.md) | [See addon versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/addons/viewport) | [See addon versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/addons/viewport) | +| | Backgrounds | [See current documentation](./essentials/backgrounds.md) | [See addon versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/addons/backgrounds) | [See addon versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/addons/backgrounds) | +| | Toolbars and globals | [See current documentation](./essentials/toolbars-and-globals.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/basics/toolbar-guide) | Non existing feature or undocumented | +| Configure | Overview | [See current documentation](./configure/overview.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/configurations/overview) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/basics/writing-stories) | +| | Integration/Frameworks | [See current documentation](./configure/frameworks.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | Integration/Framework support for frameworks | [See current documentation](./configure/frameworks-feature-support.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | Integration/Babel | [See current documentation](./configure/babel.md) | See versioned documentation [here](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/configurations/custom-babel-config) | See versioned documentation [here](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/configurations/custom-babel-config) | +| | Integration/Typescript | [See current documentation](./configure/typescript.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/configurations/typescript-config) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/configurations/typescript-config) | +| | Integration/Styling and CSS | [See current documentation](./configure/styling-and-css.md) | See versioned documentation | See versioned documentation | +| | Integration/Images and assets | [See current documentation](./configure/images-and-assets.md) | See versioned documentation | See versioned documentation | +| | Story rendering | [See current documentation](./configure/story-rendering.md) | See versioned documentation [here](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/configurations/add-custom-head-tags) and [here](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/configurations/add-custom-body) | See versioned documentation [here](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/configurations/add-custom-head-tags) | +| | Story Layout | [See current documentation](./configure/story-layout.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | User Interface/Features and behavior | [See current documentation](./configure/features-and-behavior.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/configurations/options-parameter) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/configurations/options-parameter) | +| | User Interface/Theming | [See current documentation](./configure/theming.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/configurations/theming) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/configurations/theming) | +| | User Interface/Sidebar & URLS | [See current documentation](./configure/sidebar-and-urls.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/configurations/options-parameter) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/configurations/options-parameter) | +| | Environment variables | [See current documentation](./configure/environment-variables.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/configurations/env-vars) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/configurations/env-vars) | +| Builders | Introduction | [See current documentation](./builders/overview.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | Vite | [See current documentation](./builders/vite.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | Webpack | [See current documentation](./builders/webpack.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/configurations/custom-webpack-config/index.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/configurations/custom-webpack-config/index.md) | +| | Builder API | [See current documentation](./builders/builder-api.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| Addons | Introduction | [See current documentation](./addons/introduction.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/addons/writing-addons) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/addons/writing-addons) | +| | Install addons | [See current documentation](./addons/install-addons.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/addons/using-addons/) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/addons/using-addons/) | +| | Writing Addons | [See current documentation](./addons/writing-addons.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/addons/writing-addons) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/addons/writing-addons) | +| | Writing Presets | [See current documentation](./addons/writing-presets.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/presets/writing-presets) | Non existing feature or undocumented | +| | Addons Knowledge Base | [See current documentation](./addons/addon-knowledge-base.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/addons/writing-addons) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/addons/writing-addons) | +| | Types of addons | [See current documentation](./addons/addon-types.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | Addons API | [See current documentation](./addons/addons-api.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/addons/api) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/addons/api) | +| API | @storybook/blocks/ArgTypes | [See current documentation](./api/doc-block-argtypes.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | @storybook/blocks/Canvas | [See current documentation](./api/doc-block-canvas.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | @storybook/blocks/ColorPalette | [See current documentation](./api/doc-block-colorpalette.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | @storybook/blocks/Controls | [See current documentation](./api/doc-block-controls.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | @storybook/blocks/Description | [See current documentation](./api/doc-description.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | @storybook/blocks/IconGallery | [See current documentation](./api/doc-block-icongallery.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | @storybook/blocks/Markdown | [See current documentation](./api/doc-block-markdown.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | @storybook/blocks/Meta | [See current documentation](./api/doc-block-meta.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | @storybook/blocks/Primary | [See current documentation](./api/doc-block-primary.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | @storybook/blocks/Source | [See current documentation](./api/doc-block-source.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | @storybook/blocks/Stories | [See current documentation](./api/doc-block-stories.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | @storybook/blocks/Story | [See current documentation](./api/doc-block-story.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | @storybook/blocks/Subtitle | [See current documentation](./api/doc-block-subtitle.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | @storybook/blocks/Title | [See current documentation](./api/doc-block-title.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | @storybook/blocks/Typeset | [See current documentation](./api/doc-block-typeset.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | @storybook/blocks/Unstyled | [See current documentation](./api/doc-block-unstyled.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | @storybook/blocks/useOf | [See current documentation](./api/doc-block-useof.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | Stories/Component Story Format | [See current documentation](./api/csf.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/formats/component-story-format) | Non existing feature or undocumented | +| | Stories/StoriesOF format (see note below) | [See current documentation](https://github.com/storybookjs/storybook/blob/main/code/lib/preview-api/docs/storiesOf.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/formats/storiesof-api) | Non existing feature or undocumented | +| | Frameworks | [See current documentation](./api/new-frameworks.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | CLI options | [See current documentation](./api/cli-options.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/configurations/cli-options) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/configurations/cli-options) |
With the release of version 5.3, we've updated how you can write your stories more compactly and easily. It doesn't mean that the storiesOf format has been removed. For the time being, we're still supporting it, and we have documentation for it. But be advised that this is bound to change in the future. diff --git a/docs/snippets/common/main-config-indexers-jsonstories.js.mdx b/docs/snippets/common/main-config-indexers-jsonstories.js.mdx new file mode 100644 index 000000000000..7cb2f94b111b --- /dev/null +++ b/docs/snippets/common/main-config-indexers-jsonstories.js.mdx @@ -0,0 +1,33 @@ +```js +// .storybook/main.js + +import fs from 'fs/promises'; + +const jsonStoriesIndexer = { + test: /stories\.json$/, + createIndex: async (fileName) => { + const content = JSON.parse(fs.readFileSync(fileName)); + + const stories = generateStoryIndexesFromJson(content); + + return stories.map((story) => { + type: 'story', + importPath: `virtual:jsonstories--${fileName}--${story.componentName}`, + exportName: story.name + }); + }, +}; + +const config = { + framework: '@storybook/your-framework', + stories: [ + '../src/**/*.mdx', + '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)', + // πŸ‘‡ Make sure files to index are included in `stories` + '../src/**/*.stories.json', + ], + experimental_indexers: async (existingIndexers) => [...existingIndexers, jsonStoriesIndexer]; +}; + +export default config; +``` diff --git a/docs/snippets/common/main-config-indexers-jsonstories.ts-4-9.mdx b/docs/snippets/common/main-config-indexers-jsonstories.ts-4-9.mdx new file mode 100644 index 000000000000..c979d2812e5e --- /dev/null +++ b/docs/snippets/common/main-config-indexers-jsonstories.ts-4-9.mdx @@ -0,0 +1,37 @@ +```ts +// .storybook/main.ts + +// Replace your-framework with the framework you are using (e.g., react-webpack5, vue3-vite) +import type { StorybookConfig } from '@storybook/your-framework'; +import type { Indexer } from '@storybook/types'; + +import fs from 'fs/promises'; + +const jsonStoriesIndexer: Indexer = { + test: /stories\.json$/, + createIndex: async (fileName) => { + const content = JSON.parse(fs.readFileSync(fileName)); + + const stories = generateStoryIndexesFromJson(content); + + return stories.map((story) => { + type: 'story', + importPath: `virtual:jsonstories--${fileName}--${story.componentName}`, + exportName: story.name + }); + }, +}; + +const config: StorybookConfig = { + framework: '@storybook/your-framework', + stories: [ + '../src/**/*.mdx', + '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)', + // πŸ‘‡ Make sure files to index are included in `stories` + '../src/**/*.stories.json', + ], + experimental_indexers: async (existingIndexers) => [...existingIndexers, jsonStoriesIndexer]; +}; + +export default config; +``` diff --git a/docs/snippets/common/main-config-indexers-jsonstories.ts.mdx b/docs/snippets/common/main-config-indexers-jsonstories.ts.mdx new file mode 100644 index 000000000000..c979d2812e5e --- /dev/null +++ b/docs/snippets/common/main-config-indexers-jsonstories.ts.mdx @@ -0,0 +1,37 @@ +```ts +// .storybook/main.ts + +// Replace your-framework with the framework you are using (e.g., react-webpack5, vue3-vite) +import type { StorybookConfig } from '@storybook/your-framework'; +import type { Indexer } from '@storybook/types'; + +import fs from 'fs/promises'; + +const jsonStoriesIndexer: Indexer = { + test: /stories\.json$/, + createIndex: async (fileName) => { + const content = JSON.parse(fs.readFileSync(fileName)); + + const stories = generateStoryIndexesFromJson(content); + + return stories.map((story) => { + type: 'story', + importPath: `virtual:jsonstories--${fileName}--${story.componentName}`, + exportName: story.name + }); + }, +}; + +const config: StorybookConfig = { + framework: '@storybook/your-framework', + stories: [ + '../src/**/*.mdx', + '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)', + // πŸ‘‡ Make sure files to index are included in `stories` + '../src/**/*.stories.json', + ], + experimental_indexers: async (existingIndexers) => [...existingIndexers, jsonStoriesIndexer]; +}; + +export default config; +``` diff --git a/docs/snippets/common/main-config-indexers-title.js.mdx b/docs/snippets/common/main-config-indexers-title.js.mdx new file mode 100644 index 000000000000..0f13714619f3 --- /dev/null +++ b/docs/snippets/common/main-config-indexers-title.js.mdx @@ -0,0 +1,29 @@ +```js +// .storybook/main.ts + +const combosIndexer = { + test: /\.stories\.[tj]sx?$/, + createIndex: async (fileName, { makeTitle }) => { + // πŸ‘‡ Grab title from fileName + const title = fileName.match(/\/(.*)\.stories/)[1]; + + // Read file and generate entries ... + + return entries.map((entry) => ({ + type: 'story', + // πŸ‘‡ Use makeTitle to format the title + title: `${makeTitle(title)} Custom`, + importPath: fileName, + exportName: entry.name, + })); + }, +}; + +const config = { + framework: '@storybook/your-framework', + stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'], + experimental_indexers: async (existingIndexers) => [...existingIndexers, combosIndexer]; +}; + +export default config; +``` diff --git a/docs/snippets/common/main-config-indexers-title.ts-4-9.mdx b/docs/snippets/common/main-config-indexers-title.ts-4-9.mdx new file mode 100644 index 000000000000..b8b55ac1f3fa --- /dev/null +++ b/docs/snippets/common/main-config-indexers-title.ts-4-9.mdx @@ -0,0 +1,33 @@ +```ts +// .storybook/main.ts + +// Replace your-framework with the framework you are using (e.g., react-webpack5, vue3-vite) +import type { StorybookConfig } from '@storybook/your-framework'; +import type { Indexer } from '@storybook/types'; + +const combosIndexer: Indexer = { + test: /\.stories\.[tj]sx?$/, + createIndex: async (fileName, { makeTitle }) => { + // πŸ‘‡ Grab title from fileName + const title = fileName.match(/\/(.*)\.stories/)[1]; + + // Read file and generate entries ... + + return entries.map((entry) => ({ + type: 'story', + // πŸ‘‡ Use makeTitle to format the title + title: `${makeTitle(title)} Custom`, + importPath: fileName, + exportName: entry.name, + })); + }, +}; + +const config: StorybookConfig = { + framework: '@storybook/your-framework', + stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'], + experimental_indexers: async (existingIndexers) => [...existingIndexers, combosIndexer]; +}; + +export default config; +``` diff --git a/docs/snippets/common/main-config-indexers-title.ts.mdx b/docs/snippets/common/main-config-indexers-title.ts.mdx new file mode 100644 index 000000000000..b8b55ac1f3fa --- /dev/null +++ b/docs/snippets/common/main-config-indexers-title.ts.mdx @@ -0,0 +1,33 @@ +```ts +// .storybook/main.ts + +// Replace your-framework with the framework you are using (e.g., react-webpack5, vue3-vite) +import type { StorybookConfig } from '@storybook/your-framework'; +import type { Indexer } from '@storybook/types'; + +const combosIndexer: Indexer = { + test: /\.stories\.[tj]sx?$/, + createIndex: async (fileName, { makeTitle }) => { + // πŸ‘‡ Grab title from fileName + const title = fileName.match(/\/(.*)\.stories/)[1]; + + // Read file and generate entries ... + + return entries.map((entry) => ({ + type: 'story', + // πŸ‘‡ Use makeTitle to format the title + title: `${makeTitle(title)} Custom`, + importPath: fileName, + exportName: entry.name, + })); + }, +}; + +const config: StorybookConfig = { + framework: '@storybook/your-framework', + stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'], + experimental_indexers: async (existingIndexers) => [...existingIndexers, combosIndexer]; +}; + +export default config; +``` diff --git a/docs/snippets/common/main-config-indexers.js.mdx b/docs/snippets/common/main-config-indexers.js.mdx new file mode 100644 index 000000000000..7d2b4dcd03fb --- /dev/null +++ b/docs/snippets/common/main-config-indexers.js.mdx @@ -0,0 +1,21 @@ +```js +// .storybook/main.js + +export default { + // Replace your-framework with the framework you are using (e.g., react-webpack5, vue3-vite) + framework: '@storybook/your-framework', + stories: [ + '../src/**/*.mdx', + '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)', + // πŸ‘‡ Make sure files to index are included in `stories` + '../src/**/*.custom-stories.@(js|jsx|ts|tsx)', + ], + experimental_indexers: async (existingIndexers) => { + const customIndexer = { + test: /\.custom-stories\.[tj]sx?$/, + createIndex: // See API and examples below... + }; + return [...existingIndexers, customIndexer]; + }, +}; +``` diff --git a/docs/snippets/common/main-config-indexers.ts-4-9.mdx b/docs/snippets/common/main-config-indexers.ts-4-9.mdx new file mode 100644 index 000000000000..3ceccda77765 --- /dev/null +++ b/docs/snippets/common/main-config-indexers.ts-4-9.mdx @@ -0,0 +1,25 @@ +```ts +// .storybook/main.ts + +// Replace your-framework with the framework you are using (e.g., react-webpack5, vue3-vite) +import type { StorybookConfig } from '@storybook/your-framework'; + +const config: StorybookConfig = { + framework: '@storybook/your-framework', + stories: [ + '../src/**/*.mdx', + '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)', + // πŸ‘‡ Make sure files to index are included in `stories` + '../src/**/*.custom-stories.@(js|jsx|ts|tsx)', + ], + experimental_indexers: async (existingIndexers) => { + const customIndexer = { + test: /\.custom-stories\.[tj]sx?$/, + createIndex: // See API and examples below... + }; + return [...existingIndexers, customIndexer]; + }, +}; + +export default config +``` diff --git a/docs/snippets/common/main-config-indexers.ts.mdx b/docs/snippets/common/main-config-indexers.ts.mdx new file mode 100644 index 000000000000..3ceccda77765 --- /dev/null +++ b/docs/snippets/common/main-config-indexers.ts.mdx @@ -0,0 +1,25 @@ +```ts +// .storybook/main.ts + +// Replace your-framework with the framework you are using (e.g., react-webpack5, vue3-vite) +import type { StorybookConfig } from '@storybook/your-framework'; + +const config: StorybookConfig = { + framework: '@storybook/your-framework', + stories: [ + '../src/**/*.mdx', + '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)', + // πŸ‘‡ Make sure files to index are included in `stories` + '../src/**/*.custom-stories.@(js|jsx|ts|tsx)', + ], + experimental_indexers: async (existingIndexers) => { + const customIndexer = { + test: /\.custom-stories\.[tj]sx?$/, + createIndex: // See API and examples below... + }; + return [...existingIndexers, customIndexer]; + }, +}; + +export default config +``` diff --git a/docs/snippets/common/storybook-main-csf-indexer.ts.mdx b/docs/snippets/common/storybook-main-csf-indexer.ts.mdx deleted file mode 100644 index 09f6f85f02e9..000000000000 --- a/docs/snippets/common/storybook-main-csf-indexer.ts.mdx +++ /dev/null @@ -1,23 +0,0 @@ -```ts -// .storybook/main.ts - -import { readFileSync } from 'fs'; -import { loadCsf } from '@storybook/csf-tools'; - -export default { - storyIndexers = (indexers) => { - const indexer = async (fileName, opts) => { - const code = readFileSync(fileName, { encoding: 'utf-8' }); - return loadCsf(code, { ...opts, fileName }).parse(); - }; - - return [ - { - test: /(stories|story)\.[tj]sx?$/, - indexer, - }, - ...(indexers || []), - ]; - }, -}; -``` diff --git a/docs/snippets/common/storybook-main-story-indexer-main.ts.mdx b/docs/snippets/common/storybook-main-story-indexer-main.ts.mdx deleted file mode 100644 index 7c4885028a5d..000000000000 --- a/docs/snippets/common/storybook-main-story-indexer-main.ts.mdx +++ /dev/null @@ -1,35 +0,0 @@ -```ts -// .storybook/main.ts - -// Replace your-framework with the framework you are using (e.g., angular, react-webpack5, vue3-webpack5) -import type { StorybookConfig } from '@storybook/your-framework'; - -// this can be as easy as fs.readFileSync and return the string -// or as complex as creating a new MDX file with the content of the original file -import { parseCode } from './parseCode'; - -const config: StorybookConfig = { - storyIndexers: (indexers, addonOptions) => { - const indexer = async (fileName, compilationOptions) => { - const code = parseCode(fileName, addonOptions); - const makeTitle = (userTitle) => { - // Do something with the auto title retrieved by Storybook - return userTitle; - }; - - // Parse the CSF file with makeTitle as a custom context - return loadCsf(code, { ...compilationOptions, makeTitle, fileName }).parse(); - }; - - return [ - { - test: /\.(md|html)$/, - indexer, - }, - ...(indexers || []), - ]; - }, -}; - -export default config; -``` diff --git a/docs/toc.js b/docs/toc.js index 7093616940f1..9c773a493244 100644 --- a/docs/toc.js +++ b/docs/toc.js @@ -527,6 +527,11 @@ module.exports = { pathSegment: 'main-config-features', type: 'link', }, + { + title: 'indexers', + pathSegment: 'main-config-indexers', + type: 'link', + }, { title: 'logLevel', pathSegment: 'main-config-log-level',