Skip to content

Commit 4f65276

Browse files
crisbetojelbourn
authored andcommitted
fix(list-key-manager): maintain selected index when amount of items changes (#9164)
Currently the `activeItemIndex` and the `activeItem` in the `ListKeyManager` will stay the same if the amount of items changes. This is an issue, because they'll become out of sync and focus could potentially go to a disabled item. With the following changes the key manager will ensure that the `activeItemIndex` and the `activeItem` are in sync and point to the same item.
1 parent 17e36fe commit 4f65276

File tree

2 files changed

+26
-1
lines changed

2 files changed

+26
-1
lines changed

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {ActiveDescendantKeyManager} from './activedescendant-key-manager';
77
import {FocusKeyManager} from './focus-key-manager';
88
import {ListKeyManager} from './list-key-manager';
99
import {FocusOrigin} from './focus-monitor';
10+
import {Subject} from 'rxjs/Subject';
1011

1112

1213
class FakeFocusable {
@@ -23,11 +24,13 @@ class FakeHighlightable {
2324
}
2425

2526
class FakeQueryList<T> extends QueryList<T> {
27+
changes = new Subject<FakeQueryList<T>>();
2628
items: T[];
2729
get length() { return this.items.length; }
2830
get first() { return this.items[0]; }
2931
toArray() { return this.items; }
3032
some() { return this.items.some.apply(this.items, arguments); }
33+
notifyOnChanges() { this.changes.next(this); }
3134
}
3235

3336

@@ -71,6 +74,17 @@ describe('Key managers', () => {
7174
spyOn(keyManager, 'setActiveItem').and.callThrough();
7275
});
7376

77+
it('should maintain the active item if the amount of items changes', () => {
78+
expect(keyManager.activeItemIndex).toBe(0);
79+
expect(keyManager.activeItem!.getLabel()).toBe('one');
80+
81+
itemList.items.unshift(new FakeFocusable('zero'));
82+
itemList.notifyOnChanges();
83+
84+
expect(keyManager.activeItemIndex).toBe(1);
85+
expect(keyManager.activeItem!.getLabel()).toBe('one');
86+
});
87+
7488
describe('Key events', () => {
7589

7690
it('should emit tabOut when the tab key is pressed', () => {

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

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,18 @@ export class ListKeyManager<T extends ListKeyManagerOption> {
5050
// Buffer for the letters that the user has pressed when the typeahead option is turned on.
5151
private _pressedLetters: string[] = [];
5252

53-
constructor(private _items: QueryList<T>) { }
53+
constructor(private _items: QueryList<T>) {
54+
_items.changes.subscribe((newItems: QueryList<T>) => {
55+
if (this._activeItem) {
56+
const itemArray = newItems.toArray();
57+
const newIndex = itemArray.indexOf(this._activeItem);
58+
59+
if (newIndex > -1 && newIndex !== this._activeItemIndex) {
60+
this._activeItemIndex = newIndex;
61+
}
62+
}
63+
});
64+
}
5465

5566
/**
5667
* Stream that emits any time the TAB key is pressed, so components can react

0 commit comments

Comments
 (0)