wa-checkbox と wa-switch の .checked 同期バグを修正

shoelace-style/webawesome

この PR は <wa-checkbox><wa-switch> コンポーネントで、.checked プロパティがシャドウ DOM 内の <input> 要素と正しく同期しないバグを解消し、Lit SSR 環境でも期待通りに動作するように変更します。

背景

Issue #2478 では、バージョン 3.8.0 へのアップグレード後にリアクティブフレームワークからのバインディングが機能せず、.checked プロパティの更新が UI に反映されないことが報告されました。属性ベースの初期化は機能する一方で、プロパティ経由のライブ更新が失われたため、フォームの状態管理やアクセシビリティに支障が出ていました。この問題はコンポーネント内部で属性とプロパティの扱いが分離されたことが根本原因です。

技術的な変更

SSR 環境下での .checked ハンドリングを見直し、属性とプロパティを条件分岐させるロジックを新たに導入しました。didSSR && !this.hasUpdated が真のときは属性 (checkedAttribute) に既存の checked 値を設定し、プロパティ (checkedProperty) は null にして無効化します。そうでない場合は live(this.checked) をプロパティとしてバインドし、属性は defaultChecked を使用します。

@@ -227,11 +227,17 @@ export default class WaCheckbox extends WebAwesomeFormAssociatedElement {
-            ?checked=${this.defaultChecked}
+            .checked=${ifDefined(checkedProperty)}
+            ?checked=${checkedAttribute}

WaSwitch でも同様のパターンを適用し、テンプレート内で属性とプロパティを分離しました。

@@ -216,6 +216,11 @@ export default class WaSwitch extends WebAwesomeFormAssociatedElement {
-          .checked=${live(this.checked)}
-          ?checked=${this.defaultChecked}
+          .checked=${ifDefined(checkedProperty)}
+          ?checked=${checkedAttribute}

テストコードも更新し、checked プロパティ変更後に shadowRoot 内の <input>checked 状態が期待通りになることを expect(...).to.equal(true/false) で検証しています。また、aTimeout(1) を挿入して非同期更新を待機するようにしました。

@@ -53,12 +53,20 @@ describe('<wa-checkbox>', () => {
           expect(el.checked).to.be.true;
           expect(el.value).to.equal('myvalue');
+          expect(el.shadowRoot?.querySelector('input')?.checked).to.equal(true);
@@ -58,6 +66,7 @@           el.checked = false;
           await el.updateComplete;
           expect(el.checked).to.be.false;
           expect(el.value).to.equal('myvalue');
+          expect(el.shadowRoot?.querySelector('input')?.checked).to.equal(false);
@@ -71,6 +80,13 @@           // let's recheck setting `el.checked = true`
           el.checked = true;
           await el.updateComplete;
           expect(el.shadowRoot?.querySelector('input')?.checked).to.equal(true);

package.json@lit-labs/ssr を正式な依存関係として追加し、重複していた QR クリエイターパッケージの位置を整理しました。package-lock.json の不要な dev: true フラグも削除し、ロックファイルを整合させています。

設計判断

既存のコンポーネント API(.checked プロパティ)をそのまま保持しつつ、SSR 時の初期描画を属性ベースに切り替えることで後方互換性を担保しました。新しい設定キーやプロパティを導入せず、内部ロジックだけで対応した点が、利用者側のマイグレーションコストを最小化しています。

didSSR && !this.hasUpdated の条件分岐は、Lit SSR が内部で .checked=${live(this.checked)} を常に true と評価してしまう既知の問題への回避策としてコメントで明示されています。この実装はコンポーネント単位で問題を解決するため、プロジェクト全体への影響範囲が限定的です。

最終的に、属性とプロパティの同期ロジックを明確に分離したことで、リアクティブフレームワークからのバインディングが期待通りに動作し、テストでも確実に検証できるようになりました。

まとめ

.checked プロパティと内部 <input> の状態同期バグが解消され、SSR 環境でも正しい初期化が行われます。依存関係の整理とテスト追加により、回帰リスクが低減し、既存 API の互換性を維持したままコンポーネントの信頼性が向上しました。

記事メタデータ

Generated by:
gpt-oss-120b for DiffDaily
LLM Trace:
1325ee1c

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

品質レビュー結果

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

Review Criteria:

記事構成 ✓ PASS

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

リード文、背景、技術的変更、設計判断(任意)およびまとめが明確に配置されており、総論→各論→結論の流れが保たれています。

カスタムMarkdown構文 ⚠ WARNING

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

コードブロックは diff 形式で正しく記述されていますが、ファイル名付きシンタックスハイライトは使用されていません。GitHub リンクは PR 番号の形式が [PR #2481](URL) となっており、指定の "[#123](URL)" 形式とは若干異なります。

対象読者への適合性 ✓ PASS

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

内容は Lit と Web Components に精通したエンジニア向けで、初心者向けの過度な説明はありません。

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

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

各セクションは総論パラグラフで要旨を示し、続く段落が具体的事実やコードで裏付け、最後に結論がまとめられています。段落はトピックセンテンスで始まり、長さも適切です。

Diff内容との照合 ✓ PASS

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

記事中の diff スニペットは提供された PR diff と一致し、変更箇所・追加内容ともに正確に反映されています。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

SSR、live、ifDefined、checkedProperty などの用語は PR 内容と合致し、誤用は見られません。

説明の技術的正確性 ✓ PASS

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

技術的説明は diff と PR の記述に基づき、因果関係や意図が論理的に示されています。

事実の突合 ✓ PASS

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

記事内の主張は PR タイトル、Description、Issue #2478、Diff にすべて裏付けられており、推測や捏造はありません。

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

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

PR 番号 #2481、Issue 番号 #2478、行番号・コード変更など数値は正確です。

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

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

記事タイトルは PR の内容(wa-checkbox と wa-switch の .checked バグ修正)を正確に表現しています。

外部知識の正確性 ✓ PASS

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

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

時間表現の正確性 ✓ PASS

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

時間表現の歪曲はなく、PR の記述と一致しています。