Keyboard navigation in `<wa-select>` now keeps the active option in view

shoelace-style/webawesome

<wa-select> がキーボードで操作された際、選択中のオプションがリストボックスの可視領域外に残るバグを修正し、常にスクロールして表示されるようになりました。

背景

選択肢が画面外に残る問題 が Issue #2472 で報告されました。長いオプションリストでタイプ検索や矢印キー操作を行うと、マッチした項目がスクロールされずユーザーが選択結果を確認できない状態でした。開発者は実装上のスクロール呼び出しが欠落していることを特定し、鍵となるユーザー体験の欠如を改善する必要がありました。

技術的な変更

WaSelect コンポーネントにスクロール処理を追加 しました。select.ts のオプション取得後に、ドロップダウンが開いていてリストボックスが非表示でない場合に scrollIntoView を呼び出すロジックを挿入しています。変更前後の差分は以下の通りです。

@@ -715,6 +715,10 @@ export default class WaSelect extends WebAwesomeFormAssociatedElement {
       option.current = true;
       option.tabIndex = 0;
       option.focus({ preventScroll: true });
+
+      if (this.open && !this.listbox.hidden) {
+        scrollIntoView(option, this.listbox, 'vertical', 'auto');
+      }
     }
   }

テストを追加 して回帰を防止しました。select.test.ts では、listbox.scrollTo をスタブし、擬似的なビューポート情報を設定した上で g, h, a キー入力により "Ghana" がマッチした際に scrollTo が呼び出されることを検証しています。

it('should scroll a type-to-select match into view', async () => {
  const el = await fixture<WaSelect>(html`
    <wa-select>
      <wa-option value="argentina">Argentina</wa-option>
      <wa-option value="belgium">Belgium</wa-option>
      <wa-option value="canada">Canada</wa-option>
      <wa-option value="denmark">Denmark</wa-option>
      <wa-option value="egypt">Egypt</wa-option>
      <wa-option value="france">France</wa-option>
      <wa-option value="ghana">Ghana</wa-option>
    </wa-select>
  `);
  const listbox = el.shadowRoot!.querySelector<HTMLElement>('.listbox')!;
  const ghana = el.querySelector<WaOption>('wa-option[value="ghana"]')!;
  const scrollTo = sinon.stub(listbox, 'scrollTo');
  // …設定省略…
  el.focus();
  await sendKeys({ press: 'g' });
  await sendKeys({ press: 'h' });
  await sendKeys({ press: 'a' });
  expect(scrollTo.called).to.be.true;
});

設計判断

既存のユーティリティ scrollIntoView を再利用 することで、コードベースへの影響を最小限に抑えました。新しいプロパティやオプションは導入せず、オープン状態かつリストボックスが表示されているケースだけで呼び出す条件分岐を追加した点がポイントです。これにより、従来の WaSelect 使用方法に破壊的変更はなく、既存のアクセシビリティ実装とも整合性が保たれます。さらにテストを追加したことで、将来的なリファクタリングや機能追加に対しても回帰リスクが低減されます。

まとめ

今回の変更は、キーボード操作時に選択肢が常に可視領域にスクロールされるようにしたことで、長いオプションリストでも直感的な操作体験を実現しました。実装は最小限の条件分岐とユーティリティ呼び出しに留め、後方互換性とテストカバレッジを両立させた点が設計上の主な価値です。

記事メタデータ

Generated by:
gpt-oss-120b for DiffDaily
LLM Trace:
34fcc83c

この記事はAIによって自動生成されています。内容の正確性については、必ずソースコードやPRを確認してください。

品質レビュー結果

Review Status:
承認済み
Review Count:
1回
Reviewed by:
gpt-oss-120b for DiffDaily

Review Criteria:

記事構成 ✓ PASS

Title, Context, Technical Detailの存在と明確さ

リード文・背景・技術的変更・設計判断・まとめの5つの要素が揃っており、総論→各論→結論の流れが明確です。

カスタムMarkdown構文 ⚠ WARNING

シンタックスハイライト・GitHubリンク記法の正確性

GitHubリンクは [PR #2530](URL) の形式で記載されており、[#123](URL) の厳密な書式とは異なります。リンクテキストから余分な文字列を除くと完全に合致します。

対象読者への適合性 ✓ PASS

エンジニア向けの適切な技術レベルと表現

エンジニア向けの詳細な実装説明で、初心者向けの過剰解説はありません。

パラグラフ・ライティング ✓ PASS

トピックセンテンス・1段落1トピック・段落長

各セクションは総論・各論・結論の段落構成で、トピックセンテンスが冒頭に置かれ、段落は適切な長さで空行で区切られています。

Diff内容との照合 ✓ PASS

コードブロックとDiff内容の一致

記事内のコードブロックは提供されたDiffと一致しています。追加された行とテストコードは正確に反映されています。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

使用されている用語(scrollIntoView, WaSelect, listbox など)は PR と一致し、誤用はありません。

説明の技術的正確性 ✓ PASS

技術的主張の正確性と論理性

技術的説明は Diff と PR の内容に基づいており、因果関係も正しく記述されています。

事実の突合 ✓ PASS

PR情報による主張の裏付け(ハルシネーション検出)

すべての主張は PR のタイトル、説明、Diff、Issue 番号に裏付けられており、根拠のない推測はありません。

数値・固有名詞の確認 ✓ PASS

PR番号・コミットID・バージョン等の正確性

PR 番号 #2530、Issue 番号 #2472、コード行番号 715 など、数値は正確です。

タイトル・説明との一致 ✓ PASS

記事タイトル・説明とPR内容の一致

記事タイトルは PR タイトルの意味を正しく伝えており、矛盾はありません。

外部知識の正確性 ✓ PASS

PRに記載のない外部知識(LTS、サポート状況など)の不使用

バージョンサポートやリリース日程等、外部知識は記事に含まれていません。

時間表現の正確性 ✓ PASS

時間表現がPR情報と一致しているか

時間表現は使用されていないため、PR 内容との不整合はありません。