Skip to content
This repository was archived by the owner on Jan 13, 2025. It is now read-only.

Commit 94c6a00

Browse files
authored
fix(chips): Ignore selection events in chip set (#4878)
1 parent bfed33d commit 94c6a00

File tree

12 files changed

+119
-72
lines changed

12 files changed

+119
-72
lines changed

packages/mdc-chips/README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,7 @@ Method Signature | Description
365365
`removeClassFromLeadingIcon(className: string) => void` | Removes a class from the leading icon element
366366
`eventTargetHasClass(target: EventTarget, className: string) => boolean` | Returns true if target has className, false otherwise
367367
`notifyInteraction() => void` | Notifies the Chip Set that the chip has been interacted with\*
368-
`notifySelection(selected) => void` | Notifies the Chip Set that the chip has been selected or deselected\*\*
368+
`notifySelection(selected: boolean, chipSetShouldIgnore: boolean) => void` | Notifies the Chip Set that the chip has been selected or deselected\*\*. When `chipSetShouldIgnore` is `true`, the chip set does not process the event.
369369
`notifyTrailingIconInteraction() => void` | Notifies the Chip Set that the chip's trailing icon has been interacted with\*
370370
`notifyRemoval() => void` | Notifies the Chip Set that the chip will be removed\*\*\*
371371
`getComputedStyleValue(propertyName: string) => string` | Returns the computed property value of the given style property on the root element
@@ -393,7 +393,7 @@ Method Signature | Description
393393
--- | ---
394394
`hasClass(className: string) => boolean` | Returns whether the chip set element has the given class
395395
`removeChipAtIndex(index: number) => void` | Removes the chip with the given `index` from the chip set
396-
`selectChipAtIndex(index: string, selected: boolean) => void` | Calls `MDCChip#setSelectedFromChipSet(selected)` on the chip at the given `index`
396+
`selectChipAtIndex(index: string, selected: boolean, shouldNotifyClients: boolean) => void` | Calls `MDCChip#setSelectedFromChipSet(selected)` on the chip at the given `index`. Will emit a selection event if called with `shouldNotifyClients` set to `true`. The emitted selection event will be ignored by the `MDCChipSetFoundation`.
397397
`getIndexOfChipById(id: string) => number` | Returns the index of the chip with the matching `id` or -1
398398
`focusChipPrimaryActionAtIndex(index: number) => void` | Calls `MDCChip#focusPrimaryAction()` on the chip at the given `index`
399399
`focusChipTrailingActionAtIndex(index: number) => void` | Calls `MDCChip#focusTrailingAction()` on the chip at the given `index`
@@ -409,7 +409,7 @@ Method Signature | Description
409409
--- | ---
410410
`isSelected() => boolean` | Returns true if the chip is selected
411411
`setSelected(selected: boolean) => void` | Sets the chip's selected state
412-
`setSelectedFromChipSet(selected: boolean) => void` | Sets the chip's selected state (called from the chip set)
412+
`setSelectedFromChipSet(selected: boolean, shouldNotifyClients: boolean) => void` | Sets the chip's selected state (called from the chip set) to the `selected` param. Will emit a selection event if called with `shouldNotifyClients` set to `true`. The emitted selection event will be ignored by the `MDCChipSetFoundation`.
413413
`getShouldRemoveOnTrailingIconClick() => boolean` | Returns whether a trailing icon click should trigger exit/removal of the chip
414414
`setShouldRemoveOnTrailingIconClick(shouldRemove: boolean) => void` | Sets whether a trailing icon click should trigger exit/removal of the chip
415415
`getDimensions() => ClientRect` | Returns the dimensions of the chip. This is used for applying ripple to the chip.
@@ -438,7 +438,7 @@ Method Signature | Description
438438
`getSelectedChipIds() => ReadonlyArray<string>` | Returns an array of the IDs of all selected chips
439439
`select(chipId: string) => void` | Selects the chip with the given id
440440
`handleChipInteraction(chipId: string) => void` | Handles a custom `MDCChip:interaction` event on the root element
441-
`handleChipSelection(chipId: string, selected: boolean) => void` | Handles a custom `MDCChip:selection` event on the root element
441+
`handleChipSelection(chipId: string, selected: boolean, chipSetShouldIgnore: boolean) => void` | Handles a custom `MDCChip:selection` event on the root element. When `chipSetShouldIgnore` is true, the chip set does not process the event.
442442
`handleChipRemoval(chipId: string) => void` | Handles a custom `MDCChip:removal` event on the root element
443443
`handleChipNavigation(chipId: string, key: string) => void` | Handles a custom `MDCChip:navigation` event on the root element
444444

packages/mdc-chips/chip-set/adapter.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ export interface MDCChipSetAdapter {
4343
/**
4444
* Sets the selected state of the chip at the given index.
4545
*/
46-
selectChipAtIndex(index: number, selected: boolean): void;
46+
selectChipAtIndex(index: number, isSelected: boolean, shouldNotifyClients: boolean): void;
4747

4848
/**
4949
* Returns the index of the chip at the given ID.

packages/mdc-chips/chip-set/component.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,9 @@ export class MDCChipSet extends MDCComponent<MDCChipSetFoundation> {
7373
});
7474

7575
this.handleChipInteraction_ = (evt) => this.foundation_.handleChipInteraction(evt.detail.chipId);
76-
this.handleChipSelection_ = (evt) => this.foundation_.handleChipSelection(evt.detail.chipId, evt.detail.selected);
76+
this.handleChipSelection_ = (evt) => {
77+
this.foundation_.handleChipSelection(evt.detail.chipId, evt.detail.selected, evt.detail.shouldIgnore);
78+
};
7779
this.handleChipRemoval_ = (evt) => this.foundation_.handleChipRemoval(evt.detail.chipId);
7880
this.handleChipNavigation_ = (evt) => this.foundation_.handleChipNavigation(
7981
evt.detail.chipId, evt.detail.key, evt.detail.source);
@@ -130,9 +132,9 @@ export class MDCChipSet extends MDCComponent<MDCChipSetFoundation> {
130132
removeFocusFromChipAtIndex: (index) => {
131133
this.chips_[index].removeFocus();
132134
},
133-
selectChipAtIndex: (index, selected) => {
135+
selectChipAtIndex: (index, selected, shouldNotifyClients) => {
134136
if (index >= 0 && index < this.chips_.length) {
135-
this.chips_[index].setSelectedFromChipSet(selected);
137+
this.chips_[index].setSelectedFromChipSet(selected, shouldNotifyClients);
136138
}
137139
},
138140
};

packages/mdc-chips/chip-set/foundation.ts

Lines changed: 34 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -67,21 +67,10 @@ export class MDCChipSetFoundation extends MDCFoundation<MDCChipSetAdapter> {
6767

6868
/**
6969
* Selects the chip with the given id. Deselects all other chips if the chip set is of the choice variant.
70+
* Does not notify clients of the updated selection state.
7071
*/
7172
select(chipId: string) {
72-
if (this.selectedChipIds_.indexOf(chipId) >= 0) {
73-
return;
74-
}
75-
76-
if (this.adapter_.hasClass(cssClasses.CHOICE) && this.selectedChipIds_.length > 0) {
77-
const previouslySelectedChip = this.selectedChipIds_[0];
78-
const previouslySelectedIndex = this.adapter_.getIndexOfChipById(previouslySelectedChip);
79-
this.selectedChipIds_.length = 0;
80-
this.adapter_.selectChipAtIndex(previouslySelectedIndex, false);
81-
}
82-
this.selectedChipIds_.push(chipId);
83-
const index = this.adapter_.getIndexOfChipById(chipId);
84-
this.adapter_.selectChipAtIndex(index, true);
73+
this.select_(chipId, false);
8574
}
8675

8776
/**
@@ -98,12 +87,17 @@ export class MDCChipSetFoundation extends MDCFoundation<MDCChipSetAdapter> {
9887
/**
9988
* Handles a chip selection event, used to handle discrepancy when selection state is set directly on the Chip.
10089
*/
101-
handleChipSelection(chipId: string, selected: boolean) {
90+
handleChipSelection(chipId: string, selected: boolean, shouldIgnore: boolean) {
91+
// Early exit if we should ignore the event
92+
if (shouldIgnore) {
93+
return;
94+
}
95+
10296
const chipIsSelected = this.selectedChipIds_.indexOf(chipId) >= 0;
10397
if (selected && !chipIsSelected) {
10498
this.select(chipId);
10599
} else if (!selected && chipIsSelected) {
106-
this.deselect_(chipId);
100+
this.deselectAndNotifyClients_(chipId);
107101
}
108102
}
109103

@@ -112,7 +106,7 @@ export class MDCChipSetFoundation extends MDCFoundation<MDCChipSetAdapter> {
112106
*/
113107
handleChipRemoval(chipId: string) {
114108
const index = this.adapter_.getIndexOfChipById(chipId);
115-
this.deselect_(chipId);
109+
this.deselectAndNotifyClients_(chipId);
116110
this.adapter_.removeChipAtIndex(index);
117111
const maxIndex = this.adapter_.getChipListCount() - 1;
118112
const nextIndex = Math.min(index, maxIndex);
@@ -189,12 +183,12 @@ export class MDCChipSetFoundation extends MDCFoundation<MDCChipSetAdapter> {
189183
/**
190184
* Deselects the chip with the given id.
191185
*/
192-
private deselect_(chipId: string) {
186+
private deselectAndNotifyClients_(chipId: string) {
193187
const index = this.selectedChipIds_.indexOf(chipId);
194188
if (index >= 0) {
195189
this.selectedChipIds_.splice(index, 1);
196190
const chipIndex = this.adapter_.getIndexOfChipById(chipId);
197-
this.adapter_.selectChipAtIndex(chipIndex, false);
191+
this.adapter_.selectChipAtIndex(chipIndex, /** isSelected */ false, /** shouldNotifyClients */ true);
198192
}
199193
}
200194

@@ -203,9 +197,9 @@ export class MDCChipSetFoundation extends MDCFoundation<MDCChipSetAdapter> {
203197
*/
204198
private toggleSelect_(chipId: string) {
205199
if (this.selectedChipIds_.indexOf(chipId) >= 0) {
206-
this.deselect_(chipId);
200+
this.deselectAndNotifyClients_(chipId);
207201
} else {
208-
this.select(chipId);
202+
this.selectAndNotifyClients_(chipId);
209203
}
210204
}
211205

@@ -217,6 +211,26 @@ export class MDCChipSetFoundation extends MDCFoundation<MDCChipSetAdapter> {
217211
}
218212
}
219213
}
214+
215+
private selectAndNotifyClients_(chipId: string) {
216+
this.select_(chipId, true);
217+
}
218+
219+
private select_(chipId: string, shouldNotifyClients: boolean) {
220+
if (this.selectedChipIds_.indexOf(chipId) >= 0) {
221+
return;
222+
}
223+
224+
if (this.adapter_.hasClass(cssClasses.CHOICE) && this.selectedChipIds_.length > 0) {
225+
const previouslySelectedChip = this.selectedChipIds_[0];
226+
const previouslySelectedIndex = this.adapter_.getIndexOfChipById(previouslySelectedChip);
227+
this.selectedChipIds_ = [];
228+
this.adapter_.selectChipAtIndex(previouslySelectedIndex, /** isSelected */ false, shouldNotifyClients);
229+
}
230+
this.selectedChipIds_.push(chipId);
231+
const index = this.adapter_.getIndexOfChipById(chipId);
232+
this.adapter_.selectChipAtIndex(index, /** isSelected */ true, shouldNotifyClients);
233+
}
220234
}
221235

222236
// tslint:disable-next-line:no-default-export Needed for backward compatibility with MDC Web v0.44.0 and earlier.

packages/mdc-chips/chip/adapter.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ export interface MDCChipAdapter {
7070
/**
7171
* Emits a custom "MDCChip:selection" event denoting the chip has been selected or deselected.
7272
*/
73-
notifySelection(selected: boolean): void;
73+
notifySelection(selected: boolean, chipSetShouldIgnore: boolean): void;
7474

7575
/**
7676
* Emits a custom "MDCChip:trailingIconInteraction" event denoting the trailing icon has been
@@ -129,7 +129,7 @@ export interface MDCChipAdapter {
129129
hasTrailingAction(): boolean;
130130

131131
/**
132-
* Sets the the attribute on the trailing action if it exists.
132+
* Sets the attribute on the trailing action if it exists.
133133
*/
134134
setTrailingActionAttr(attr: string, value: string): void;
135135

packages/mdc-chips/chip/component.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -192,8 +192,8 @@ export class MDCChip extends MDCComponent<MDCChipFoundation> implements MDCRippl
192192
this.emit<MDCChipRemovalEventDetail>(
193193
strings.REMOVAL_EVENT, {chipId: this.id, root: this.root_}, true /* shouldBubble */);
194194
},
195-
notifySelection: (selected) => this.emit<MDCChipSelectionEventDetail>(
196-
strings.SELECTION_EVENT, {chipId: this.id, selected}, true /* shouldBubble */),
195+
notifySelection: (selected, shouldIgnore) => this.emit<MDCChipSelectionEventDetail>(
196+
strings.SELECTION_EVENT, {chipId: this.id, selected, shouldIgnore}, true /* shouldBubble */),
197197
notifyTrailingIconInteraction: () => this.emit<MDCChipInteractionEventDetail>(
198198
strings.TRAILING_ICON_INTERACTION_EVENT, {chipId: this.id}, true /* shouldBubble */),
199199
removeClass: (className) => this.root_.classList.remove(className),
@@ -217,8 +217,8 @@ export class MDCChip extends MDCComponent<MDCChipFoundation> implements MDCRippl
217217
return new MDCChipFoundation(adapter);
218218
}
219219

220-
setSelectedFromChipSet(selected: boolean) {
221-
this.foundation_.setSelectedFromChipSet(selected);
220+
setSelectedFromChipSet(selected: boolean, shouldNotifyClients: boolean) {
221+
this.foundation_.setSelectedFromChipSet(selected, shouldNotifyClients);
222222
}
223223

224224
focusPrimaryAction() {

packages/mdc-chips/chip/foundation.ts

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -84,11 +84,15 @@ export class MDCChipFoundation extends MDCFoundation<MDCChipAdapter> {
8484
}
8585

8686
setSelected(selected: boolean) {
87-
this.setSelected_(selected, true);
87+
this.setSelected_(selected);
88+
this.notifySelection_(selected);
8889
}
8990

90-
setSelectedFromChipSet(selected: boolean) {
91-
this.setSelected_(selected, false);
91+
setSelectedFromChipSet(selected: boolean, shouldNotifyClients: boolean) {
92+
this.setSelected_(selected);
93+
if (shouldNotifyClients) {
94+
this.notifyIgnoredSelection_(selected);
95+
}
9296
}
9397

9498
getShouldRemoveOnTrailingIconClick() {
@@ -325,18 +329,22 @@ export class MDCChipFoundation extends MDCFoundation<MDCChipAdapter> {
325329
return isDeletable && (evt.key === strings.BACKSPACE_KEY || evt.key === strings.DELETE_KEY);
326330
}
327331

328-
private setSelected_(selected: boolean, shouldNotify: boolean) {
332+
private setSelected_(selected: boolean) {
329333
if (selected) {
330334
this.adapter_.addClass(cssClasses.SELECTED);
331335
this.adapter_.setPrimaryActionAttr(strings.ARIA_CHECKED, 'true');
332336
} else {
333337
this.adapter_.removeClass(cssClasses.SELECTED);
334338
this.adapter_.setPrimaryActionAttr(strings.ARIA_CHECKED, 'false');
335339
}
340+
}
336341

337-
if (shouldNotify) {
338-
this.adapter_.notifySelection(selected);
339-
}
342+
private notifySelection_(selected: boolean) {
343+
this.adapter_.notifySelection(selected, false);
344+
}
345+
346+
private notifyIgnoredSelection_(selected: boolean) {
347+
this.adapter_.notifySelection(selected, true);
340348
}
341349
}
342350

packages/mdc-chips/chip/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ export interface MDCChipInteractionEventDetail {
2929

3030
export interface MDCChipSelectionEventDetail extends MDCChipInteractionEventDetail {
3131
selected: boolean;
32+
shouldIgnore: boolean;
3233
}
3334

3435
export interface MDCChipRemovalEventDetail extends MDCChipInteractionEventDetail {

0 commit comments

Comments
 (0)