Skip to content

Commit 6e83d60

Browse files
committed
fix(cdk/tree): CdkTreeNodeToggle focuses node when toggling it
Fix focus behavior of CdkTreeNodeToggle. When toggling the expanded or collapsed state of a node, focus that node. Fix issue where end user cannot tab into tree component when collaping the parent of the active node. Before this commit is applied, there is a bug where end user cannot tab into the tree. Reproduction steps 1. Active a tree node 2. (focus state renders) 3. Using mouse, collapse parent of node from step 1. 4. (tree node collapses) 5. Press Tab, then shift + tab 6. (item before tree is focused. Can tab into the tree) With this commit applied, above issue is no longer expected to reproduce. This commit message is only for reviewers of this PR and can be deleted when landing this change in main.
1 parent 2098b41 commit 6e83d60

File tree

2 files changed

+40
-3
lines changed

2 files changed

+40
-3
lines changed

src/cdk/tree/toggle.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,17 @@ export class CdkTreeNodeToggle<T, K = T> {
3434

3535
constructor(protected _tree: CdkTree<T, K>, protected _treeNode: CdkTreeNode<T, K>) {}
3636

37+
// Toggle the expanded or collapsed state of this node.
38+
//
39+
// Focus this node with expanding or collapsing it. This ensures that the active node will always
40+
// be visible when expanding and collapsing.
3741
_toggle(event: Event): void {
3842
this.recursive
3943
? this._tree.toggleDescendants(this._treeNode.data)
4044
: this._tree.toggle(this._treeNode.data);
4145

46+
this._tree._keyManager.focusItem(this._treeNode);
47+
4248
event.stopPropagation();
4349
}
4450
}

src/cdk/tree/tree-redesign.spec.ts

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,26 @@ describe('CdkTree redesign', () => {
270270
.toBe(0);
271271
});
272272

273+
it('should focus a node when collapsing it', () => {
274+
dataSource.clear();
275+
const parent = dataSource.addData();
276+
const child = dataSource.addChild(parent);
277+
component.tree.expandAll();
278+
fixture.detectChanges();
279+
280+
// focus the child node
281+
getNodes(treeElement)[1].click();
282+
fixture.detectChanges();
283+
284+
// collapse the parent node
285+
getNodes(treeElement)[0].click();
286+
fixture.detectChanges();
287+
288+
expect(getNodes(treeElement).map(x => x.getAttribute('tabindex')))
289+
.withContext(`Expecting parent node to be focused since it was collapsed.`)
290+
.toEqual(['0', '-1']);
291+
});
292+
273293
it('should expand/collapse the node recursively', () => {
274294
expect(dataSource.data.length).toBe(3);
275295

@@ -1310,22 +1330,33 @@ class FakeDataSource extends DataSource<TestData> {
13101330
return child;
13111331
}
13121332

1313-
addData(level: number = 1) {
1333+
addData(level: number = 1): TestData {
13141334
const nextIndex = ++this.dataIndex;
13151335

13161336
let copiedData = this.data.slice();
1317-
copiedData.push(
1318-
new TestData(`topping_${nextIndex}`, `cheese_${nextIndex}`, `base_${nextIndex}`, level),
1337+
const newData = new TestData(
1338+
`topping_${nextIndex}`,
1339+
`cheese_${nextIndex}`,
1340+
`base_${nextIndex}`,
1341+
level,
13191342
);
1343+
copiedData.push(newData);
13201344

13211345
this.data = copiedData;
1346+
1347+
return newData;
13221348
}
13231349

13241350
getRecursiveData(nodes: TestData[] = this._dataChange.getValue()): TestData[] {
13251351
return [
13261352
...new Set(nodes.flatMap(parent => [parent, ...this.getRecursiveData(parent.children)])),
13271353
];
13281354
}
1355+
1356+
clear() {
1357+
this.data = [];
1358+
this.dataIndex = 0;
1359+
}
13291360
}
13301361

13311362
function getNodes(treeElement: Element): HTMLElement[] {

0 commit comments

Comments
 (0)