全部文章

试了下react + storybook

2018-06-15 @sunderls

react storybook jest

设想你使用React开发一个项目,其中为了让代码整洁,写了单独的component,这很好。

如何给这些component列一份文档呢?为了给非工程师的成员确认?这就需要单独开发组件一览页面了,然后这些组件的属性啥的,需要单独的显示出来的等等。

这,还挺麻烦的。但是storybook已经帮我们做好了。我们来试试

install storybook

设想你有一个react工程,其中有components/Button.js, 其中Button.js如下:

import React from 'react';
import PropTypes from 'prop-types';

function Button(props) {
    const {children, ...other} = props;
    return <button {...other}>{props.children}</button>;
}

Button.propTypes = {
    size: PropTypes.oneOf(['small', 'big'])
};

export default Button;

我们按照官方文档安装所需的库:

npm i --save-dev @storybook/react

修改package.json,指定说参考.storybook的配置进行显示

{
  "scripts": {
    "storybook": "start-storybook -p 9001 -c .storybook"
  }
}

配置storybook

上面设定的是.storybook,然后我们在其中添加配置,首先是.storybook/config.js:

import { configure } from '@storybook/react';

function loadStories() {
  require('../stories/index.js');
}

configure(loadStories, module);

这里意思就是把stories中的所有story全部load。

添加story

那我们在strories/中添加一个story - index.js

import React from 'react';
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
storiesOf('Button', module)
  .add('with text', () => {
    return <Button >Arunoda Susiripala</Button>
  })
  .add('with some emoji', () => (
    <Button ><span role="img" aria-label="so cool">😀 😎 👍 💯</span></Button>
  ));   
> npm run storybook

可以看到:

  1. 侧边栏显示了层级结构
  2. 右侧显示了story的内容。

knobs

knob意思是抽屉把手,就是说可以操纵显示内容呗。 如果想要直接操纵组件的话,可以使用插件:

npm install @storybook/addon-knobs --save-dev

然后在.storybook/addons.js中引入:

import '@storybook/addon-knobs/register';

最后就可以在story中使用这个插件了

import { withKnobs, text, boolean, number } from '@storybook/addon-knobs';

storiesOf('Button', module)
  .addDecorator(withKnobs)
  .add('with text', () => {
    const name = text('Name', 'Arunoda Susiripala');
    return <Button onClick={action('clicked')}>{name}</Button>
  })

可以看到在新增了knobs panel,里面输入文字可以直接编辑其name。

PropType

不过,react component的props如何处理呢? 我们首先定义Button的props

import React from 'react';
import PropTypes from 'prop-types';

const fontSize = {
    small: '12px',
    big: '16px'
};

function Button(props) {
    const {children, ...other} = props;
    return <button {...other} style={{fontSize: fontSize[props.size]}}>{props.children}</button>;
}

Button.propTypes = {
    size: PropTypes.oneOf(['large', 'big']),
    color: PropTypes.string
};

export default Button;

然后在story中设置变量

import { withKnobs, text, select } from '@storybook/addon-knobs';

storiesOf('Button', module)
  .addDecorator(withKnobs)
  .add('with text', () => {
    const name = text('Name', 'Arunoda Susiripala');
    const size = select('size', ['large', 'small']);
    const color = text('color', 'green')
    return <Button onClick={action('clicked')} size={size} color={color}>{name}</Button>
  })

这样就能说,显示内容中,size和color和name都可以操作。所以我们看到knobs panel里出现了可以编辑的选项。

snapshot test

如何参与测试呢?首先在普通的unit test的时候

  1. 用jest enzyme,写单元测试
  2. 或者用jest使用snapshot test

首先看没有storybook的时候如何snapshot。

npm install --save-dev jest react-test-renderer babel-preset-env babel-preset-react

packae.json

"scripts": {
    "test": "jest --no-watchman",
    "storybook": "start-storybook -p 9001 -c .storybook"
},

然后添加一个test spec, __test__/Button.test.js

import React from 'react';
import Button from '../components/Button';
import renderer from 'react-test-renderer';

it('renders correctly', () => {
  const tree = renderer
    .create(<Button size="large" color="green">sunderls</Button>)
    .toJSON();
    expect(tree).toMatchSnapshot();
});

注意添加preset到babelrc,否则jest不会transpile test spec

{
    "presets": ["env", "react"]
}

npm run test之后可以看到 __test__下增加了__snapshots__,里面含有book的test 快照。

之后再一次执行npm run test的时候,就会和这个快照比对,比如我们更改了Button中的代码,不小心把20px改为了21px的时候,再次run test就会发现快照不一致而报错。嗯,非常方便。(虽然在动态测试后的以后有点不方便)

好了,回到storybook,可以看到storybook的书写方式其实和test spec很像,都是在一个抽象的component上添加必要的props显示为一个instance。既然如此,其中的部分逻辑就可以合并。

首先安装storyshots插件

npm i -D @storybook/addon-storyshots

然后添加一个storyshots.test.js

import initStoryshots from '@storybook/addon-storyshots';
initStoryshots({});

npm run test之后,可以看到声称了两个snapshot,这个时候,比如Button中20px不小心成了21px,会包两个错误。

当然需要更新snapshot的时候也存在,在package.json中添加如下command即可

 "update-snapshot": "jest --updateSnapshot --no-watchman"