Combobox

入力しながら候補を絞り込んで選ぶフォーム部品(WAI-ARIA の combobox + listbox)。既定の絞り込みはひらがな・カタカナ・全角半角・大文字小文字を区別せず、IME 変換中の Enter や矢印キーは無視します。選択肢が少なく絞り込みが不要なら Select を使います。

プレビュー

「とうきょう」「オオサカ」でも絞り込めます

使いどころ

使う場面

  • 選択肢が 10 件を超えるとき、または名前の一部で探したいとき(取引先、担当者、国、地名)
  • ふりがなや別名で探せるようにしたいとき。keywords にヨミや略称を持たせる
  • 名前(label)ではなく ID(value)をフォームで送りたいとき。name を付けると hidden で value が送られる

使わない場面

  • 選択肢が 10 件以下。Select のほうが速く、モバイルで OS の標準ピッカーが使える
  • 自由入力も受けたいとき。Combobox は候補にない文字を確定しない。Input を使う
  • 複数選択。Combobox は 1 つだけ。Checkbox の並びか FilterPill を使う
  • 操作を探すこと(コマンド、ページ移動)。CommandPalette を使う

使い方

import { Combobox, FormField } from "kaze-design-system";

const options = [
  { value: "tokyo", label: "東京都", keywords: ["とうきょう"] },
  { value: "osaka", label: "大阪府", keywords: ["おおさか"] },
];

<FormField label="都道府県" error={error}>
  <Combobox
    name="prefecture"
    options={options}
    value={prefecture}
    onValueChange={setPrefecture}
  />
</FormField>

プロパティ

名前型デフォルト説明
options必須ComboboxOption[]なし候補。keywords は表示せず検索だけに使う読みや別名
valuestring | nullなし選択中の value(制御)。未選択は null
defaultValuestring | nullnull初期の選択(非制御)
onValueChange(value: string | null) => voidなし選択が変わる時のコールバック。入力を空にして離れると null
filter(option: ComboboxOption, query: string) => booleanなし絞り込みの差し替え。既定は label と keywords への includesJa
namestringなし指定すると hidden input で value(ラベルではなく)をフォーム送信する
size"sm" | "md""md"サイズ
emptyMessagestring"候補がありません"一致する候補がないときの文言
wrapperClassNamestringなし外側の要素に足すクラス
aria-invalidboolean | 'true' | 'false'なしtrue で Input と同じエラー表示。FormField の error から自動で渡される

型と必須かどうかはソースの型定義から自動で生成しています。表にない HTML 標準の属性(className や aria-* など)も、ルート要素にそのまま渡せます。

キーボード操作

キー動作
↓ / ↑一覧を開き、候補を移動する。端では反対側へ回る。無効な候補は飛ばす
Home / End一覧が開いているとき、最初と最後の候補へ移る
Enterハイライト中の候補を選ぶ(フォームは送信されない)
Esc一覧を閉じ、入力を選択中のラベルに戻す
Tab一覧を閉じて次へ移る。ラベルと一致する文字は選択として扱う

アクセシビリティ

  • input に role="combobox"、一覧に role="listbox"、候補に role="option" を付け、ハイライトは aria-activedescendant で伝える。フォーカスは常に入力欄に残る
  • 候補がないとき、emptyMessage が role="status" で読まれる。文言は emptyMessage で変える
  • IME の変換中は矢印キーや Enter を候補の操作として扱わない。日本語入力で候補を誤って確定しない
  • 一覧の名前は aria-label で渡す。ラベルがない場合は必ず指定する

文言

  • placeholder に「担当者を検索」のように、探せることを書く
  • emptyMessage は「見つかりません」ではなく、次の行動が分かる文言にする(「該当する取引先がありません。綴りを変えてください」)

関連するコンポーネント