Skip to content

Commit 232783a

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 0d787b8 commit 232783a

File tree

2 files changed

+42
-3
lines changed

2 files changed

+42
-3
lines changed

src/cdk/tree/toggle.ts

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

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

42+
// Toggle the expanded or collapsed state of this node.
43+
//
44+
// Focus this node with expanding or collapsing it. This ensures that the active node will always
45+
// be visible when expanding and collapsing.
4246
_toggle(event: Event): void {
4347
this.recursive
4448
? this._tree.toggleDescendants(this._treeNode.data)
4549
: this._tree.toggle(this._treeNode.data);
4650

51+
this._tree._keyManager.focusItem(this._treeNode);
52+
4753
event.stopPropagation();
4854
}
4955

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

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

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

@@ -1312,22 +1334,33 @@ class FakeDataSource extends DataSource<TestData> {
13121334
return child;
13131335
}
13141336

1315-
addData(level: number = 1) {
1337+
addData(level: number = 1): TestData {
13161338
const nextIndex = ++this.dataIndex;
13171339

13181340
let copiedData = this.data.slice();
1319-
copiedData.push(
1320-
new TestData(`topping_${nextIndex}`, `cheese_${nextIndex}`, `base_${nextIndex}`, level),
1341+
const newData = new TestData(
1342+
`topping_${nextIndex}`,
1343+
`cheese_${nextIndex}`,
1344+
`base_${nextIndex}`,
1345+
level,
13211346
);
1347+
copiedData.push(newData);
13221348

13231349
this.data = copiedData;
1350+
1351+
return newData;
13241352
}
13251353

13261354
getRecursiveData(nodes: TestData[] = this._dataChange.getValue()): TestData[] {
13271355
return [
13281356
...new Set(nodes.flatMap(parent => [parent, ...this.getRecursiveData(parent.children)])),
13291357
];
13301358
}
1359+
1360+
clear() {
1361+
this.data = [];
1362+
this.dataIndex = 0;
1363+
}
13311364
}
13321365

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

0 commit comments

Comments
 (0)