disabled状態のドロップダウンアイテムでclickイベントが発火する問題を修正

shoelace-style/webawesome

wa-dropdown-itemコンポーネントで、disabled属性が設定されている場合でもclickイベントが発火してしまう問題が修正されました。CSSとJavaScriptの両面からイベント伝播を防ぐことで、動的な状態変更にも対応した実装になっています。

背景

#1817で報告されたように、wa-dropdown-itemdisabled状態でもclickイベントを発火していました。これはUIコンポーネントとして期待される動作ではなく、無効化されたアイテムをクリックした際にアプリケーションロジックが実行されてしまう可能性がありました。

一般的なフォーム要素や他のUIコンポーネントでは、disabled状態ではユーザー操作を受け付けないことが標準的な動作です。この不整合を解消する必要がありました。

技術的な変更

修正はCSSJavaScriptの二段構えで実装されています。これにより、静的なdisabled属性の指定と動的な状態変更の両方に対応しています。

CSS層での対応

dropdown-item.styles.tspointer-events: noneを追加し、ブラウザレベルでポインタイベントを無効化しました。

変更後:

:host(:state(disabled)),
:host([disabled]) {
  opacity: 0.5;
  cursor: not-allowed;
  pointer-events: none;
}

:host(:state(disabled)):host([disabled])の両方にスタイルを適用することで、カスタムステートと属性の両方の指定方法に対応しています。

JavaScript層での対応

dropdown-item.tsでは、2つの変更が加えられました。

1. インラインスタイルの動的設定

updated()ライフサイクルメソッドで、disabledプロパティの変更時にインラインスタイルを設定します。

updated(changedProperties: PropertyValues) {
  if (changedProperties.has('disabled')) {
    this.setAttribute('aria-disabled', this.disabled ? 'true' : 'false');
    this.customStates.set('disabled', this.disabled);
    this.style.pointerEvents = this.disabled ? 'none' : '';
  }
}

2. イベントハンドラによる伝播防止

handleClickメソッドをキャプチャフェーズで登録し、disabled状態ではイベントを停止します。

private handleClick = (event: MouseEvent) => {
  if (this.disabled) {
    event.preventDefault();
    event.stopImmediatePropagation();
  }
};

connectedCallback() {
  super.connectedCallback();
  this.addEventListener('mouseenter', this.handleMouseEnter.bind(this));
  this.shadowRoot!.addEventListener('click', this.handleClick, { capture: true });
  this.shadowRoot!.addEventListener('slotchange', this.handleSlotChange);
}

{ capture: true }オプションにより、イベントがバブリングフェーズに到達する前に捕捉できます。これにより、disabled状態でのイベント発火を確実に防止しています。

リグレッションテストの追加

dropdown-item.test.tsに2つのテストケースが追加され、修正の正しさを保証しています。

it('should not fire click event when disabled', async () => {
  const el = await fixture<WaDropdownItem>(html` <wa-dropdown-item disabled>Item</wa-dropdown-item> `);
  await el.updateComplete;

  const clickHandler = sinon.spy();
  el.addEventListener('click', clickHandler);
  await clickOnElement(el);
  expect(clickHandler).not.to.have.been.called;
});

it('should fire click event when not disabled', async () => {
  const el = await fixture<WaDropdownItem>(html` <wa-dropdown-item>Item</wa-dropdown-item> `);
  const clickHandler = sinon.spy();
  el.addEventListener('click', clickHandler);
  await clickOnElement(el);
  expect(clickHandler).to.have.been.calledOnce;
});

設計判断

PRのDescriptionでは、CSSとJavaScriptの両方を使用する理由が明示されています。

CSSは静的なdisabled状態に対して基本的なスタイリングを提供します。一方、JavaScriptのインラインスタイルは、disabledプロパティが動的に変更された際の処理を担当します。

この役割分担により、コンポーネントの初期状態と実行時の状態変更の両方に対応した実装になっています。

本PRは、UIコンポーネントの基本的な期待動作を満たすための実装です。CSSとJavaScriptの役割を明確に分離し、静的・動的な状態変更の両方に対応することで、disabled状態でのクリックイベント発火を防止しています。リグレッションテストによって動作が保証されています。

記事メタデータ

Generated by:
Claude Sonnet 4.5 for DiffDaily

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

品質レビュー結果

Review Status:
リトライ後承認
Review Count:
2回 (改善を経て承認)
Reviewed by:
Gemini 2.5 Pro for DiffDaily

Review Criteria:

記事構成 ✓ PASS

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

リード文(総論)、背景・技術詳細(各論)、まとめ(結論)の3部構成が明確に適用されており、必須要素がすべて含まれています。

カスタムMarkdown構文 ✓ PASS

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

ファイル名付きシンタックスハイライト、PR番号・Issue番号のGitHubリンク記法がすべて正しく使用されています。

対象読者への適合性 ✓ PASS

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

Webコンポーネント、イベントハンドリング、ライフサイクルメソッドに関する内容で、専門知識を持つエンジニアという対象読者に適しています。

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

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

各セクションが総論→各論で構成され、各段落がトピックセンテンスで始まるなど、パラグラフ・ライティングの原則が守られており、高い可読性を実現しています。

Diff内容との照合 ✓ PASS

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

PRのDiff情報を正確に反映しています。特に、PR Descriptionにはない`handleClick`の実装詳細をDiffから抽出し、解説に含めている点は優れています。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

「キャプチャフェーズ」「stopImmediatePropagation」などの技術用語が文脈に応じて正確に使用されています。

説明の技術的正確性 ✓ PASS

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

CSSとJavaScriptの二段構えで修正した理由や、キャプチャフェーズを利用する意図など、技術的な説明は正確かつ論理的です。

事実の突合 ✓ PASS

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

記事内のすべての主張は、PRのDescriptionまたはDiff内のコードによって裏付けられており、ハルシネーション(捏造)はありません。

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

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

PR番号(#2023)やIssue番号(#1817)などの数値・固有名詞は正確に記載されています。

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

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

記事のタイトル「disabled状態のドロップダウンアイテムでclickイベントが発火する問題を修正」は、PRの主題を的確に表現しています。

外部知識の正確性 ✓ PASS

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

PR情報に含まれないバージョン情報やサポート状況などの外部知識の追記はなく、提供された情報に忠実です。

時間表現の正確性 ✓ PASS

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

「修正されました」といった過去形の表現が適切に使われており、時間表現の歪曲はありません。