Используйте StoryBook для разработки управления компонентами пользовательского интерфейса

внешний интерфейс GitHub React.js Redux

предисловие

Недавно я обнаружил, что есть еще технический пост в блоге, который я не написал в этом месяце~, хотя я могу просто взять статью, накопленную за день, или вынуть некоторые моменты недавнего рефакторинга, я могу обмануть прошлое, но я решил что то сделать.. . .

Недавно появилось такое требование. В проекте рефакторинга, над которым я работаю, есть некоторые компоненты, которые я хочу извлечь и использовать для других проектов в будущем. Затем мне нужно разработать два фронтенд-проекта. общие компоненты. Чисто статические страницы не подходят, потому что у меня сейчас React + Typescipt в моем стеке технологий. Я хочу быть подключи и играй. Кстати, определите props, state и другие вещи, и вы сможете изменить проект позже. Изначально я ставил флаг, чтобы приготовить что-то самому, но в группе WeChat кто-то скинул ссылкуstorybook,

На первый взгляд кажется, что это то, что мне нужно. Итак, сегодняшняя цель — поиграться со средой разработки.

Определите потребности

Storybook — это среда разработки компонентов пользовательского интерфейса. Он позволяет просматривать библиотеку компонентов, просматривать различные состояния каждого компонента, а также интерактивно разрабатывать и тестировать компоненты.

Но введение официального гитхаба очень плохое, так что предлагаю вам его посмотретьIntroduction to StorybookУзнать больше.

так же какguide

Уточним наши потребности:

  1. Поддержка загрузки библиотек пользовательского интерфейса, таких как ant-design
  2. Поддержка машинописного текста
  3. поддержка редукса
  4. Поддержка отладки параметров

Официально начать

По идее, сначала создайте реактивный проект, поддерживающий ts

create-react-app my-app --scripts-version=react-scripts-ts

Затем обновите зависимости

yarn upgrade

затем следуйте сборник рассказов


npm i -g @storybook/cli
cd my-app
getstorybook

бежать сразу заyarn run storybookвы можете увидеть интерфейс

imgn

Однако правда не так проста. Потому что ts поддерживается самим проектом, а сборник рассказов независим. Поэтому вам нужно сделать различные модификации в соответствии с конфигурацией.

первый в.storybookСоздано в каталогеwebpack.config.js

загрузить внутрьtypescript-loader


// load the default config generator.
const genDefaultConfig = require('@storybook/react/dist/server/config/defaults/webpack.config.js');
module.exports = (baseConfig, env) => {
    const config = genDefaultConfig(baseConfig, env);
    // Extend it as you need.
    // For example, add typescript loader:
    config.module.rules.push({
        test: /\.(ts|tsx)$/,
        loader: require.resolve('awesome-typescript-loader')
    });
    config.resolve.extensions.push('.ts', '.tsx');
    return config;
};

тогда правильноpackage.jsonсделать макияж


{ 
  "name": "my-app",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "react": "^16.0.0",
    "react-dom": "^16.0.0",
    "react-scripts-ts": "2.7.0"
  },
  "scripts": {
    "start": "react-scripts-ts start",
    "build": "react-scripts-ts build",
    "test": "react-scripts-ts test --env=jsdom",
    "eject": "react-scripts-ts eject",
    "storybook": "start-storybook -p 6006",
    "build-storybook": "build-storybook"
  },
  "devDependencies": {
    "@storybook/addon-actions": "^3.2.12",
    "@storybook/addon-links": "^3.2.12",
    "@storybook/react": "^3.2.12",
    "@types/jest": "^21.1.2",
    "@types/node": "^8.0.39",
    "@types/react": "^16.0.12",
    "@types/react-dom": "^16.0.1",
    "@types/storybook__react": "^3.0.5",
    "awesome-typescript-loader": "^3.2.3"
  }
}


Самый важный момент после этого — поместить корневую директориюstoriesкаталог перемещен вsrcпод каталогом.

Напишиindex.tsx


import React from 'react';

import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import { linkTo } from '@storybook/addon-links';

import { Button, Welcome } from '@storybook/react/demo';

storiesOf('Welcome', module).add('to Storybook', () => <Welcome showApp={linkTo('Button')} />);

storiesOf('Button', module)
  .add('with text', () => <Button onClick={action('clicked')}>Hello Button</Button>)
  .add('with some emoji', () => <Button onClick={action('clicked')}>😀 😎 👍 💯</Button>);

бежать заyarn run storybookВы можете добиться поддержки синтаксиса ts.

После этого нам нужно подумать о том, как должны быть организованы наши компоненты пользовательского интерфейса.Глядя на исходный код на gitHub, есть два основных способа. Один из них — добавить его непосредственно под одноименным компонентом..stories.tsдокумент


./Button.jsx
./Button.stories.ts

одинstoriesСоздайте index.ts в каталоге, чтобы ссылаться на другие компоненты.

Берем последнюю, что для удобства управления. И это можно сделать прямо на основе нашего существующего кода.

Мы рассматриваем возможность сделать демо, сейчасreact + reduxВсе демо используютсяtodolistЧто нужно сделать. Но тут мы прямо подставляем зрелогоreduxстроить планы.

Сначала мы видимsrcТекущая структура каталога:


.
├── App.css
├── App.test.tsx
├── App.tsx
├── index.css
├── index.tsx
├── logo.svg
├── registerServiceWorker.ts
├── stories
│   └── index.jsx
├── webpack.config.1.js
└── webpack.config.js

явно типичныйcreate-appСтруктура. Затем мы смотрим непосредственно на структуру проекта после добавления редукса:


── src
│   ├── actions
│   │   └── index.ts
│   ├── components
│   ├── constants
│   │   └── index.ts
│   ├── containers
│   │   └── App
│   ├── index.tsx
│   ├── logo.svg
│   ├── reducers
│   │   ├── index.ts
│   │   └── info.ts
│   ├── registerServiceWorker.ts
│   ├── store
│   │   └── index.ts
│   ├── stories
│   │   └── index.jsx
│   ├── typing.d.ts
│   ├── webpack.config.1.js
│   └── webpack.config.js

Здесь также важно отметить, что вам необходимоtsconfigЧтобы сделать что-то целое и поддержать Less, я также внес некоторые изменения в webpack.

Затем пишем простуюaction to reducer

action:


import { INFO_LIST } from '../constants/index'
const saveList = (data: Object) => ({
  type: INFO_LIST,
  data: data,
})

export function infoListRemote () {
  const info = {
    data: {
      item: 'Hello LinShuiZhaoYing',
      cnItem: '你好, 临水照影'
    }
  }
  return (dispatch: any) => {
    dispatch(saveList(info))
    return info
  }
}

reducer:


import { INFO_LIST } from '../constants';

const initialState = {
   info:''
}

const info = (state = initialState, action: any) => {
  // console.log(action)
  switch (action.type) {
    case INFO_LIST:
      return {
        ...state,
        info:action.data.data
      }
    default:
      return state
  }
}

export default info;

App:

index.tsx:


import * as React from 'react';
import { Button, Icon } from 'antd';
import { connect } from 'react-redux';
import { infoListRemote } from '../../actions/index';
import './index.css';

class App extends React.Component<any, any> {
  constructor (props: any) {
    super(props)
    this.state = {
      infoList: '',
    }
  }

  componentWillMount() {

  }
  componentDidMount() {
    // console.log(this.props)
  }
  componentWillReceiveProps(nextProps: any) {
    // console.log(nextProps)
    if (nextProps.info) {
      this.setState({
        infoList: nextProps.info.item
      })
    }
  }

  getInfo = () => {
    const { dispatch } = this.props;
    dispatch(infoListRemote())
  }

  render() {
    return (
      <div className="App">
        <div className="test"> {this.state.infoList} </div>
        <Button type="danger" onClick={this.getInfo}> Click Me</Button>
        <Icon type="play-circle-o" />
      </div>
    );
  }
}
const mapStateToProps = (state: any) => ({
  info: state.info.info,
})
let AppWrapper = App
AppWrapper = connect(mapStateToProps)(App);

export default AppWrapper;

Тогда посмотрите на рендеры:

imgn

Вы можете видеть, что данные были успешно доставлены.

Далее нужно написатьstories, потому что мы использовалиreduxПоэтому нам нужно использоватьaddDecoratorчтобы обернуть наши компоненты


import React from 'react';

import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import { linkTo } from '@storybook/addon-links';
import { Provider } from 'react-redux';
import { Button, Welcome } from '@storybook/react/demo';
import { createBrowserHistory } from 'history';
import configureStore from '../store';

import  AppWrapper  from '../containers/App'

const store = configureStore(createBrowserHistory);

storiesOf('AppWrapper', module)
.addDecorator(story => <Provider store={store}>{story()}</Provider>)
.add('empty App', () => <AppWrapper />);

storiesOf('Button', module)
  .add('with text', () => <Button onClick={action('clicked')}>Hello Button</Button>)
  .add('with some emoji', () => <Button onClick={action('clicked')}>😀 😎 👍 💯</Button>);


imgn

Последний шаг — добавить отладку параметров, здесь нам нужно добавить некоторыеaddonдля повышения опыта.

мы можемMore addonsСписок дополнений смотрите здесь. Увеличение в соответствии со спросом. Затем перейдите на соответствующий веб-сайт git, чтобы увидеть использование и присоединиться.

присоединиться первымaddon-notes, он используется для написания описания компонента, и после тестирования его можно добавить в Html-код, чтобы вы могли сначала определить единый формат самостоятельно, а затем добавлять контент.

imgn

Вы также можете настроить некоторую информацию, такую ​​как использование параметров, открытые интерфейсы и т. д. нагрузкаInfo Addonможет быть достигнут.

imgn

Следующим ядром является увеличение функции отладки параметров

Вот пример фрагмента кода:


storiesOf('AppWrapper', module)
.addDecorator(withKnobs)
.addDecorator(story => <Provider store={store}>{story()}</Provider>)
.add('knobs App', () =><AppWrapper text={text('Label', 'Hello World')}></AppWrapper>)
.add('with all knobs', () => {
  const name = text('Name', 'Tom Cary');
  const dob = date('DOB', new Date('January 20 1887'));

  const bold = boolean('Bold', false);
  const selectedColor = color('Color', 'black');
  const favoriteNumber = number('Favorite Number', 42);
  const comfortTemp = number('Comfort Temp', 72, { range: true, min: 60, max: 90, step: 1 });

  const passions = array('Passions', ['Fishing', 'Skiing']);

  const customStyle = object('Style', {
    fontFamily: 'Arial',
    padding: 20,
  });

  const style = {
    ...customStyle,
    fontWeight: bold ? 800 : 400,
    favoriteNumber,
    color: selectedColor,
  };

  return (
    <div style={style}>
      I like: <ul>{passions.map((p, i) => <li key={i}>{p}</li>)}</ul>
      <p>My favorite number is {favoriteNumber}.</p>
      <p>My most comfortable room temperature is {comfortTemp} degrees Fahrenheit.</p>
    </div>
  );
});

Вам необходимо установить динамически передаваемые параметры во время разработки. Это может быть отображено немедленно. Схема эффекта выглядит следующим образом:

imgn

тогда позвониaddon options

Добавьте в config.js


setOptions({
  downPanelInRight: true,
})

Измените доску отображения горизонтальной оси на вертикальную.

Есть еще много интересногоaddons, такие как улучшенная версия Inforeadme.и смена фона в один кликbackgrounds. Существуют также существующиеMaterial-UI. Существует также прямое отображение вашего исходного кода Jsx.Storybook-addon-jsx.и контрольная версия показываетstorybook-addon-versions, что позволяет напрямую сравнивать различия между несколькими версиями. Генерация всех скриншотов в один кликStorybook Chrome Screenshot Addon. эти сообществаaddonsочень полезны. Если вы заинтересованы, вы можете добавить его самостоятельно.

конец

Наконец, мы отлично выполнили предыдущие требования, и есть несколько неожиданных сюрпризов.

Согласно моей конфигурации среды, он может быть расширен сам по себе, хотя я сейчас разрабатываю на основе React. ноStoryBookТакже поддерживает Vue.

Наконец-то выложили проект как обычногитхаб-адрес

использованная литература

Схема разработки фронтенд-компонента и его применение в React Native

react-template

playbook_ts_sample

d3-storybook-test

react-storybook-demo

Introduction to Storybook

StoryBook meets redux