From 935ddad7c00734086ddd3667f2920521e7713e53 Mon Sep 17 00:00:00 2001 From: Galina Edinakova Date: Tue, 14 Oct 2025 15:58:32 +0300 Subject: [PATCH 1/2] test(TreeGrid): Adding a test for order of records after hierarchizing --- .../tree-grid/tree-grid-integration.spec.ts | 68 +++++++++++++++++++ .../test-utils/tree-grid-components.spec.ts | 11 ++- 2 files changed, 77 insertions(+), 2 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-integration.spec.ts b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-integration.spec.ts index d02235cd5e2..cdf530a8f3c 100644 --- a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-integration.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-integration.spec.ts @@ -198,6 +198,74 @@ describe('IgxTreeGrid - Integration #tGrid', () => { treeGrid = fix.componentInstance.treeGrid; }); + it('should preserve the order of records on inner levels', () => { + fix.componentInstance.sortByName = true; + fix.detectChanges(); + treeGrid.columns[0].hidden = true; + treeGrid.columns[1].hidden = true; + fix.detectChanges(); + + const expectedFlatData = [ + { + "ID": 1, + "ParentID": -1, + "Name": "Casey Houston", + "JobTitle": "Vice President", + "Age": 32 + }, + { + "ID": 2, + "ParentID": 1, + "Name": "Gilberto Todd", + "JobTitle": "Director", + "Age": 41 + }, + { + "ID": 7, + "ParentID": 2, + "Name": "Debra Morton", + "JobTitle": "Associate Software Developer", + "Age": 35 + }, + { + "ID": 3, + "ParentID": 2, + "Name": "Tanya Bennett", + "JobTitle": "Director", + "Age": 29 + }, + { + "ID": 4, + "ParentID": 1, + "Name": "Jack Simon", + "JobTitle": "Software Developer", + "Age": 33 + }, + { + "ID": 10, + "ParentID": -1, + "Name": "Eduardo Ramirez", + "JobTitle": "Manager", + "Age": 53 + }, + { + "ID": 9, + "ParentID": 10, + "Name": "Leslie Hansen", + "JobTitle": "Associate Software Developer", + "Age": 44 + }, + { + "ID": 6, + "ParentID": -1, + "Name": "Erma Walsh", + "JobTitle": "CEO", + "Age": 52 + }, + ] + expect(treeGrid.flatData).toEqual(expectedFlatData); + }); + it('should transform a non-tree column into a tree column when pinning it', () => { TreeGridFunctions.verifyTreeColumn(fix, 'ID', 5); diff --git a/projects/igniteui-angular/src/lib/test-utils/tree-grid-components.spec.ts b/projects/igniteui-angular/src/lib/test-utils/tree-grid-components.spec.ts index 122aaa8a80f..9d0706598a8 100644 --- a/projects/igniteui-angular/src/lib/test-utils/tree-grid-components.spec.ts +++ b/projects/igniteui-angular/src/lib/test-utils/tree-grid-components.spec.ts @@ -159,10 +159,17 @@ export class IgxTreeGridWithNoScrollsComponent { `, imports: [IgxTreeGridComponent, IgxColumnComponent, IgxPaginatorComponent] }) -export class IgxTreeGridPrimaryForeignKeyComponent { +export class IgxTreeGridPrimaryForeignKeyComponent implements OnInit { @ViewChild(IgxTreeGridComponent, { static: true }) public treeGrid: IgxTreeGridComponent; - public data = SampleTestData.employeePrimaryForeignKeyTreeData(); + public data = []; public paging = false; + public sortByName = false; + + public ngOnInit(): void { + this.data = !this.sortByName + ? SampleTestData.employeePrimaryForeignKeyTreeData() + : SampleTestData.employeePrimaryForeignKeyTreeData().sort((a, b) => a.Name.localeCompare(b.Name)); + } } @Component({ From 8d200bd0d178a5b9863f19a132b005eee373d09a Mon Sep 17 00:00:00 2001 From: Galina Edinakova Date: Mon, 20 Oct 2025 09:23:51 +0300 Subject: [PATCH 2/2] fix(TreeGrid): Preserve the original order in the collection when hierarchizing. --- .../tree-grid/tree-grid-integration.spec.ts | 5 +-- .../lib/grids/tree-grid/tree-grid.pipes.ts | 44 ++++++++++++------- 2 files changed, 31 insertions(+), 18 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-integration.spec.ts b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-integration.spec.ts index cdf530a8f3c..c8b02b03ac6 100644 --- a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-integration.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-integration.spec.ts @@ -199,11 +199,10 @@ describe('IgxTreeGrid - Integration #tGrid', () => { }); it('should preserve the order of records on inner levels', () => { + fix = TestBed.createComponent(IgxTreeGridPrimaryForeignKeyComponent); fix.componentInstance.sortByName = true; fix.detectChanges(); - treeGrid.columns[0].hidden = true; - treeGrid.columns[1].hidden = true; - fix.detectChanges(); + treeGrid = fix.componentInstance.treeGrid; const expectedFlatData = [ { diff --git a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid.pipes.ts b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid.pipes.ts index 319724f9538..64271708e8d 100644 --- a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid.pipes.ts +++ b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid.pipes.ts @@ -53,30 +53,44 @@ export class IgxTreeGridHierarchizingPipe implements PipeTransform { return primaryKey ? rowData[primaryKey] : rowData; } - private hierarchizeFlatData(collection: any[], primaryKey: string, foreignKey: string, - map: Map, flatData: any[]): - ITreeGridRecord[] { - const result: ITreeGridRecord[] = []; - const missingParentRecords: ITreeGridRecord[] = []; + /** + * Converts a flat array of data into a hierarchical (tree) structure, + * preserving the original order of the records among siblings. + * + * It uses a two-pass approach: + * 1. Creates all ITreeGridRecord objects and populates the Map for quick lookup. + * 2. Links the records by iterating again, ensuring children are added to + * their parent's children array in the order they appeared in the + * original collection. + * + * @param collection The flat array of data to be hierarchized. This is the array whose order should be preserved. + * @param primaryKey The name of the property in the data objects that serves as the unique identifier (e.g., 'id'). + * @param foreignKey The name of the property in the data objects that links to the parent's primary key (e.g., 'parentId'). + * @param map A pre-existing Map object (key: primaryKey value, value: ITreeGridRecord) used to store and quickly look up all created records. + * @param flatData The original flat data array. Used for passing to the setIndentationLevels method (not directly used for hierarchy building). + * @returns An array of ITreeGridRecord objects representing the root nodes of the hierarchy, ordered as they appeared in the original collection. + */ + private hierarchizeFlatData( + collection: any[], + primaryKey: string, + foreignKey: string, + map: Map, + flatData: any[] + ): ITreeGridRecord[] { collection.forEach(row => { const record: ITreeGridRecord = { key: this.getRowID(primaryKey, row), data: row, children: [] }; - const parent = map.get(row[foreignKey]); - if (parent) { - record.parent = parent; - parent.children.push(record); - } else { - missingParentRecords.push(record); - } - map.set(row[primaryKey], record); }); - missingParentRecords.forEach(record => { - const parent = map.get(record.data[foreignKey]); + const result: ITreeGridRecord[] = []; + collection.forEach(row => { + const record: ITreeGridRecord = map.get(row[primaryKey])!; + const parent = map.get(row[foreignKey]); + if (parent) { record.parent = parent; parent.children.push(record);