material-uiの見た目を調整する3つの方法

# 概要
* 普通のやり方で調整 -> style, className
* 個別に調整 -> withStyles
* 全体の見た目を調整 -> MuiThemeProvider

# 環境
* material-ui v1.0.0-beta.17

（本質ではないけど、rails 5.1.4のwebpackerで動作させています）

# 最終的なフォルダ構成
[![https://gyazo.com/e70becf1b424f4588bcbbad056341545](https://i.gyazo.com/e70becf1b424f4588bcbbad056341545.png)](https://gyazo.com/e70becf1b424f4588bcbbad056341545)

# アプリ（調整前）
以下のアプリに対して、「普通のやり方」、「個別に調整」、「全体の見た目を調整」を適用していく。

[![https://gyazo.com/0b777b8d8cde386ead0e8bf075870706](https://i.gyazo.com/0b777b8d8cde386ead0e8bf075870706.gif)](https://gyazo.com/0b777b8d8cde386ead0e8bf075870706)

```app/javascript/packs/draft.jsx
import React from 'react'
import ReactDOM from 'react-dom'
import DraftApp from '../containers/draft/DraftApp'

document.addEventListener('DOMContentLoaded', () => {
  ReactDOM.render(
    <DraftApp/>
    ,
    document.getElementById('draftIndexRoot'),
  )
});
```


```app/javascript/containers/draft/DraftApp.jsx
import React from 'react'
import Header from '../common/Header'
import Snackbar from 'material-ui/Snackbar';
import Slide from 'material-ui/transitions/Slide';
import DraftIndex from './DraftIndex';

class DraftApp extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      snackbarOpen: false,
      snackbarMessage: ''
    }
  }

  handleSnackbar = ({open, message, direction}) => () => {
    this.setState({
      snackbarOpen: open,
      snackbarMessage: message,
      snackbarDirection: direction
    })
  };

  render() {
    return (
      <div>
        <Header/>
        <DraftIndex
          handleSnackbar={this.handleSnackbar}
        />
        <Snackbar
          open={this.state.snackbarOpen}
          onRequestClose={
            this.handleSnackbar({
              open: false,
              message: ''
            })
          }
          message={this.state.snackbarMessage}
          transition={<Slide direction={this.state.direction}/>}
        />
      </div>
    )
  }
}

export default DraftApp

```

```app/javascript/containers/common/Header.jsx
import React from 'react';
import AppBar from 'material-ui/AppBar';
import Toolbar from 'material-ui/Toolbar';
import Typography from 'material-ui/Typography';

class Header extends React.Component {
  render() {
    return (
      <div>
        <AppBar position='static'>
          <Toolbar>
            <Typography type='title'>
              テストアプリ
            </Typography>
          </Toolbar>
        </AppBar>
      </div>
    )
  }
}

export default Header
```

```app/javascript/containers/draft/DraftIndex.jsx
import React from 'react'
import Typography from 'material-ui/Typography';
import Button from 'material-ui/Button';

class DraftIndex extends React.Component {
  render() {
    return (
      <div>
        <Typography type='display1'>スナックバーテスト</Typography>
        <Typography type='body1'>ボタン押すとsnackbarがでてきます</Typography>
        <Button
          raised
          onClick={
            this.props.handleSnackbar({open: true, message: 'スナックバー！', direction: 'down'})
          }
        >
          ボタン
        </Button>
      </div>
    )
  }
}

export default DraftIndex

```

# 普通のやり方で調整する場合
## styleで指定する場合
タグに``style={{}}``を追加して調整する。
とりあえず簡単に見た目を調整できる。

* 例 font-sizeの調整
cssのプロパティをキャメルケースで表現して指定する。font-sizeであればfontSize。
プロパティの値は`''`で囲んで表現する。

```
style={{fontSize: '4em'}}
```

実際に適用するとこんな感じ。

[![https://gyazo.com/36d00d2d597e441bc1bf4045389b8700](https://i.gyazo.com/36d00d2d597e441bc1bf4045389b8700.png)](https://gyazo.com/36d00d2d597e441bc1bf4045389b8700)

```app/javascript/containers/draft/DraftIndex.jsx
import React from 'react'
import Typography from 'material-ui/Typography';
import Button from 'material-ui/Button';

class DraftIndex extends React.Component {
  render() {
    return (
      <div>
        <Typography type='display1'>スナックバーテスト</Typography>
        <Typography type='body1'>ボタン押すとsnackbarがでてきます</Typography>
        <Button
          raised
          onClick={
            this.props.handleSnackbar({open: true, message: 'スナックバー！', direction: 'down'})
          }
          style={{fontSize: '4em'}}
        >
          ボタン
        </Button>
      </div>
    )
  }
}

export default DraftIndex

```



## classNameで指定する場合
classNameにcssが対応するクラス名を指定すると反映される。例えば、bootstrapを導入していれば、bootstrapの独自のクラス名を記述すれば適用される。

```
<div
  className='btn btn-primary'
>
  class nameで指定
</div>
```

実際に適用するとこんな感じ。
[![https://gyazo.com/b3be6828f835aa8e6a11379fb60b9c3c](https://i.gyazo.com/b3be6828f835aa8e6a11379fb60b9c3c.png)](https://gyazo.com/b3be6828f835aa8e6a11379fb60b9c3c)

```app/javascript/containers/draft/DraftIndex.jsx
import React from 'react'
import Typography from 'material-ui/Typography';
import Button from 'material-ui/Button';

class DraftIndex extends React.Component {
  render() {
    return (
      <div>
        <Typography type='display1'>スナックバーテスト</Typography>
        <Typography type='body1'>ボタン押すとsnackbarがでてきます</Typography>
        <Button
          raised
          onClick={
            this.props.handleSnackbar({open: true, message: 'スナックバー！', direction: 'down'})
          }
          style={{fontSize: '4em'}}
        >
          ボタン
        </Button>
        </Button>
        <div
          className='btn btn-primary'
        >
          class nameで指定
        </div>        
      </div>
    )
  }
}

export default DraftIndex

```

# 個別に調整するばあい
外部のCSSを使わず、javascriptでCSSを管理するためにに、material-uiで用意されているwithStylesを利用する。

material-uiのコンポーネントの場合、`classes`を利用することで、スタイルを細かに指定することができる。指定できるプロパティは[material-ui/src/](https://github.com/callemall/material-ui/tree/v1-beta/src) の各コンポーネントのjsファイル内の`export const styles`を参照する。

AppBarコンポーネントの場合、[material-ui/src/AppBar/AppBar.js](https://github.com/callemall/material-ui/blob/v1-beta/src/AppBar/AppBar.js)を見ると、下記の様になっている。

たとえば、colorPrimaryに対して、background-colorとcolorを指定できることがわかる。

```material-ui/src/AppBar/AppBar.js
export const styles = (theme: Object) => ({
  root: {
    display: 'flex',
    flexDirection: 'column',
    width: '100%',
    boxSizing: 'border-box', // Prevent padding issue with the Modal and fixed positioned AppBar.
    zIndex: theme.zIndex.appBar,
    flexShrink: 0,
  },
  positionFixed: {
    position: 'fixed',
    top: 0,
    left: 'auto',
    right: 0,
  },
  positionAbsolute: {
    position: 'absolute',
    top: 0,
    left: 'auto',
    right: 0,
  },
  positionStatic: {
    position: 'static',
    flexShrink: 0,
  },
  colorDefault: {
    backgroundColor: theme.palette.background.appBar,
    color: theme.palette.getContrastText(theme.palette.background.appBar),
  },
  colorPrimary: {
    backgroundColor: theme.palette.primary[500],
    color: theme.palette.getContrastText(theme.palette.primary[500]),
  },
  colorAccent: {
    backgroundColor: theme.palette.secondary.A200,
    color: theme.palette.getContrastText(theme.palette.secondary.A200),
  },
});
```


上記を踏まえて、
1. 見た目を調整したいコンポーネントを選ぶ
2. 上記コンポーネントのうち、[material-ui/src/](https://github.com/callemall/material-ui/tree/v1-beta/src) をみて指定できるmaterial-uiのプロパティを選ぶ
3. styleファイルに固有の名称とともにcssプロパティを記述する
4. コンポーネントを含むclassにwithStylesを追加し、styleファイルを読み込む
5. 変更したいコンポーネントにclassesを追加し、``{material-uiのプロパティ: this.props.classes.固有の名称}``を付与する

## （事例）AppBarの背景色を変更する場合
[material-ui/src/AppBar/AppBar.js](https://github.com/callemall/material-ui/blob/v1-beta/src/AppBar/AppBar.js)を見るとAppBarの背景色はmaterial-uiのプロパティはcolorPrimaryで変更できる事がわかるのでこれを変更する。



### styleファイルを作成
下記のフォーマットでオブジェクトを作成する。プロパティとプロパティの値は``styleで指定する場合``参照のこと。

```
スタイル名 = {
	固有の名称: {
	  プロパティ: プロパティの値
	}
}
```

上記を受けて下記の様に作成した。

```app/javascript/assets/style.js
export const commonStyle = {
  appBarColorDefault: {
    backgroundColor: '#fff'
  }
};
```

### Header.jsxにclassにwithStylesを追加
styleファイルとwithStylesをimportし、`export default withStyles Header`を`export default withStyles(commonStyle)(Header)`と修正する。

これにより、this.props.classesとしてcommonStyleオブジェクトを使うことができるようになる。

```app/javascript/containers/common/Header.jsx
import React from 'react';
import AppBar from 'material-ui/AppBar';
import Toolbar from 'material-ui/Toolbar';
import Typography from 'material-ui/Typography';
import {withStyles} from 'material-ui/styles';
import {commonStyle} from '../../assets/style';

class Header extends React.Component {
  render() {
    return (
      <div>
        <AppBar position='static'>
          <Toolbar>
            <Typography type='title'>
              テストアプリ
            </Typography>
          </Toolbar>
        </AppBar>
      </div>
    )
  }
}

export default withStyles(commonStyle)(Header)
```

### Header.jsxの調整したいコンポーネントにclassesを追加する
AppBarにclassesプロパティを追加し、変更したいmaterial-uiのプロパティ``colorPrimary``に対してthis.props.classes.appBarColoDefaultを指定する

追加前

```
<AppBar position='static'>
```

追加後

```
<AppBar position='static' classes={{colorPrimary: this.props.classes.appBarColorDefault}}>

```

このような見た目になる。

[![https://gyazo.com/ec1da7436130e4a295be4230918db9e6](https://i.gyazo.com/ec1da7436130e4a295be4230918db9e6.png)](https://gyazo.com/ec1da7436130e4a295be4230918db9e6)


# 全体の見た目を調整する場合
アプリ全体の見た目を調整する場合はMuiThemeProviderを使う。
MuitThemProviderで囲んだ箇所から子要素全てに一定のスタイルを適用することができる。

## （事例）Typographyを変更する
変更できる箇所は、以下のページを参照のこと。現時点で変更できる箇所と初期値は以下のようにのっているので、そちらを参照して変更する。

https://material-ui-next.com/customization/themes/

```javascript
{
  "direction": "ltr",
  "palette": {
    "common": {
      "black": "#000",
      "white": "#fff",
      "transparent": "rgba(0, 0, 0, 0)",
      "fullBlack": "rgba(0, 0, 0, 1)",
      "darkBlack": "rgba(0, 0, 0, 0.87)",
      "lightBlack": "rgba(0, 0, 0, 0.54)",
      "minBlack": "rgba(0, 0, 0, 0.26)",
      "faintBlack": "rgba(0, 0, 0, 0.12)",
      "fullWhite": "rgba(255, 255, 255, 1)",
      "darkWhite": "rgba(255, 255, 255, 0.87)",
      "lightWhite": "rgba(255, 255, 255, 0.54)"
    },
以下省略
}
```

typographyのdipslay1とbody1を変更する場合は下記の様なthemeファイルを作成する。

```app/javascript/assets/theme.js
import {createMuiTheme} from 'material-ui/styles';

export const theme = createMuiTheme({
  typography: {
    display1: {
      fontSize: '5em',
      color: 'rgba(255, 0, 0, 1)'
    },
    body1: {
      fontSize: '2em',
      color: 'rgba(0,255,0,1)'
    }
  }
});

```

上記ファイルを適用する
MuiThemeProviderとthemeをimportし、<MuiThemeProvider theme={theme}>で囲む。

```app/javascript/containers/draft/DraftApp.jsx
import React from 'react'
import Header from '../common/Header'
import Snackbar from 'material-ui/Snackbar';
import Slide from 'material-ui/transitions/Slide';
import DraftIndex from './DraftIndex';
import {MuiThemeProvider} from 'material-ui/styles';
import {theme} from '../../assets/theme';

省略
render() {
    return (
      <MuiThemeProvider theme={theme}>
        <div>
          <Header/>
          <DraftIndex
            handleSnackbar={this.handleSnackbar}
          />
          <Snackbar
            open={this.state.snackbarOpen}
            onRequestClose={
              this.handleSnackbar({
                open: false,
                message: ''
              })
            }
            message={this.state.snackbarMessage}
            transition={<Slide direction={this.state.direction}/>}
          />
        </div>
      </MuiThemeProvider>
    )
  }
}
```

結果以下のとおり。

[![https://gyazo.com/d872bf10efff223bc9ee5974dfd6791b](https://i.gyazo.com/d872bf10efff223bc9ee5974dfd6791b.png)](https://gyazo.com/d872bf10efff223bc9ee5974dfd6791b)

# （応用編）themeに用意されていないコンポーネントのスタイルを適用したい場合 -> override
https://material-ui-next.com/customization/themes/ で調整できないコンポーネントの場合は、overridesを使う。

指定はtheme内で行い、指定はoverrides: {Muiコンポーネント名: {}}でおこなう。

下記にSnackbarContentの背景色を変更したい場合を示す。

```
export const theme = createMuiTheme({
  typography: {
    display1: {
      fontSize: '5em',
      color: 'rgba(255, 0, 0, 1)'
    },
    body1: {
      fontSize: '2em',
      color: 'rgba(0,255,0,1)'
    }
  },
  overrides: {
    MuiSnackbarContent: {
      root: {
        backgroundColor: 'rgba(0,255,255,1)',
      }
    },
  },
});
```

# アプリ（調整後）
結果以下の通りになる。

[![https://gyazo.com/089e595bf7db517d225fb7dd0756bfda](https://i.gyazo.com/089e595bf7db517d225fb7dd0756bfda.gif)](https://gyazo.com/089e595bf7db517d225fb7dd0756bfda)