用storybook来管理你的UI组件库

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了用storybook来管理你的UI组件库相关的知识,希望对你有一定的参考价值。

参考技术A 简述

可以帮助构建 UI 组件,组合组件,说明文档,测试和部署 UI 组件的工具,与业务功能隔离开来,专注于开发组件。

支持框架 React,Vue等

提供丰富的插件供开发使用,比如docs可以生产组件参数说明(参数描述,参数定义,支持的类型等), knobs 帮助您在隔离构建UI组件时 可以动态地修改组件的参数来验证交互;  notes可以展示你的md文档;addon-storysource可以直接在页面看到你的源码;一键生成所有截图的Storybook Chrome Screenshot Addon。这些社区的addons都非常实用。感兴趣可以自己增加

支持模拟不同设备下的组件

jest自动化测试组件还有依靠视觉,快照和视觉回归测试

灵活度很高,如果想使用js/ts,css,/scss/styled-components, redux 都是可以的,在其他组件库基础上进行二次开发,需要自行配置项目

安装

 npx sb init 然后选择react, 回车下一步

 yarn storybook

开始

写一个  stories/*.stories.js

import React from 'react';

// 生产storybook文档目录,Design System大类下一个Table组件

export default

title: 'Design System/Table',

component:

;

// 组件

export const Basic = () =>

return



=> 生成的storybook 如下图

如何配合lerna发包:

大家可以看下eric和david同学的分享 使用Lerna管理发布javascript包 和 如何开发和发布npm包

如何安装storybook(https://storybook.js.org/addons/)插件库:在文件夹.storybook内添加配置即可

// .main.js

module.exports =

"stories": [

"../stories/**/*.stories.mdx",

"../stories/**/*.stories.@(js|jsx|ts|tsx)"

],

"addons": [

"@storybook/addon-links",

"@storybook/addon-essentials"

]



其他同类型产品:

docz:  https://github.com/doczjs/docz/ https://www.docz.site/

Styleguidist  https://react-styleguidist.js.org/通过Markdown文档化组件

资源

教程:https://www.learnstorybook.com/intro-to-storybook/react/zh-CN/get-started/

官网:https://storybook.js.org/

medium:https://medium.com/storybookjs

其他

别人家怎么用storybook的:

http://storybook.anyvision.co/?path=/story/style-colors--base 

https://reaviz.io/?path=/story/docs-advanced-custom-charts–page

https://reaviz.io/?path=/docs/charts-sankey-plot--autosize

React 17 + StoryBook 打造自己团队的UI库

前言

为了提升团队前端工作效率,打造一套团队UI库是有一个有效的方法。既可以减少重复劳动,又可以提高组件的复用率与统一性。

创建了团队UI库后,就很有必要搭建一个静态文档网站,用于规范UI库的开发扩展以及规范使用。毕竟这东西不是你一个人开发的,也不是面向你一个人用的,有手册可以查,当然比每天都去问开发人员或者自己摸索来的快。

以前是比较趋向于Docz这种静态文档生成插件,Docz的特色是零配置、简单、快速,它使用Markdown语法的扩展MDX(在 Markdown 里引入 React 组件并渲染出组件)来书写文档,对于熟悉Markdown的开发者是可以直接上手的。

但是类似于Docz这种在已经满足不了团队的需求了,我想要的不仅仅是一个文档手册集合,我还想要在线调试,实时查看组件效果。这时候就有了Storybook,Storybook是一个UI开发环境,允许开发者设置单独的开发工作、文档以及测试。

如何在React中引入Storybook

1、首先创建一个React + Storybook项目。

npx create-react-app myapp --template typescript  //创建React项目 v17.02
npx sb init // 构造成Storybook项目

构造完后的文件夹主要有如下的改变:


├── .storybook
│       │── main.js         //主文件
│       └── preview.js      //预览设置文件
│
├─ src 
│    └── stories
│             │── assets              //静态资源文件夹
│             │── button.css          //按钮样式文件
│             │── Button.tsx          //按钮主文件
│             │── Button.stories.tsx  //按钮文档页面
│             │── Introduction.stories.mdx //欢迎介绍页面,直接使用mdx文件,类似Docz
│             └── .....

因为这个目录结构是自动生成的,对于我们以后UI管理不太方便,所以我们稍微将文件目录再稍微改造下src目录结构。

在src下新建一个compnent文件夹,然后其中再以组件名文件夹分类,方便管理,如下:


├─ src 
│    │──component
│    │      │──  style  //样式文件
│    │      │        │── button.scss 
│    │      │        │── index.scss
│    │      │        └──  ... 
│    │      │──  button
│    │      │        │── button.tsx
│    │      │        │── Button.stories.tsx
│    │      └──  ... //参考上述文件夹
│    └── stories
│             │── assets              //静态资源文件夹
│             │── Introduction.stories.mdx //欢迎介绍页面,直接使用mdx文件,类似Docz
│             └── .....

2、main.js

因为修改了文件目录机构,所以我们也要对应的修改main.js的文件匹配机制。

const path = require(\'path\');

module.exports = {
       //使用scss,使用前记得安装对应的loader
    webpackFinal: async (config, { configType }) => {
        config.module.rules.push({
            test: /\\.scss$/,
            use: [\'style-loader\', \'css-loader\', \'sass-loader\'],
            include: path.resolve(__dirname, \'../\'),
        });

        // Return the altered config
        return config;
    },
    stories: [
            \'../src/**/*.stories.mdx\', 
            \'../src/component/**/*.stories.@(js|jsx|ts|tsx)\'
        ],
    addons: [\'@storybook/addon-links\', \'@storybook/addon-essentials\'],
};
其中主要是stories和addons,更多的配置项可以查看StoryBook Main Config。

stories:用来描述你的故事相对于配置文件的位置,从默认的配置可以看出格式为\'*.stories.@(js|jsx|ts|tsx)\';

addons: 用来描述你要引入的storybook插件。

webpackFinal:自定义webpack配置。

3、preview.js

import \'../src/component/style/index.scss\';  //引入全局样式文件
import { configure } from \'@storybook/react\';

//排列目录的顺序
const loaderFn = () => {
    const allExports = [
        require(\'../src/stories/Introduction.stories.mdx\'),
        require(`../src/component/button/button.stories.tsx`),
    ];
    return allExports;
};

configure(loaderFn, module);

export const parameters = {
    layout: \'centered\', // canvas页面示范元素位置居中
    actions: { argTypesRegex: \'^on[A-Z].*\' },
    controls: {
        expanded: true, //canvas页面显示描述文档的描述,类型,初始值
        matchers: {
            color: /(background|color)$/i,
            date: /Date$/,
        },
    },
};

主要是自定义左侧导航栏的顺序,还有一些页面元素的布局和样式引入。

4、组件故事模式

在使用storyBook接触最多的就是Component Story Format(CSF),这是使用了ES6提供的module编写的方式,我们可以看下一个我编写的例子,去更好的理解这种模式。


import { Meta, Story } from \'@storybook/react\';
import { Button as Component, ButtonProps } from \'./button\';
import { ButtonGroup } from \'../button-group/button-group\';

export default {
    title: \'Component/Base/Button\',
    component: Component,
    argTypes: {
        showGroup: {
            description: \'是否显示按钮组,仅供测试使用,非正式属性\',
            defaultValue: false,
            control: {
                type: \'boolean\',
            },
            table: {
                category: \'Test\',
            },
        },
        //button
        type: {
            description: \'类型\',
            defaultValue: \'default\',
            control: {
                type: \'select\',
                options: [\'default\', \'primary\', \'success\', \'warning\', \'danger\', \'text\'],
            },
            table: {
                category: \'Button\',
                type: { summary: \'default | primary  | success | warning | danger | text\' },
            },
        },
        size: {
            description: \'尺寸\',
            defaultValue: \'medium\',
            control: {
                type: \'select\',
                options: [\'mini\', \'small\', \'medium\', \'large\', \'big\', \'huge\'],
            },
            table: {
                category: \'Button\',
                type: { summary: \'mini | small | medium | large | big | huge\' },
            },
        },
        prefixIcon: {
            description: \'前置图标\',
            control: {
                type: \'text\',
            },
            table: {
                category: \'Button\',
            },
        },
        sufixIcon: {
            description: \'后置图标\',
            control: {
                type: \'text\',
            },
            table: {
                category: \'Button\',
            },
        },
        color: {
            description: \'颜色\',
            control: {
                type: \'color\',
            },
            table: {
                category: \'Button\',
            },
        },
        textColor: {
            description: \'文本颜色\',
            control: {
                type: \'color\',
            },
            table: {
                category: \'Button\',
            },
        },
        nativeType: {
            description: \'按钮原生类型,可参考<a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#attr-type">HTML标准</a>\',
            defaultValue: \'button\',
            control: {
                type: \'select\',
                options: [\'submit\', \'button\', \'reset\'],
            },
            table: {
                category: \'Button\',
                type: { summary: "submit | button | reset" },
            },
        },
        block: {
            description: \'是否为块元素\',
            control: {
                type: \'boolean\',
            },
            table: {
                category: \'Button\',
            },
        },
        disabled: {
            description: \'是否禁用\',
            control: {
                type: \'boolean\',
            },
            table: {
                category: \'Button\',
            },
        },
        plain: {
            description: \'是否开启幽灵按钮\',
            control: {
                type: \'boolean\',
            },
            table: {
                category: \'Button\',
            },
        },
        round: {
            description: \'是否开启圆角\',
            control: {
                type: \'boolean\',
            },
            table: {
                category: \'Button\',
            },
        },
        loading: {
            description: \'是否开启等待动效\',
            table: {
                category: \'Button\',
            },
        },
        to: {
            description: \'跳转地址,测试用例只支持文本输入\',
            control: {
                type: \'text\',
            },
            table: {
                category: \'Button\',
            },
        },
        onClick: {
            description: \'点击事件,返回Event\',
            control: {
                type: null,
            },
            table: {
                category: \'Button\',
            },
        },
        //ButtonGroup
        direction: {
            description: \'方向\',
            defaultValue: \'horizontal\',
            control: {
                type: \'select\',
                options: [\'horizontal\', \'vertical\'],
            },
            table: {
                category: \'ButtonGroup\',
            },
        },
        //hidden
        style: {
            table: {
                disable: true,
            },
        },
        children: {
            table: {
                disable: true,
            },
        },
        className: {
            table: {
                disable: true,
            },
        },
    },
    args: {
        showGroup: false,
        children: \'Preview\',
    },
} as Meta;

export const Button = (args: any) => {
    const { showGroup, direction, ...other } = args;
    if (showGroup) {
        return (
            <ButtonGroup direction={direction}>
                <Component {...other}>PreView</Component>
                <Component {...other}>Next</Component>
            </ButtonGroup>
        );
    } else {
        return <Component {...other}></Component>;
    }
};

export default 定义了组件的相关配置,其中分别为:

title:导航栏层级,用\'/\'号分隔开,而且title不能重复;
component:组件实际元素;
argTypes:文档描述项,默认会显示组件props的入参项,也可以自定增加或者重写,具体配置下面详解。
argTypes配置通过一个例子解释比较容易:

type: { // 字段名为type
   description: \'类型\',  // 属性描述
   defaultValue: \'default\',//开发环境默认值
   control: {
     type: \'select\', //文档控制器类型text(文本),select(选择)... 根据实际情况定义
     options: [\'default\', \'primary\', \'success\', \'warning\', \'danger\', \'text\'], //选项内容
   },
   table: {
     category: \'Button\', //定义描述分类,可多个组件联合开发区分使用
     type: { summary: \'default | primary  | success | warning | danger | text\' }, // 字段属性类型重写
    defaultValue:{summary:\'default\'} //文档描述显示的默认值 
  },
},

实际效果如下:

每个export const 定义侧边栏子菜单以及相关的文档,例子中是有两个组件的联合调试,可以在argTypes李增加一个showGroup字段,显示单个按钮还是按钮组。

虽然这个方法可以多个组件联合调试,但是功能也是有限的,不如直接代码编辑的快。

5、运行storybook

npm i -D node-sass@5.0 sass-loader@10.0.5 style-loader@1.3.0 //安装相关插件
npm run storybook
运行后的样子是这样的,从此你就可以开始扩展组件库了。

以上是关于用storybook来管理你的UI组件库的主要内容,如果未能解决你的问题,请参考以下文章

React 17 + StoryBook 打造自己团队的UI库

storybook创建vue组件库文档

在基于vue的组件库内使用Storybook搭建组件使用文档

用于设置服务器 url 的 Storybook 插件

:Element-ui组件库

:Element-ui组件库