想搞個組件可視化好久了,一開始就是想作個和antd相似的,而後就遇到了一系列的問題javascript
組件可視化的目的:css
調研發現,antd使用的是阿里系的bisheng這個包vue
目的是將markdown轉成靜態站點,antd就是這樣產生的java
這就是本次要說的storybook了,目前3.7w個star,且生態比較好,插件也很多react
npm init -y
npm install @storybook/react --save-dev
npm install react react-dom --save
npm install babel-loader @babel/core --save-dev
複製代碼
在package.json中添加webpack
{
"scripts": {
"storybook": "start-storybook"
}
}
複製代碼
建立.storybook/config.js文件git
import { configure } from '@storybook/react';
function loadStories() {
require('../stories/index.js');
// 能夠添加多個store
// You can require as many stories as you need.
}
configure(loadStories, module);
複製代碼
建立../stories/index.js文件github
import React from 'react';
import { storiesOf } from '@storybook/react';
import { Button } from '@storybook/react/demo';
storiesOf('Button', module)
.add('with text', () => (
<Button>Hello Button</Button>
))
.add('with emoji', () => (
<Button><span role="img" aria-label="so cool">😀 😎 👍 💯</span></Button>
));
複製代碼
npm run storybook
複製代碼
官方提供了三種,我選擇了我的認爲比較清晰的方式
web
按照上面的設計估計應該就可以知足咱們的需求了chrome
只是簡單配置可不行,若是不在一開始就搞好的話,隨着後面業務的擴展會帶來不少問題,像按需加載這種功能仍是一開始就設計比如較好
修改.storybook/config.js
import { configure } from '@storybook/react';
const req = require.context('../src/components', true, /\.stories\.js$/);
function loadStories() {
req.keys().forEach(filename => req(filename));
}
configure(loadStories, module);
複製代碼
addons
**
重要的事情說三遍:
在.storybook配置文件中,添加一個addons.js文件
**在.storybook配置文件中,添加一個addons.js文件
**在.storybook配置文件中,添加一個addons.js文件
addons.js文件是用來維護插件的配置文件
官網上Addons目錄就是官方推薦的插件列表,開始一個個嘗試
npm i @storybook/addon-knobs -D
複製代碼
找到.storybook/addons.js在其中添加一句話
import '@storybook/addon-knobs/register';
複製代碼
按照github地址,添加示例代碼
import React from 'react';
import { storiesOf } from '@storybook/react';
import { withKnobs, text, boolean, number } from '@storybook/addon-knobs';
const stories = storiesOf('Storybook Knobs', module);
// Add the `withKnobs` decorator to add knobs support to your stories.
// You can also configure `withKnobs` as a global decorator.
stories.addDecorator(withKnobs);
// Knobs for React props
stories.add('button', () => (
<button disabled={boolean('不可用', false)} > {text('文案', '牛逼的knobs')} </button>
));
// Knobs as dynamic variables.
stories.add('as dynamic variables', () => {
const name = text('Name', 'Arunoda Susiripala');
const age = number('Age', 89);
const content = `I am ${name} and I'm ${age} years old.`;
return (<div>{content}</div>);
});
複製代碼
上面一共作了幾件事情:
直接看效果
上面例子中,第二個case,包含計算變量
// Knobs as dynamic variables.
stories.add('as dynamic variables', () => {
const name = text('Name', 'Arunoda Susiripala');
const age = number('Age', 89);
const content = `I am ${name} and I'm ${age} years old.`;
return (<div>{content}</div>);
});
複製代碼
npm i -D @storybook/addon-actions
複製代碼
import '@storybook/addon-actions/register';
複製代碼
import { storiesOf } from '@storybook/react';
import { action, configureActions } from '@storybook/addon-actions';
import Button from './button';
storiesOf('Button', module).add('default view', () => (
<Button onClick={action('button-click')}>Hello World!</Button>
));
複製代碼
import { storiesOf } from '@storybook/react';
import { actions } from '@storybook/addon-actions';
import Button from './button';
// This will lead to { onClick: action('onClick'), ... }
const eventsFromNames = actions('onClick', 'onMouseOver');
// This will lead to { onClick: action('clicked'), ... }
const eventsFromObject = actions({ onClick: 'clicked', onMouseOver: 'hovered' });
storiesOf('Button', module)
.add('default view', () => <Button {...eventsFromNames}>Hello World!</Button>)
.add('default view, different actions', () => (
<Button {...eventsFromObject}>Hello World!</Button>
));
複製代碼
npm i @storybook/addon-storysource --dev
複製代碼
module.exports = function({ config }) {
config.module.rules.push({
test: /\.stories\.jsx?$/,
loaders: [require.resolve('@storybook/addon-storysource/loader')],
enforce: 'pre',
});
return config;
};
複製代碼
這個和前面兩個插件不一樣之處就在這,它須要在webpack中添加一個loader,經過storybook自己的webpack對外暴露,對其添加插件
npm i -D @storybook/addon-info
複製代碼
addon-info採用的是修飾器模式,能夠全局添加,也能夠局部添加
addDecorator(withInfo); // Globally in your .storybook/config.js.
複製代碼
storiesOf('Component', module)
.addDecorator(withInfo) // At your stories directly.
.add(...);
複製代碼
import React from 'react';
import PropTypes from 'prop-types';
const Test = (props)=><div>{props.children}</div>
Test.propTypes = {
text: PropTypes.string.isRequired,
onDelete: PropTypes.func,
}
export default Test
複製代碼
添加addons-info插件後,就會增長showinfo按鈕
import React from 'react';
import PropTypes from 'prop-types';
const Test = (props)=><div>{props.children}</div>
Test.defaultProps = {
text: '默認值',
onDelete: () => {}
};
Test.propTypes = {
/** 這裏text是註釋 */
text: PropTypes.string.isRequired,
/** 這裏onDelete是註釋 */
onDelete: PropTypes.func,
}
export default Test
複製代碼
viewport你們都很熟悉,這個就是用來搞移動端的
npm i --save-dev @storybook/addon-viewport
複製代碼
在.storyboos/config中註冊
import '@storybook/addon-viewport/register';
複製代碼
import React from 'react';
import { configure,addDecorator,addParameters } from '@storybook/react';
// config.js
import { withInfo } from '@storybook/addon-info';
addDecorator(withInfo);
addParameters({
viewport: { defaultViewport: 'iphone6' },
});
const req = require.context('../src/components', true, /\.stories\.js$/);
function loadStories() {
req.keys().forEach(filename => req(filename));
}
configure(loadStories, module);
複製代碼
這裏就是使用addParameters方法,很方便就能夠
這部分是測試用例相關,短期內啃不下來,待後面有時間再補充
npm i @storybook/addon-a11y --dev
複製代碼
修改addons
import '@storybook/addon-a11y/register';
複製代碼
在config文件中添加
addDecorator(withA11y);
addParameters({
a11y: {
// ... axe options
element: '#root', // optional selector which element to inspect
config: {}, // axe-core configurationOptions (https://github.com/dequelabs/axe-core/blob/develop/doc/API.md#parameters-1)
options: {} // axe-core optionsParameter (https://github.com/dequelabs/axe-core/blob/develop/doc/API.md#options-parameter)
},
});
複製代碼
ok,這樣的話,若是書寫不符合規範的按鈕,則會報錯
這個插件解決的問題是,story之間的相互跳轉,用法也很簡單,只須要參考文檔就好
目前比較好的插件列表以下