LoginSignup
4
2

divタグにonClickが設定されるのをESLintでチェックする

Last updated at Posted at 2023-12-01

概要

アクセシビリティのチェックポイントとして、
ユーザーが操作するコンポーネントを適切なタグで実装するか、
適切に属性を設定する必要があるというものがあります。

create-next-appで作成したばかりのプロジェクトでは、
divタグなど非対話型コンテンツにonClickが設定されただけの場合、ESLintで特に指摘されません。
また、StorybookとAccessibility addonでコンポーネントをチェックしても指摘されず、
そのまま実装を進めてしまいがちです。

最近自分もこの事に気付いてコードレビューで指摘される前に検知・改善できないか調査しました。
この記事ではESLintルールを変更して、静的解析で適切なタグが利用できてないことを検知する設定を紹介します。
eslint-plugin-jsx-a11yを推奨ルールで利用していると記事内のルールが適用済みのはずです)

ご自身のプロジェクトにてルール設定を見直してみるきっかけになれば幸いです。

デフォルトの設定を検証する

検証に利用したツール

  • Next.js: v14.0.3
  • React.js: v18.0.2
  • Storybook: v7.6.1
  • storybook-addon-a11y: v7.6.1

検証用のプロジェクトとコンポーネントを用意

まず、以下の流れでプロジェクトを準備します。

  • npx create-next-app@latest でNext.jsプロジェクトを作成
  • npx storybook@latest init でStorybookをインストール
  • storybook-addon-a11yをインストールして有効化

コンポーネントをチェックする

Storybookのinitコマンドを実行するとサンプルでボタンコンポーネントが追加されるので
これをbuttonタグからdivタグに変更してみます。

"use client";
import React from "react";
import "./button.css";

interface ButtonProps {
  /**
   * Is this the principal call to action on the page?
   */
  primary?: boolean;
  /**
   * What background color to use
   */
  backgroundColor?: string;
  /**
   * How large should the button be?
   */
  size?: "small" | "medium" | "large";
  /**
   * Button contents
   */
  label: string;
  /**
   * Optional click handler
   */
  onClick?: () => void;
}

/**
 * Primary UI component for user interaction
 */
// NOTE: 元のコードではスプレッド構文でpropsを受け取っていましたが変更しています
// スプレッド構文だとonClickなどが設定されているか検知できなくなります
// propsでのスプレッド構文利用の是非はこの記事の範囲外なので詳細は割愛します
export const Button = ({
  primary = false,
  size = "medium",
  backgroundColor,
  label,
  onClick,
}: ButtonProps) => {
  const mode = primary
    ? "storybook-button--primary"
    : "storybook-button--secondary";
  return (
    // NOTE: buttonからdivに変更し、type propsを削除
    <div
      className={["storybook-button", `storybook-button--${size}`, mode].join(
        " "
      )}
      onClick={onClick}
    >
      {label}
      <style jsx>{`
        button {
          background-color: ${backgroundColor};
        }
      `}</style>
    </div>
  );
};

VS Code上では特にエラーは出ていません。

上に記載したButtonコンポーネントをVS Codeで開いてESLintエラーが出てない状態のキャプチャ画像

StorybookのAccessibility addonでもコントラスト不足の指摘のみとなっています。

divタグのボタン要素をStorybookで開いてコントラスト不足の指摘のみ表示されている状態のキャプチャ画像

eslint-plugin-jsx-a11yのルールを変更する

今回のような実装はeslint-plugin-jsx-a11yのjsx-a11y/no-static-element-interactionsを有効化することで検知可能です。
Next.jsのプロジェクトではeslint-plugin-jsx-a11yのセットアップがされているので
.eslintrc.jsonを編集し、プラグインのREADMEで推奨されている内容でルール追加を行いました。

{
  "extends": ["next/core-web-vitals", "plugin:storybook/recommended"],
  "rules": {
    "jsx-a11y/no-static-element-interactions": [
      "error",
      {
        "handlers": [
          "onClick",
          "onMouseDown",
          "onMouseUp",
          "onKeyPress",
          "onKeyDown",
          "onKeyUp"
        ],
        "allowExpressionValues": true
      }
    ]
  }
}

VS Codeで指摘されるようになりました。

設定を変えたあとESLintエラーがVS Codeで出ている状態でのキャプチャ画像

ちょっとした補足

eslint-plugin-jsx-a11yとStorybookのAccessibility addonはそれぞれチェック対象やルールが異なります。
2つのツールを利用することで、より多くのフィードバックが得られます。
web.devでも同様にeslint-plugin-jsx-a11yとレンダリングされたDOMのテストツールを組み合わせる記事があります。
Accessibility auditing with react-axe and eslint-plugin-jsx-a11y

Next.jsのデフォルト設定は以下のリンクで確認できますが、
いくつかのjsx-a11yルールが適用されているのでno-static-element-interactionsが有効になっていないのにも理由があるかもしれません。(深掘りはできてないです)
https://github.com/vercel/next.js/blob/canary/packages/eslint-config-next/index.js

StorybookのAccessibility addonはaxe-coreを利用しているので
チェックできるルールは以下のリンクで確認できます。
https://github.com/dequelabs/axe-core/blob/master/doc/rule-descriptions.md

例えばbuttonタグの中にaタグがある場合など、
対話型コンテンツがネストしているとStorybook側で指摘されます。

Storybookでbuttonタグにaタグをネストしてアクセシビリティアドオンに指摘されているキャプチャ画像

まとめ

この記事ではeslint-plugin-jsx-a11yとStorybookのAccessibility addonを組み合わせて
ユーザーが操作するコンポーネントを適切なタグでマークアップできるよう静的解析で検知することを紹介しました。

それぞれのプロジェクトで設定したアクセシビリティ目標を達成できるように
コードとともにチェックツールの設定なども改善していけるとよさそうです。

4
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2