Storybook でアクセシビリティの自動チェック

※ 以前投稿した[Storybook でアクセシビリティ（a11y）改善](https://qiita.com/kalbeekatz/items/6f17129b6a978052fd64)の記事の続きになります。**そもそもアクセシビリティとは？storybook-addon-a11y って何？**という方は前回投稿の記事を事前にお読みいただくとよいかもしれません。

<hr>

***🎅🏻 一足早いクリスマスプレゼントをどうぞ 🔔***

リモートワークの方もそうでない方も日々のお仕事お疲れ様です！
自分はリモートワークでついついだらけちゃう日もあります。（大抵は頑張っています。許してください。）
同じ会社の仲間が[だらけると叱ってくれるアプリを作った](https://qiita.com/bunhojun/items/b258f1a895f4fdabcfc1)そうなので使おうかしらと思っている今日この頃。

そんなついついだらけちゃう仲間に一足早いクリスマスプレゼントをお届けします。

# 🎉 Storybook で動くアクセシビリティの自動チェックツール 🎁

[storybook-a11y-report](https://www.npmjs.com/package/storybook-a11y-report)
※ 作ったばかりのalpha版で Readme すらまともに書けていません、、急いで書きます🏃🏻‍♂️💨
※ 人生初の npm publish で右も左も分かりません。どうか優しく見守ってください。

このツールを使えばただコマンドを一つ打って ~~皿でも洗ったり歯間ブラシしたり洗濯ものが乾いてるか見たりして~~ 待っているだけで、あなたの愛おしいコンポーネントたちがアクセシビリティを満たしているのかチェックしてくれるのです。

## サクッと試す
[サンプルリポジトリ](https://github.com/kalbeekatz/storybook-a11y-report-sandbox)を clone して下記の手順でどうぞ。
※ 環境: node.js v10.x ~

**① Storybook の起動**

```shell:ターミナル1
# cd storybook-a11y-report-sandbox
$ npm install
$ npm run storybook
```

**② `ターミナル1` でStorybook を起動した状態でチェックを開始**

```shell:ターミナル2
# Storybook が http://localhost:6006 で起動した状態で実行
$ npm run a11y-report
```
※ 結果がでるまで数分かかる場合があります。🍜 食べて待ちましょう。

正常に終わるとキャプチャのようにターミナルにレポートが表示されます。
![スクリーンショット 2020-12-11 12.52.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/435654/728a2e5a-473a-ede8-2683-48162731e542.png)


## すでに Storybook を運用している方へ

[storybook-addon-a11y](https://github.com/storybookjs/storybook/tree/next/addons/a11y) が導入されている必要があります。[公式ドキュメント](https://github.com/storybookjs/storybook/tree/next/addons/a11y)に記載の通りサクッと導入できます。

storybook-addon-a11y が動く状態で下記のように install して実行するだけです。✨✨ Eleganza Extravaganza ✨✨

```shell
$ npm install --save-dev storybook-a11y-report
$ npx storybook-a11y-report # Storybook の起動をお忘れなく
```

これだけです。

**⚡️注意⚡️ alpha版ですので今後大幅に改変される可能性があります。**

## オプションについて

```
--include, -i: チェック対象の story を絞り込みます。minimatch に対応しています。
--exclude, -e: チェック対象から story を除外します。minimatch に対応しています。
--filter, -f: チェック対象の a11y ルールを絞り込みます。
--omit, -o: チェック対象から a11y ルールを除外します。
--exit, -q: レポート結果に a11y 違反がある場合はプロセスを異常終了させます。主にCI用です。
--storybookUrl: Storybook のURLを指定します。デフォルトは 'http://localhost:6006'
--outDir: レポートファイルの出力先を指定します。デフォルトは '__report__'
```

<hr>

ここからは誕生の経緯や運用上の注意点について記載します。

<hr>

# storybook-addon-a11y を運用シーンで使う

## なぜ私達は Storybook を使うのか

[Storybook でアクセシビリティ（a11y）改善](https://qiita.com/kalbeekatz/items/6f17129b6a978052fd64)の記事でも書いている通り、Storybook の利点として様々な状態のコンポーネントを管理できることが挙げられます。
例えば React の場合、条件に合わせて異なる props の story を用意することで初期描画では表示されないようなコンポーネントが表示された状態をあらかじめ用意することができます。

ピンと来ない方は下記の例をご参考ください。

------- 例 始まり -------

まずコンテナ層です。
※ コンテナ層について馴染みのない方は[ComponentとContainerについて](https://qiita.com/ShiratoriTenta/items/7b4d2ea1a4d1d2336032)が参考になるかもしれません。

```jsx:container.jsx
import { Presentation } from './presentation'
 
const Container = () => {
  const [buttonType, setButtonType] = useState('blue')
  const handleOnChange = (e) => { setButtonType(e.target.value) }
  return <Presentation buttonType={buttonType} handleOnChange={handleOnChange} />
}
```

`buttonType` の初期値に `'blue'` を設定していて、それを更新する `handleOnChange` を `<Presentation>` に渡しています。

次にプレゼンテーション層です。

```jsx:presentation.jsx
export const Presentation = ({buttonType, handleOnChange}) => (
  <div>
    <select value={buttonType} onChange={handleOnChange}>
      <option value="blue" />
      <option value="yellow" />
    </select>
    <button style={{color: buttonType}} />
  </div>
)
```

`<select>` の `onChange` で `handleOnChange` が発火するようになっているので、選択肢を変えると `Container` の `buttonType` が更新されます。選択肢は `blue` か `yellow` です。

そして `<button>` のstyle属性のcolorプロパティに `buttonType` が設定されています。

`<select>` の選択肢の変更に応じて `<button>` の文字色が変わるようなコンポーネントです。

そして story です。

```jsx:index.stories.jsx
import { Presentation } from './presentation'

const dummyHandler = () => {}

export default { title: 'select button color' }
export const Blue = () => <Presentation buttonType="blue" handleOnChange={dummyHandler} />
export const Yellow = () => <Presentation buttonType="yellow" handleOnChange={dummyHandler} />
```

`blue` と `yellow` の両パターンを用意できています。

------- 例 終わり -------

条件分岐が多く様々な状態のコンポーネントがある場合、画面をいじってアクセシビリティを満たしているかチェックする（E2E的な手法でやる）のはとても大変ですが、Storybook にあらかじめ登録しておけばポチポチせずに済みます。
他の多くのツール（Lighthouse や URL を入力してチェックするタイプのもの）では、これは難しいのではと、、

## いちいち story 開かないといけないのダルすぎん？

storybook-addon-a11y を使ってアクセシビリティチェックの運用を開始してみました。
すると思うことが一つ。

***いちいち story 開かないといけないのダルすぎん？***

![スクリーンショット 2020-11-23 16.37.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/435654/005c1864-3782-3657-10bb-6231dc9bda78.png)
このスクショのように3個くらいだったら鼻くそほじりながら確認できますが、そこそこのプロダクトだとこれが1000個とかになる訳です。鼻がなくなります 😤
（最初の3日間くらいはショートカット（`⌥ + →` `⌥ + ↓`）も覚えてやってたんですよ。許して。）

大体、似たようなエラーが重複して「あ、これ他のコンポーネントと同じエラーだ」となります。
そして「〇〇さんに修正依頼する時にエラー毎に対象画面まとめておいた方がいいよな」ってなって、爆速で `⌥ + →` を連打してエラー箇所を拾い集める旅に出るわけです。ドラゴンボールだったらいいですけど、こんなん7個集めたって神龍なんか出ませんからね 🔮 🐉

***やってられない！！！***

## 自動化で変わる私達の未来

こんなことで挫ける僕ってダメなのかな？？って考えたりもしました。
しかし気づいたのです！

***僕には[プログラマの3大美徳](https://moneyforward.com/engineers_blog/2016/02/08/engineers-virtue/)が芽生え始めているんだ！！！***

「怠慢(Laziness)」ってやつですね。

こうして storybook-a11y-report を作り始めたのでした。

今ではCIに組み込まれていて、どこぞの誰かがa11y違反を犯すとマージできなくなります👮🏻‍♀️🚨

## 自動化は終わりではなく始まりだ

無事に自動化できて時間ができたので[ルポールのドラァグ・レース](https://www.netflix.com/jp/title/70187741)を見てました。

> Good luck and don’t f*ck it up!

しびれました。自動化してできた時間は有効に使わないとですね、、

storybook-addon-a11y のチェックは機械的です。実はどうしてもチェックできない部分があります。
例えば、WCAG には
> 3.3.1 エラーの特定: 入力エラーが自動的に検出された場合は、エラーとなっている箇所が特定され、そのエラーが利用者にテキストで説明される
引用：https://waic.jp/docs/WCAG20/Overview.html#minimize-error

という項目があり、エラー部分には `role="alert"` をつけるなどしてスクリーンリーダーでも検知できるようにする必要があります。
しかし、「あるコンポーネントがエラー表示であること」というのは、現状のツールでは判断できず、人が気づき考えるしかありません。

**文章の意味とそのHTMLが適切か（セマンティクス）は人が考える必要があります**

また、実際にスクリーンリーダーなどを使ってみると気づくことが沢山あります。

実際に発見したのは

- ページ遷移するたびに毎回ヘッダー ナビゲーションから読み上げが始まり、本文に到達するまで時間がかかる
- `<input>` のバリデーションエラーが読み上げられず、エラーが発生していることに気づけない
- 「1/3」は「いちスラッシュさん」と読まれて「1月3日」か「三分の一」か、はたまた他のものか判別がつかない

などです。

いくら storybook-addon-a11y ではクリアと判断されていても、アクセシブルとは言えないですよね 😏
機械で最低限の部分を担保し、人が考える時間を増やします。

<hr>

そして、人の努力によりボールが集まるのです。

僕はもう大人でサンタが来てくれないことは分かっています。

でも僕もプレゼントが欲しいな。

***ぎゃるのぱんてぃおくれーーーーっ***

✨✨ Eleganza Extravaganza ✨✨

<hr>

大変参考になりました。

アクセシビリティについて
[Youtube Ch 辻ちゃん・ウエちゃんのAccessiブルGoGo!](https://www.youtube.com/channel/UCyQCN1dT6we67xl79zjhNxg)

人生初の npm publish にあたって
[初めてのnpm パッケージ公開](https://qiita.com/TsutomuNakamura/items/f943e0490d509f128ae2)
[TypeScriptでnpmライブラリ開発ことはじめ](https://qiita.com/saltyshiomix/items/d889ba79978dadba63fd)

各名言について
[帰国子女が教える！ル・ポール名言&フレーズ](https://rupauls-dragrace.com/2020/03/13/post-54/)
[ギャルのパンティおくれ](https://dic.pixiv.net/a/%E3%82%AE%E3%83%A3%E3%83%AB%E3%81%AE%E3%83%91%E3%83%B3%E3%83%86%E3%82%A3%E3%81%8A%E3%81%8F%E3%82%8C)

次は、storybook-a11y-report のコード説明の記事を投稿しようかなと思っています！乞うご期待👋🏻