Skip to content

Commit c825fa2

Browse files
committed
fix(cdk/tree): no nodes focusable if data is replaced (#32781)
Fixes that none of the tree nodes were focusable if the data is swapped out after initialization. Fixes #32779. (cherry picked from commit b15b48d)
1 parent de23ad4 commit c825fa2

File tree

3 files changed

+26
-13
lines changed

3 files changed

+26
-13
lines changed

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

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -102,19 +102,9 @@ export class TreeKeyManager<T extends TreeKeyManagerItem> implements TreeKeyMana
102102
// items aren't being collected via `ViewChildren` or `ContentChildren`).
103103
if (items instanceof QueryList) {
104104
this._items = items.toArray();
105-
items.changes.subscribe((newItems: QueryList<T>) => {
106-
this._items = newItems.toArray();
107-
this._typeahead?.setItems(this._items);
108-
this._updateActiveItemIndex(this._items);
109-
this._initializeFocus();
110-
});
105+
items.changes.subscribe((newItems: QueryList<T>) => this._itemsChanged(newItems.toArray()));
111106
} else if (isObservable(items)) {
112-
items.subscribe(newItems => {
113-
this._items = newItems;
114-
this._typeahead?.setItems(newItems);
115-
this._updateActiveItemIndex(newItems);
116-
this._initializeFocus();
117-
});
107+
items.subscribe(newItems => this._itemsChanged(newItems));
118108
} else {
119109
this._items = items;
120110
this._initializeFocus();
@@ -219,6 +209,19 @@ export class TreeKeyManager<T extends TreeKeyManagerItem> implements TreeKeyMana
219209
return this._activeItem;
220210
}
221211

212+
/** Called when the list of items has changed. */
213+
private _itemsChanged(newItems: T[]) {
214+
if (this._hasInitialFocused && this._activeItem && !newItems.includes(this._activeItem)) {
215+
this._activeItem = null;
216+
this._hasInitialFocused = false;
217+
}
218+
219+
this._items = newItems;
220+
this._typeahead?.setItems(this._items);
221+
this._updateActiveItemIndex(this._items);
222+
this._initializeFocus();
223+
}
224+
222225
/** Focus the first available item. */
223226
private _focusFirstItem(): void {
224227
this.focusItem(this._findNextAvailableItemIndex(-1));

src/cdk/tree/tree.spec.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1281,6 +1281,16 @@ describe('CdkTree', () => {
12811281
expect(nodes.map(x => x.getAttribute('tabindex')).join(', ')).toEqual('0, -1, -1');
12821282
});
12831283

1284+
it('should ensure that at least one item is focusable when the items are swapped out', () => {
1285+
expect(nodes.map(x => x.getAttribute('tabindex')).join(', ')).toEqual('0, -1, -1');
1286+
1287+
dataSource.data = [new TestData('foo'), new TestData('bar'), new TestData('baz')];
1288+
fixture.detectChanges();
1289+
nodes = getNodes(treeElement);
1290+
1291+
expect(nodes.map(x => x.getAttribute('tabindex')).join(', ')).toEqual('0, -1, -1');
1292+
});
1293+
12841294
it('maintains tabindex when component is blurred', () => {
12851295
// activate the second child by clicking on it
12861296
nodes[1].click();

src/material/tree/node.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ export class MatTreeNode<T, K = T> extends CdkTreeNode<T, K> implements OnInit,
8484
*/
8585
defaultTabIndex = 0;
8686

87-
protected _getTabindexAttribute() {
87+
protected _getTabindexAttribute(): number | null {
8888
if (isNoopTreeKeyManager(this._tree._keyManager)) {
8989
return this.tabIndexInputBinding;
9090
}

0 commit comments

Comments
 (0)