From 8d8ac4c8d5a399df7815bda6955643ee77be48ac Mon Sep 17 00:00:00 2001 From: Cheng-Hsuan Tsai Date: Thu, 23 Apr 2026 03:24:04 +0000 Subject: [PATCH] fix(cdk/tree): enter/space key on child node should not toggle parent node expansion --- goldens/cdk/tree/index.api.md | 2 +- src/cdk/tree/toggle.ts | 10 ++++++---- src/cdk/tree/tree.spec.ts | 28 ++++++++++++++++++++++++++++ 3 files changed, 35 insertions(+), 5 deletions(-) diff --git a/goldens/cdk/tree/index.api.md b/goldens/cdk/tree/index.api.md index 3f3e5912dce3..42dbb07f3828 100644 --- a/goldens/cdk/tree/index.api.md +++ b/goldens/cdk/tree/index.api.md @@ -272,7 +272,7 @@ export class CdkTreeNodeToggle { static ngAcceptInputType_recursive: unknown; recursive: boolean; // (undocumented) - _toggle(): void; + _toggle(event: Event): void; // (undocumented) protected _tree: CdkTree; // (undocumented) diff --git a/src/cdk/tree/toggle.ts b/src/cdk/tree/toggle.ts index 3d18948f59a3..a6f06fa14990 100644 --- a/src/cdk/tree/toggle.ts +++ b/src/cdk/tree/toggle.ts @@ -16,9 +16,9 @@ import {CdkTree, CdkTreeNode} from './tree'; @Directive({ selector: '[cdkTreeNodeToggle]', host: { - '(click)': '_toggle(); $event.stopPropagation();', - '(keydown.Enter)': '_toggle(); $event.preventDefault();', - '(keydown.Space)': '_toggle(); $event.preventDefault();', + '(click)': '_toggle($event)', + '(keydown.Enter)': '_toggle($event); $event.preventDefault();', + '(keydown.Space)': '_toggle($event); $event.preventDefault();', 'tabindex': '-1', }, }) @@ -34,7 +34,9 @@ export class CdkTreeNodeToggle { // // Focus this node with expanding or collapsing it. This ensures that the active node will always // be visible when expanding and collapsing. - _toggle(): void { + _toggle(event: Event): void { + event.stopPropagation(); + this.recursive ? this._tree.toggleDescendants(this._treeNode.data) : this._tree.toggle(this._treeNode.data); diff --git a/src/cdk/tree/tree.spec.ts b/src/cdk/tree/tree.spec.ts index ae6fe563e1cc..014e897cd72e 100644 --- a/src/cdk/tree/tree.spec.ts +++ b/src/cdk/tree/tree.spec.ts @@ -966,6 +966,34 @@ describe('CdkTree', () => { [`topping_3 - cheese_3 + base_3`], ); }); + + it('should not collapse parent when child is toggled via keyboard', () => { + component.toggleRecursively = false; + fixture.changeDetectorRef.markForCheck(); + let data = dataSource.data; + const child = dataSource.addChild(data[1], false); + dataSource.addChild(child, false); + fixture.detectChanges(); + + // Expand parent + (getNodes(treeElement)[1] as HTMLElement).click(); + fixture.detectChanges(); + + expect(component.tree.isExpanded(data[1])).toBe(true); + + // Focus child node (which is now at index 2) + const childNode = getNodes(treeElement)[2] as HTMLElement; + + // Simulate Enter key on child node + const event = createKeyboardEvent('keydown', undefined, 'Enter'); + childNode.dispatchEvent(event); + fixture.detectChanges(); + + // Verify parent is still expanded + expect(component.tree.isExpanded(data[1])) + .withContext('Parent should remain expanded') + .toBe(true); + }); }); describe('nested tree with array data source', () => {