React storybook tutorial

## 準備

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

```
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](https://qiita-image-store.s3.amazonaws.com/0/27622/8bfdfe05-c02f-6c96-2aef-da2f0be0da32.gif)

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

## 練習1: button を作成する

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

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

create `src/components/Button.js`

```javascript
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.js` を `src/stories/Button.js` で warp してカタログ化します。
`getstorybook` を実行した時に既に生成されているファイルがありますがそれを編集します。

edit `stories/index.js`

```javascript
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`

```diff
 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`

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

## 練習3: 継承する

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

```javascript
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`

```javascript
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`

```javascript
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` を修正します

```diff

 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`

```javascript
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/