Skip to content

Commit

Permalink
Merge pull request #25800 from storybookjs/docs-sub-components
Browse files Browse the repository at this point in the history
Docs: Re-document subcomponents
  • Loading branch information
kylegach authored Jan 31, 2024
2 parents 58316ad + be42da5 commit b34c5dd
Show file tree
Hide file tree
Showing 14 changed files with 313 additions and 5 deletions.
2 changes: 1 addition & 1 deletion docs/api/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ An overview of all available API references for Storybook.
<tr>
<td><a href="../api/parameters">Parameters</a></td>
<td>
Parameters are static metadata used to configure your <a href="../get-started/whats-a-story.md">stories</a> <a href="../addons/introduction.md">addons</a> in Storybook. They are specified at the story, meta (component), project (global) levels.
Parameters are static metadata used to configure your <a href="../get-started/whats-a-story.md">stories</a> <a href="../addons/index.md">addons</a> in Storybook. They are specified at the story, meta (component), project (global) levels.
</td>
</tr>
</tbody>
Expand Down
4 changes: 2 additions & 2 deletions docs/api/parameters.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -36,7 +36,7 @@ Parameters specified at the story level apply to that story only. They are defin

</div>

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):

<!-- prettier-ignore-start -->

Expand Down
39 changes: 39 additions & 0 deletions docs/snippets/angular/list-story-with-subcomponents.ts.mdx
Original file line number Diff line number Diff line change
@@ -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<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<List>;

export const Empty: Story = {};

export const OneItem: Story = {
args: {},
render: (args) => ({
props: args,
template: `
<app-list>
<app-list-item></app-list-item>
</app-list>
`,
}),
};
```
22 changes: 22 additions & 0 deletions docs/snippets/react/list-story-with-subcomponents.js.mdx
Original file line number Diff line number Diff line change
@@ -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) => (
<List {...args}>
<ListItem />
</List>
),
};
```
26 changes: 26 additions & 0 deletions docs/snippets/react/list-story-with-subcomponents.ts-4-9.mdx
Original file line number Diff line number Diff line change
@@ -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<typeof List>;
export default meta;

type Story = StoryObj<typeof meta>;

export const Empty: Story = {};

export const OneItem: Story = {
render: (args) => (
<List {...args}>
<ListItem />
</List>
),
};
```
26 changes: 26 additions & 0 deletions docs/snippets/react/list-story-with-subcomponents.ts.mdx
Original file line number Diff line number Diff line change
@@ -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<typeof List> = {
component: List,
subcomponents: { ListItem }, //👈 Adds the ListItem component as a subcomponent
};
export default meta;

type Story = StoryObj<typeof List>;

export const Empty: Story = {};

export const OneItem: Story = {
render: (args) => (
<List {...args}>
<ListItem />
</List>
),
};
```
27 changes: 27 additions & 0 deletions docs/snippets/vue/list-story-with-sub-components.js.mdx
Original file line number Diff line number Diff line change
@@ -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: '<List/>',
}),
};

export const OneItem = {
render: (args) => ({
components: { List, ListItem },
setup() {
return { args }
}
template: '<List v-bind="args"><ListItem /></List>',
}),
};
```
32 changes: 32 additions & 0 deletions docs/snippets/vue/list-story-with-sub-components.ts-4-9.mdx
Original file line number Diff line number Diff line change
@@ -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<typeof List>;
export default meta;

type Story = StoryObj<typeof meta>;

export const Empty: Story = {
render: () => ({
components: { List },
template: '<List />',
}),
};

export const OneItem: Story = {
render: (args) => ({
components: { List, ListItem },
setup() {
return { args }
}
template: '<List v-bind="args"><ListItem /></List>',
}),
};
```
32 changes: 32 additions & 0 deletions docs/snippets/vue/list-story-with-sub-components.ts.mdx
Original file line number Diff line number Diff line change
@@ -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<typeof List> = {
component: List,
subcomponents: { ListItem }, //👈 Adds the ListItem component as a subcomponent
};
export default meta;

type Story = StoryObj<typeof List>;

export const Empty: Story = {
render: () => ({
components: { List },
template: '<List />',
}),
};

export const OneItem: Story = {
render: (args) => ({
components: { List, ListItem },
setup() {
return { args }
}
template: '<List v-bind="args"><ListItem /></List>',
}),
};
```
20 changes: 20 additions & 0 deletions docs/snippets/web-components/list-story-with-subcomponents.js.mdx
Original file line number Diff line number Diff line change
@@ -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`
<demo-list>
<demo-list-item></demo-list-item>
</demo-list>
`,
};
```
25 changes: 25 additions & 0 deletions docs/snippets/web-components/list-story-with-subcomponents.ts.mdx
Original file line number Diff line number Diff line change
@@ -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`
<demo-list>
<demo-list-item></demo-list-item>
</demo-list>
`,
};
```
30 changes: 30 additions & 0 deletions docs/writing-docs/autodocs.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

<!-- prettier-ignore-start -->

<CodeSnippets
paths={[
'react/list-story-with-subcomponents.js.mdx',
'react/list-story-with-subcomponents.ts.mdx',
'angular/list-story-with-subcomponents.ts.mdx',
'vue/list-story-with-sub-components.js.mdx',
'vue/list-story-with-sub-components.ts.mdx',
'web-components/list-story-with-subcomponents.js.mdx',
'web-components/list-story-with-subcomponents.ts.mdx',
]}
usesCsf3
csf2Path="writing-stories/stories-for-multiple-components#snippet-list-story-with-subcomponents"
/>

<!-- prettier-ignore-end -->

![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.
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
33 changes: 31 additions & 2 deletions docs/writing-stories/stories-for-multiple-components.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

<!-- prettier-ignore-start -->

<CodeSnippets
paths={[
'react/list-story-with-subcomponents.js.mdx',
'react/list-story-with-subcomponents.ts.mdx',
'angular/list-story-with-subcomponents.ts.mdx',
'vue/list-story-with-sub-components.js.mdx',
'vue/list-story-with-sub-components.ts.mdx',
'web-components/list-story-with-subcomponents.js.mdx',
'web-components/list-story-with-subcomponents.ts.mdx',
]}
usesCsf3
csf2Path="writing-stories/stories-for-multiple-components#snippet-list-story-with-subcomponents"
/>

<!-- prettier-ignore-end -->

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`:

<!-- prettier-ignore-start -->

Expand Down

0 comments on commit b34c5dd

Please sign in to comment.