`<wa-select>` の空値オプション選択時にラベル重複が起きるバグを修正

shoelace-style/webawesome

<wa-select> で空文字列("")をバリューに持つオプションを選択した際、ラベルが入力内のテキストと重なる表示バグが修正されました。:state(blank) カスタムステートの判定ロジックを displayLabel も考慮する形に拡張したことで、バリューと表示テキストが独立して扱われるようになっています。

背景

<wa-select> は、ラベルを「入力の内側」に配置するかどうかの判定を value の有無のみで行っていました。しかし value="" かつ <wa-option> にテキストコンテンツがある場合(例:「全従業員」のような初期選択肢で空文字列をバリューとして使うケース)、バリューが空でも表示テキストが存在するため、ラベルと選択テキストが同一領域に重なって描画されるという問題が発生していました。

これは Issue #1920 として報告されており、サーバーサイドのフィルタリング機能で「フィルターなし」を意味する空文字列バリューを使用しているケースが具体的なユースケースとして示されています。空文字列バリューを避けるワークアラウンド("all" などの非空文字列に置き換える)は存在しましたが、バックエンド側のデータモデルとの乖離を生じさせるものでした。

技術的な変更

変更の中心は select.ts における :state(blank) カスタムステートの判定条件の拡張と、クリアアイコンの表示条件の修正、そして handleValueChange でのリセット処理です。

:state(blank) の判定条件を拡張:

変更前は value のみを参照していたため、空文字列バリューのオプションが選択された状態でも blank ステートが付与されていました。

変更前:

if (changedProperties.has('value')) {
  this.customStates.set('blank', !this.value);
}

変更後:

if (changedProperties.has('value') || changedProperties.has('displayLabel')) {
  this.customStates.set('blank', !this.value && !this.displayLabel);
}

changedProperties の監視対象に displayLabel を追加し、blank の判定を「valuedisplayLabel も空である」という複合条件に変更しています。これにより、空文字列バリューのオプションが選択されていても displayLabel にテキストが入っていれば blank ステートは付与されず、ラベルが入力内部に折り畳まれたままになります。

クリアアイコンの表示条件を修正:

withClear 属性が指定された場合のクリアアイコン表示条件も同様に修正されています。

変更前:

const hasClearIcon =
  (this.hasUpdated || isServer) && this.withClear && !this.disabled && this.value && this.value.length > 0;

変更後:

const hasClearIcon =
  (this.hasUpdated || isServer) &&
  this.withClear &&
  !this.disabled &&
  (this.displayLabel || (this.value && this.value.length > 0));

value が空文字列でも displayLabel が存在する場合にクリアアイコンを表示するよう拡張されており、ユーザーが空バリューの選択を解除できるようになっています。

値変更ハンドラーでの displayLabel リセット:

if (this.value !== null) {
  this.displayLabel = '';  // 追加
  this.selectionOrder.clear();
  this.setSelectedOptions([]);
  this.displayInput.focus({ preventScroll: true });
}

handleValueChange の冒頭で displayLabel を明示的にクリアすることで、値の変更時に前の表示ラベルが残存しないよう保証しています。

設計判断

valuedisplayLabel を独立した状態として扱う という方針が採られています。Web Awesomeの <wa-select> では、フォームの送信値(value)と表示テキスト(displayLabel)は概念的に異なるものです。空文字列は有効なフォーム値であり、その場合でも「何かが選択されている」状態を正確に反映するためには displayLabel を独立した信号として扱う必要があります。

:state(blank) カスタムステートはCSSのスタイリングに使われるため、このステートが誤って付与されると視覚表現(ラベルの浮動・ テキストの重なり)に直接影響します。value 単体ではなく「表示する内容が何もない」という意味論的な状態を正確に表現するための修正といえます。

まとめ

本PRは、valuedisplayLabel を独立した状態として扱うことで、フォームの送信値と表示テキストの乖離が起きうるユースケースに対応した修正です。空文字列を有効なフォーム値として使いながらも、ラベルの表示位置やクリアアイコンの可視性が正しく制御されるようになり、バックエンドのデータモデルに合わせたUI実装が可能になります。

記事メタデータ

Generated by:
Claude Sonnet 4.6 for DiffDaily
LLM Trace:
bbe9f0c1

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

品質レビュー結果

Review Status:
承認済み
Review Count:
1回
Reviewed by:
Gemini 2.5 Pro for DiffDaily

Review Criteria:

記事構成 ✓ PASS

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

リード文(総論)→セクション群(背景、技術的な変更、設計判断)(各論)→まとめ(結論)の3部構成が明確に適用されており、模範的な構成です。

カスタムMarkdown構文 ✓ PASS

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

ファイル名付きシンタックスハイライト(```typescript:path/to/file.ts)とGitHubのPR/Issueリンク記法([#123](URL))が正しく使用されています。

対象読者への適合性 ✓ PASS

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

Webコンポーネントの内部実装に関する内容であり、`:state(blank)`や`displayLabel`といった専門用語を前提としており、対象読者であるエンジニアに適した技術レベルです。

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

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

各セクションが総論→各論で構成され、各段落がトピックセンテンスで始まるなど、パラグラフ・ライティングの原則が遵守されており、非常に読みやすいです。

Diff内容との照合 ✓ PASS

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

記事で引用されている3つのコードブロック(変更前・変更後)は、提供されたDiff情報と完全に一致しています。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

`:state(blank)`、`displayLabel`、`changedProperties`などの技術用語が正確かつ適切な文脈で使用されています。

説明の技術的正確性 ✓ PASS

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

`:state(blank)`の判定ロジック変更や、クリアアイコンの表示条件拡張に関する説明は、コードの変更内容と完全に一致しており、技術的に正確です。

事実の突合 ✓ PASS

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

記事内のすべての主張は、PRのDescription、関連Issue、およびDiff内のコード変更によって裏付けられており、ハルシネーションは検出されませんでした。「設計判断」セクションはコードの意図を正確に読み取ったもので、創作ではありません。

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

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

PR番号(#2234)およびIssue番号(#1920)は、提供された情報と正確に一致しています。

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

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

記事のタイトル「`<wa-select>` の空値オプション選択時にラベル重複が起きるバグを修正」は、PRのタイトル「Fix blank state in `<wa-select>`」をより具体的に説明しており、内容と完全に一致しています。

外部知識の正確性 ✓ PASS

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

記事には、PR情報に記載のないバージョンサポート状況やリリース日程などの外部知識は含まれていません。

時間表現の正確性 ✓ PASS

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

「修正されました」「発生していました」といった過去形の表現が使われており、完了した変更を報告する記事として時間表現は正確です。