From 82b63d545902874ae7b5bf82af0ef7bfda518cd4 Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Wed, 24 Jan 2024 14:25:25 +0100 Subject: [PATCH 01/18] Update StorybookConfig import in core-webpack types.ts --- code/lib/core-webpack/src/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/lib/core-webpack/src/types.ts b/code/lib/core-webpack/src/types.ts index 01a68acfa950..bb7be85ffa5d 100644 --- a/code/lib/core-webpack/src/types.ts +++ b/code/lib/core-webpack/src/types.ts @@ -1,4 +1,4 @@ -import type { Options, StorybookConfigRaw as StorybookConfigBase } from '@storybook/types'; +import type { Options, StorybookConfig as StorybookConfigBase } from '@storybook/types'; export type { Options, Preset, BuilderResult, TypescriptOptions } from '@storybook/types'; From ec359a442de6623a06c311a06f4af1438fb702ea Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Thu, 25 Jan 2024 10:51:09 +0100 Subject: [PATCH 02/18] Fix types in presets --- code/builders/builder-webpack5/src/types.ts | 5 ++- code/frameworks/angular/src/preset.ts | 4 +- code/frameworks/ember/src/preset.ts | 2 +- code/frameworks/html-vite/src/preset.ts | 3 +- code/frameworks/nextjs/src/preset.ts | 28 ------------- code/frameworks/preact-vite/src/preset.ts | 2 +- code/frameworks/react-vite/src/preset.ts | 2 +- code/frameworks/react-webpack5/src/preset.ts | 40 ++----------------- code/frameworks/svelte-vite/src/preset.ts | 2 +- code/frameworks/vue3-webpack5/src/preset.ts | 5 +-- .../web-components-vite/src/preset.ts | 3 +- 11 files changed, 16 insertions(+), 80 deletions(-) diff --git a/code/builders/builder-webpack5/src/types.ts b/code/builders/builder-webpack5/src/types.ts index 6e55f145ca22..38621af56274 100644 --- a/code/builders/builder-webpack5/src/types.ts +++ b/code/builders/builder-webpack5/src/types.ts @@ -3,11 +3,12 @@ import type { Options, BuilderResult as BuilderResultBase, StorybookConfig, + TypescriptOptions as WebpackTypescriptOptions, } from '@storybook/core-webpack'; import type ForkTsCheckerWebpackPlugin from 'fork-ts-checker-webpack-plugin'; -type TypeScriptOptionsBase = Required['typescript']; +type TypeScriptOptionsBase = Partial; /** * Options for TypeScript usage within Storybook. @@ -19,7 +20,7 @@ export interface TypescriptOptions extends TypeScriptOptionsBase { checkOptions?: ConstructorParameters[0]; } -export interface StorybookConfigWebpack extends Pick { +export interface StorybookConfigWebpack extends Omit { /** * Modify or return a custom Webpack config after the Storybook's default configuration * has run (mostly used by addons). diff --git a/code/frameworks/angular/src/preset.ts b/code/frameworks/angular/src/preset.ts index da3bb0192f0d..139b5477110e 100644 --- a/code/frameworks/angular/src/preset.ts +++ b/code/frameworks/angular/src/preset.ts @@ -22,7 +22,7 @@ export const previewAnnotations: PresetProperty<'previewAnnotations'> = (entries return annotations; }; -export const core: PresetProperty<'core', StorybookConfig> = async (config, options) => { +export const core: PresetProperty<'core'> = async (config, options) => { const framework = await options.presets.apply('framework'); return { @@ -34,7 +34,7 @@ export const core: PresetProperty<'core', StorybookConfig> = async (config, opti }; }; -export const typescript: PresetProperty<'typescript', StorybookConfig> = async (config) => { +export const typescript: PresetProperty<'typescript'> = async (config) => { return { ...config, skipCompiler: true, diff --git a/code/frameworks/ember/src/preset.ts b/code/frameworks/ember/src/preset.ts index 9de2b1ab35c0..7f0a07ce7cbb 100644 --- a/code/frameworks/ember/src/preset.ts +++ b/code/frameworks/ember/src/preset.ts @@ -43,7 +43,7 @@ export const webpackFinal: StorybookConfig['webpackFinal'] = async (baseConfig, }; }; -export const core: PresetProperty<'core', StorybookConfig> = async (config, options) => { +export const core: PresetProperty<'core'> = async (config, options) => { const framework = await options.presets.apply('framework'); return { diff --git a/code/frameworks/html-vite/src/preset.ts b/code/frameworks/html-vite/src/preset.ts index 4ae871b6e589..965ba77956a0 100644 --- a/code/frameworks/html-vite/src/preset.ts +++ b/code/frameworks/html-vite/src/preset.ts @@ -1,12 +1,11 @@ import type { PresetProperty } from '@storybook/types'; import { dirname, join } from 'path'; -import type { StorybookConfig } from './types'; function getAbsolutePath(value: I): I { return dirname(require.resolve(join(value, 'package.json'))) as any; } -export const core: PresetProperty<'core', StorybookConfig> = { +export const core: PresetProperty<'core'> = { builder: getAbsolutePath('@storybook/builder-vite'), renderer: getAbsolutePath('@storybook/html'), }; diff --git a/code/frameworks/nextjs/src/preset.ts b/code/frameworks/nextjs/src/preset.ts index 95f4cc062702..2ea65b700691 100644 --- a/code/frameworks/nextjs/src/preset.ts +++ b/code/frameworks/nextjs/src/preset.ts @@ -27,33 +27,6 @@ export const addons: PresetProperty<'addons'> = [ dirname(require.resolve(join('@storybook/preset-react-webpack', 'package.json'))), ]; -const defaultFrameworkOptions: FrameworkOptions = {}; - -export const frameworkOptions: PresetProperty<'framework'> = async (_, options) => { - const config = await options.presets.apply('framework'); - - if (typeof config === 'string') { - return { - name: config, - options: defaultFrameworkOptions, - }; - } - if (typeof config === 'undefined') { - return { - name: require.resolve('@storybook/nextjs') as '@storybook/nextjs', - options: defaultFrameworkOptions, - }; - } - - return { - name: config.name, - options: { - ...defaultFrameworkOptions, - ...config.options, - }, - }; -}; - export const core: PresetProperty<'core'> = async (config, options) => { const framework = await options.presets.apply('framework'); @@ -136,7 +109,6 @@ export const babel: PresetProperty<'babel'> = async (baseConfig: TransformOption }; export const webpackFinal: StorybookConfig['webpackFinal'] = async (baseConfig, options) => { - // eslint-disable-next-line @typescript-eslint/no-shadow const frameworkOptions = await options.presets.apply<{ options: FrameworkOptions }>( 'frameworkOptions' ); diff --git a/code/frameworks/preact-vite/src/preset.ts b/code/frameworks/preact-vite/src/preset.ts index 4002182ea926..768ef8a60924 100644 --- a/code/frameworks/preact-vite/src/preset.ts +++ b/code/frameworks/preact-vite/src/preset.ts @@ -5,7 +5,7 @@ import type { StorybookConfig } from './types'; const getAbsolutePath = (input: I): I => dirname(require.resolve(join(input, 'package.json'))) as any; -export const core: PresetProperty<'core', StorybookConfig> = { +export const core: PresetProperty<'core'> = { builder: getAbsolutePath('@storybook/builder-vite'), renderer: getAbsolutePath('@storybook/preact'), }; diff --git a/code/frameworks/react-vite/src/preset.ts b/code/frameworks/react-vite/src/preset.ts index 638a006ad9ce..35a83a306ce0 100644 --- a/code/frameworks/react-vite/src/preset.ts +++ b/code/frameworks/react-vite/src/preset.ts @@ -5,7 +5,7 @@ import type { StorybookConfig } from './types'; const getAbsolutePath = (input: I): I => dirname(require.resolve(join(input, 'package.json'))) as any; -export const core: PresetProperty<'core', StorybookConfig> = { +export const core: PresetProperty<'core'> = { builder: getAbsolutePath('@storybook/builder-vite'), renderer: getAbsolutePath('@storybook/react'), }; diff --git a/code/frameworks/react-webpack5/src/preset.ts b/code/frameworks/react-webpack5/src/preset.ts index 18444c990380..80bf2fdce588 100644 --- a/code/frameworks/react-webpack5/src/preset.ts +++ b/code/frameworks/react-webpack5/src/preset.ts @@ -1,6 +1,6 @@ import { dirname, join } from 'path'; -import type { PresetProperty, Options } from '@storybook/types'; -import type { FrameworkOptions, StorybookConfig } from './types'; +import type { PresetProperty } from '@storybook/types'; +import type { StorybookConfig } from './types'; const getAbsolutePath = (input: I): I => dirname(require.resolve(join(input, 'package.json'))) as any; @@ -9,46 +9,14 @@ export const addons: PresetProperty<'addons'> = [ getAbsolutePath('@storybook/preset-react-webpack'), ]; -const defaultFrameworkOptions: FrameworkOptions = { - legacyRootApi: true, -}; - -export const frameworkOptions = async ( - _: never, - options: Options -): Promise => { - const config = await options.presets.apply('framework'); - - if (typeof config === 'string') { - return { - name: config, - options: defaultFrameworkOptions, - }; - } - if (typeof config === 'undefined') { - return { - name: getAbsolutePath('@storybook/react-webpack5'), - options: defaultFrameworkOptions, - }; - } - - return { - name: config.name, - options: { - ...defaultFrameworkOptions, - ...config.options, - }, - }; -}; - export const core: PresetProperty<'core'> = async (config, options) => { - const framework = await options.presets.apply('framework'); + const presetFramework = await options.presets.apply('framework'); return { ...config, builder: { name: getAbsolutePath('@storybook/builder-webpack5'), - options: typeof framework === 'string' ? {} : framework.options.builder || {}, + options: typeof presetFramework === 'string' ? {} : presetFramework.options.builder || {}, }, renderer: getAbsolutePath('@storybook/react'), }; diff --git a/code/frameworks/svelte-vite/src/preset.ts b/code/frameworks/svelte-vite/src/preset.ts index 9ef71bb26e89..e487cc2a1c58 100644 --- a/code/frameworks/svelte-vite/src/preset.ts +++ b/code/frameworks/svelte-vite/src/preset.ts @@ -7,7 +7,7 @@ import { svelteDocgen } from './plugins/svelte-docgen'; const getAbsolutePath = (input: I): I => dirname(require.resolve(join(input, 'package.json'))) as any; -export const core: PresetProperty<'core', StorybookConfig> = { +export const core: PresetProperty<'core'> = { builder: getAbsolutePath('@storybook/builder-vite'), renderer: getAbsolutePath('@storybook/svelte'), }; diff --git a/code/frameworks/vue3-webpack5/src/preset.ts b/code/frameworks/vue3-webpack5/src/preset.ts index 1a46a7574516..a19cb8e0d8ee 100644 --- a/code/frameworks/vue3-webpack5/src/preset.ts +++ b/code/frameworks/vue3-webpack5/src/preset.ts @@ -1,13 +1,10 @@ import { dirname, join } from 'path'; import type { PresetProperty } from '@storybook/types'; -import type { StorybookConfig } from './types'; const getAbsolutePath = (input: I): I => dirname(require.resolve(join(input, 'package.json'))) as any; -export const addons: PresetProperty<'addons', StorybookConfig> = [ - getAbsolutePath('@storybook/preset-vue3-webpack'), -]; +export const addons: PresetProperty<'addons'> = [getAbsolutePath('@storybook/preset-vue3-webpack')]; export const core: PresetProperty<'core'> = async (config, options) => { const framework = await options.presets.apply('framework'); diff --git a/code/frameworks/web-components-vite/src/preset.ts b/code/frameworks/web-components-vite/src/preset.ts index a3bb7c4d66a2..2edf8f450c75 100644 --- a/code/frameworks/web-components-vite/src/preset.ts +++ b/code/frameworks/web-components-vite/src/preset.ts @@ -1,11 +1,10 @@ import type { PresetProperty } from '@storybook/types'; import { dirname, join } from 'path'; -import type { StorybookConfig } from './types'; const getAbsolutePath = (input: I): I => dirname(require.resolve(join(input, 'package.json'))) as any; -export const core: PresetProperty<'core', StorybookConfig> = { +export const core: PresetProperty<'core'> = { builder: getAbsolutePath('@storybook/builder-vite'), renderer: getAbsolutePath('@storybook/web-components'), }; From dec75ae18edaa6502eaecb09850f6f5153d0483c Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Fri, 26 Jan 2024 15:18:20 +0100 Subject: [PATCH 03/18] Refactor preset.ts to use 'framework' instead of 'presetFramework' variable --- code/frameworks/react-webpack5/src/preset.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/frameworks/react-webpack5/src/preset.ts b/code/frameworks/react-webpack5/src/preset.ts index 80bf2fdce588..eae5bc696986 100644 --- a/code/frameworks/react-webpack5/src/preset.ts +++ b/code/frameworks/react-webpack5/src/preset.ts @@ -10,13 +10,13 @@ export const addons: PresetProperty<'addons'> = [ ]; export const core: PresetProperty<'core'> = async (config, options) => { - const presetFramework = await options.presets.apply('framework'); + const framework = await options.presets.apply('framework'); return { ...config, builder: { name: getAbsolutePath('@storybook/builder-webpack5'), - options: typeof presetFramework === 'string' ? {} : presetFramework.options.builder || {}, + options: typeof framework === 'string' ? {} : framework.options.builder || {}, }, renderer: getAbsolutePath('@storybook/react'), }; From fea8bb5a25ddd5eceb40ca0939fee911dac586f9 Mon Sep 17 00:00:00 2001 From: Kyle Gach Date: Mon, 29 Jan 2024 14:03:30 -0700 Subject: [PATCH 04/18] Fix bad docs paths --- docs/api/index.md | 2 +- docs/api/parameters.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/api/index.md b/docs/api/index.md index 168f0d0456a4..60edd614c32c 100644 --- a/docs/api/index.md +++ b/docs/api/index.md @@ -82,7 +82,7 @@ An overview of all available API references for Storybook. Parameters - Parameters are static metadata used to configure your stories addons in Storybook. They are specified at the story, meta (component), project (global) levels. + Parameters are static metadata used to configure your stories addons in Storybook. They are specified at the story, meta (component), project (global) levels. diff --git a/docs/api/parameters.md b/docs/api/parameters.md index 53174e387492..70c555c61783 100644 --- a/docs/api/parameters.md +++ b/docs/api/parameters.md @@ -2,7 +2,7 @@ title: 'Parameters' --- -Parameters are static metadata used to configure your [stories](../get-started/whats-a-story.md) and [addons](../addons/introduction.md) in Storybook. They are specified at the story, meta (component), project (global) levels. +Parameters are static metadata used to configure your [stories](../get-started/whats-a-story.md) and [addons](../addons/index.md) in Storybook. They are specified at the story, meta (component), project (global) levels. ## Story parameters @@ -36,7 +36,7 @@ Parameters specified at the story level apply to that story only. They are defin -Parameter's specified in a [CSF](../writing-stories/introduction.md#component-story-format-csf) file's meta configuration apply to all stories in that file. They are defined in the `parameters` property of the `meta` (default export): +Parameter's specified in a [CSF](../writing-stories/index.md#component-story-format-csf) file's meta configuration apply to all stories in that file. They are defined in the `parameters` property of the `meta` (default export): From 8ab95188f9ce9a79c251dacd4675f4f8084d6e2c Mon Sep 17 00:00:00 2001 From: Kyle Gach Date: Mon, 29 Jan 2024 14:06:52 -0700 Subject: [PATCH 05/18] Re-document subcomponents - Largely reverted relevant portions of #20233 & #21742 --- .../list-story-with-subcomponents.ts.mdx | 50 ++++++++++++++++++ .../common/subcomponents-in-meta.js.mdx | 10 ++++ .../common/subcomponents-in-meta.ts-4-9.mdx | 15 ++++++ .../common/subcomponents-in-meta.ts.mdx | 15 ++++++ .../list-story-with-subcomponents.js.mdx | 28 ++++++++++ .../list-story-with-subcomponents.ts-4-9.mdx | 32 +++++++++++ .../list-story-with-subcomponents.ts.mdx | 31 +++++++++++ .../vue/list-story-with-sub-components.js.mdx | 35 ++++++++++++ .../list-story-with-sub-components.ts-4-9.mdx | 41 ++++++++++++++ .../vue/list-story-with-sub-components.ts.mdx | 41 ++++++++++++++ .../list-story-with-subcomponents.js.mdx | 21 ++++++++ .../list-story-with-subcomponents.ts.mdx | 26 +++++++++ docs/writing-docs/autodocs.md | 23 ++++++++ .../doc-block-arg-types-subcomponents.png | Bin 0 -> 10007 bytes ...block-arg-types-subcomponents-for-list.png | Bin 0 -> 16815 bytes .../stories-for-multiple-components.md | 33 +++++++++++- 16 files changed, 399 insertions(+), 2 deletions(-) create mode 100644 docs/snippets/angular/list-story-with-subcomponents.ts.mdx create mode 100644 docs/snippets/common/subcomponents-in-meta.js.mdx create mode 100644 docs/snippets/common/subcomponents-in-meta.ts-4-9.mdx create mode 100644 docs/snippets/common/subcomponents-in-meta.ts.mdx create mode 100644 docs/snippets/react/list-story-with-subcomponents.js.mdx create mode 100644 docs/snippets/react/list-story-with-subcomponents.ts-4-9.mdx create mode 100644 docs/snippets/react/list-story-with-subcomponents.ts.mdx create mode 100644 docs/snippets/vue/list-story-with-sub-components.js.mdx create mode 100644 docs/snippets/vue/list-story-with-sub-components.ts-4-9.mdx create mode 100644 docs/snippets/vue/list-story-with-sub-components.ts.mdx create mode 100644 docs/snippets/web-components/list-story-with-subcomponents.js.mdx create mode 100644 docs/snippets/web-components/list-story-with-subcomponents.ts.mdx create mode 100644 docs/writing-docs/doc-block-arg-types-subcomponents.png create mode 100644 docs/writing-stories/doc-block-arg-types-subcomponents-for-list.png diff --git a/docs/snippets/angular/list-story-with-subcomponents.ts.mdx b/docs/snippets/angular/list-story-with-subcomponents.ts.mdx new file mode 100644 index 000000000000..17e8ab2e60aa --- /dev/null +++ b/docs/snippets/angular/list-story-with-subcomponents.ts.mdx @@ -0,0 +1,50 @@ +```ts +// List.stories.ts + +import type { Meta, StoryObj } from '@storybook/angular'; + +import { moduleMetadata } from '@storybook/angular'; + +import { CommonModule } from '@angular/common'; + +import { List } from './list.component'; +import { ListItem } from './list-item.component'; + +const meta: Meta = { + /* πŸ‘‡ The title prop is optional. + * See https://storybook.js.org/docs/7.0/angular/configure/overview#configure-story-loading + * to learn how to generate automatic titles + */ + title: 'List', + component: List, + subcomponents: { ListItem }, //πŸ‘ˆ Adds the ListItem component as a subcomponent + decorators: [ + moduleMetadata({ + declarations: [List, ListItem], + imports: [CommonModule], + }), + ], +}; + +export default meta; +type Story = StoryObj; + +/* + *πŸ‘‡ Render functions are a framework specific feature to allow you control on how the component renders. + * See https://storybook.js.org/docs/7.0/angular/api/csf + * to learn how to use render functions. + */ +export const Empty: Story = {}; + +export const OneItem: Story = { + args: {}, + render: (args) => ({ + props: args, + template: ` + + + + `, + }), +}; +``` diff --git a/docs/snippets/common/subcomponents-in-meta.js.mdx b/docs/snippets/common/subcomponents-in-meta.js.mdx new file mode 100644 index 000000000000..c925367d0b09 --- /dev/null +++ b/docs/snippets/common/subcomponents-in-meta.js.mdx @@ -0,0 +1,10 @@ +```js +// ButtonGroup.stories.js|jsx + +import { Button, ButtonGroup } from './ButtonGroup'; + +export default { + component: ButtonGroup, + subcomponents: { Button }, +}; +``` diff --git a/docs/snippets/common/subcomponents-in-meta.ts-4-9.mdx b/docs/snippets/common/subcomponents-in-meta.ts-4-9.mdx new file mode 100644 index 000000000000..49f5deefb5ca --- /dev/null +++ b/docs/snippets/common/subcomponents-in-meta.ts-4-9.mdx @@ -0,0 +1,15 @@ +```ts +// ButtonGroup.stories.ts|tsx + +// Replace your-framework with the name of your framework +import type { Meta, StoryObj } from '@storybook/your-framework'; + +import { Button, ButtonGroup } from './ButtonGroup'; + +const meta = { + component: ButtonGroup, + subcomponents: { Button }, +} satisfies Meta; + +export default meta; +``` \ No newline at end of file diff --git a/docs/snippets/common/subcomponents-in-meta.ts.mdx b/docs/snippets/common/subcomponents-in-meta.ts.mdx new file mode 100644 index 000000000000..8847609e9fac --- /dev/null +++ b/docs/snippets/common/subcomponents-in-meta.ts.mdx @@ -0,0 +1,15 @@ +```ts +// ButtonGroup.stories.ts|tsx + +// Replace your-framework with the name of your framework +import type { Meta } from '@storybook/your-framework'; + +import { Button, ButtonGroup } from './ButtonGroup'; + +const meta: Meta = { + component: ButtonGroup, + subcomponents: { Button }, +}; + +export default meta; +``` \ No newline at end of file diff --git a/docs/snippets/react/list-story-with-subcomponents.js.mdx b/docs/snippets/react/list-story-with-subcomponents.js.mdx new file mode 100644 index 000000000000..1bae39ac9a4e --- /dev/null +++ b/docs/snippets/react/list-story-with-subcomponents.js.mdx @@ -0,0 +1,28 @@ +```js +// List.stories.js|jsx + +import React from 'react'; + +import { List } from './List'; +import { ListItem } from './ListItem'; + +export default { + /* πŸ‘‡ The title prop is optional. + * See https://storybook.js.org/docs/7.0/react/configure/overview#configure-story-loading + * to learn how to generate automatic titles + */ + title: 'List', + component: List, + subcomponents: { ListItem }, //πŸ‘ˆ Adds the ListItem component as a subcomponent +}; + +export const Empty = {}; + +export const OneItem = { + render: (args) => ( + + + + ), +}; +``` diff --git a/docs/snippets/react/list-story-with-subcomponents.ts-4-9.mdx b/docs/snippets/react/list-story-with-subcomponents.ts-4-9.mdx new file mode 100644 index 000000000000..71abc96d3c2c --- /dev/null +++ b/docs/snippets/react/list-story-with-subcomponents.ts-4-9.mdx @@ -0,0 +1,32 @@ +```ts +// List.stories.ts|tsx + +import type { Meta, StoryObj } from '@storybook/react'; + +import { List } from './List'; +import { ListItem } from './ListItem'; + +const meta = { + /* πŸ‘‡ The title prop is optional. + * See https://storybook.js.org/docs/7.0/react/configure/overview#configure-story-loading + * to learn how to generate automatic titles + */ + title: 'List', + component: List, + //πŸ‘ˆ Adds the ListItem component as a subcomponent + subcomponents: { ListItem }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Empty: Story = {}; + +export const OneItem: Story = { + render: (args) => ( + + + + ), +}; +``` diff --git a/docs/snippets/react/list-story-with-subcomponents.ts.mdx b/docs/snippets/react/list-story-with-subcomponents.ts.mdx new file mode 100644 index 000000000000..dcb959020f98 --- /dev/null +++ b/docs/snippets/react/list-story-with-subcomponents.ts.mdx @@ -0,0 +1,31 @@ +```tsx +// List.stories.ts|tsx + +import type { Meta, StoryObj } from '@storybook/react'; + +import { List } from './List'; +import { ListItem } from './ListItem'; + +const meta: Meta = { + /* πŸ‘‡ The title prop is optional. + * See https://storybook.js.org/docs/7.0/react/configure/overview#configure-story-loading + * to learn how to generate automatic titles + */ + title: 'List', + component: List, + subcomponents: { ListItem }, //πŸ‘ˆ Adds the ListItem component as a subcomponent +}; + +export default meta; +type Story = StoryObj; + +export const Empty: Story = {}; + +export const OneItem: Story = { + render: (args) => ( + + + + ), +}; +``` diff --git a/docs/snippets/vue/list-story-with-sub-components.js.mdx b/docs/snippets/vue/list-story-with-sub-components.js.mdx new file mode 100644 index 000000000000..9d674dbfebf4 --- /dev/null +++ b/docs/snippets/vue/list-story-with-sub-components.js.mdx @@ -0,0 +1,35 @@ +```js +// List.stories.js + +import List from './List.vue'; +import ListItem from './ListItem.vue'; + +export default { + /* πŸ‘‡ The title prop is optional. + * See https://storybook.js.org/docs/7.0/vue/configure/overview#configure-story-loading + * to learn how to generate automatic titles + */ + title: 'List', + component: List, + subcomponents: { ListItem }, //πŸ‘ˆ Adds the ListItem component as a subcomponent +}; + +/* + *πŸ‘‡ Render functions are a framework specific feature to allow you control on how the component renders. + * See https://storybook.js.org/docs/7.0/vue/api/csf + * to learn how to use render functions. + */ +export const Empty = { + render: () => ({ + components: { List }, + template: '', + }), +}; + +export const OneItem = { + render: () => ({ + components: { List, ListItem }, + template: '', + }), +}; +``` diff --git a/docs/snippets/vue/list-story-with-sub-components.ts-4-9.mdx b/docs/snippets/vue/list-story-with-sub-components.ts-4-9.mdx new file mode 100644 index 000000000000..b4d2023904bc --- /dev/null +++ b/docs/snippets/vue/list-story-with-sub-components.ts-4-9.mdx @@ -0,0 +1,41 @@ +```ts +// List.stories.ts + +// Replace vue3 with vue if you are using Storybook for Vue 2 +import type { Meta, StoryObj } from '@storybook/vue3'; + +import List from './List.vue'; +import ListItem from './ListItem.vue'; + +const meta = { + /* πŸ‘‡ The title prop is optional. + * See https://storybook.js.org/docs/7.0/vue/configure/overview#configure-story-loading + * to learn how to generate automatic titles + */ + title: 'List', + component: List, + subcomponents: { ListItem }, //πŸ‘ˆ Adds the ListItem component as a subcomponent +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +/* + *πŸ‘‡ Render functions are a framework specific feature to allow you control on how the component renders. + * See https://storybook.js.org/docs/7.0/vue/api/csf + * to learn how to use render functions. + */ +export const Empty = { + render: () => ({ + components: { List }, + template: '', + }), +}; + +export const OneItem: Story = { + render: (args) => ({ + components: { List, ListItem }, + template: '', + }), +}; +``` diff --git a/docs/snippets/vue/list-story-with-sub-components.ts.mdx b/docs/snippets/vue/list-story-with-sub-components.ts.mdx new file mode 100644 index 000000000000..e6e6a2a58942 --- /dev/null +++ b/docs/snippets/vue/list-story-with-sub-components.ts.mdx @@ -0,0 +1,41 @@ +```ts +// List.stories.ts + +// Replace vue3 with vue if you are using Storybook for Vue 2 +import type { Meta, StoryObj } from '@storybook/vue3'; + +import List from './List.vue'; +import ListItem from './ListItem.vue'; + +const meta: Meta = { + /* πŸ‘‡ The title prop is optional. + * See https://storybook.js.org/docs/7.0/vue/configure/overview#configure-story-loading + * to learn how to generate automatic titles + */ + title: 'List', + component: List, + subcomponents: { ListItem }, //πŸ‘ˆ Adds the ListItem component as a subcomponent +}; + +export default meta; +type Story = StoryObj; + +/* + *πŸ‘‡ Render functions are a framework specific feature to allow you control on how the component renders. + * See https://storybook.js.org/docs/7.0/vue/api/csf + * to learn how to use render functions. + */ +export const Empty: Story = { + render: () => ({ + components: { List }, + template: '', + }), +}; + +export const OneItem: Story = { + render: () => ({ + components: { List, ListItem }, + template: '', + }), +}; +``` diff --git a/docs/snippets/web-components/list-story-with-subcomponents.js.mdx b/docs/snippets/web-components/list-story-with-subcomponents.js.mdx new file mode 100644 index 000000000000..ced80cc450bf --- /dev/null +++ b/docs/snippets/web-components/list-story-with-subcomponents.js.mdx @@ -0,0 +1,21 @@ +```js +// List.stories.js + +import { html } from 'lit'; + +export default { + title: 'List', + component: 'demo-list', + subcomponents: { ListItem: 'demo-list-item' }, // πŸ‘ˆ Adds the ListItem component as a subcomponent +}; + +export const Empty = {}; + +export const OneItem = { + render: () => html` + + + + `, +}; +``` diff --git a/docs/snippets/web-components/list-story-with-subcomponents.ts.mdx b/docs/snippets/web-components/list-story-with-subcomponents.ts.mdx new file mode 100644 index 000000000000..511213a1030e --- /dev/null +++ b/docs/snippets/web-components/list-story-with-subcomponents.ts.mdx @@ -0,0 +1,26 @@ +```ts +// List.stories.ts + +import type { Meta, StoryObj } from '@storybook/web-components'; + +import { html } from 'lit'; + +const meta: Meta = { + title: 'List', + component: 'demo-list', + subcomponents: { ListItem: 'demo-list-item' }, // πŸ‘ˆ Adds the ListItem component as a subcomponent +}; +export default meta; + +type Story = StoryObj; + +export const Empty: Story = {}; + +export const OneItem: Story = { + render: () => html` + + + + `, +}; +``` diff --git a/docs/writing-docs/autodocs.md b/docs/writing-docs/autodocs.md index 4c1dcaea5fdb..20b459e03f09 100644 --- a/docs/writing-docs/autodocs.md +++ b/docs/writing-docs/autodocs.md @@ -183,6 +183,29 @@ Creating automated documentation with Storybook's Autodocs provides you with the ## Advanced configuration +### Documenting multiple components + +Sometimes it's helpful to document multiple components together. For example, a component library’s ButtonGroup and Button components might not make sense without one another. + +Autodocs allows you to document your "main" component, defined by the `component` property, as well as one or more `subcomponents` related to it. + + + + + + + +![Subcomponents in ArgTypes doc block](./doc-block-arg-types-subcomponents.png) + +The main component and its subcomponents will show up in a tabbed version of the [`ArgTypes` doc block](./doc-blocks.md#argtypes). The tab titles will correspond to the keys of the subcomponents object. + +If you want to organize your documentation differently for component groups, we recommend [using MDX](./mdx.md). It gives you complete control over how your components are displayed and supports any configuration. + ### Customize the Docs Container The Docs Container is the component that wraps up the documentation page. It's responsible for rendering the documentation page in Storybook's UI. You can customize it by creating your own component and updating your Storybook UI configuration file (i.e., `.storybook/preview.js`) to reference it. diff --git a/docs/writing-docs/doc-block-arg-types-subcomponents.png b/docs/writing-docs/doc-block-arg-types-subcomponents.png new file mode 100644 index 0000000000000000000000000000000000000000..bd883288f6f154c547b35ce94b3a972a4494ba5c GIT binary patch literal 10007 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 literal 0 HcmV?d00001 diff --git a/docs/writing-stories/doc-block-arg-types-subcomponents-for-list.png b/docs/writing-stories/doc-block-arg-types-subcomponents-for-list.png new file mode 100644 index 0000000000000000000000000000000000000000..b0d5469f0e3f441ce6413bab9ef1b5fa80719647 GIT binary patch literal 16815 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 + + + + + +Note that by adding a `subcomponents` property to the default export, we get an extra panel on the [ArgTypes](../writing-docs/doc-blocks.md#argtypes) and [Controls](../essentials/controls.md#) tables, listing the props of `ListItem`: + +![Subcomponents in ArgTypes doc block](./doc-block-arg-types-subcomponents-for-list.png) + +The downside of the approach used above, where each story creates its own combination of components, is that it does not take advantage of Storybook [Args](../writing-stories/args.md) meaning: + +1. You cannot change the stories via the controls panel +2. There is no [args reuse](../writing-stories/index.md#using-args) possible, which makes the stories harder to maintain. + +Let's talk about some techniques you can use to mitigate the above, which are especially useful in more complicated situations. ## Reusing subcomponent stories -The simplest approach we can take is to reuse the stories of the `ListItem` in the `List`: +The simplest change we can make to the above is to reuse the stories of the `ListItem` in the `List`: From f18392262c76070787dcb7e62158b96d6f2a3b38 Mon Sep 17 00:00:00 2001 From: Kyle Gach Date: Mon, 29 Jan 2024 17:09:41 -0700 Subject: [PATCH 06/18] Update docs/writing-docs/autodocs.md Co-authored-by: Jeppe Reinhold --- docs/writing-docs/autodocs.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/writing-docs/autodocs.md b/docs/writing-docs/autodocs.md index 20b459e03f09..9ea752168c12 100644 --- a/docs/writing-docs/autodocs.md +++ b/docs/writing-docs/autodocs.md @@ -202,7 +202,7 @@ Autodocs allows you to document your "main" component, defined by the `component ![Subcomponents in ArgTypes doc block](./doc-block-arg-types-subcomponents.png) -The main component and its subcomponents will show up in a tabbed version of the [`ArgTypes` doc block](./doc-blocks.md#argtypes). The tab titles will correspond to the keys of the subcomponents object. +The main component and its subcomponents will show up in a tabbed version of the [`ArgTypes` doc block](./doc-blocks.md#argtypes). The tab titles will correspond to the keys of the `subcomponents` object. If you want to organize your documentation differently for component groups, we recommend [using MDX](./mdx.md). It gives you complete control over how your components are displayed and supports any configuration. From fd87a7f3e4d26df27f9ec557f541e1d975287dcd Mon Sep 17 00:00:00 2001 From: Kyle Gach Date: Mon, 29 Jan 2024 17:10:44 -0700 Subject: [PATCH 07/18] Address comments - Update un-reverted snippets for v8 --- .../list-story-with-subcomponents.ts.mdx | 13 +------------ .../react/list-story-with-subcomponents.js.mdx | 8 +------- .../list-story-with-subcomponents.ts-4-9.mdx | 14 ++++---------- .../react/list-story-with-subcomponents.ts.mdx | 9 ++------- .../vue/list-story-with-sub-components.js.mdx | 11 ----------- .../list-story-with-sub-components.ts-4-9.mdx | 18 +++--------------- .../vue/list-story-with-sub-components.ts.mdx | 16 ++-------------- .../list-story-with-subcomponents.js.mdx | 1 - .../list-story-with-subcomponents.ts.mdx | 1 - 9 files changed, 13 insertions(+), 78 deletions(-) diff --git a/docs/snippets/angular/list-story-with-subcomponents.ts.mdx b/docs/snippets/angular/list-story-with-subcomponents.ts.mdx index 17e8ab2e60aa..e79ac6301ded 100644 --- a/docs/snippets/angular/list-story-with-subcomponents.ts.mdx +++ b/docs/snippets/angular/list-story-with-subcomponents.ts.mdx @@ -1,6 +1,5 @@ ```ts // List.stories.ts - import type { Meta, StoryObj } from '@storybook/angular'; import { moduleMetadata } from '@storybook/angular'; @@ -11,11 +10,6 @@ import { List } from './list.component'; import { ListItem } from './list-item.component'; const meta: Meta = { - /* πŸ‘‡ The title prop is optional. - * See https://storybook.js.org/docs/7.0/angular/configure/overview#configure-story-loading - * to learn how to generate automatic titles - */ - title: 'List', component: List, subcomponents: { ListItem }, //πŸ‘ˆ Adds the ListItem component as a subcomponent decorators: [ @@ -25,15 +19,10 @@ const meta: Meta = { }), ], }; - export default meta; + type Story = StoryObj; -/* - *πŸ‘‡ Render functions are a framework specific feature to allow you control on how the component renders. - * See https://storybook.js.org/docs/7.0/angular/api/csf - * to learn how to use render functions. - */ export const Empty: Story = {}; export const OneItem: Story = { diff --git a/docs/snippets/react/list-story-with-subcomponents.js.mdx b/docs/snippets/react/list-story-with-subcomponents.js.mdx index 1bae39ac9a4e..4edaa677c3f5 100644 --- a/docs/snippets/react/list-story-with-subcomponents.js.mdx +++ b/docs/snippets/react/list-story-with-subcomponents.js.mdx @@ -1,17 +1,11 @@ -```js +```jsx // List.stories.js|jsx - import React from 'react'; import { List } from './List'; import { ListItem } from './ListItem'; export default { - /* πŸ‘‡ The title prop is optional. - * See https://storybook.js.org/docs/7.0/react/configure/overview#configure-story-loading - * to learn how to generate automatic titles - */ - title: 'List', component: List, subcomponents: { ListItem }, //πŸ‘ˆ Adds the ListItem component as a subcomponent }; diff --git a/docs/snippets/react/list-story-with-subcomponents.ts-4-9.mdx b/docs/snippets/react/list-story-with-subcomponents.ts-4-9.mdx index 71abc96d3c2c..d527fe149fac 100644 --- a/docs/snippets/react/list-story-with-subcomponents.ts-4-9.mdx +++ b/docs/snippets/react/list-story-with-subcomponents.ts-4-9.mdx @@ -1,23 +1,17 @@ -```ts +```tsx // List.stories.ts|tsx - +import React from 'react'; import type { Meta, StoryObj } from '@storybook/react'; import { List } from './List'; import { ListItem } from './ListItem'; const meta = { - /* πŸ‘‡ The title prop is optional. - * See https://storybook.js.org/docs/7.0/react/configure/overview#configure-story-loading - * to learn how to generate automatic titles - */ - title: 'List', component: List, - //πŸ‘ˆ Adds the ListItem component as a subcomponent - subcomponents: { ListItem }, + subcomponents: { ListItem }, //πŸ‘ˆ Adds the ListItem component as a subcomponent } satisfies Meta; - export default meta; + type Story = StoryObj; export const Empty: Story = {}; diff --git a/docs/snippets/react/list-story-with-subcomponents.ts.mdx b/docs/snippets/react/list-story-with-subcomponents.ts.mdx index dcb959020f98..71a08b1fe6bb 100644 --- a/docs/snippets/react/list-story-with-subcomponents.ts.mdx +++ b/docs/snippets/react/list-story-with-subcomponents.ts.mdx @@ -1,22 +1,17 @@ ```tsx // List.stories.ts|tsx - +import React from 'react'; import type { Meta, StoryObj } from '@storybook/react'; import { List } from './List'; import { ListItem } from './ListItem'; const meta: Meta = { - /* πŸ‘‡ The title prop is optional. - * See https://storybook.js.org/docs/7.0/react/configure/overview#configure-story-loading - * to learn how to generate automatic titles - */ - title: 'List', component: List, subcomponents: { ListItem }, //πŸ‘ˆ Adds the ListItem component as a subcomponent }; - export default meta; + type Story = StoryObj; export const Empty: Story = {}; diff --git a/docs/snippets/vue/list-story-with-sub-components.js.mdx b/docs/snippets/vue/list-story-with-sub-components.js.mdx index 9d674dbfebf4..0c6e082e6a92 100644 --- a/docs/snippets/vue/list-story-with-sub-components.js.mdx +++ b/docs/snippets/vue/list-story-with-sub-components.js.mdx @@ -1,24 +1,13 @@ ```js // List.stories.js - import List from './List.vue'; import ListItem from './ListItem.vue'; export default { - /* πŸ‘‡ The title prop is optional. - * See https://storybook.js.org/docs/7.0/vue/configure/overview#configure-story-loading - * to learn how to generate automatic titles - */ - title: 'List', component: List, subcomponents: { ListItem }, //πŸ‘ˆ Adds the ListItem component as a subcomponent }; -/* - *πŸ‘‡ Render functions are a framework specific feature to allow you control on how the component renders. - * See https://storybook.js.org/docs/7.0/vue/api/csf - * to learn how to use render functions. - */ export const Empty = { render: () => ({ components: { List }, diff --git a/docs/snippets/vue/list-story-with-sub-components.ts-4-9.mdx b/docs/snippets/vue/list-story-with-sub-components.ts-4-9.mdx index b4d2023904bc..7d9c073bfa83 100644 --- a/docs/snippets/vue/list-story-with-sub-components.ts-4-9.mdx +++ b/docs/snippets/vue/list-story-with-sub-components.ts-4-9.mdx @@ -1,31 +1,19 @@ ```ts // List.stories.ts - -// Replace vue3 with vue if you are using Storybook for Vue 2 import type { Meta, StoryObj } from '@storybook/vue3'; import List from './List.vue'; import ListItem from './ListItem.vue'; const meta = { - /* πŸ‘‡ The title prop is optional. - * See https://storybook.js.org/docs/7.0/vue/configure/overview#configure-story-loading - * to learn how to generate automatic titles - */ - title: 'List', component: List, subcomponents: { ListItem }, //πŸ‘ˆ Adds the ListItem component as a subcomponent } satisfies Meta; - export default meta; + type Story = StoryObj; -/* - *πŸ‘‡ Render functions are a framework specific feature to allow you control on how the component renders. - * See https://storybook.js.org/docs/7.0/vue/api/csf - * to learn how to use render functions. - */ -export const Empty = { +export const Empty: Story = { render: () => ({ components: { List }, template: '', @@ -33,7 +21,7 @@ export const Empty = { }; export const OneItem: Story = { - render: (args) => ({ + render: () => ({ components: { List, ListItem }, template: '', }), diff --git a/docs/snippets/vue/list-story-with-sub-components.ts.mdx b/docs/snippets/vue/list-story-with-sub-components.ts.mdx index e6e6a2a58942..b299d5350314 100644 --- a/docs/snippets/vue/list-story-with-sub-components.ts.mdx +++ b/docs/snippets/vue/list-story-with-sub-components.ts.mdx @@ -1,34 +1,22 @@ ```ts // List.stories.ts - -// Replace vue3 with vue if you are using Storybook for Vue 2 import type { Meta, StoryObj } from '@storybook/vue3'; import List from './List.vue'; import ListItem from './ListItem.vue'; const meta: Meta = { - /* πŸ‘‡ The title prop is optional. - * See https://storybook.js.org/docs/7.0/vue/configure/overview#configure-story-loading - * to learn how to generate automatic titles - */ - title: 'List', component: List, subcomponents: { ListItem }, //πŸ‘ˆ Adds the ListItem component as a subcomponent }; - export default meta; + type Story = StoryObj; -/* - *πŸ‘‡ Render functions are a framework specific feature to allow you control on how the component renders. - * See https://storybook.js.org/docs/7.0/vue/api/csf - * to learn how to use render functions. - */ export const Empty: Story = { render: () => ({ components: { List }, - template: '', + template: '', }), }; diff --git a/docs/snippets/web-components/list-story-with-subcomponents.js.mdx b/docs/snippets/web-components/list-story-with-subcomponents.js.mdx index ced80cc450bf..2e34a5e7104d 100644 --- a/docs/snippets/web-components/list-story-with-subcomponents.js.mdx +++ b/docs/snippets/web-components/list-story-with-subcomponents.js.mdx @@ -1,6 +1,5 @@ ```js // List.stories.js - import { html } from 'lit'; export default { diff --git a/docs/snippets/web-components/list-story-with-subcomponents.ts.mdx b/docs/snippets/web-components/list-story-with-subcomponents.ts.mdx index 511213a1030e..d433d2f02fd0 100644 --- a/docs/snippets/web-components/list-story-with-subcomponents.ts.mdx +++ b/docs/snippets/web-components/list-story-with-subcomponents.ts.mdx @@ -1,6 +1,5 @@ ```ts // List.stories.ts - import type { Meta, StoryObj } from '@storybook/web-components'; import { html } from 'lit'; From b644216cc4c8be2e0d95271b60bd7f9675f86a57 Mon Sep 17 00:00:00 2001 From: Kyle Gach Date: Tue, 30 Jan 2024 10:07:24 -0700 Subject: [PATCH 08/18] Consolidate snippets --- .../common/subcomponents-in-meta.js.mdx | 10 ---------- .../common/subcomponents-in-meta.ts-4-9.mdx | 15 --------------- .../common/subcomponents-in-meta.ts.mdx | 15 --------------- docs/writing-docs/autodocs.md | 13 ++++++++++--- .../doc-block-arg-types-subcomponents.png | Bin 10007 -> 0 bytes 5 files changed, 10 insertions(+), 43 deletions(-) delete mode 100644 docs/snippets/common/subcomponents-in-meta.js.mdx delete mode 100644 docs/snippets/common/subcomponents-in-meta.ts-4-9.mdx delete mode 100644 docs/snippets/common/subcomponents-in-meta.ts.mdx delete mode 100644 docs/writing-docs/doc-block-arg-types-subcomponents.png diff --git a/docs/snippets/common/subcomponents-in-meta.js.mdx b/docs/snippets/common/subcomponents-in-meta.js.mdx deleted file mode 100644 index c925367d0b09..000000000000 --- a/docs/snippets/common/subcomponents-in-meta.js.mdx +++ /dev/null @@ -1,10 +0,0 @@ -```js -// ButtonGroup.stories.js|jsx - -import { Button, ButtonGroup } from './ButtonGroup'; - -export default { - component: ButtonGroup, - subcomponents: { Button }, -}; -``` diff --git a/docs/snippets/common/subcomponents-in-meta.ts-4-9.mdx b/docs/snippets/common/subcomponents-in-meta.ts-4-9.mdx deleted file mode 100644 index 49f5deefb5ca..000000000000 --- a/docs/snippets/common/subcomponents-in-meta.ts-4-9.mdx +++ /dev/null @@ -1,15 +0,0 @@ -```ts -// ButtonGroup.stories.ts|tsx - -// Replace your-framework with the name of your framework -import type { Meta, StoryObj } from '@storybook/your-framework'; - -import { Button, ButtonGroup } from './ButtonGroup'; - -const meta = { - component: ButtonGroup, - subcomponents: { Button }, -} satisfies Meta; - -export default meta; -``` \ No newline at end of file diff --git a/docs/snippets/common/subcomponents-in-meta.ts.mdx b/docs/snippets/common/subcomponents-in-meta.ts.mdx deleted file mode 100644 index 8847609e9fac..000000000000 --- a/docs/snippets/common/subcomponents-in-meta.ts.mdx +++ /dev/null @@ -1,15 +0,0 @@ -```ts -// ButtonGroup.stories.ts|tsx - -// Replace your-framework with the name of your framework -import type { Meta } from '@storybook/your-framework'; - -import { Button, ButtonGroup } from './ButtonGroup'; - -const meta: Meta = { - component: ButtonGroup, - subcomponents: { Button }, -}; - -export default meta; -``` \ No newline at end of file diff --git a/docs/writing-docs/autodocs.md b/docs/writing-docs/autodocs.md index 9ea752168c12..a89dd0a51d2f 100644 --- a/docs/writing-docs/autodocs.md +++ b/docs/writing-docs/autodocs.md @@ -193,14 +193,21 @@ Autodocs allows you to document your "main" component, defined by the `component -![Subcomponents in ArgTypes doc block](./doc-block-arg-types-subcomponents.png) +![Subcomponents in ArgTypes doc block](../writing-stories/doc-block-arg-types-subcomponents-for-list.png) The main component and its subcomponents will show up in a tabbed version of the [`ArgTypes` doc block](./doc-blocks.md#argtypes). The tab titles will correspond to the keys of the `subcomponents` object. diff --git a/docs/writing-docs/doc-block-arg-types-subcomponents.png b/docs/writing-docs/doc-block-arg-types-subcomponents.png deleted file mode 100644 index bd883288f6f154c547b35ce94b3a972a4494ba5c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10007 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 From de25f2e937e45e3a3efcfd4a94495f893d16b4cd Mon Sep 17 00:00:00 2001 From: Kyle Gach Date: Tue, 30 Jan 2024 10:14:55 -0700 Subject: [PATCH 09/18] Clarify subcomponents limitations --- docs/writing-stories/stories-for-multiple-components.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/writing-stories/stories-for-multiple-components.md b/docs/writing-stories/stories-for-multiple-components.md index f03ec9a3c8e0..708ccce28787 100644 --- a/docs/writing-stories/stories-for-multiple-components.md +++ b/docs/writing-stories/stories-for-multiple-components.md @@ -26,10 +26,10 @@ Note that by adding a `subcomponents` property to the default export, we get an ![Subcomponents in ArgTypes doc block](./doc-block-arg-types-subcomponents-for-list.png) -The downside of the approach used above, where each story creates its own combination of components, is that it does not take advantage of Storybook [Args](../writing-stories/args.md) meaning: +Subcomponents are only intended for documentation purposes and have some limitations: -1. You cannot change the stories via the controls panel -2. There is no [args reuse](../writing-stories/index.md#using-args) possible, which makes the stories harder to maintain. +1. The [argTypes](../api/arg-types.md) of subcomponents are [inferred (for the renderers that support that feature)](../api/arg-types.md#automatic-argtype-inference) and cannot be manually defined or overridden. +2. The table for each documented subcomponent does _not_ include [controls](../essentials/controls.md) to change the value of the props, because controls always apply to the main component's args. Let's talk about some techniques you can use to mitigate the above, which are especially useful in more complicated situations. From a96d34843e2b34c69444a409a09fd6ad61ea8bd8 Mon Sep 17 00:00:00 2001 From: Kyle Gach Date: Tue, 30 Jan 2024 10:20:01 -0700 Subject: [PATCH 10/18] Update Vue snippets --- docs/snippets/vue/list-story-with-sub-components.js.mdx | 7 +++++-- .../snippets/vue/list-story-with-sub-components.ts-4-9.mdx | 7 +++++-- docs/snippets/vue/list-story-with-sub-components.ts.mdx | 7 +++++-- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/docs/snippets/vue/list-story-with-sub-components.js.mdx b/docs/snippets/vue/list-story-with-sub-components.js.mdx index 0c6e082e6a92..a19b58f3b3d1 100644 --- a/docs/snippets/vue/list-story-with-sub-components.js.mdx +++ b/docs/snippets/vue/list-story-with-sub-components.js.mdx @@ -16,9 +16,12 @@ export const Empty = { }; export const OneItem = { - render: () => ({ + render: (args) => ({ components: { List, ListItem }, - template: '', + setup() { + return { args } + } + template: '', }), }; ``` diff --git a/docs/snippets/vue/list-story-with-sub-components.ts-4-9.mdx b/docs/snippets/vue/list-story-with-sub-components.ts-4-9.mdx index 7d9c073bfa83..6e99a25445df 100644 --- a/docs/snippets/vue/list-story-with-sub-components.ts-4-9.mdx +++ b/docs/snippets/vue/list-story-with-sub-components.ts-4-9.mdx @@ -21,9 +21,12 @@ export const Empty: Story = { }; export const OneItem: Story = { - render: () => ({ + render: (args) => ({ components: { List, ListItem }, - template: '', + setup() { + return { args } + } + template: '', }), }; ``` diff --git a/docs/snippets/vue/list-story-with-sub-components.ts.mdx b/docs/snippets/vue/list-story-with-sub-components.ts.mdx index b299d5350314..87ae30aed24a 100644 --- a/docs/snippets/vue/list-story-with-sub-components.ts.mdx +++ b/docs/snippets/vue/list-story-with-sub-components.ts.mdx @@ -21,9 +21,12 @@ export const Empty: Story = { }; export const OneItem: Story = { - render: () => ({ + render: (args) => ({ components: { List, ListItem }, - template: '', + setup() { + return { args } + } + template: '', }), }; ``` From 4e3f70548a20c10c8bdf360a673dcdf3f3760931 Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Wed, 31 Jan 2024 10:18:18 +0100 Subject: [PATCH 11/18] fix https://github.com/storybookjs/storybook/discussions/25695#discussioncomment-8257624 --- .../core-server/src/presets/common-override-preset.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/code/lib/core-server/src/presets/common-override-preset.ts b/code/lib/core-server/src/presets/common-override-preset.ts index 1680b5b146cd..51d1af8998d4 100644 --- a/code/lib/core-server/src/presets/common-override-preset.ts +++ b/code/lib/core-server/src/presets/common-override-preset.ts @@ -51,9 +51,11 @@ const createTestBuildFeatures = (value: boolean): Required => ({ export const build: PresetProperty<'build'> = async (value, options) => { return { ...value, - test: { - ...createTestBuildFeatures(!!options.test), - ...value?.test, - }, + test: options.test + ? { + ...createTestBuildFeatures(!!options.test), + ...value?.test, + } + : createTestBuildFeatures(false), }; }; From 63d70210d72e20e51a261526517a70d9ba2435e1 Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Wed, 31 Jan 2024 10:51:53 +0100 Subject: [PATCH 12/18] add test handling to angular build --- code/frameworks/angular/src/builders/build-storybook/index.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/code/frameworks/angular/src/builders/build-storybook/index.ts b/code/frameworks/angular/src/builders/build-storybook/index.ts index abe8fbd6a060..81d6b2689c2a 100644 --- a/code/frameworks/angular/src/builders/build-storybook/index.ts +++ b/code/frameworks/angular/src/builders/build-storybook/index.ts @@ -49,6 +49,7 @@ export type StorybookBuilderOptions = JsonObject & { | 'configDir' | 'loglevel' | 'quiet' + | 'test' | 'webpackStatsJson' | 'disableTelemetry' | 'debugWebpack' @@ -87,6 +88,7 @@ const commandBuilder: BuilderHandlerFn = ( configDir, docs, loglevel, + test, outputDir, quiet, enableProdMode = true, @@ -104,6 +106,7 @@ const commandBuilder: BuilderHandlerFn = ( ...(docs ? { docs } : {}), loglevel, outputDir, + test, quiet, enableProdMode, disableTelemetry, From 62be2a525f96ce2ac521a0c727cf57bc6318425c Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Wed, 31 Jan 2024 15:18:36 +0100 Subject: [PATCH 13/18] Revert "CI: Bump E2E resource class to large" --- .circleci/config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index c1871d2eba22..b212c825ddc2 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -410,7 +410,7 @@ jobs: parallelism: type: integer executor: - class: large + class: medium name: sb_playwright parallelism: << parameters.parallelism >> steps: @@ -433,7 +433,7 @@ jobs: parallelism: type: integer executor: - class: large + class: medium name: sb_playwright parallelism: << parameters.parallelism >> steps: From 941826389d295db1ba975ab3193142c588d46938 Mon Sep 17 00:00:00 2001 From: storybook-bot <32066757+storybook-bot@users.noreply.github.com> Date: Wed, 31 Jan 2024 14:54:30 +0000 Subject: [PATCH 14/18] Update CHANGELOG.md for v7.6.12 [skip ci] --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a70cafb01a6..c1a25029e92c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 7.6.12 + +- CLI: Fix `upgrade` detecting the wrong version of existing Storybooks - [#25752](https://github.com/storybookjs/storybook/pull/25752), thanks [@JReinhold](https://github.com/JReinhold)! + ## 7.6.11 - CLI: Update init for react native v7 - [#25780](https://github.com/storybookjs/storybook/pull/25780), thanks [@dannyhw](https://github.com/dannyhw)! From f70a6b955d492056bb592e9baa0b8a1f0bb4af8e Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Wed, 31 Jan 2024 16:16:16 +0100 Subject: [PATCH 15/18] Fix vitest patch --- .../@vitest-expect-npm-1.1.3-2062bf533f.patch | 26 ++++++++----------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/code/.yarn/patches/@vitest-expect-npm-1.1.3-2062bf533f.patch b/code/.yarn/patches/@vitest-expect-npm-1.1.3-2062bf533f.patch index ea5e834a06df..9a1b74e203bd 100644 --- a/code/.yarn/patches/@vitest-expect-npm-1.1.3-2062bf533f.patch +++ b/code/.yarn/patches/@vitest-expect-npm-1.1.3-2062bf533f.patch @@ -1,10 +1,10 @@ diff --git a/dist/index.js b/dist/index.js -index 974d6b26f626024fc9904908100c9ecaa54f43e1..5be2d35267e7f0525c6588758dbebe72599f88a9 100644 +index 974d6b26f626024fc9904908100c9ecaa54f43e1..5d9d92a0796e02630ccdd1174d4fd25e016d2b06 100644 --- a/dist/index.js +++ b/dist/index.js -@@ -6,31 +6,37 @@ import { processError } from '@vitest/utils/error'; +@@ -6,28 +6,35 @@ import { processError } from '@vitest/utils/error'; import { util } from 'chai'; - + const MATCHERS_OBJECT = Symbol.for("matchers-object"); -const JEST_MATCHERS_OBJECT = Symbol.for("$$jest-matchers-object"); +// Patched this symbol for storybook, so that @storybook/test can be used in a jest environment as well. @@ -12,17 +12,14 @@ index 974d6b26f626024fc9904908100c9ecaa54f43e1..5be2d35267e7f0525c6588758dbebe72 +const JEST_MATCHERS_OBJECT = Symbol.for("$$jest-matchers-object-storybook"); const GLOBAL_EXPECT = Symbol.for("expect-global"); const ASYMMETRIC_MATCHERS_OBJECT = Symbol.for("asymmetric-matchers-object"); - + if (!Object.prototype.hasOwnProperty.call(globalThis, MATCHERS_OBJECT)) { const globalState = /* @__PURE__ */ new WeakMap(); - const matchers = /* @__PURE__ */ Object.create(null); - const assymetricMatchers = /* @__PURE__ */ Object.create(null); +- const assymetricMatchers = /* @__PURE__ */ Object.create(null); Object.defineProperty(globalThis, MATCHERS_OBJECT, { get: () => globalState }); -+ Object.defineProperty(globalThis, ASYMMETRIC_MATCHERS_OBJECT, { -+ get: () => assymetricMatchers -+ }); +} +if (!Object.prototype.hasOwnProperty.call(globalThis, JEST_MATCHERS_OBJECT)) { + const matchers = /* @__PURE__ */ Object.create(null); @@ -34,15 +31,14 @@ index 974d6b26f626024fc9904908100c9ecaa54f43e1..5be2d35267e7f0525c6588758dbebe72 matchers }) }); -- Object.defineProperty(globalThis, ASYMMETRIC_MATCHERS_OBJECT, { -- get: () => assymetricMatchers -- }); ++} ++if (!Object.prototype.hasOwnProperty.call(globalThis, ASYMMETRIC_MATCHERS_OBJECT)) { ++ const assymetricMatchers = /* @__PURE__ */ Object.create(null); + Object.defineProperty(globalThis, ASYMMETRIC_MATCHERS_OBJECT, { + get: () => assymetricMatchers + }); } + function getState(expect) { return globalThis[MATCHERS_OBJECT].get(expect); } -+ - function setState(state, expect) { - const map = globalThis[MATCHERS_OBJECT]; - const current = map.get(expect) || {}; From 8f52eac95be2a4104e5d291c9c87a6f26d198741 Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Wed, 31 Jan 2024 16:18:18 +0100 Subject: [PATCH 16/18] Fix yarn.lock --- code/yarn.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/yarn.lock b/code/yarn.lock index 7ba452bc433f..e0901c716f38 100644 --- a/code/yarn.lock +++ b/code/yarn.lock @@ -8479,12 +8479,12 @@ __metadata: "@vitest/expect@patch:@vitest/expect@npm%3A1.1.3#~/.yarn/patches/@vitest-expect-npm-1.1.3-2062bf533f.patch": version: 1.1.3 - resolution: "@vitest/expect@patch:@vitest/expect@npm%3A1.1.3#~/.yarn/patches/@vitest-expect-npm-1.1.3-2062bf533f.patch::version=1.1.3&hash=5d51c9" + resolution: "@vitest/expect@patch:@vitest/expect@npm%3A1.1.3#~/.yarn/patches/@vitest-expect-npm-1.1.3-2062bf533f.patch::version=1.1.3&hash=8fb073" dependencies: "@vitest/spy": "npm:1.1.3" "@vitest/utils": "npm:1.1.3" chai: "npm:^4.3.10" - checksum: 426287f864f58b05b1c4689bc87b4ef2ca7b3316a22e8e42d94ee9c125cbc0caf294618c9a1201a8ddf8ab68ce1ab194d1e34589f7d608906a3dc679074cfe22 + checksum: c3bbcae82050b7e92438c85e679ef2cb09162dc5638a10b3f0b5a8fc5600dfb0be578a244d84012ae2f1715748189393ac0fc72b891efff3503338221795ebe5 languageName: node linkType: hard From 08800fe63d3af33efd0b1fb9a704b8d879f24481 Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Wed, 31 Jan 2024 16:36:33 +0100 Subject: [PATCH 17/18] Upgrade testing-library/jest-dom --- code/lib/test/package.json | 2 +- code/lib/test/src/expect.ts | 3 ++- code/yarn.lock | 51 +++++++++++++++++++++++++++++++++++-- 3 files changed, 52 insertions(+), 4 deletions(-) diff --git a/code/lib/test/package.json b/code/lib/test/package.json index f49a54ad4b89..ef71e012cd78 100644 --- a/code/lib/test/package.json +++ b/code/lib/test/package.json @@ -48,7 +48,7 @@ "@storybook/instrumenter": "workspace:*", "@storybook/preview-api": "workspace:*", "@testing-library/dom": "^9.3.1", - "@testing-library/jest-dom": "^6.1.3", + "@testing-library/jest-dom": "^6.4.0", "@testing-library/user-event": "14.3.0", "@vitest/expect": "1.1.3", "@vitest/spy": "^1.1.3", diff --git a/code/lib/test/src/expect.ts b/code/lib/test/src/expect.ts index 7e8b9d95cf03..da17eb52ff59 100644 --- a/code/lib/test/src/expect.ts +++ b/code/lib/test/src/expect.ts @@ -16,9 +16,10 @@ import { } from '@vitest/expect'; import * as matchers from '@testing-library/jest-dom/matchers'; import type { PromisifyObject } from './utils'; +import type { TestingLibraryMatchers } from '@testing-library/jest-dom/types/matchers'; type Matchers = PromisifyObject> & - matchers.TestingLibraryMatchers, Promise>; + TestingLibraryMatchers, Promise>; // We only expose the jest compatible API for now export interface Assertion extends Matchers { diff --git a/code/yarn.lock b/code/yarn.lock index e0901c716f38..02847b06a441 100644 --- a/code/yarn.lock +++ b/code/yarn.lock @@ -31,6 +31,13 @@ __metadata: languageName: node linkType: hard +"@adobe/css-tools@npm:^4.3.2": + version: 4.3.3 + resolution: "@adobe/css-tools@npm:4.3.3" + checksum: e76e712df713964b87cdf2aca1f0477f19bebd845484d5fcba726d3ec7782366e2f26ec8cb2dcfaf47081a5c891987d8a9f5c3f30d11e1eb3c1848adc27fcb24 + languageName: node + linkType: hard + "@ampproject/remapping@npm:2.2.1, @ampproject/remapping@npm:^2.2.0, @ampproject/remapping@npm:^2.2.1": version: 2.2.1 resolution: "@ampproject/remapping@npm:2.2.1" @@ -6663,7 +6670,7 @@ __metadata: "@storybook/instrumenter": "workspace:*" "@storybook/preview-api": "workspace:*" "@testing-library/dom": "npm:^9.3.1" - "@testing-library/jest-dom": "npm:^6.1.3" + "@testing-library/jest-dom": "npm:^6.4.0" "@testing-library/user-event": "npm:14.3.0" "@vitest/expect": "npm:1.1.3" "@vitest/spy": "npm:^1.1.3" @@ -6925,7 +6932,7 @@ __metadata: languageName: node linkType: hard -"@testing-library/jest-dom@npm:^6.1.3, @testing-library/jest-dom@npm:^6.1.4": +"@testing-library/jest-dom@npm:^6.1.4": version: 6.1.5 resolution: "@testing-library/jest-dom@npm:6.1.5" dependencies: @@ -6955,6 +6962,39 @@ __metadata: languageName: node linkType: hard +"@testing-library/jest-dom@npm:^6.4.0": + version: 6.4.0 + resolution: "@testing-library/jest-dom@npm:6.4.0" + dependencies: + "@adobe/css-tools": "npm:^4.3.2" + "@babel/runtime": "npm:^7.9.2" + aria-query: "npm:^5.0.0" + chalk: "npm:^3.0.0" + css.escape: "npm:^1.5.1" + dom-accessibility-api: "npm:^0.6.3" + lodash: "npm:^4.17.15" + redent: "npm:^3.0.0" + peerDependencies: + "@jest/globals": ">= 28" + "@types/bun": "*" + "@types/jest": ">= 28" + jest: ">= 28" + vitest: ">= 0.32" + peerDependenciesMeta: + "@jest/globals": + optional: true + "@types/bun": + optional: true + "@types/jest": + optional: true + jest: + optional: true + vitest: + optional: true + checksum: 6b7eba9ca388986a721fb12f84adf0f5534bf7ec5851982023a889c4a0afac6e9e91291bdac39e1f59a05adefd7727e30463d98b21c3da32fbfec229ccb11ef1 + languageName: node + linkType: hard + "@testing-library/react@npm:^11.2.2": version: 11.2.7 resolution: "@testing-library/react@npm:11.2.7" @@ -12993,6 +13033,13 @@ __metadata: languageName: node linkType: hard +"dom-accessibility-api@npm:^0.6.3": + version: 0.6.3 + resolution: "dom-accessibility-api@npm:0.6.3" + checksum: 10bee5aa514b2a9a37c87cd81268db607a2e933a050074abc2f6fa3da9080ebed206a320cbc123567f2c3087d22292853bdfdceaffdd4334ffe2af9510b29360 + languageName: node + linkType: hard + "dom-converter@npm:^0.2.0": version: 0.2.0 resolution: "dom-converter@npm:0.2.0" From 0f3d22c79bcd28e6946a07ae500a543efa12333d Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Wed, 31 Jan 2024 16:46:01 +0100 Subject: [PATCH 18/18] Add a test for using storybook/test in vitest --- code/lib/test/src/index.test.ts | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 code/lib/test/src/index.test.ts diff --git a/code/lib/test/src/index.test.ts b/code/lib/test/src/index.test.ts new file mode 100644 index 000000000000..bed58592c1d6 --- /dev/null +++ b/code/lib/test/src/index.test.ts @@ -0,0 +1,8 @@ +import { it } from 'vitest'; +import { expect, fn } from '@storybook/test'; + +it('storybook expect and fn can be used in vitest test', () => { + const spy = fn(); + spy(1); + expect(spy).toHaveBeenCalledWith(1); +});