Storybook 使用手册——编写组件文档

https://images.unsplash.com/photo-1585647347384-2593bc35786b?ixlib=rb-1.2.1&q=85&fm=jpg&crop=entropy&cs=srgb

其实在使用手册的前两篇中,已经实现了组件文档的编写。开发过程中编写Story的同时,创建了基本的文档。

此外,Storybook还提供了一些工具,可以用内容和布局来拓展这个基本文档,以突出组件和Story。这在创建组件库的使用指南,设计系统网站时非常好用。下面是一个来自官方的案例

image

如果是第一次在你的项目中加入Storybook,可以使用DocsPage,这是一个文档模板,列出了一个组件的所有 Story 和相关的元数据。它根据源代码、类型和JSDoc注释来推断元数据值。如果你需要,你可以定制这个页面来创建你自己的自定义模板。

如果你已经在使用Storybook并且更新到最新版本,建议安装@storybook/addon-essentials,以便将这个和其他功能纳入项目。

也可以使用MDX为每个组件创建自由格式的页面,这是一种同时记录组件和编写故事的格式。在这两种情况下,需要使用Doc Blocks 作为构建模块来创建全功能的文档。Docs是自动配置的,可以在大多数使用情况下开箱即用。在某些情况下,可能需要调整配置,具体的使用可以参考 addon-docs

DocsPage的使用

DocsPage 是一个零配置的默认文档,所有的Story都是开箱即得。它将Story、文本描述、docgen注释,args和代码实例都聚合到每一个组件的页面上。

你可以理解成,最后每个组件对应的那个文档页就是所谓的DocsPage

文档的最佳实践是,每个组件都有自己的DocPage 和Story。

组件参数

Storybook 使用Story文件中默认导出的 component 来提取组件的描述和属性。

// MyComponent.stories.js|jsx|ts|tsx

import { MyComponent } from './MyComponent';

export default {
  /* 👇 The title prop is optional.
  * See https://storybook.js.org/docs/react/configure/overview#configure-story-loading
  * to learn how to generate automatic titles
  */
  title: 'MyComponent',
  component: MyComponent,
};

// Your stories

子组件参数

有时候需要展示多个组件组合使用的场景,比如Button和ButtonGroup,Select和SelectOptions。DocsPage提供了 subcomponent 属性来接受多个子组件。

// ButtonGroup.stories.js|jsx

import { Button, ButtonGroup } from './ButtonGroup';

export default {
  /* 👇 The title prop is optional.
  * See https://storybook.js.org/docs/react/configure/overview#configure-story-loading
  * to learn how to generate automatic titles
  */
  title: 'ButtonGroup',
  component: ButtonGroup,
  subcomponents: { Button },
};

子组件的参数表格会以Tab的形式展示在文档中。

替换 DocsPage 的内容

当然你也可以自定义DocsPage的内容。

使用null去除文档

docs.page 设置为 null 可以去除文档的内容。

// Button.stories.js|jsx|ts|tsx

import { Button } from './Button';

export default {
  /* 👇 The title prop is optional.
  * See https://storybook.js.org/docs/react/configure/overview#configure-story-loading
  * to learn how to generate automatic titles
  */
  title: 'Button',
  component: Button,
  argTypes: {
    backgroundColor: { control: 'color' },
  },
  parameters: {
    docs: {
      page: null,
    },
  },
};

使用 MDX 写文档

用MDX编写你的文档,更新docs.page参数来显示它。引用的id遵循的模式是:group-subgroup-…-name,其中组和子组的定义是根据分组文档的。

首先编写单独的MDX文件

<!-- Custom-MDX-Documentation.mdx -->

# Replacing DocsPage with custom `MDX` content

This file is a documentation-only `MDX`file to customize Storybook's [DocsPage](https://storybook.js.org/docs/react/writing-docs/docs-page#replacing-docspage).

It can be further expanded with your own code snippets and include specific information related to your stories.

For example:

import { Story } from "@storybook/addon-docs";

## Button

Button is the primary component. It has four possible states.

- [Primary](#primary)
- [Secondary](#secondary)
- [Large](#large)
- [Small](#small)

## With the story title defined

If you included the title in the story's default export, use this approach.

### Primary

<Story id="example-button--primary" />

### Secondary

<Story id="example-button--secondary" />

### Large

<Story id="example-button--large" />

### Small

<Story id="example-button--small" />

## Without the story title defined

If you didn't include the title in the story's default export, use this approach.

### Primary

<Story id="your-directory-button--primary"/>

### Secondary

<Story id="your-directory-button--secondary"/>

### Large

<Story id="your-directory-button--large"/>

### Small

<Story id="your-directory-button--small" />

然后在Story中引入,

// Button.stories.js|jsx|ts|tsx

import { Button } from './Button';

import CustomMDXDocumentation from './Custom-MDX-Documentation.mdx';

export default {
  /* 👇 The title prop is optional.
  * See https://storybook.js.org/docs/react/configure/overview#configure-story-loading
  * to learn how to generate automatic titles
  */
  title: 'Button',
  component: Button,
  argTypes: {
    backgroundColor: { control: 'color' },
  },
  parameters: {
    docs: {
      page: CustomMDXDocumentation,
    },
  },
};

const Template = (args) => ({
  //👇 Your template goes here
});

export const Primary = Template.bind({});
Primary.args = {
  backgroundColor: 'primary',
};

export const Secondary = Template.bind({});
Secondary.args = {
  backgroundColor: 'secondary',
};

export const Large = Template.bind({});
Large.args = {
  size: 'large',
};

export const Small = Template.bind({});
Small.args = {
  size: 'small',
};

使用自定义的组件

Storybook的UI界面使用React开发的。如果你想使用一个自定义组件来展示文档,可能需要一些项目所需的配置和环境。然后用自定义的组件替换 docs.page 参数即可。比如下面是自定义的组件,

// CustomDocumentationComponent.js|jsx

import React from 'react';

export function CustomDocumentationComponent() {
  return (
    <div>
      <h1>Replacing DocsPage with a custom component</h1>
      <p>
        The Docs page can be customized with your own custom content written as a React Component.
      </p>
      <p>
       Write your own code here👇
      </p>
    </div>
  );
}

然后依样画葫芦替换参数,

// Button.stories.js|jsx|ts|tsx

import { Button } from './Button';

import { CustomDocumentationComponent } from './CustomDocumentationComponent';

export default {
  /* 👇 The title prop is optional.
  * See https://storybook.js.org/docs/react/configure/overview#configure-story-loading
  * to learn how to generate automatic titles
  */
  title: 'Button',
  component: Button,
  argTypes: {
    backgroundColor: { control: 'color' },
  },
  parameters: {
    docs: {
      **page: CustomDocumentationComponent,**
    },
  },
};

混合使用 Doc blocks

Doc blocks 是Storybook Docs的基本构建块。 DocsPage 对 Doc Block进行组合,提供一个靠谱的,开箱即用的UI文档体验。

如果你想对默认的DocsPage做一些小的定制,但又不想写你的MDX,你可以重新混合DocsPage。这样可以在不失去 Storybook 的自动文档生成功能的情况下,重新排序、添加或省略文档块。

下面是一个使用 Doc Block 为Button组件重建DocsPage的例子。

// Button.stories.js|jsx

import React from 'react';

import {
  Title,
  Subtitle,
  Description,
  Primary,
  ArgsTable,
  Stories,
  PRIMARY_STORY,
} from '@storybook/addon-docs';

import { Button } from './Button';

export default {
  /* 👇 The title prop is optional.
  * See https://storybook.js.org/docs/react/configure/overview#configure-story-loading
  * to learn how to generate automatic titles
  */
  title: 'Button',
  component: Button,
  parameters: {
    docs: {
      page: () => (
        <>
          <Title />
          <Subtitle />
          <Description />
          <Primary />
          <ArgsTable story={PRIMARY_STORY} />
          <Stories />
        </>
      ),
    },
  },
};

结束语

简单介绍了Storybook中DocsPage的概念,以及DocsPage的使用方法。文中也提到了MDX和DocBlock,接下来的内容将详细介绍这两个新鲜事物。