React storybook tutorial

  • 4
    いいね
  • 0
    コメント

準備

必要なコマンドをインストール

npm i -g create-react-app @storybook/cli

version は以下の通りになっていればOK

create-react-app --version
1.4.0
getstorybook --version
3.2.3

練習用のプロジェクトディレクトリを作成して、storybook を起動します

create-react-app practice-storybook
cd practice-storybook
getstorybook
npm run storybook

2月-03-2017 11-13-40.gif

browser で動作確認をしましょう。

練習1: button を作成する

src/stories/Button.js が既に存在していますがこの Button とは別に独自の Button を作成します。

mkdir src/components
touch src/components/Button.js

create src/components/Button.js

import React from 'react'

const styles = {  
  button: {
    borderRadius: "3px",
    backgroundColor: "#3498db",
    padding: "7px 16px",
    width: "100px",
    fontFamily: "YuGo-Bold",
    fontSize: "12px",
    fontWeight: "normal",
    fontStyle: "normal",
    fontStretch: "normal",
    lineHeight: "12px",
    letterSpacing: "normal",
    textAlign: "center",
    color: "#fff",
  }
}  

const Button = ({ children, onClick }) => (
  <div style={styles.button} onClick={onClick}>
    {children}
  </div>
)

export default Button

上記の src/components/Button.jssrc/stories/Button.js で warp してカタログ化します。
getstorybook を実行した時に既に生成されているファイルがありますがそれを編集します。

edit stories/index.js

import React from 'react';

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

import Button from '../src/components/Button'

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

この状態で一度動作確認をします。

練習2: 擬似クラスへの対応方法

mouseOver した時に色を少し変えたい場合、mouseEnter, mouseOut などでは頑張りたくないと思います。
<div style={{property: values}}>..</div> のように sytle に記述した場合、:hover のような擬似クラスを記述できない(勘違いだったらすみません)ので aphrodite というモジュールを使用します。

npm install --save aphrodite

edit src/components/Button.js

 import React from 'react'
+import { StyleSheet, css } from 'aphrodite'

-const styles = {
+const styles = StyleSheet.create({
   button: {
     borderRadius: "3px",
     backgroundColor: "#3498db",
@@ -16,10 +17,10 @@ const styles = {
     textAlign: "center",
     color: "#fff",
   }
-}
+})

 const Button = ({ children, onClick }) => (
-  <div style={styles.button} onClick={onClick}>
+  <div className={css(styles.button)} onClick={onClick}>
     {children}
   </div>
 )

const styles = StyleSheet.create({...}) を実行すると HTML に以下の style 要素が追加されます

<style type="text/css" data-aphrodite="">.button_1w5cv5t{font-weight:normal !important;border-radius:3px !important;padding:17px 0 !important;max-width:300px !important;font-family:YuGo-Bold !important;font-size:16px !important;background-color:#3498db !important;font-style:normal !important;font-stretch:normal !important;line-height:16px !important;letter-spacing:normal !important;text-align:center !important;color:#fff !important;}</style>

css(styles.button) を実行すると以下の class名を取得します

button_1w5cv5t

この仕組みを利用して :hover 擬似クラスを使います。

edit src/components/Button.js

     letterSpacing: "normal",
     textAlign: "center",
     color: "#fff",
+    ':hover': {
+      backgroundColor: "#3182B8"
+    }
   }

練習3: 継承する

scss などで @extend したい場合があると思います。
Javascript で同じ事をする場合色々な方法があると思いますがここでは以下のように適用させたいスタイルを配列で css 関数に渡す方法を紹介します。

const styles = StyleSheet.create({
  fontFamily = { 
    fontFamily: "YuGo-Bold",
    fontSize: "12px",
    fontWeight: "normal",
    fontStyle: "normal",
    fontStretch: "normal",
  },
  button = {
    borderRadius: "3px",
    fontSize: "12px",
    backgroundColor: "#3498db",
    padding: "7px 16px",
    width: "100px",
    lineHeight: "12px",
    letterSpacing: "normal",
    textAlign: "center",
    color: "#fff",
    ':hover': {
      backgroundColor: "#3182B8"
    }
  }
})
className={css([styles.fontFamily, styles.button])}

配列順にスタイルを読み込みます。同じスタイルを読み込んだ場合は後から読んだスタイルで上書きされます。

練習4: modal を作成する

component の再利用を行います。自分以外の優れたエンジニアがすでにコンポーネントを用意してくれています。
いろんな人が modal component を作ってくれていると思いますが今回は https://github.com/yuanyan/boron を使う方法を紹介します。

npm install --save boron
touch src/components/Modal.js src/stories/Modal.js

最初にシンプルな Modal Component を作成します。

create src/components/Modal.js

import React, { Component } from 'react'
import { StyleSheet, css } from 'aphrodite'
import FadeModal from 'boron/FadeModal'

class Modal extends Component {

  showModal() {
    this.refs.modal.show()
  }

  hideModal() {
    this.refs.modal.hide()
  }

  render() {
    return (
      <div>
        <FadeModal ref="modal">
          <h2>I am a dialog</h2>
          <button onClick={this.hideModal.bind(this)}>Close</button>
        </FadeModal>
      </div>
    )
  }
}

export default Modal

次に先ほど作成した Modal のカタログとなる Component を作成します。

create src/stories/Modal.js

import React, { Component } from 'react'
import Modal from '../components/Modal'

class ModalStory extends Component {

  showModal() {
    this.refs.modal.showModal()
  }

  render() {
    return (
      <div>
        <button onClick={this.showModal.bind(this)}>Open</button>
        <Modal ref="modal" />
      </div>
    )   
  }
}

export default ModalStory

最後に src/stories/index.js を修正します


 import { storiesOf, action, linkTo } from '@kadira/storybook'
 import Button from './Button'
 import Welcome from './Welcome'
+import Modal from './Modal'

 storiesOf('Welcome', module)
   .add('to Storybook', () => (
@@ -15,3 +16,8 @@ storiesOf('Button', module)
   .add('with some emoji', () => (
     <Button onClick={action('clicked')}>😀 😎 👍 💯</Button>
   ));
+
+storiesOf('Modal', module)
+  .add('my modal', () => (
+    <Modal />
+  ))

この状態で動作確認をします。

練習5: modal を装飾する

svg なアイコンを install します

npm install --save react-icons

edit src/components/Modal.js

import React, { Component } from 'react'
import { StyleSheet, css } from 'aphrodite'
import FadeModal from 'boron/FadeModal'
import MdClose from 'react-icons/lib/md/close'
import Button from './Button'

const styles = StyleSheet.create({
  modal: {
    borderRadius: "4px",
    border: "solid 1px #dddddd",
  },
  header: {
    padding: "17px 12px",
    fontFamily: "YuGo-Bold",
    fontSize: "15px",
    fontWeight: "normal",
    fontStyle: "normal",
    fontStretch: "normal",
    lineHeight: "15px",
    letterSpacing: "normal",
    backgroundColor: "#fafafa",
    borderBottom: "solid 1px #dddddd",
    color: "#333",
  },
  icon: {
    float: "right",
    width: "15px",
    height: "15px",
    cursor: "pointer",
  },
  body: {
    padding: "30px",
    fontFamily: "YuGo",
    fontSize: "14px",
    fontWeight: "normal",
    fontStyle: "normal",
    fontStretch: "normal",
    lineHeight: "14px",
    letterSpacing: "normal",
    backgroundColor: "#fff",
    borderBottom: "solid 1px #dddddd",
    color: "#333",
  },
  footer: {
    textAlign: "right",
    padding: "30px",
    backgroundColor: "#fff",
  },
  buttons: {
    margin: 0,
    padding: 0,
  },
  button: {
    display: "inline-block",
    marginRight: "20px",
    ":last-child": {
      marginRight: 0
    }
  }
})

class Modal extends Component {

  showModal() {
    this.refs.modal.show()
  }

  hideModal() {
    this.refs.modal.hide()
  }

  render() {
    return (
      <FadeModal contentStyle={{backgroundColor: "none"}} ref="modal">
        <div className={css(styles.modal)}>
          <div className={css(styles.header)}>
            header xxxxxxxxx
            <MdClose className={css(styles.icon)} onClick={this.hideModal.bind(this)} />
          </div>
          <div className={css(styles.body)}>
            body xxxxxxxxxxxxxxx
          </div>
          <div className={css(styles.footer)}>
            <ul className={css(styles.buttons)}>
              <li className={css(styles.button)}>
                <Button onClick={this.hideModal.bind(this)}>Close</Button>
              </li>
              <li className={css(styles.button)}>
                <Button onClick={this.hideModal.bind(this)}>Submit</Button>
              </li>
            </ul>
          </div>
        </div>
      </FadeModal>
    )
  }
}

export default Modal

storybook

https://okamuuu.github.io/practice-storybook/