美文网首页
组件库开发—Storybook生成UI文档

组件库开发—Storybook生成UI文档

作者: 说叁两事 | 来源:发表于2022-01-24 16:32 被阅读0次

    简介

    Storybook是一个UI组件的开发环境。

    使用

    初始化StoryBook环境

    $ npx -p @storybook/cli sb init
    

    storybook自动检测开发环境,安装依赖。

    执行以上命令行会进行以下操作:

    1. 自动生成以下目录结构:

    ├─.storybook // Storybook 全局配置文件
        ├─ main.js // 入口文件
        └─ preview.js // 页面展示、全局资源配置
    └─stories // 示例代码
        └─assets
    

    2. 更新pkg#run-scripts:

    ...
      "scripts": {
        "storybook": "start-storybook -p 6006 -h 0.0.0.0", // 启动本地服务以预览
        "build-storybook": "build-storybook"  // 构建
      },
    ...
    
    

    核心文件

    main.js

    module.exports = {
      "stories": [  // 组件Stories目录所在 —— Storybook会载入配置路径下的指定文件渲染展示
        "../stories/**/*.stories.mdx",
        "../stories/**/*.stories.@(js|jsx|ts|tsx)"
      ],
      "addons": [  // Storybook所用插件 —— Storybook功能增强
        "@storybook/addon-links",
        "@storybook/addon-essentials"
      ],
      "framework": "@storybook/vue3" // Storybook所用框架 —— Vue环境支持
    }
    

    该文件定义StoryBook与编译相关的配置。

    preview.js

    export const parameters = {
      actions: { argTypesRegex: "^on[A-Z].*" },
      controls: {
        matchers: {
          color: /(background|color)$/i,
          date: /Date$/,
        },
      }
    }
    

    该文件引入全局依赖,定义StoryBook渲染相关的配置。

    简单示例

    入口配置

    更新.storybook/main.js,将组件所在目录注册到入口文件声明中:

    module.exports = {
      "stories": [  // 组件Stories目录所在 —— Storybook会载入配置路径下的指定文件渲染展示
        "../packages/**/*.stories.@(js|jsx|ts|tsx)"
      ],
      ...
    }
    

    组件Story编写

    import SubmitForm from "./index"; // 引入组件
    import { SchemaType, RuleTrigger } from "./src/schemas/baseSchema";
    
    const caseSchema = [ // 示例数据
      {
        key: "moduleName",
        name: "title",
        type: SchemaType.Text,
        label: "栏目名称",
        placeholder: "请输入栏目名称",
        attrs: {
          //
        },
        rules: [
          {
            required: true,
            message: "栏目名称必填~",
            trigger: RuleTrigger.Blur,
          },
        ],
      },
      ...
    ];
    
    export default {
      title: "ui组件/SubmitForm", // 展示标题:使用路径定义命名空间 —— 分组、分类
      component: SubmitForm,
    };
    
    const Template = (args: any) => ({ // 渲染组件
      components: { SubmitForm },
      setup() {
        return {
          ...args,
        };
      },
      template: '<submit-form :schema="schema"></submit-form>',
    });
    
    export const 基本应用 = Template.bind({}); // 组件应用示例
    
    (基本应用 as any).args = {
      schema: caseSchema,
      ref: "submitFormRef",
    };
    

    其中,

    默认导出的是组件的元数据,包含归属组件、组件所属StoryBook文档分类、组件参数交互式声明...

    更多配置参见:

    ArgsTable配置

    Contorl配置

    命名导出的Story ( export const 基本应用 = Template.bind({}); ) 是一个函数,变量名为StoryBook文档展示的标题,另一种导出方式参考下文。

    全局依赖配置

    因为示例代码中依赖element-plus,通过上述展现的页面没有样式,所以,StoryBook渲染需要额外引入element-plus主题:

    // .storybook/preview.js
    import "element-plus/lib/theme-chalk/index.css";
    
    export const parameters = {
      actions: { argTypesRegex: "^on[A-Z].*" },
      controls: {
        matchers: {
          color: /(background|color)$/i,
          date: /Date$/,
        },
      }
    }
    

    启动本地服务

    启动服务

    更新pkg#storybook:

      // package.json
      "scripts": {
        "storybook": "start-storybook -p 6006 -h 0.0.0.0",
        "build-storybook": "build-storybook"
      },
    
    

    命令行执行:

    $ npm run storybook
    
    

    效果展示

    image

    默认参数栏只展示两项,如需更多参数信息,修改 preview.js 文件:

    export const parameters = {
      actions: { argTypesRegex: "^on[A-Z].*" },
      controls: {
        matchers: {
          color: /(background|color)$/i,
          date: /Date$/,
        },
      },
      controls: {
        expanded: true // 展开所有参数信息
      }
    }
    
    
    image

    在Stories中使用第三方UI库

    以ElementPlus为例:

    全局配置

    如果 babel.config 没有配置按需加载,可直接编辑.storybook/preview.js

    // .storybook/preview.js
    import elementPlus from 'element-plus';
    import { app } from '@storybook/vue3'
    
    app.use(elementPlus);
    export const decorators = [
      (story) => ({
        components: { story, elementPlus },
        template: '<elementPlus><story/></elementPlus>'
      })
    ];
    import "element-plus/lib/theme-chalk/index.css";
    
    export const parameters = {
      actions: { argTypesRegex: "^on[A-Z].*" },
      controls: {
        matchers: {
          color: /(background|color)$/i,
          date: /Date$/,
        },
      }
    }
    

    Notes:配置按需加载后,import elementPlus from 'element-plus';导入elementPlus报错:elementPlus is not defined —— 全局加载、按需加载不能在同一项目中使用。

    按需加载

    在需要使用ElementPlus的Stories中直接引入即可:

    // SubmitForm.stories.ts
    import { ElButton } from 'element-plus';
    import SubmitForm from "./index";
    import { SchemaType, RuleTrigger } from "./src/schemas/baseSchema";
    
    const caseSchema = [
      {
        key: "moduleName",
        name: "title",
        type: SchemaType.Text,
        label: "栏目名称",
        placeholder: "请输入栏目名称",
        attrs: {
          //
        },
        rules: [
          {
            required: true,
            message: "栏目名称必填~",
            trigger: RuleTrigger.Blur,
          },
        ],
      },
      ...
    ];
    
    export default {
      title: "ui组件/SubmitForm",
      component: SubmitForm,
    };
    
    const Template = (args: any) => ({
      components: { SubmitForm, ElButton },
      setup() {
        return {
          ...args,
        };
      },
      template: '<submit-form :schema="schema" ref="submitFormRef"></submit-form><el-button @click="submit">提交</el-button>',
    });
    export const 基本应用 = Template.bind({});
    (基本应用 as any).args = {
      schema: caseSchema,
    };
    

    补充已有示例交互

    // SubmitForm.stories.ts
    import { ElButton } from "element-plus";
    import { ref } from "vue";
    import SubmitForm from "./index";
    import { SchemaType, RuleTrigger } from "./src/schemas/baseSchema";
    
    const caseSchema = [
      {
        key: "moduleName",
        name: "title",
        type: SchemaType.Text,
        label: "栏目名称",
        placeholder: "请输入栏目名称",
        attrs: {
          //
        },
        rules: [
          {
            required: true,
            message: "栏目名称必填~",
            trigger: RuleTrigger.Blur,
          },
        ],
      },
      ...
    ];
    
    export default {
      title: "ui组件/SubmitForm",
      component: SubmitForm,
    };
    const Template = (args: any) => ({
      components: { SubmitForm, ElButton },
      setup() {
        const { refName } = args;
        const submitFormRef = ref();
        function submit() {
          console.log(submitFormRef.value.values);
        }
        function onRuntimeChange(name: string, value: any) {
          console.log(name, " = ", value);
        }
        return {
          submit,
          onRuntimeChange,
          [refName]: submitFormRef,
          ...args,
        };
      },
      template: `
          <submit-form :schema="schema" :ref="refName" @runtimeChange="onRuntimeChange"></submit-form>
          <el-button @click="submit">提交</el-button>
        `,
    });
    export const 基本应用 = Template.bind({});
    
    (基本应用 as any).args = {
      refName: "submitFormRef",
      schema: caseSchema,
    };
    

    这里做了两件事:

    • 增加提交按钮

    • 增加数据提交交互

    配置参数文档

    默认文档展示

    默认查看到的文档是两栏展示:

    image

    更新 .storybook/preview.js 文件:

    export const parameters = {
      actions: { argTypesRegex: "^on[A-Z].*" },
      controls: {
        matchers: {
          color: /(background|color)$/i,
          date: /Date$/,
        },
      },
      controls: {
        expanded: true
      }
    }
    

    参数的所有配置都展示:

    image

    参数配置

    通过配置argTypes可以补充参数信息:

    // SubmitForm.stories.ts
    ...
    export default {
      title: "ui组件/SubmitForm",
      component: SubmitForm,
      argTypes: {
        refName: {
          description: '表单组件引用',
          type: {
            required: true,
          },
          table: {
            defaultValue: {
              summary: 'defaultNameRef',
            }
          },
          control: {
            type: 'text'
          }
        },
        schema: {
          type: {
            required: true,
          },
          table: {
            type: {
              summary: '渲染表单所需JSON结构',
              detail: 'JSON结构包含表单渲染、交互所需要的必要字段,也包含表单的校验规则',
            },
            defaultValue: {
              summary: '[]',
              detail: `[
                  {
                    key: "moduleName",
                    name: "title",
                    type: SchemaType.Text,
                    label: "栏目名称",
                    placeholder: "请输入栏目名称",
                    attrs: {
                      //
                    },
                    rules: [
                      {
                        required: true,
                        message: "栏目名称必填~",
                        trigger: RuleTrigger.Blur,
                      },
                    ],
                  }
                ]
              `
            }
          }
        },
        runtimeChange: {
          description: '实时监听表单的更新',
          table: {
            category: 'Events',
          },
        }
      }
    };
    ...
    

    更多相关配置见:

    ArgsTable配置

    Contorl配置

    StoryBook功能模块

    一个Story是以函数形式描述如何渲染组件的方式。

    args提供动态参数,提供省时的便利:

    • 参数可在Controls面板实时编辑,可检测组件在不同参数下的状态。

    • 事件可在Actions面板查看日志输出。

      • 需要配置actions

    自定义Stories展示名称

    命名模块

    export const 自定义名称 = () => ({
      components: { Button },
      template: '<Button primary label="Button" />',
    });
    

    【推荐】storyName属性设定

    export const Primary = () => ({
      components: { Button },
      template: '<Button primary label="Button" />',
    });
    Primary.storyName = '自定义名称';
    
    

    Args

    提供交互动态修改Props、Slots、styles、inputs...的方式,允许在Storybook交互界面中实时编辑,不必改动底层组件代码。

    通过Storybook交互界面指定Args

    界面直接修改

    通过URL设定Args

    ?path=/story/avatar--default&args=style:rounded;size:100

    由于安全策略(XSS攻击)和特殊值,传入URL需要处理,见详情

    全局依赖

    // .storybook/preview.js
    import { app } from '@storybook/vue3';
    import Vuex from 'vuex';
    
    //👇 Storybook Vue app being extended and registering the library
    app.use(Vuex);
    
    export const decorators = [
      (story) => ({
        components: { story },
        template: '<div style="margin: 3em;"><story /></div>',
      }),
    ];
    

    静态资源访问

    // .storybook/main.js
    
    module.exports = {
      stories: [],
      addons: [],
      staticDirs: ['../public'],
    };
    

    // .storybook/main.js
    
    module.exports = {
      staticDirs: [
         { 
            from: '../my-custom-assets/images', 
            to: '/assets' 
         }
      ],
    };
    

    查看更多配置

    定制化主题

    修改Logo

    安装依赖:

    npm i -D @storybook/addons @storybook/theming
    

    修改pkg#scripts

    // pkg#scripts
    "storybook": "start-storybook -p 6006 -s public",
    "build-storybook": "build-storybook -s public",
    

    新建.storybook/manager.js文件:

    import { addons } from "@storybook/addons";
    import theme from "./themes/theme";
    
    addons.setConfig({
      theme: theme
    })
    

    创建./storybook/themes/theme.js:

    // .storybook/themes/theme.js
    import { create } from '@storybook/theming';
    
    export default create({
      base: 'light',
      brandTitle: 'Custom StoryBook', // logo不展示时,替代文本alt
      brandImage: '/logo.png',
    });
    

    Notes:brandImage和brandTitle同时配置的情况下,只有一项起作用,优先级brandImage > brandTitle

    Notes:自定义主题时,base配置是必填的。

    // package.json
    {
      ...
      "scripts": {
        "replace": "rimraf storybook-static/favicon.ico && cpr .storybook/themes/favicon.ico storybook-static/favicon.ico",
        "storybook": "start-storybook -p 6006 -h 0.0.0.0",
        "build-storybook": "build-storybook && npm run replace"
      },
    }
    

    Notes:打包的话,需要用本地图标替换storybook包内的默认图标。

    这里使用了Cli参数-s指定静态文件访问地址,更推荐在main.js中配置:

    // .storybook/main.js
    
    module.exports = {
      stories: [],
      addons: [],
      staticDirs: ['/public'],
    };
    

    修改站点Title、favicon

    新增.storybook/manager-head.html:

    <link rel="shortcut icon" href="/favicon.ico">
    <script>
      var observer = new MutationObserver(function(mutations) {
        if (document.title.match(/Storybook$/)) {
          document.title = "M.UI | GameCenter";
        }
      }).observe(document.querySelector("title"), {
        childList: true,
        subtree: true,
        characterData: true
      });
    </script>
    

    参见更多配置

    组件样式—Scss

    组件样式需要storybook-addon的支持

    npm i -D @storybook/preset-scss
    
    

    修改.storybook/main.js

    module.exports = {
      "stories": [
        "../packages/**/*.stories.@(js|jsx|ts|tsx)",
      ],
      "addons": [
        "@storybook/addon-links",
        "@storybook/addon-essentials",
        "@storybook/preset-scss"
      ],
      "framework": "@storybook/vue3"
    }
    

    Rem支持

    替换@storybook/preset-scss为@storybook/addon-postcss:

    npm uninstall -D @storybook/preset-scss
    npm i -D @storybook/addon-postcss
    # 修改webpack内核为版本5
    # 初始化环境时修改
    npx -y sb init --builder webpack5
    # 初始化时未设定,后续修改
    npm i -D @storybook/builder-webpack5
    npm i -D @storybook/manager-webpack5
    
    // 修改.storybook/main.js
    module.exports = {
      "stories": [
        "../packages/**/*.stories.@(js|jsx|ts|tsx)",
      ],
      "addons": [
        "@storybook/addon-links",
        "@storybook/addon-essentials",
        {
          name: '@storybook/addon-postcss',
          options: {
            postcssLoaderOptions: {
              implementation: require('postcss'),
            },
            sassLoaderOptions: {
              implementation: require('sass'),
            }
          },
        }
      ],
      core: {
        builder: 'webpack5',
      },
      webpackFinal: (config) => {
        config.module.rules.push({
          test: /\.scss$/,
          use: ['style-loader', 'css-loader', 'postcss-loader', 'sass-loader'],
        });
        return config
      },
      "framework": "@storybook/vue3"
    }
    

    在preview.js中引入flexible.js,并定义视窗:

    import { INITIAL_VIEWPORTS } from '@storybook/addon-viewport';
    import '../assets/js/flexible.all';
    import './assets/stylesheets/sb.scss';
    import '../assets/stylesheets/reset.scss';
    
    export const parameters = {
      actions: { argTypesRegex: "^on[A-Z].*" },
      controls: {
        matchers: {
          color: /(background|color)$/i,
          date: /Date$/,
        },
      },
      viewport: {
        viewports: INITIAL_VIEWPORTS, // newViewports would be an ViewportMap. (see below for examples)
        defaultViewport: 'iphone6',
      },
      controls: {
        expanded: true
      }
    }
    

    详细内容参见文章地址

    同类对比

    Decorator自定义画布、文档样式

    Decorator通过包裹Story来增强其表现形式。
    作用域:全局 > Component > Story(按执行顺序排列)

    // js示例
    export default {
      title: 'YourComponent',
      component: YourComponent,
      decorators: [() => ({ template: '<div style="margin: 3em;"><story/></div>' })],
    };
    // mdx示例
    <Meta
      title="YourComponent"
      component={YourComponent}
      decorators={[
        () => ({
          template: '<div style="margin: 3em;"><story /></div>',
        }),
      ]}
    />
    

    Decorator可在.storybook/preview.js、组件声明、Story声明中定义,最终会合并,执行顺序为 Global > Component > Story,可通过定义ClassName的命名空间来定制样式。

    更多配置: https://storybook.js.org/docs/vue/writing-stories/decorators#wrap-stories-with-extra-markup

    画布、文档的分离

    默认文档中包含画布信息,若不想文档中渲染Story,需要写.stories.js和.stories.mdx两个文件
    其中,.stories.js中默认导出不添加任何元数据,转移至.stories.mdx中,示例如下:

    // *.stories.ts
    export default {}
    const Template = (args: any) => ({
      components: {
        Drawer
      },
      props: Object.keys(args),
      methods: {
        onClose: action('onClose'),
        onStretched: action('onStretched')
      },
      setup () {
        const state = reactive({
          ...args
        })
        function toogleVisible () {
          state.visible = true
        }
        return {
          state,
          toogleVisible
        }
      },
      template: `
        <Drawer v-bind="state" v-model:visible="state.visible" @close="onClose" @stretched="onStretched">
        </Drawer>
        <button @click="toogleVisible">切换可见性</button>
      `
    })
    export const Primary = Template.bind({}) as any
    (Primary as any).args = {
      visible: false,
      title: '示例'
    }
    Primary.parameters = { docs: { disable: true } };
    
    
    // *.stories.mdx
    import { Meta, Story } from '@storybook/addon-docs'
    import { Primary } from './Drawer.stories.ts';
    import Drawer from './index'
    export const argsType = {
      visible: {
        description: '是否显示Dialog,支持.sync修饰符',
        type: {
          required: true
        },
        table: {
          type: {
            summary: 'Boolean'
          },
          defaultValue: {
            summary: false
          }
          // category: 'Boolean' // 参数分组
          // subcategory: 'Button colors', // 子分组
        },
        control: 'boolean'
      },
      ...
    }
    # Drawer
    
    ## 基本用法
    <Story
      name="Drawer"
       decorators={[
        () => ({
          template: '<div id="custom-root" style="background: red;"><story /></div>',
        })
      ]}
      story={Primary} />
    
    ## 自定义内容
    
    ## 参数文档
    
    <Meta 
      title="组件/Basic/Drawer" 
      component={Drawer} 
      argTypes={{
        ...argsType
      }}
    />
    

    parameters = { docs: { disable: true } }可在docs中禁止渲染Story
    https://storybook.js.org/docs/vue/api/mdx#documentation-only-mdx
    https://github.com/storybookjs/storybook/blob/master/addons/docs/docs/recipes.md#docspage
    https://github.com/storybookjs/storybook/blob/master/addons/docs/docs/mdx.md#documentation-only-mdx

    Notes:

    • mdx文件中jsx和markdown语法之间要用空行分隔;
    • jsx定义对象,尤其是空对象,不能有多余的空行;
    export const argTypes = {
    
    }
    # 这样会报错
    

    正确写法:

    export const argTypes = {}
    
    # 这样才能正确解析
    

    侧边栏忽略子节点

    若不想要侧边栏创建子节点,可以定义Story.storyName与export default的组件title保持一致:

    // 示例
    export default {
      title: '组件/Basic/Drawer'
    }
    export const Primary = Template.bind({}) as any
    Primary.storyName = 'Drawer';
    

    调整docs优先展示权

    默认优先展示stories,可以通过优先展示docs

    parameters = {
      docs: {
        disable: true
      },
      viewMode: 'docs'
    }
    
    

    https://github.com/storybookjs/storybook/blob/761501bf1344c5bab34e881702cc1938557b611c/addons/docs/docs/recipes.md#controlling-a-storys-view-mode

    隐藏Canvas

      previewTabs: {
        canvas: {
            hidden: true,
        }
      },
    

    可以隐藏当前Stories的Canvas面板

    修改Logo跳转地址

    // .storybook/themes/theme.js
    import { create } from '@storybook/theming';
    
    export default create({
      base: 'light',
      brandTitle: 'StoryBook',
      brandUrl: '/?path=/docs/快速开始--primary',
      brandImage: '/logo.png',
    });
    

    关闭Addons

    showPanel无论设置在哪一层级,都是全局的

    parameters = {
      docs: {
        disable: true
      },
      controls: {
        disable: true
      },
      options: {
        showPanel: false
      }
    }
    
    

    MDX写法

    动机

    上述的写法为CSF,component story format,是 Storybook 官方推荐的一种基于 ES module 的 stories 编写方法,由一个 export default 和一个或多个 export 组成。

    它是Storybook 提供了一些封装过后的组件供使用,让我们能够较为快速的生成 stories。

    代价是灵活度会相对的没有高,当然,如果只是简单的展示组件及其接收参数,那其实已经完全足够了。

    可如果在展示组件之余,还想要编写一个额外的文档,比如介绍一下组件封装的背景,用到的技术等,CSF 就不是那么好用了。

    基于这样的需求,Storybook 也支持使用 MDX 格式编写 stories。

    MDX,如同 TSX,就是一种能够在 Markdown 文档中写 JSX 的格式。使用 MDX 格式编写 stories,文字部分内容的编写会更加灵活,没有了官方预置的内容,真正的所写即所得。

    MDX示例

    import { ArgsTable, Canvas, Meta, Story } from '@storybook/addon-docs';
    import { action } from '@storybook/addon-actions'
    import { reactive } from 'vue'
    import Dialog from './index'
    
    # Dialog
    
    export const argsType = {
      visible: {
        description: '是否显示Dialog,支持.sync修饰符',
        type: {
          required: true
        },
        table: {
          type: {
            summary: 'Boolean'
          },
          defaultValue: {
            summary: false
          }
          // category: 'Boolean' // 参数分组
          // subcategory: 'Button colors', // 子分组
        },
        control: 'boolean'
      },
      showCancel: {
        description: '展示独立的关闭按钮X',
        table: {
          type: {
            summary: 'Boolean'
          },
          defaultValue: {
            summary: true
          }
        },
        control: 'boolean'
      },
      title: {
        description: 'Dialog的标题,也可通过具名slot(见下表)传入',
        table: {
          defaultValue: {
            summary: '示例Dialog'
          }
        },
        control: 'text'
      },
      rawHtml: {
        description: 'dialog主体内容',
        table: {
          type: {
            summary: 'string / htmlString'
          },
          defaultValue: {
            summary: 'DJ小能手',
            detail: '<span style="color: red;">DJ</span>小能手'
          }
        },
        control: 'text'
      },
      confirm: {
        description: '确认相关的配置项目',
        mapping: {
          label: '确定按钮的文本内容',
          handler: '确认的回调'
        },
        options: ['label', 'handler'],
        table: {
          type: {
            summary: 'Object',
            detail: `
              confirm.label: 确定按钮的文本内容;
              confirm.handler: 确认的回调;
            `
          }
        },
        control: 'object'
      },
      cancel: {
        description: '取消相关的配置项目',
        table: {
          type: {
            summary: 'Object',
            detail: `
            cancel.label: 取消按钮的文本内容;
            cancel.handler: 取消的回调;`
          }
        },
        control: 'object'
      },
      header: {
        description: 'Dialog标题区的内容',
        table: {
          type: {
            summary: 'Vnode'
          },
          defaultValue: ''
        },
        control: 'text',
        category: 'Slots'
      },
      default: {
        description: 'Dialog的内容',
        table: {
          type: {
            summary: 'Vnode'
          },
          defaultValue: ''
        },
        control: 'text',
        category: 'Slots'
      },
      'update:visible': {
        table: {
          disable: true
        }
      }
    }
    
    export const actionData = {
      updateVisible: action('update:visible')
    }
    
    ## 参数文档
    
    <Meta
      title="组件/Basic/Dialog"
      component={Dialog}
      argTypes={{
        ...argsType
      }}
    />
    
    <ArgsTable story="基本用法" />
    
    ## 基本用法
    
    export const argsData = {
      visible: false,
      showCancel: true,
      confirm: {
        label: '确定',
        handler () {
          console.log('确定')
        }
      },
      cancel: {
        handler () {
          console.log('X')
        }
      }
    }
    
    export const HTMLTemplate = `
      <Dialog v-bind="state" v-model:visible="state.visible" @update:visible="updateVisible">
        <template v-slot:header v-if="state.header">
          <header v-html="state.header"></header>
        </template>
        <template v-slot:default v-if="state.default">
          <main v-html="state.default"></main>
        </template>
      </Dialog>
      <button @click="toggleVisible">切换可见性</button>
    `
    
    export const ConstructorFactory = (args) => ({
      components: { Dialog },
      props: Object.keys(args),
      setup () {
        const state = reactive({
          ...args
        })
        function toggleVisible () {
          state.visible = true
        }
        return {
          state,
          toggleVisible
        }
      },
      methods: {
        ...actionData
      },
      template: HTMLTemplate
    })
    
    <Canvas
      mdxSource={HTMLTemplate}
    >
      <Story
        name="基本用法"
        args={{...argsData}}
      >
        {
          ConstructorFactory.bind({})
        }
      </Story>
    </Canvas>
    
    

    内置组件

    Meta

    声明本 MDX 文件渲染的页面的标题,对应的组件等。作用和 CSF 写法中的 export default 一致;

    ArgsTable

    自定义arguments类型,用于Props、Slots、Events展示与交互。

    属性 示例 属性说明 属性值 属性值示例
    of <ArgsTable of={ComponentObj} /> 自动解析组件的Props、Slots、Events等声明,依据Storybook内置类型声明输出Storybook文档 import导入的组件对象 import Dialog from './index'的"Dialog"
    story <ArgsTable story="StoryNameString" /> 自定义arguments,需要使用story属性承接arguments类型声明。 Story标签的name属性值 <Story name="storyName">的"storyName"

    Canvas

    生成一个 Canvas 画板,用于展示我们自己编写的组件。

    画布会提供一些便捷的功能,比如展示当前组件的源代码等;

    Canvas默认将Stroybook的Template函数作为源码输出,若想自定义源码输出,将源码作为属性mdxSource的值即可。

    点击链接查看更多属性配置

    Story

    生成一个 story。

    Notes:并不是一定要把 story 写在 <Canvas />组件中,Canvas 组件只是能够提供一些其他的功能(展示源码、复制代码之类的)。直接编写 story 的话,组件也能正常渲染。

    image

    区别在于,写在<Canvas />中,Canvas会提供便捷的功能:工具栏、源代码查看...

    vscode语法提示插件

    名称: MDX

    ID: silvenon.mdx
    说明: Provides syntax highlighting and bracket matching for MDX (JSX in Markdown) files.
    版本: 0.1.0
    发布者: Matija Marohnić
    VS Marketplace 链接: https://marketplace.visualstudio.com/items?itemName=silvenon.mdx

    名称: MDX Preview

    ID: xyc.vscode-mdx-preview
    说明: MDX Preview
    版本: 0.3.3
    发布者: Xiaoyi Chen
    VS Marketplace 链接: https://marketplace.visualstudio.com/items?itemName=xyc.vscode-mdx-preview

    名称: Vue Storybook Snippets

    ID: megrax.vue-storybook-snippets
    说明: Storybook Snippets for Vue
    版本: 0.0.7
    发布者: Megrax
    VS Marketplace 链接: https://marketplace.visualstudio.com/items?itemName=Megrax.vue-storybook-snippets

    参考文档:

    相关文章

      网友评论

          本文标题:组件库开发—Storybook生成UI文档

          本文链接:https://www.haomeiwen.com/subject/zevbhrtx.html