Skip to content

Commit 01f08ef

Browse files
committed
feat(list-key-manager): accept item references in setActiveItem
* Adds an overload to `setActiveItem` to allow for an item to be set as active, rather than its index. This avoids awkward conversions in certain cases (see the chips and select changes). * Deprecates the `updateActiveItemIndex` method in favor of `updateActiveItem` which accepts both an index and an item. * Fixes the active item not being updated when it is set via `updateActiveItemIndex`.
1 parent 45399c6 commit 01f08ef

File tree

6 files changed

+89
-22
lines changed

6 files changed

+89
-22
lines changed

src/cdk/a11y/key-manager/activedescendant-key-manager.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,22 @@ export interface Highlightable extends ListKeyManagerOption {
2424
export class ActiveDescendantKeyManager<T> extends ListKeyManager<Highlightable & T> {
2525

2626
/**
27-
* This method sets the active item to the item at the specified index.
28-
* It also adds active styles to the newly active item and removes active
29-
* styles from the previously active item.
27+
* Sets the active item to the item at the specified index and adds the
28+
* active styles to the newly active item. Also removes active styles
29+
* from the previously active item.
30+
* @param index Index of the item to be set as active.
3031
*/
31-
setActiveItem(index: number): void {
32+
setActiveItem(index: number): void;
33+
34+
/**
35+
* Sets the active item to the item to the specified one and adds the
36+
* active styles to the it. Also removes active styles from the
37+
* previously active item.
38+
* @param item Item to be set as active.
39+
*/
40+
setActiveItem(item: T): void;
41+
42+
setActiveItem(index: any): void {
3243
if (this.activeItem) {
3344
this.activeItem.setInactiveStyles();
3445
}

src/cdk/a11y/key-manager/focus-key-manager.ts

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,20 @@ export class FocusKeyManager<T> extends ListKeyManager<FocusableOption & T> {
3232
}
3333

3434
/**
35-
* This method sets the active item to the item at the specified index.
36-
* It also adds focuses the newly active item.
35+
* Sets the active item to the item at the specified
36+
* index and focuses the newly active item.
37+
* @param index Index of the item to be set as active.
3738
*/
38-
setActiveItem(index: number): void {
39-
super.setActiveItem(index);
39+
setActiveItem(index: number): void;
40+
41+
/**
42+
* Sets the active item to the item that is specified and focuses it.
43+
* @param item Item to be set as active.
44+
*/
45+
setActiveItem(item: T): void;
46+
47+
setActiveItem(item: any): void {
48+
super.setActiveItem(item);
4049

4150
if (this.activeItem) {
4251
this.activeItem.focus(this._origin);

src/cdk/a11y/key-manager/list-key-manager.spec.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,27 @@ describe('Key managers', () => {
323323
.toBe(1, `Expected activeItemIndex to be updated when setActiveItem() was called.`);
324324
});
325325

326+
it('should be able to set the active item by reference', () => {
327+
expect(keyManager.activeItemIndex)
328+
.toBe(0, `Expected first item of the list to be active.`);
329+
330+
keyManager.setActiveItem(itemList.items[2]);
331+
expect(keyManager.activeItemIndex)
332+
.toBe(2, `Expected activeItemIndex to be updated.`);
333+
});
334+
335+
it('should be able to set the active item without emitting an event', () => {
336+
const spy = jasmine.createSpy('change spy');
337+
const subscription = keyManager.change.subscribe(spy);
338+
339+
expect(keyManager.activeItemIndex).toBe(0);
340+
341+
keyManager.updateActiveItem(2);
342+
343+
expect(keyManager.activeItemIndex).toBe(2);
344+
expect(spy).not.toHaveBeenCalled();
345+
});
346+
326347
it('should expose the active item correctly', () => {
327348
keyManager.onKeydown(fakeKeyEvents.downArrow);
328349

src/cdk/a11y/key-manager/list-key-manager.ts

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -144,14 +144,21 @@ export class ListKeyManager<T extends ListKeyManagerOption> {
144144
* Sets the active item to the item at the index specified.
145145
* @param index The index of the item to be set as active.
146146
*/
147-
setActiveItem(index: number): void {
147+
setActiveItem(index: number): void;
148+
149+
/**
150+
* Sets the active item to the specified item.
151+
* @param item The item to be set as active.
152+
*/
153+
setActiveItem(item: T): void;
154+
155+
setActiveItem(item: any): void {
148156
const previousIndex = this._activeItemIndex;
149157

150-
this._activeItemIndex = index;
151-
this._activeItem = this._items.toArray()[index];
158+
this.updateActiveItem(item);
152159

153160
if (this._activeItemIndex !== previousIndex) {
154-
this.change.next(index);
161+
this.change.next(this._activeItemIndex);
155162
}
156163
}
157164

@@ -246,12 +253,34 @@ export class ListKeyManager<T extends ListKeyManagerOption> {
246253
: this._setActiveItemByDelta(-1);
247254
}
248255

256+
/**
257+
* Allows setting the active without any other effects.
258+
* @param index Index of the item to be set as active.
259+
*/
260+
updateActiveItem(index: number): void;
261+
262+
/**
263+
* Allows setting the active item without any other effects.
264+
* @param item Item to be set as active.
265+
*/
266+
updateActiveItem(item: T): void;
267+
268+
updateActiveItem(item: any): void {
269+
const itemArray = this._items.toArray();
270+
const index = typeof item === 'number' ? item : itemArray.indexOf(item);
271+
272+
this._activeItemIndex = index;
273+
this._activeItem = itemArray[index];
274+
}
275+
249276
/**
250277
* Allows setting of the activeItemIndex without any other effects.
251278
* @param index The new activeItemIndex.
279+
* @deprecated Use `updateActiveItem` instead.
280+
* @deletion-target 7.0.0
252281
*/
253-
updateActiveItemIndex(index: number) {
254-
this._activeItemIndex = index;
282+
updateActiveItemIndex(index: number): void {
283+
this.updateActiveItem(index);
255284
}
256285

257286
/**

src/lib/chips/chip-list.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -562,14 +562,11 @@ export class MatChipList extends _MatChipListMixinBase implements MatFormFieldCo
562562
// Shift focus to the active item. Note that we shouldn't do this in multiple
563563
// mode, because we don't know what chip the user interacted with last.
564564
if (correspondingChip) {
565-
const correspondingChipIndex = this.chips.toArray().indexOf(correspondingChip);
566-
567565
if (isUserInput) {
568-
this._keyManager.setActiveItem(correspondingChipIndex);
566+
this._keyManager.setActiveItem(correspondingChip);
569567
} else {
570-
this._keyManager.updateActiveItemIndex(correspondingChipIndex);
568+
this._keyManager.updateActiveItem(correspondingChip);
571569
}
572-
573570
}
574571
}
575572
}

src/lib/select/select.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -808,7 +808,7 @@ export class MatSelect extends _MatSelectMixinBase implements AfterContentInit,
808808
// Shift focus to the active item. Note that we shouldn't do this in multiple
809809
// mode, because we don't know what option the user interacted with last.
810810
if (correspondingOption) {
811-
this._keyManager.setActiveItem(this.options.toArray().indexOf(correspondingOption));
811+
this._keyManager.setActiveItem(correspondingOption);
812812
}
813813
}
814814

@@ -910,7 +910,7 @@ export class MatSelect extends _MatSelectMixinBase implements AfterContentInit,
910910
this._selectionModel.toggle(option);
911911
this.stateChanges.next();
912912
wasSelected ? option.deselect() : option.select();
913-
this._keyManager.setActiveItem(this._getOptionIndex(option)!);
913+
this._keyManager.setActiveItem(option);
914914
this._sortValues();
915915
} else {
916916
this._clearSelection(option.value == null ? undefined : option);
@@ -976,7 +976,7 @@ export class MatSelect extends _MatSelectMixinBase implements AfterContentInit,
976976
if (this.empty) {
977977
this._keyManager.setFirstItemActive();
978978
} else {
979-
this._keyManager.setActiveItem(this._getOptionIndex(this._selectionModel.selected[0])!);
979+
this._keyManager.setActiveItem(this._selectionModel.selected[0]);
980980
}
981981
}
982982
}

0 commit comments

Comments
 (0)