`<wa-select>`の`with-clear`使用時に`selected`属性が無視される不具合を修正

shoelace-style/webawesome

<wa-select>with-clear属性が指定されている場合、子要素の<wa-option>selected属性が無視される不具合が修正されました。これにより、Vue.jsなどのフレームワークでプロパティバインディングを使用した際の選択状態が正しく反映されるようになります。

背景

with-clear属性付きの<wa-select>で、<wa-option>selected属性を指定してもその選択状態が反映されず、セレクトボックスが空のまま表示される問題が報告されていました。#1922によると、placeholder属性と併用した場合でも同様の現象が発生していました。

<wa-select placeholder="Placeholder" with-clear>
  <wa-option value="option-1" selected>Option 1</wa-option>
  <wa-option value="option-2">Option 2</wa-option>
  <wa-option value="option-3">Option 3</wa-option>
</wa-select>

上記のコードでは「Option 1」が選択されるべきですが、実際にはプレースホルダーが表示されたままとなっていました。この問題は特にVue.jsの:selected構文を使用した際に顕著でした。

根本原因

不具合の原因は<wa-option>willUpdateライフサイクルメソッドにありました。初回レンダリング時にselected = defaultSelectedが無条件に実行され、フレームワークが直接設定したselectedプロパティの値を上書きしていました。

修正前のコード:

if (changedProperties.has('defaultSelected')) {
  if (!this.closest<WaSelect>('wa-combobox, wa-select')?.hasInteracted) {
    const oldVal = this.selected;
    this.selected = this.defaultSelected;
    this.requestUpdate('selected', oldVal);
  }
}

defaultSelectedはデフォルトでfalseであるため、Vue.jsが:selected="true"でプロパティを設定しても、このコードによってfalseに戻されていました。hasInteractedfalseの状態(ユーザーがまだ操作していない状態)では、常にこの上書きが発生します。

技術的な変更

修正は2つのライフサイクルメソッドに対して行われました。

willUpdateメソッドの修正

defaultSelectedtrueの場合のみselectedを同期するように条件を追加しました。

if (changedProperties.has('defaultSelected')) {
  if (!this.closest<WaSelect>('wa-combobox, wa-select')?.hasInteracted) {
    // Only sync if defaultSelected is becoming true
    if (this.defaultSelected) {
      const oldVal = this.selected;
      this.selected = this.defaultSelected;
      this.requestUpdate('selected', oldVal);
    }
  }
}

この変更により、defaultSelectedfalseの場合にselectedプロパティが上書きされることがなくなります。フレームワークが直接selectedを設定した値は保持されます。

firstUpdatedメソッドの追加

初回レンダリング完了時に、selectedtruedefaultSelectedfalseの場合に親の<wa-select>に選択変更を通知する処理を追加しました。

protected firstUpdated(changedProperties: PropertyValues<this>) {
  super.firstUpdated(changedProperties);

  if (this.selected && !this.defaultSelected) {
    const parent = this.closest<WaSelect>('wa-select, wa-combobox');
    if (parent && !parent.hasInteracted) {
      parent.selectionChanged?.();
    }
  }
}

この処理により、Vue.jsの:selectedバインディングなど、プロパティとして直接設定された選択状態が親コンポーネントに正しく伝播します。

テストの追加

修正を検証するため、select.test.tsに3つのテストケースが追加されました。

  • with-clear単体での動作確認: selected属性が正しく反映されることを検証
  • with-clearplaceholderの併用: #1922で報告された組み合わせでの動作を検証
  • 複数選択モードでの動作確認: multiple属性との併用時に複数のselectedが正しく反映されることを検証
it('should select options with selected attribute, with-clear, and placeholder', async () => {
  const el = await fixture<WaSelect>(html`
    <wa-select placeholder="Placeholder" with-clear>
      <wa-option value="option-1" selected>Option 1</wa-option>
      <wa-option value="option-2">Option 2</wa-option>
      <wa-option value="option-3">Option 3</wa-option>
    </wa-select>
  `);

  expect(el.value).to.equal('option-1');
  expect(el.displayInput.value).to.equal('Option 1');
});

すべてのテストは成功しており、既存のセレクトコンポーネントの動作に影響がないことが確認されています。

設計判断

selecteddefaultSelectedの使い分けを明確にする方向で修正が行われました。

defaultSelectedはHTML属性からの初期値を表し、selectedは現在の選択状態を表すプロパティです。フレームワークがプロパティバインディングでselectedを直接設定する場合、それはdefaultSelectedとは独立した値として扱われるべきです。

PRの説明によると、この修正は<wa-option>単体での動作に限定されており、<wa-input>など他のwith-clearを使用するコンポーネントには影響しません。これらのコンポーネントは子要素のselected属性を使用しない設計のためです。

まとめ

本PRは、ライフサイクルメソッドの条件分岐を精緻化することで、HTML属性ベースの初期化とフレームワークのプロパティバインディングを両立させる修正です。defaultSelectedtrueの場合のみselectedを同期し、それ以外の場合はフレームワークが設定した値を尊重する設計により、宣言的UIフレームワークとの互換性が向上しています。

記事メタデータ

Generated by:
Claude Sonnet 4.5 for DiffDaily

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

品質レビュー結果

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

Review Criteria:

記事構成 ✓ PASS

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

リード文(総論)、背景・技術詳細・設計判断(各論)、まとめ(結論)という「総論→各論→結論」の構成が明確に適用されています。各セクションの役割が明確で、非常に理解しやすい構造です。

カスタムMarkdown構文 ✓ PASS

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

ファイル名付きシンタックスハイライト(```typescript:ファイルパス)やGitHubのIssue/PRへのリンク記法([#1922](URL))がガイドライン通りに正しく使用されています。

対象読者への適合性 ✓ PASS

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

Webコンポーネントのライフサイクルメソッドやフレームワークとの連携といった専門的な内容を扱っており、対象読者であるエンジニアに適した技術レベルと表現で書かれています。

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

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

各セクションが総論→各論の構成で書かれ、各段落はトピックセンテンスで始まっています。1段落1トピックの原則も守られており、可読性が非常に高いです。

Diff内容との照合 ✓ PASS

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

記事内のコード引用は、提供されたDiffの内容と正確に一致しています。ファイルパスも正しく、どのファイルへの変更か明確に理解できます。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

「ライフサイクルメソッド」「プロパティバインディング」「selected/defaultSelected」など、PRの文脈で使われている技術用語を正確に使用しています。

説明の技術的正確性 ✓ PASS

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

不具合の根本原因(willUpdateでの無条件の上書き)から修正内容(条件分岐の追加とfirstUpdatedでの通知)まで、DiffとPR Descriptionに基づいた技術的に正確な説明がなされています。

事実の突合 ✓ PASS

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

記事内のすべての主張(Vue.jsでの問題、他のコンポーネントへの影響がない点など)は、PR Descriptionで裏付けられており、ハルシネーションは検出されませんでした。

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

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

PR番号(#1985)およびIssue番号(#1922)が正確に記載・リンクされており、元情報への参照が容易です。

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

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

記事のタイトルは、PRのタイトル「Fix selected attribute on <wa-option> when <wa-select> has with-clear #1922」の内容を的確に要約しており、主題と完全に一致しています。

外部知識の正確性 ✓ PASS

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

記事の内容は提供されたPR情報に限定されており、バージョンサポート状況やリリース日程といったPR外の知識は含まれていません。

時間表現の正確性 ✓ PASS

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

「報告されていました」といった過去の事象や、「修正されました」といった完了した変更について、時間表現は適切に使用されています。