【Material-UI】TextFieldコンポーネントのdisabledスタイルをカスタマイズする方法

# はじめに
フレームワークやライブラリを利用して開発を行うと、やはり少なからず、カスタマイズする必要が出てくることがあります。
今回は、「React」 × 「UIフレームワークのMaterial-UI」を使って開発する際にちょっと手こずったポイントがありましたので紹介です。

## 対象のコンポーネント 【TextField】
インプットフィールドや、セレクトボックスなど、HTMLタグの`<input>`として活用できます。

# Material-UIについて
Material-UIはもともとあらゆるAPIが用意されており、カスタマイズ性も結構担保されており使いやすい印象です。
しかし、今回紹介する`TextField`コンポーネントで入力させたくない`disabled`のUIを変更したい際に問題が出てきました。


## 基本的なカスタマイズ
基本的に用意されているカスタマイズ方法として、公式ドキュメントをベースに`Button`コンポーネントのカスタマイズを行った際のソースコードが以下です。

```jsx
import React from "react";
import Button from "@material-ui/core/Button";
import FormLabel from "@material-ui/core/FormLabel";
import { makeStyles } from "@material-ui/core/styles";
import "./styles.css";

const styles = makeStyles(theme => ({
  rootBtn: { // Buttonクラスのrootクラス
    marginRight: "1em",
    backgroundColor: "#ff8800",
    "&:disabled": {
      color: "#bababa",
      backgroundColor: "#dadada"
    }
  }
}));

export default function App() {
  const classes = styles();

  return (
    <div className="App">
      <h1>ボタンの例</h1>
      <div>
        <Button className={classes.rootBtn}>Button</Button>
        <FormLabel>通常デザイン</FormLabel>
      </div>
      <div>
        <Button className={classes.rootBtn} disabled>
          Button
        </Button>
        <FormLabel>disabledデザイン</FormLabel>
      </div>
    </div>
  );
}
```

[![Image from Gyazo](https://i.gyazo.com/7b396e9a744691dc3928d351f4931152.png)](https://gyazo.com/7b396e9a744691dc3928d351f4931152)

上が機能するデザインで、下が`disabled`にした場合になります。
公式ドキュメントにあるように、`disabled`APIがtrueの場合、rootに対して擬似クラスを宣言するように指示されています。

>Pseudo-class applied to the root element if disabled={true}.
参照：公式ドキュメントより

今回、rootクラスに`rootBtn`を指定して、その擬似クラスに`disabled`のスタイルを定義しています。
有効になるトリガーは、`disabled`のAPIがtrueの場合になるため、下のボタンの時のみこのスタイルが有効になっています。

このように、APIにCSSクラスに用意されたものがあり、簡単にスタイルを環境に合わせてカスタマイズすることができるのは大きな魅力です。

# TextFieldでのカスタマイズ
上記の`Button`コンポーネントの感じで作っていたら失敗しました。
全然スタイルが効かない。仕方なく、ドキュメントを眺めていても一向に解決方法は見当たりませんでした。

そこで、構造を見にいくと、`TextField`コンポーネントは、いくつかのコンポーネントが合わさって構成されているようでした。

```html
<!-- inputタグラップされて形成されている -->
<div class="TextField">
  <input class="input-base">
  ...
  </>
</>
```

disabledのスタイルは`<input>`で定義されているようだったので、どうにかここを上書きする方法が必要でした。

## ローカルルールを参照させる `$ruleName`
こんな書き方があったなんて知らなかった。

```jsx

const styles = {
  root: {
    '&$disabled': {
      color: 'white',
    },
  },
  disabled: {},
};
```


これを参考にスタイルするとうまくスタイルが効きました。
ソースは以下です。

```jsx
import React from "react";
import TextField from "@material-ui/core/TextField";
import FormLabel from "@material-ui/core/FormLabel";
import { makeStyles } from "@material-ui/core/styles";
import "./styles.css";

const styles = makeStyles(theme => ({
  rootTxt: {
    marginRight: "1em",
    "&$disabled": {
      color: "#bababa",
      backgroundColor: "#dadada"
    }
  },
  disabled: {}
}));

export default function App() {
  const classes = styles();

  return (
    <div className="App">
      <h1>テキストフィールドの例</h1>
      <div>
        <TextField
          InputProps={{
            classes: {
              root: classes.rootTxt,
              disabled: classes.disabled
            }
          }}
          defaultValue="TextField"
        />
        <FormLabel>通常デザイン</FormLabel>
      </div>
      <br />
      <div>
        <TextField
          InputProps={{
            classes: {
              root: classes.rootTxt,
              disabled: classes.disabled
            }
          }}
          defaultValue="TextField"
          disabled
        />
        <FormLabel>disabledデザイン</FormLabel>
      </div>
    </div>
  );
}
```
[![Image from Gyazo](https://i.gyazo.com/64f05ac471f448a975d137554cae1484.png)](https://gyazo.com/64f05ac471f448a975d137554cae1484)

# さいごに
今回取り上げたのは、`TextField`コンポーネントですが、他にもコンポーネントを掛け合わせた形で表現されているコンポーネントはたくさんあります。
そういったコンポーネントもおそらくこの書き方が必要になってきそうですね。

前提として知っているのと知っていないので、使い方や設計が大きく変わってきそうだなと感じながら今回はなんとか解決することができました！

## 紹介した内容のSnadbox
[![Edit Material-UI_TextField](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/material-uitextfield-bu8j5?fontsize=14&hidenavigation=1&theme=dark)

### P.S
気がついた頃にはこんなこと書いてた！
>The TextField is a convenience wrapper for the most common cases (80%). It cannot be all things to all people, otherwise the API would grow out of control.