diff --git a/chips/internal/filter-chip.ts b/chips/internal/filter-chip.ts index 89127d4f8e..c361b4373b 100644 --- a/chips/internal/filter-chip.ts +++ b/chips/internal/filter-chip.ts @@ -108,11 +108,17 @@ export class FilterChip extends MultiActionChip { return; } + // Store prevValue to revert in case `chip.selected` is changed during an + // event listener. + const prevValue = this.selected; this.selected = !this.selected; const preventDefault = !redispatchEvent(this, event); if (preventDefault) { - this.selected = !this.selected; + // We should not do `this.selected = !this.selected`, since a client + // click listener could change its value. Instead, always revert to the + // original value. + this.selected = prevValue; return; } } diff --git a/chips/internal/filter-chip_test.ts b/chips/internal/filter-chip_test.ts index 2665515983..ae894b1be8 100644 --- a/chips/internal/filter-chip_test.ts +++ b/chips/internal/filter-chip_test.ts @@ -66,5 +66,38 @@ describe('Filter chip', () => { await harness.clickWithMouse(); expect(handler).toHaveBeenCalledTimes(0); }); + + it('always reverts value on preventDefault() even if selected is changed in listener', async () => { + const {chip, harness} = await setupTest(); + + chip.addEventListener( + 'click', + (event) => { + event.preventDefault(); + chip.selected = false; + }, + {once: true}, + ); + + await harness.clickWithMouse(); + expect(chip.selected) + .withContext('chip.selected reverts to false') + .toBeFalse(); + + chip.selected = true; + chip.addEventListener( + 'click', + (event) => { + event.preventDefault(); + chip.selected = false; + }, + {once: true}, + ); + + await harness.clickWithMouse(); + expect(chip.selected) + .withContext('chip.selected reverts to true') + .toBeTrue(); + }); }); });