From 9bf043c8fc16467be173cb89ab7b967955091305 Mon Sep 17 00:00:00 2001 From: Cassandra Choi Date: Tue, 13 Aug 2024 07:37:10 -0400 Subject: [PATCH] fix(cdk/tree): fix issue where `isExpanded` wouldn't be set if placed before `isExpandable` (#29565) * fix(cdk/tree): fix issue where `isExpanded` wouldn't be set if placed before `isExpandable` * fix(cdk/tree): actually fix the issue * fix(cdk/tree): actually actually fix tests --- src/cdk/tree/tree.spec.ts | 41 ++++++++++++++++++++++++++++++++++++++- src/cdk/tree/tree.ts | 12 ++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/src/cdk/tree/tree.spec.ts b/src/cdk/tree/tree.spec.ts index ccde2b218e8c..92d4356e0cf4 100644 --- a/src/cdk/tree/tree.spec.ts +++ b/src/cdk/tree/tree.spec.ts @@ -1448,6 +1448,17 @@ describe('CdkTree', () => { }); }); }); + + it('sets a node as expanded if attribute is ordered before `isExpandable`', () => { + configureCdkTreeTestingModule([IsExpandableOrderingTest]); + const fixture = TestBed.createComponent(IsExpandableOrderingTest); + fixture.detectChanges(); + + const component = fixture.componentInstance; + expect(getExpandedNodes(component.dataSource, component.tree).length) + .withContext(`expect an expanded node`) + .toBe(1); + }); }); export class TestData { @@ -1554,7 +1565,7 @@ function getNodes(treeElement: Element): HTMLElement[] { return Array.from(treeElement.querySelectorAll('.cdk-tree-node')); } -function getExpandedNodes(nodes: TestData[] | undefined, tree: CdkTree): TestData[] { +function getExpandedNodes(nodes: T[] | undefined, tree: CdkTree): T[] { return nodes?.filter(node => tree.isExpanded(node)) ?? []; } @@ -2100,3 +2111,31 @@ class FlatTreeWithThreeNodes { @ViewChild('tree', {read: ElementRef}) tree: ElementRef; @ViewChildren('node') treeNodes: QueryList>; } + +@Component({ + template: ` + + + {{node.name}} + + + `, +}) +class IsExpandableOrderingTest { + getChildren = (node: MinimalTestData) => node.children; + + @ViewChild(CdkTree) tree: CdkTree; + + dataSource: MinimalTestData[]; + + constructor() { + const children = [new MinimalTestData('child')]; + const data = [new MinimalTestData('parent')]; + data[0].children = children; + + this.dataSource = data; + } +} diff --git a/src/cdk/tree/tree.ts b/src/cdk/tree/tree.ts index fc2de444679a..c604ddaf2838 100644 --- a/src/cdk/tree/tree.ts +++ b/src/cdk/tree/tree.ts @@ -1176,6 +1176,16 @@ export class CdkTreeNode implements OnDestroy, OnInit, TreeKeyManagerI } set isExpandable(isExpandable: boolean) { this._inputIsExpandable = isExpandable; + if ((this.data && !this._isExpandable) || !this._inputIsExpandable) { + return; + } + // If the node is being set to expandable, ensure that the status of the + // node is propagated + if (this._inputIsExpanded) { + this.expand(); + } else if (this._inputIsExpanded === false) { + this.collapse(); + } } @Input() @@ -1183,6 +1193,7 @@ export class CdkTreeNode implements OnDestroy, OnInit, TreeKeyManagerI return this._tree.isExpanded(this._data); } set isExpanded(isExpanded: boolean) { + this._inputIsExpanded = isExpanded; if (isExpanded) { this.expand(); } else { @@ -1227,6 +1238,7 @@ export class CdkTreeNode implements OnDestroy, OnInit, TreeKeyManagerI readonly _dataChanges = new Subject(); private _inputIsExpandable: boolean = false; + private _inputIsExpanded: boolean | undefined = undefined; /** * Flag used to determine whether or not we should be focusing the actual element based on * some user interaction (click or focus). On click, we don't forcibly focus the element