Disclosureについて

React Ariaの実装読むぞ


目次
  1. 使用例
  2. 本題
    1. role と
    2. hidden=“until-found”
  3. まとめ
Warn

この記事は他サイトから移行したものです。

Note

この記事は React Aria の実装読むぞ - Qiita Advent Calendar 2024 の 13 日目の記事です。

こんにちは、フロントエンドエンジニアの mehm8128 です。 今日は Disclosure について書いていきます。

最初にちょっと記事書いていたときはまだ本番環境のドキュメントに存在していなくて、http://localhost:1234/react-aria/useDisclosure.htmlを貼ろうとしていたのですが、11 月のリリースで入ったようなので見れるようになっていました。

使用例 #

ドキュメントからそのまま取ってきています。

function Disclosure(props) {
  let state = useDisclosureState(props);
  let panelRef = React.useRef<HTMLDivElement | null>(null);
  let triggerRef = React.useRef<HTMLButtonElement | null>(null);
  let { buttonProps: triggerProps, panelProps } = useDisclosure(
    props,
    state,
    panelRef
  );
  let { buttonProps } = useButton(triggerProps, triggerRef);
  let { isFocusVisible, focusProps } = useFocusRing();

  return (
    <div className="disclosure">
      <h3>
        <button
          className="trigger"
          ref={triggerRef}
          {...mergeProps(buttonProps, focusProps)}
          style={{ outline: isFocusVisible ? "2px solid dodgerblue" : "none" }}
        >
          <svg viewBox="0 0 24 24">
            <path d="m8.25 4.5 7.5 7.5-7.5 7.5" />
          </svg>
          {props.title}
        </button>
      </h3>
      <div className="panel" ref={panelRef} {...panelProps}>
        <p>{props.children}</p>
      </div>
    </div>
  );
}

本題 #

APG はこちらです。 https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/

grouprole とaria-属性 #

ボタンとパネルを結びつけたり、現在 disclosure が開いているかどうかを表したりするために、いくつかのaria-属性が用いられています。

aria-expandedの boolean で現在開いているかどうかの状態を表し、aria-controlsでパネル(コンテンツ)と結びつけています。

また、非表示のときはaria-hiddenhidden属性がついています。

grouprole が用いられているのは、detail要素の暗黙の ARIA role がgrouprole だからです。

hidden=“until-found” #

hidden="until-found"がつけられています。

詳しい説明は MDN に任せるのですが、disclosure が閉じている状態でもページ内検索などでは disclosure 内のコンテンツがヒットするようにし、その結果コンテンツまでスクロールされたらhidden属性を外してコンテンツを表示するようにする、というものです。

ページ内検索やフラグメントナビゲーション(URL の後ろに#をつけるやつ)で対象のコンテンツを表示しようとしたときにbeforematchイベントが発火され、それを購読して以下のコードの箇所で処理を行っています。

なお、React 側がまだ対応していないので先ほどのコードのようにuseLayoutEffect内で無理やり属性をつけていたり、Firefox と Safari がまだbeforematchサポートしていないので対応しているブラウザのみで処理を行うようなロジックになっています。 https://github.com/facebook/react/pull/24741 https://caniuse.com/mdn-html_global_attributes_hidden_until-found_value https://caniuse.com/mdn-api_element_beforematch_event

まとめ #

明日の担当は @mehm8128 さんで、番外編 テストについての記事です。お楽しみにー