Storybook & Addons

Start writing your stories

What is storybook ?

  • create components independently

Setup Guide

.storybook

main.js

preview-head.html

(required)

preview-body.html

preview.js

保留字

main.js

module.exports = {
  stories: ['../stories/**/*.stories.jsx'],
  addons: [
    '@storybook/addon-actions/register',
    '@storybook/addon-notes/register',
  ],
  webpackFinal: (config) => {
  /* Add your custom webpack config */
    return config;
  },
};
{
  test: /\.(mjs|jsx?)$/,
  use: [ { loader: 'babel-loader', options: [Object] } ],
  include: [ '/Users/travor/Desktop/rytass/NTCH-Official' ],
  exclude: [ '/Users/travor/Desktop/rytass/NTCH-Official/node_modules' ]
}
{
  test: /\.md$/,
  use: [
    {
      loader: '/node_modules/raw-loader/dist/cjs.js'
    }
  ]
}
{
  test: /\.css$/,
  sideEffects: true,
  use: [
    '/Users/travor/Desktop/rytass/NTCH-Official/node_modules/style-loader/dist/cjs.js',
    {
      loader: '/node_modules/css-loader/dist/cjs.js',
      options: [Object]
    },
    {
      loader: '/node_modules/postcss-loader/src/index.js',
      options: [Object]
    }
  ]
}
{
  test: /\.(svg|ico|jpg|jpeg|png|gif|eot|otf|webp|ttf|woff|woff2|cur|ani|pdf)(\?.*)?$/,
  loader: '/node_modules/@storybook/core/node_modules/file-loader/dist/cjs.js',
  query: { name: 'static/media/[name].[hash:8].[ext]' }
}
{
  test: /\.(mp4|webm|wav|mp3|m4a|aac|oga)(\?.*)?$/,
  loader: '/node_modules/@storybook/core/node_modules/url-loader/dist/cjs.js',
  query: { limit: 10000, name: 'static/media/[name].[hash:8].[ext]' }
}

Default webpack module rules

main.js

const webpack = require('webpack');
const COLORS = require('../colors.json');

module.exports = {
  stories: ['../stories/**/*.stories.jsx'],
  addons: [
    '@storybook/addon-actions/register',
    '@storybook/addon-notes/register',
  ],
  webpackFinal: (config) => {
    config.plugins.push(new webpack.DefinePlugin({
      Colors: JSON.stringify(COLORS),
    }));

    return config;
  },
};

main.js

const customWebpackConfig = require('../client/webpack.config.js');

module.exports = {
  stories: ['../stories/**/*.stories.jsx'],
  addons: [
    '@storybook/addon-actions/register',
    '@storybook/addon-notes/register',
  ],
  webpackFinal: (config) => {
    return {
      ...config,
      plugins: [
        ...config.plugins,
        ...customWebpackConfig.plugins,
      ],
      module: {
        ...config.module,
        rules: [
          ...config.module.rules,
          ...customWebpackConfig.module.rules,
        ]
      }
    };
  },
};

preview.js

放 global decorator 的地方

import React from 'react';
import { Provider } from 'react-redux';
import {
  addDecorator,
} from '@storybook/react';
import {
  BrowserRouter,
} from 'react-router-dom';
import '../static/main.css';
import './canvas.css';
import store from '../store';
import 'moment/locale/zh-tw';

addDecorator(storyFn => (
  <Provider store={store}>
    <BrowserRouter basename="/">
      {storyFn()}
    </BrowserRouter>
  </Provider>
));

from NTCH

import {
  addParameters,
  addDecorator,
} from '@storybook/client-api';
import {
  INITIAL_VIEWPORTS,
} from '@storybook/addon-viewport';
import {
  jsxDecorator,
} from 'storybook-addon-jsx';

addDecorator(jsxDecorator);

addParameters({
  viewport: {
    viewPorts: INITIAL_VIEWPORTS,
  },
});

from NTCH-Official

<style type="text/css">
   * {
     box-sizing: border-box;
     font-family: '微軟正黑體', sans-serif;
     outline: none;
   }

   html {
     width: 100vw;
     height: 100vh;
   }

   body {
     width: 100vw;
     min-height: 100vh;
     position: relative;
   }
</style>

index.html

preview-head.js

<link href='//api.tiles.mapbox.com/mapbox-gl-js/v1.6.1/mapbox-gl.css' rel='stylesheet' />
<style type="text/css">
  ...
</style>

custom html <head>

preview-body.js

<div id="portal-container" />

custom html <body>

Addons

My Suggestions

log action

customize props

desktop/mobile viewport

description/notes

accessibility test

preview jsx code

Writing Stories

import React from 'react';

import Button from '../SomeWhere/Button';

export default {
  title: 'Form/Button',
};

const defaultProps = {
  disabled: false,
  variant: 'TEXT',
  inverse: false,
};

export const Default = () => (
  <Button
    {...defaultProps}>
    預設按鈕
  </Button>
);

.storybook

main.js

stories

Modal.stories.js

Button.stories.js

.storybook

main.js

stories

import React from 'react';

import Button from '../SomeWhere/Button';
import note from './Button.stories.md';

export default {
  title: 'Form/Button',
  parameters: {
    notes: { markdown: note },
  },
};

const defaultProps = {
  disabled: false,
  variant: 'TEXT',
  inverse: false,
};

export const Default = () => (
  <Button
    {...defaultProps}>
    預設按鈕
  </Button>
);

Button.stories.md

Button.stories.js

Modal.stories.js

.storybook

main.js

stories

import React from 'react';

import Button from '../SomeWhere/Button';
import note from './Button.stories.md';

export default {
  title: 'Form/Button',
  parameters: {
    notes: { markdown: note },
  },
};

const defaultProps = {
  disabled: false,
  variant: 'TEXT',
  inverse: false,
};

export const Default = () => (
  <Button
    {...defaultProps}>
    預設按鈕
  </Button>
);

export const Disabled = () => (
  <Button
    {...defaultProps}
    disabled>
    不可按
  </Button>
);

Button.stories.md

Modal.stories.js

Button.stories.js

.storybook

main.js

stories

Button.stories.js

Modal.stories.js

import React from 'react';
import { withKnobs, boolean, select } from '@storybook/addon-knobs';

import Button from '../SomeWhere/Button';
import note from './Button.stories.md';

export default {
  title: 'Form/Button',
  parameters: {
    notes: { markdown: note },
  },
  decorators: [withKnobs],
};

const defaultProps = {
  disabled: false,
  variant: 'TEXT',
  inverse: false,
};

const variantOptions = ['TEXT', 'MAIN'];

export const Default = () => {
  const knobs = {
    disabled: boolean('disabled', defaultProps.disabled),
    variant: select('variant', variantOptions, defaultProps.variant),
    inverse: boolean('inverse', defaultProps.inverse),
  };

  return (
    <Button
      {...defaultProps}
      {...knobs}>
      預設按鈕
    </Button>
  );
};

export const Disabled = () => (
  <Button
    {...defaultProps}
    disabled>
    不可按
  </Button>
);

Button.stories.md

.storybook

main.js

stories

Button.stories.js

Modal.stories.js

import React from 'react';
import { withKnobs, boolean, select } from '@storybook/addon-knobs';
import { action } from '@storybook/addon-actions';

import Button from '../SomeWhere/Button';
import note from './Button.stories.md';

export default {
  title: 'Form/Button',
  parameters: {
    notes: { markdown: note },
  },
  decorators: [withKnobs],
};

const defaultProps = {
  disabled: false,
  variant: 'TEXT',
  inverse: false,
  onClick: action('clicked'),
};

const variantOptions = ['TEXT', 'MAIN'];

export const Default = () => {
  const knobs = {
    disabled: boolean('disabled', defaultProps.disabled),
    variant: select('variant', variantOptions, defaultProps.variant),
    inverse: boolean('inverse', defaultProps.inverse),
  };

  return (
    <Button
      {...defaultProps}
      {...knobs}>
      預設按鈕
    </Button>
  );
};

export const Disabled = () => (
  <Button
    {...defaultProps}
    disabled>
    不可按
  </Button>
);

Button.stories.md

.storybook

main.js

stories

Button.stories.js

Modal.stories.js

import React from 'react';
import { withKnobs, boolean, select } from '@storybook/addon-knobs';
import { action } from '@storybook/addon-actions';
import { withA11y } from '@storybook/addon-a11y';

import Button from '../SomeWhere/Button';
import note from './Button.stories.md';

export default {
  title: 'Form/Button',
  parameters: {
    notes: { markdown: note },
  },
  decorators: [withKnobs, withA11y],
};

const defaultProps = {
  disabled: false,
  variant: 'TEXT',
  inverse: false,
  onClick: action('clicked'),
};

const variantOptions = ['TEXT', 'MAIN'];

export const Default = () => {
  const knobs = {
    disabled: boolean('disabled', defaultProps.disabled),
    variant: select('variant', variantOptions, defaultProps.variant),
    inverse: boolean('inverse', defaultProps.inverse),
  };

  return (
    <Button
      {...defaultProps}
      {...knobs}>
      預設按鈕
    </Button>
  );
};

export const Disabled = () => (
  <Button
    {...defaultProps}
    disabled>
    不可按
  </Button>
);

Button.stories.md

.storybook

main.js

stories

Button.stories.js

Modal.stories.js

import { addParameters, addDecorator } from '@storybook/client-api';
import { INITIAL_VIEWPORTS } from '@storybook/addon-viewport';
import { jsxDecorator } from 'storybook-addon-jsx';

addDecorator(jsxDecorator);

addParameters({
  viewport: {
    viewPorts: INITIAL_VIEWPORTS,
  },
});

Button.stories.md

preview.js

Thanks

storybook and addons

By Travor Lee

storybook and addons

  • 250