From b76fc5be8483bde5d515f91d4985967c1344dbe7 Mon Sep 17 00:00:00 2001 From: yiran Date: Tue, 6 May 2025 18:07:58 +0800 Subject: [PATCH 1/2] fix(cdk/tree): cdk tree with levelAccessor should only render expended nodes instead of all nodes --- src/cdk/tree/tree-with-tree-control.spec.ts | 44 ++++++++++++++++- src/cdk/tree/tree.spec.ts | 47 +++++++++++++++++-- src/cdk/tree/tree.ts | 16 +++++-- ...k-tree-flat-children-accessor-example.html | 4 -- .../cdk-tree-flat-level-accessor-example.html | 4 -- .../cdk-tree-flat/cdk-tree-flat-example.html | 4 -- 6 files changed, 98 insertions(+), 21 deletions(-) diff --git a/src/cdk/tree/tree-with-tree-control.spec.ts b/src/cdk/tree/tree-with-tree-control.spec.ts index d74997773e4d..69e69c4c6bbf 100644 --- a/src/cdk/tree/tree-with-tree-control.spec.ts +++ b/src/cdk/tree/tree-with-tree-control.spec.ts @@ -133,7 +133,12 @@ describe('CdkTree with TreeControl', () => { dataSource.addChild(data[0], true); fixture.detectChanges(); - const ariaLevels = getNodes(treeElement).map(n => n.getAttribute('aria-level')); + let ariaLevels = getNodes(treeElement).map(n => n.getAttribute('aria-level')); + expect(ariaLevels).toEqual(['2', '2', '2']); + + component.treeControl.expandAll(); + fixture.detectChanges(); + ariaLevels = getNodes(treeElement).map(n => n.getAttribute('aria-level')); expect(ariaLevels).toEqual(['2', '3', '2', '2']); }); @@ -143,7 +148,7 @@ describe('CdkTree with TreeControl', () => { dataSource.addChild(data[2]); fixture.detectChanges(); let ariaExpandedStates = getNodes(treeElement).map(n => n.getAttribute('aria-expanded')); - expect(ariaExpandedStates).toEqual([null, null, 'false', null]); + expect(ariaExpandedStates).toEqual([null, null, 'false']); component.treeControl.expandAll(); fixture.detectChanges(); @@ -387,6 +392,17 @@ describe('CdkTree with TreeControl', () => { treeElement = fixture.nativeElement.querySelector('cdk-tree'); data = dataSource.data; expect(data.length).toBe(4); + expectFlatTreeToMatch( + treeElement, + 28, + 'px', + [`[topping_1] - [cheese_1] + [base_1]`], + [`[topping_2] - [cheese_2] + [base_2]`], + [`[topping_3] - [cheese_3] + [base_3]`], + ); + component.treeControl.expandAll(); + fixture.detectChanges(); + treeElement = fixture.nativeElement.querySelector('cdk-tree'); expectFlatTreeToMatch( treeElement, 28, @@ -434,6 +450,18 @@ describe('CdkTree with TreeControl', () => { treeElement = fixture.nativeElement.querySelector('cdk-tree'); data = dataSource.data; expect(data.length).toBe(4); + expectFlatTreeToMatch( + treeElement, + 28, + 'px', + [`[topping_1] - [cheese_1] + [base_1]`], + [`[topping_2] - [cheese_2] + [base_2]`], + [`[topping_3] - [cheese_3] + [base_3]`], + ); + + (getNodes(treeElement)[1] as HTMLElement).click(); + fixture.detectChanges(); + treeElement = fixture.nativeElement.querySelector('cdk-tree'); expectFlatTreeToMatch( treeElement, 28, @@ -481,6 +509,18 @@ describe('CdkTree with TreeControl', () => { treeElement = fixture.nativeElement.querySelector('cdk-tree'); data = dataSource.data; expect(data.length).toBe(4); + expectFlatTreeToMatch( + treeElement, + 28, + 'px', + [`[topping_1] - [cheese_1] + [base_1]`], + [`[topping_2] - [cheese_2] + [base_2]`], + [`[topping_3] - [cheese_3] + [base_3]`], + ); + + (getNodes(treeElement)[1] as HTMLElement).click(); + fixture.detectChanges(); + treeElement = fixture.nativeElement.querySelector('cdk-tree'); expectFlatTreeToMatch( treeElement, 28, diff --git a/src/cdk/tree/tree.spec.ts b/src/cdk/tree/tree.spec.ts index b9d5dce285ab..800d5deb12f1 100644 --- a/src/cdk/tree/tree.spec.ts +++ b/src/cdk/tree/tree.spec.ts @@ -139,7 +139,12 @@ describe('CdkTree', () => { dataSource.addChild(data[0], true); fixture.detectChanges(); - const ariaLevels = getNodes(treeElement).map(n => n.getAttribute('aria-level')); + let ariaLevels = getNodes(treeElement).map(n => n.getAttribute('aria-level')); + expect(ariaLevels).toEqual(['2', '2', '2']); + + tree.expandAll(); + fixture.detectChanges(); + ariaLevels = getNodes(treeElement).map(n => n.getAttribute('aria-level')); expect(ariaLevels).toEqual(['2', '3', '2', '2']); }); @@ -149,7 +154,7 @@ describe('CdkTree', () => { dataSource.addChild(data[2]); fixture.detectChanges(); let ariaExpandedStates = getNodes(treeElement).map(n => n.getAttribute('aria-expanded')); - expect(ariaExpandedStates).toEqual([null, null, 'false', null]); + expect(ariaExpandedStates).toEqual([null, null, 'false']); component.tree.expandAll(); fixture.detectChanges(); @@ -334,7 +339,7 @@ describe('CdkTree', () => { expect(getNodes(treeElement).map(x => x.getAttribute('tabindex'))) .withContext(`Expecting parent node to be focused since it was collapsed.`) - .toEqual(['0', '-1']); + .toEqual(['0']); }); it('should expand/collapse the node recursively', () => { @@ -439,6 +444,18 @@ describe('CdkTree', () => { treeElement = fixture.nativeElement.querySelector('cdk-tree'); data = dataSource.data; expect(data.length).toBe(4); + expectFlatTreeToMatch( + treeElement, + 28, + 'px', + [`[topping_1] - [cheese_1] + [base_1]`], + [`[topping_2] - [cheese_2] + [base_2]`], + [`[topping_3] - [cheese_3] + [base_3]`], + ); + + tree.expandAll(); + fixture.detectChanges(); + treeElement = fixture.nativeElement.querySelector('cdk-tree'); expectFlatTreeToMatch( treeElement, 28, @@ -485,6 +502,18 @@ describe('CdkTree', () => { treeElement = fixture.nativeElement.querySelector('cdk-tree'); data = dataSource.data; expect(data.length).toBe(4); + expectFlatTreeToMatch( + treeElement, + 28, + 'px', + [`[topping_1] - [cheese_1] + [base_1]`], + [`[topping_2] - [cheese_2] + [base_2]`], + [`[topping_3] - [cheese_3] + [base_3]`], + ); + + tree.expandAll(); + fixture.detectChanges(); + treeElement = fixture.nativeElement.querySelector('cdk-tree'); expectFlatTreeToMatch( treeElement, 28, @@ -532,6 +561,18 @@ describe('CdkTree', () => { treeElement = fixture.nativeElement.querySelector('cdk-tree'); data = dataSource.data; expect(data.length).toBe(4); + expectFlatTreeToMatch( + treeElement, + 28, + 'px', + [`[topping_1] - [cheese_1] + [base_1]`], + [`[topping_2] - [cheese_2] + [base_2]`], + [`[topping_3] - [cheese_3] + [base_3]`], + ); + + tree.expandAll(); + fixture.detectChanges(); + treeElement = fixture.nativeElement.querySelector('cdk-tree'); expectFlatTreeToMatch( treeElement, 28, diff --git a/src/cdk/tree/tree.ts b/src/cdk/tree/tree.ts index 497ba28d4b86..edd18d0a0b0e 100644 --- a/src/cdk/tree/tree.ts +++ b/src/cdk/tree/tree.ts @@ -1049,6 +1049,12 @@ export class CdkTree ); } + private _shouldFlattenNodeRender(node: T): boolean { + const key = this._getExpansionKey(node); + const parent = this._parents.get(key); + return !parent || (this.isExpanded(parent) && this._shouldFlattenNodeRender(parent)); + } + /** * Converts children for certain tree configurations. * @@ -1095,10 +1101,12 @@ export class CdkTree // with the TreeControl, and so no conversions are necessary. Otherwise, // we've already confirmed that the data model matches up with the // desired node type here. - return observableOf({renderNodes: nodes, flattenedNodes: nodes}).pipe( - tap(({flattenedNodes}) => { - this._calculateParents(flattenedNodes); - }), + return observableOf(nodes).pipe( + tap(nodes => this._calculateParents(nodes)), + map(nodes => ({ + renderNodes: nodes.filter(node => this._shouldFlattenNodeRender(node)), + flattenedNodes: nodes, + })), ); } else { // clear previously generated data so we don't keep end up retaining data overtime causing diff --git a/src/components-examples/cdk/tree/cdk-tree-flat-children-accessor/cdk-tree-flat-children-accessor-example.html b/src/components-examples/cdk/tree/cdk-tree-flat-children-accessor/cdk-tree-flat-children-accessor-example.html index 87f4087456e4..29a32068a4ae 100644 --- a/src/components-examples/cdk/tree/cdk-tree-flat-children-accessor/cdk-tree-flat-children-accessor-example.html +++ b/src/components-examples/cdk/tree/cdk-tree-flat-children-accessor/cdk-tree-flat-children-accessor-example.html @@ -1,8 +1,6 @@ @@ -12,8 +10,6 @@ @@ -12,8 +10,6 @@ @@ -11,8 +9,6 @@ @@ -12,8 +10,6 @@ diff --git a/src/components-examples/cdk/tree/cdk-tree-custom-key-manager/cdk-tree-custom-key-manager-example.ts b/src/components-examples/cdk/tree/cdk-tree-custom-key-manager/cdk-tree-custom-key-manager-example.ts index bc66a7a4eb95..2d377a882621 100644 --- a/src/components-examples/cdk/tree/cdk-tree-custom-key-manager/cdk-tree-custom-key-manager-example.ts +++ b/src/components-examples/cdk/tree/cdk-tree-custom-key-manager/cdk-tree-custom-key-manager-example.ts @@ -336,17 +336,6 @@ export class CdkTreeCustomKeyManagerExample { return null; } - - shouldRender(node: ExampleFlatNode) { - let parent = this.getParentNode(node); - while (parent) { - if (!parent.isExpanded) { - return false; - } - parent = this.getParentNode(parent); - } - return true; - } } const EXAMPLE_DATA: ExampleFlatNode[] = [ diff --git a/src/components-examples/cdk/tree/cdk-tree-flat-children-accessor/cdk-tree-flat-children-accessor-example.ts b/src/components-examples/cdk/tree/cdk-tree-flat-children-accessor/cdk-tree-flat-children-accessor-example.ts index cb5ecbbd3d8e..2bbafb6a5102 100644 --- a/src/components-examples/cdk/tree/cdk-tree-flat-children-accessor/cdk-tree-flat-children-accessor-example.ts +++ b/src/components-examples/cdk/tree/cdk-tree-flat-children-accessor/cdk-tree-flat-children-accessor-example.ts @@ -55,17 +55,6 @@ export class CdkTreeFlatChildrenAccessorExample { return null; } - - shouldRender(node: NestedFoodNode) { - let parent = this.getParentNode(node); - while (parent) { - if (!this.tree.isExpanded(parent)) { - return false; - } - parent = this.getParentNode(parent); - } - return true; - } } const EXAMPLE_DATA: NestedFoodNode[] = [ diff --git a/src/components-examples/cdk/tree/cdk-tree-flat-level-accessor/cdk-tree-flat-level-accessor-example.ts b/src/components-examples/cdk/tree/cdk-tree-flat-level-accessor/cdk-tree-flat-level-accessor-example.ts index 9c4b6da9d73d..052ec6a62be6 100644 --- a/src/components-examples/cdk/tree/cdk-tree-flat-level-accessor/cdk-tree-flat-level-accessor-example.ts +++ b/src/components-examples/cdk/tree/cdk-tree-flat-level-accessor/cdk-tree-flat-level-accessor-example.ts @@ -45,12 +45,6 @@ export class CdkTreeFlatLevelAccessorExample { return null; } - - shouldRender(node: FlatFoodNode): boolean { - // This node should render if it is a root node or if all of its ancestors are expanded. - const parent = this.getParentNode(node); - return !parent || (!!this.tree?.isExpanded(parent) && this.shouldRender(parent)); - } } const EXAMPLE_DATA: FlatFoodNode[] = [ diff --git a/src/components-examples/cdk/tree/cdk-tree-flat/cdk-tree-flat-example.ts b/src/components-examples/cdk/tree/cdk-tree-flat/cdk-tree-flat-example.ts index 8cd723ace64d..757792bb9f4d 100644 --- a/src/components-examples/cdk/tree/cdk-tree-flat/cdk-tree-flat-example.ts +++ b/src/components-examples/cdk/tree/cdk-tree-flat/cdk-tree-flat-example.ts @@ -43,17 +43,6 @@ export class CdkTreeFlatExample { return null; } - - shouldRender(node: ExampleFlatNode) { - let parent = this.getParentNode(node); - while (parent) { - if (!parent.isExpanded) { - return false; - } - parent = this.getParentNode(parent); - } - return true; - } } const EXAMPLE_DATA: ExampleFlatNode[] = [ diff --git a/src/components-examples/cdk/tree/cdk-tree-nested-children-accessor/cdk-tree-nested-children-accessor-example.ts b/src/components-examples/cdk/tree/cdk-tree-nested-children-accessor/cdk-tree-nested-children-accessor-example.ts index c839d255fadf..8309975a167d 100644 --- a/src/components-examples/cdk/tree/cdk-tree-nested-children-accessor/cdk-tree-nested-children-accessor-example.ts +++ b/src/components-examples/cdk/tree/cdk-tree-nested-children-accessor/cdk-tree-nested-children-accessor-example.ts @@ -52,12 +52,6 @@ export class CdkTreeNestedChildrenAccessorExample { return null; } - - shouldRender(node: NestedFoodNode): boolean { - // This node should render if it is a root node or if all of its ancestors are expanded. - const parent = this.getParentNode(node); - return !parent || (!!this.tree?.isExpanded(parent) && this.shouldRender(parent)); - } } const EXAMPLE_DATA: NestedFoodNode[] = [ diff --git a/src/components-examples/cdk/tree/cdk-tree-nested-level-accessor/cdk-tree-nested-level-accessor-example.ts b/src/components-examples/cdk/tree/cdk-tree-nested-level-accessor/cdk-tree-nested-level-accessor-example.ts index 1809bc1a1598..c5dd3be425e1 100644 --- a/src/components-examples/cdk/tree/cdk-tree-nested-level-accessor/cdk-tree-nested-level-accessor-example.ts +++ b/src/components-examples/cdk/tree/cdk-tree-nested-level-accessor/cdk-tree-nested-level-accessor-example.ts @@ -44,12 +44,6 @@ export class CdkTreeNestedLevelAccessorExample { return null; } - - shouldRender(node: FlatFoodNode): boolean { - // This node should render if it is a root node or if all of its ancestors are expanded. - const parent = this.getParentNode(node); - return !parent || (!!this.tree?.isExpanded(parent) && this.shouldRender(parent)); - } } const EXAMPLE_DATA: FlatFoodNode[] = [