前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >使用storybook管理React组件

使用storybook管理React组件

作者头像
IMWeb前端团队
发布2019-12-03 18:24:48
3.2K0
发布2019-12-03 18:24:48
举报
文章被收录于专栏:IMWeb前端团队IMWeb前端团队

本文作者:IMWeb Terrance 原文出处:IMWeb社区 未经同意,禁止转载

Storybook is a development environment for UI components. It allows you to browse a component library, view the different states of each component, and interactively develop and test components.

2018年10月storybook发布了4.0版本,在UI层支持、构建、移动端、stroy参数等多个方面进行了升级优化。本文已React的UI组件为例,演示如何新建/集成Storybook到项目中,并对UI组件进行全方位的管理,包括发布、demo文档、测试等。

1. 新建一个Storybook React项目

  • 按照官方教程使用npx -p [@storybook](/user/storybook)/cli sb init安装,一直会报错:
代码语言:javascript
复制
TypeError: Cannot create property 'dependencies' on boolean 'false'
  • 我采用的是手动创建的方式
    1. 首先在React项目中手动添加@storybook/react和babel依赖和运行脚本
代码语言:javascript
复制
"scripts": {
    "storybook": "start-storybook -p 9001 -c .storybook"
 }
"devDependencies": {
    "[@storybook](/user/storybook)/addon-actions": "^4.0.11",
    "[@storybook](/user/storybook)/react": "^4.0.11",
    "babel-core": "^6.26.3",
    "babel-loader": "^7.1.5"
},
"dependencies": {
    "react": "^16.6.3",
    "react-dom": "^16.6.3"
}

PS:由于babel-loader的最新版本是v8,需要babel版本是v7,所以按照官方教程直接安装babel-core(最高版本是v6)运行会失败,这里选择安装的是babel6。

  1. 添加storybook配置文件
代码语言:javascript
复制
import { configure, addDecorator } from '[@storybook](/user/storybook)/react';
function loadStories() {
  require('../stories/index.js');
  // You can require as many stories as you need.
}
configure(loadStories, module);
  1. 添加story
代码语言:javascript
复制
// /stories/index.js
import React from 'react';
import { storiesOf } from '[@storybook](/user/storybook)/react';
import { Button } from '[@storybook](/user/storybook)/react/demo';

storiesOf('Button', module)
  .addDecorator(story => <div style={{ textAlign: 'center' }}>{story()}</div>)
  .add('with text abc', () => <Button onClick={action('clicked')}>hello world!</Button>, {
    notes: { markdown: docs },
  })
  .add('with some emoji', () => (
    <Button onClick={action('clicked')}>
      <span role="img" aria-label="so cool">
        ? ? ? ?
      </span>
    </Button>
  ), {
    notes: { markdown: docs },
  });
  1. 运行npm run storybook,这时启动一个server,并自动打开一个storybook的页面

2. 使用storybook的插件功能

storybook的很多功能都是靠插件来实现的,大多数插件都需要提前注册,在页面中有一个单独的tab来对storybook进行增强。

下面介绍几款官方插件:

代码语言:javascript
复制
// /.storybook/addons.js
import '[@storybook](/user/storybook)/addon-actions/register'; // 记录事件日志
import '[@storybook](/user/storybook)/addon-notes/register'; // story笔记文档,支持markdown
import '[@storybook](/user/storybook)/addon-options/register'; // storybook页面自定义
import '[@storybook](/user/storybook)/addon-links/register'; // storybook页面跳转
import '[@storybook](/user/storybook)/addon-knobs/register'; // 组件可视化配置

@storybook/addon-info插件比较特殊,不需要提前注册,它可以显示story的源码,并针对props提供一些文档。

3. 以一个分页组件为例

从团队的stoneUI组件库直接移植过来

  1. PaginationIconV组件源码放入components目录;
  2. 编写story:
代码语言:javascript
复制
import React from 'react';
import { storiesOf } from '[@storybook](/user/storybook)/react';
import { withKnobs, number } from '[@storybook](/user/storybook)/addon-knobs';

import Pagination from '../components/Pagination';
import paginationDoc from '../components/Pagination/readme.md';

storiesOf('Stone UI', module)
  .addDecorator(story => <div style={{ marginTop: '50px' }}>{story()}</div>)
  .addDecorator(withKnobs)
  .add('Pagination', () => {
    const totalPage = number('totalPage', 100);
    const currentPage = number('currentPage', 45);
    const maxDisplayNumber = number('maxDisplayNumber', 4);

    return (<Pagination totalPage={totalPage} page={currentPage - 1} maxDisplayNumber={maxDisplayNumber} />);
  }, {
    notes: { markdown: paginationDoc },
  });
  1. 运行效果如下:

4. 测试UI组件

4.1 写测试用例的原因

  • 找到bug
  • 新修改没有改变已有的接口和功能
  • 将测试用例作为文档

4.2 测试结构

使用storyshots插件来实现,其核心是使用Jest,原理是每次生成一份DOM结构文档(类似于html源码),可以无痛集成到组件测试中。

对于React项目,需额外安装如下npm包:

代码语言:javascript
复制
npm i -D [@storybook](/user/storybook)/addon-storyshots jest react-test-renderer

新建一个测试文件storyshots.test.js(路径随意,以.test.js结尾即可)

代码语言:javascript
复制
import initStoryshots from '[@storybook](/user/storybook)/addon-storyshots';

initStoryshots({ /* configuration options */ });

在控制台运行npm test即可(在package.json中配置好scripts:"test": "jest"),测试完成后会在storyshots.test.js生成一个stories/index.js对应的DOM快照。

PS:下次运行Jest时,只有DOM结构与上次完全一致测试才会通过,通常会有两种方法来解决这种情况:

  1. 找到问题,修复不同;
  2. 用新的DOM结构替换旧的。

4.3 测试交互

storybook交互性测试可以使用 Enzyme来模拟用户输入,然后使用Mocha or Jest来进行结果测试,storybook又一个专门的插件帮助我们集成他们:specifications

首先,需要安装如下npm包:

代码语言:javascript
复制
npm i -D enzyme enzyme-adapter-react-16 expect storybook-addon-specifications

storybook/config.js中配置enzyme

代码语言:javascript
复制
import { configure as enzymeConfigure } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';

enzymeConfigure({ adapter: new Adapter() });

stories/test.js中编写测试用例:

代码语言:javascript
复制
import React from 'react';
import { storiesOf } from '[@storybook](/user/storybook)/react';

import { specs, describe, it } from 'storybook-addon-specifications';
import { mount } from 'enzyme';
import expect from 'expect';

storiesOf('Interaction test', module)
  .add('Button test', () => {
    const story = (
      <button onClick={action('Hello World')}>
        Hello World
      </button>
    );

    specs(() => describe('Hello World', () => {
      it('Should have the Hello World label', () => {
        const output = mount(story);
        expect(output.text()).toContain('Hello World');
      });
    }));

    return story;
  });

在组件挂载后,通过断言来测试UI组件的属性,更多使用方法可以参考specifications插件的使用

4.4 测试样式

样式测试这里采用PuppeteerJest来实现,其原理是利用Puppeteer的无头的chrome浏览器和storybook的url绑定组件特点,来渲染不同的UI组件,再进行图片快照的对比。

首先安装几个npm包:(puppeteer默认会下载Chromium,比较慢要耐心等候)

代码语言:javascript
复制
npm install --save-dev jest puppeteer jest-puppeteer jest-image-snapshot start-server-and-test

然后添加一些文件到integration目录下:

代码语言:javascript
复制
// integration/jest.config.js
module.exports = {
  preset: 'jest-puppeteer',
  testRegex: './*\\.test\\.js$',
  setupTestFrameworkScriptFile: './setupTests.js',
};

// integration/setupTests.js
import { toMatchImageSnapshot } from 'jest-image-snapshot';
expect.extend({ toMatchImageSnapshot });

// integration/Button.test.js
describe('Button', () => {
  it('visually looks correct', async () => {
    // APIs from jest-puppeteer
    await page.goto('http://localhost:9009/iframe.html?selectedKind=Button&selectedStory=with+text');
    const image = await page.screenshot();

    // API from jest-image-snapshot
    expect(image).toMatchImageSnapshot();
  });
});

然后在package.json中添加两个scripts命令:

代码语言:javascript
复制
"jest:integration": "jest -c integration/jest.config.js",
"test:integration": "start-server-and-test storybook http-get://localhost:9009 jest:integration",

第一次运行npm run test:integration可以生成UI组件渲染的一次快照,再次运行会将新旧快照进行对比,只有完全一致才能测试通过。

PS:测试不通过时,运行npm run jest:integration将强制更新原有快照。

4.5 手动测试

再好的自动化测试,都和人的体验存在差距,所以发布之前还是需要经过人眼测试,因为storybook活文档的特点,我们可以直接运行体验UI组件,通过交互操作、knobs插件等来进行全面体验。

5. 包管理

使用lerna进行包管理和发布。

6. 参考链接

7. 写在最后

本文是作者学习storybook的一些总结,总体感觉是接入成本不算高,但是模块包版本安装可能会有一些坑,但收获是给组件的管理、文档和测试提供了一个一体化的解决方案,还是很值得的。

PS:文中所涉及的demo已放入Github仓库storybook-react

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018-12-10 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 新建一个Storybook React项目
  • 2. 使用storybook的插件功能
  • 3. 以一个分页组件为例
  • 4. 测试UI组件
    • 4.1 写测试用例的原因
      • 4.2 测试结构
        • 4.3 测试交互
          • 4.4 测试样式
            • 4.5 手动测试
            • 5. 包管理
            • 6. 参考链接
            • 7. 写在最后
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档