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): 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..e79ac6301ded --- /dev/null +++ b/docs/snippets/angular/list-story-with-subcomponents.ts.mdx @@ -0,0 +1,39 @@ +```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 = { + component: List, + subcomponents: { ListItem }, //πŸ‘ˆ Adds the ListItem component as a subcomponent + decorators: [ + moduleMetadata({ + declarations: [List, ListItem], + imports: [CommonModule], + }), + ], +}; +export default meta; + +type Story = StoryObj; + +export const Empty: Story = {}; + +export const OneItem: Story = { + args: {}, + render: (args) => ({ + props: args, + template: ` + + + + `, + }), +}; +``` 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..4edaa677c3f5 --- /dev/null +++ b/docs/snippets/react/list-story-with-subcomponents.js.mdx @@ -0,0 +1,22 @@ +```jsx +// List.stories.js|jsx +import React from 'react'; + +import { List } from './List'; +import { ListItem } from './ListItem'; + +export default { + 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..d527fe149fac --- /dev/null +++ b/docs/snippets/react/list-story-with-subcomponents.ts-4-9.mdx @@ -0,0 +1,26 @@ +```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 = { + component: List, + subcomponents: { ListItem }, //πŸ‘ˆ Adds the ListItem component as a subcomponent +} 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..71a08b1fe6bb --- /dev/null +++ b/docs/snippets/react/list-story-with-subcomponents.ts.mdx @@ -0,0 +1,26 @@ +```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 = { + 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..a19b58f3b3d1 --- /dev/null +++ b/docs/snippets/vue/list-story-with-sub-components.js.mdx @@ -0,0 +1,27 @@ +```js +// List.stories.js +import List from './List.vue'; +import ListItem from './ListItem.vue'; + +export default { + component: List, + subcomponents: { ListItem }, //πŸ‘ˆ Adds the ListItem component as a subcomponent +}; + +export const Empty = { + render: () => ({ + components: { List }, + template: '', + }), +}; + +export const OneItem = { + render: (args) => ({ + components: { List, ListItem }, + 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 new file mode 100644 index 000000000000..6e99a25445df --- /dev/null +++ b/docs/snippets/vue/list-story-with-sub-components.ts-4-9.mdx @@ -0,0 +1,32 @@ +```ts +// List.stories.ts +import type { Meta, StoryObj } from '@storybook/vue3'; + +import List from './List.vue'; +import ListItem from './ListItem.vue'; + +const meta = { + component: List, + subcomponents: { ListItem }, //πŸ‘ˆ Adds the ListItem component as a subcomponent +} satisfies Meta; +export default meta; + +type Story = StoryObj; + +export const Empty: Story = { + render: () => ({ + components: { List }, + template: '', + }), +}; + +export const OneItem: Story = { + render: (args) => ({ + components: { List, ListItem }, + 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 new file mode 100644 index 000000000000..87ae30aed24a --- /dev/null +++ b/docs/snippets/vue/list-story-with-sub-components.ts.mdx @@ -0,0 +1,32 @@ +```ts +// List.stories.ts +import type { Meta, StoryObj } from '@storybook/vue3'; + +import List from './List.vue'; +import ListItem from './ListItem.vue'; + +const meta: Meta = { + component: List, + subcomponents: { ListItem }, //πŸ‘ˆ Adds the ListItem component as a subcomponent +}; +export default meta; + +type Story = StoryObj; + +export const Empty: Story = { + render: () => ({ + components: { List }, + template: '', + }), +}; + +export const OneItem: Story = { + render: (args) => ({ + components: { List, ListItem }, + setup() { + return { args } + } + 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..2e34a5e7104d --- /dev/null +++ b/docs/snippets/web-components/list-story-with-subcomponents.js.mdx @@ -0,0 +1,20 @@ +```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..d433d2f02fd0 --- /dev/null +++ b/docs/snippets/web-components/list-story-with-subcomponents.ts.mdx @@ -0,0 +1,25 @@ +```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..a89dd0a51d2f 100644 --- a/docs/writing-docs/autodocs.md +++ b/docs/writing-docs/autodocs.md @@ -183,6 +183,36 @@ 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](../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. + +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-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 000000000000..b0d5469f0e3f Binary files /dev/null and b/docs/writing-stories/doc-block-arg-types-subcomponents-for-list.png differ diff --git a/docs/writing-stories/stories-for-multiple-components.md b/docs/writing-stories/stories-for-multiple-components.md index 4e6ac15a18ef..708ccce28787 100644 --- a/docs/writing-stories/stories-for-multiple-components.md +++ b/docs/writing-stories/stories-for-multiple-components.md @@ -2,11 +2,40 @@ title: 'Stories for multiple components' --- -It's useful to write stories that [render two or more components](../writing-stories/index.md#stories-for-two-or-more-components) at once if those components are designed to work together. For example, `ButtonGroups`, `Lists`, and `Page` components. +It's useful to write stories that [render two or more components](../writing-stories/index.md#stories-for-two-or-more-components) at once if those components are designed to work together. For example, `ButtonGroups`, `Lists`, and `Page` components. Here's an example with `List` and `ListItem` components: + + + + + + + +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) + +Subcomponents are only intended for documentation purposes and have some limitations: + +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. ## 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`: