Skip to content

Commit dbdb58b

Browse files
authored
feat(igx-hierarchical-grid): export hierarchical grid (#9206)
* feat(igx-hierarchical-grid): export hierarchical grid * feat(hierarchical-grid): excel export major refactoring * chore(*): edit changelog.md
1 parent 544be48 commit dbdb58b

15 files changed

+1003
-264
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ All notable changes for each version of this project will be documented in this
3131
- `onDataPreLoad` -> `dataPreLoad`
3232

3333
### New Features
34+
- `IgxHierarchicalGrid`
35+
- Added support for exporting hierarchical data.
3436
- `IgxForOf`, `IgxGrid`, `IgxTreeGrid`, `IgxHierarchicalGrid`
3537
- **Behavioral Change** - Virtual containers now scroll smoothly when using the mouse wheel(s) to scroll them horizontally or vertically. This behavior more closely resembles the scrolling behavior of non-virtualized containers in most modern browsers.
3638
- `IgxGrid`, `IgxTreeGrid`, `IgxHierarchicalGrid`

projects/igniteui-angular/src/lib/services/csv/char-separated-value-data.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ export class CharSeparatedValueData {
3232
return '';
3333
}
3434

35-
this._isSpecialData = ExportUtilities.isSpecialData(this._data);
35+
this._isSpecialData = ExportUtilities.isSpecialData(this._data[0]);
3636
this._escapeCharacters.push(this._delimiter);
3737

3838
this._headerRecord = this.processHeaderRecord(keys);
@@ -52,7 +52,7 @@ export class CharSeparatedValueData {
5252
done('');
5353
}
5454

55-
this._isSpecialData = ExportUtilities.isSpecialData(this._data);
55+
this._isSpecialData = ExportUtilities.isSpecialData(this._data[0]);
5656
this._escapeCharacters.push(this._delimiter);
5757

5858
this._headerRecord = this.processHeaderRecord(keys);

projects/igniteui-angular/src/lib/services/excel/excel-exporter-grid.spec.ts

+107-6
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,15 @@ import { configureTestSuite } from '../../test-utils/configure-suite';
2626
import { IgxTreeGridPrimaryForeignKeyComponent } from '../../test-utils/tree-grid-components.spec';
2727
import { IgxTreeGridModule, IgxTreeGridComponent } from '../../grids/tree-grid/public_api';
2828
import { IgxNumberFilteringOperand } from '../../data-operations/filtering-condition';
29-
import { wait } from '../../test-utils/ui-interactions.spec';
29+
import { UIInteractions, wait } from '../../test-utils/ui-interactions.spec';
3030
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
3131
import { FilteringExpressionsTree } from '../../data-operations/filtering-expressions-tree';
3232
import { FilteringLogic } from '../../data-operations/filtering-expression.interface';
33+
import { IgxHierarchicalGridExportComponent } from '../../test-utils/hierarchical-grid-components.spec';
34+
import { IgxHierarchicalGridModule,
35+
IgxHierarchicalGridComponent,
36+
} from '../../grids/hierarchical-grid/public_api';
37+
import { IgxHierarchicalRowComponent } from '../../grids/hierarchical-grid/hierarchical-row.component';
3338

3439
describe('Excel Exporter', () => {
3540
configureTestSuite();
@@ -47,9 +52,10 @@ describe('Excel Exporter', () => {
4752
GridWithEmptyColumnsComponent,
4853
GridIDNameJobTitleHireDataPerformanceComponent,
4954
GridHireDateComponent,
50-
GridExportGroupedDataComponent
55+
GridExportGroupedDataComponent,
56+
IgxHierarchicalGridExportComponent
5157
],
52-
imports: [IgxGridModule, IgxTreeGridModule, NoopAnimationsModule]
58+
imports: [IgxGridModule, IgxTreeGridModule, IgxHierarchicalGridModule, NoopAnimationsModule]
5359
}).compileComponents();
5460
}));
5561

@@ -140,7 +146,6 @@ describe('Excel Exporter', () => {
140146
await wait();
141147

142148
const grid = fix.componentInstance.grid;
143-
options.ignoreColumnsOrder = true;
144149
options.ignoreColumnsVisibility = false;
145150

146151
expect(grid.visibleColumns.length).toEqual(3, 'Invalid number of visible columns!');
@@ -632,6 +637,101 @@ describe('Excel Exporter', () => {
632637
});
633638
});
634639

640+
describe('', () => {
641+
let fix;
642+
let hGrid: IgxHierarchicalGridComponent;
643+
644+
beforeEach(waitForAsync(() => {
645+
options = createExportOptions('HierarchicalGridExcelExport');
646+
fix = TestBed.createComponent(IgxHierarchicalGridExportComponent);
647+
fix.detectChanges();
648+
649+
hGrid = fix.componentInstance.hGrid;
650+
}));
651+
652+
it('should export hierarchical grid', async () => {
653+
await exportAndVerify(hGrid, options, actualData.exportHierarchicalData);
654+
});
655+
656+
it('should export hierarchical grid respecting options width.', async () => {
657+
options = createExportOptions('HierarchicalGridExcelExport', 50);
658+
await exportAndVerify(hGrid, options, actualData.exportHierarchicalDataWithColumnWidth);
659+
});
660+
661+
it('should export sorted hierarchical grid data', async () => {
662+
hGrid.sort({fieldName: 'GrammyNominations', dir: SortingDirection.Desc});
663+
664+
fix.detectChanges();
665+
666+
await exportAndVerify(hGrid, options, actualData.exportSortedHierarchicalData);
667+
});
668+
669+
it('should export hierarchical grid data with ignored sorting', async () => {
670+
hGrid.sort({fieldName: 'GrammyNominations', dir: SortingDirection.Desc});
671+
672+
options.ignoreSorting = true;
673+
fix.detectChanges();
674+
675+
await exportAndVerify(hGrid, options, actualData.exportHierarchicalData);
676+
});
677+
678+
it('should export filtered hierarchical grid data', async () => {
679+
hGrid.filter('Debut', '2009', IgxStringFilteringOperand.instance().condition('contains'), true);
680+
fix.detectChanges();
681+
682+
await exportAndVerify(hGrid, options, actualData.exportFilteredHierarchicalData);
683+
});
684+
685+
it('should export hierarchical grid data with ignored filtering', async () => {
686+
hGrid.filter('Debut', '2009', IgxStringFilteringOperand.instance().condition('contains'), true);
687+
fix.detectChanges();
688+
689+
options.ignoreFiltering = true;
690+
691+
await exportAndVerify(hGrid, options, actualData.exportHierarchicalData);
692+
});
693+
694+
it('should export hierarchical grid with expanded rows.', async () => {
695+
const firstRow = hGrid.hgridAPI.get_row_by_index(0) as IgxHierarchicalRowComponent;
696+
const secondRow = hGrid.hgridAPI.get_row_by_index(1) as IgxHierarchicalRowComponent;
697+
698+
UIInteractions.simulateClickAndSelectEvent(firstRow.expander);
699+
fix.detectChanges();
700+
expect(firstRow.expanded).toBe(true);
701+
702+
let childGrids = hGrid.hgridAPI.getChildGrids(false);
703+
704+
const firstChildGrid = childGrids[0];
705+
const firstChildRow = firstChildGrid.hgridAPI.get_row_by_index(2) as IgxHierarchicalRowComponent;
706+
707+
UIInteractions.simulateClickAndSelectEvent(firstChildRow.expander);
708+
fix.detectChanges();
709+
expect(firstChildRow.expanded).toBe(true);
710+
711+
const secondChildGrid = childGrids[1];
712+
const secondChildRow = secondChildGrid.hgridAPI.get_row_by_index(0) as IgxHierarchicalRowComponent;
713+
714+
UIInteractions.simulateClickAndSelectEvent(secondChildRow.expander);
715+
fix.detectChanges();
716+
expect(secondChildRow.expanded).toBe(true);
717+
718+
UIInteractions.simulateClickAndSelectEvent(secondRow.expander);
719+
fix.detectChanges();
720+
expect(secondRow.expanded).toBe(true);
721+
722+
childGrids = hGrid.hgridAPI.getChildGrids(false);
723+
724+
const thirdChildGrid = childGrids[3];
725+
const thirdChildRow = thirdChildGrid.hgridAPI.get_row_by_index(0) as IgxHierarchicalRowComponent;
726+
727+
UIInteractions.simulateClickAndSelectEvent(thirdChildRow.expander);
728+
fix.detectChanges();
729+
expect(thirdChildRow.expanded).toBe(true);
730+
731+
await exportAndVerify(hGrid, options, actualData.exportHierarchicalDataWithExpandedRows);
732+
});
733+
});
734+
635735
describe('', () => {
636736
let fix;
637737
let treeGrid: IgxTreeGridComponent;
@@ -855,9 +955,10 @@ describe('Excel Exporter', () => {
855955
};
856956

857957
const exportAndVerify = async (component, exportOptions, expectedData) => {
958+
const isHGrid = component instanceof IgxHierarchicalGridComponent;
858959
const wrapper = await getExportedData(component, exportOptions);
859-
await wrapper.verifyStructure();
860-
await wrapper.verifyDataFilesContent(expectedData);
960+
await wrapper.verifyStructure(isHGrid);
961+
await wrapper.verifyDataFilesContent(expectedData, '', isHGrid);
861962
};
862963
});
863964

projects/igniteui-angular/src/lib/services/excel/excel-exporter.ts

+24-4
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { ExcelElementsFactory } from './excel-elements-factory';
55
import { ExcelFolderTypes } from './excel-enums';
66
import { IgxExcelExporterOptions } from './excel-exporter-options';
77
import { IExcelFolder } from './excel-interfaces';
8-
import { IExportRecord, IgxBaseExporter } from '../exporter-common/base-export-service';
8+
import { ExportRecordType, IExportRecord, IgxBaseExporter, DEFAULT_OWNER } from '../exporter-common/base-export-service';
99
import { ExportUtilities } from '../exporter-common/export-utilities';
1010
import { WorksheetData } from './worksheet-data';
1111
import { IBaseEventArgs } from '../../core/utils';
@@ -72,9 +72,13 @@ export class IgxExcelExporterService extends IgxBaseExporter {
7272
}
7373

7474
protected exportDataImplementation(data: IExportRecord[], options: IgxExcelExporterOptions): void {
75-
const level = data[0]?.level;
75+
const firstDataElement = data[0];
76+
let rootKeys;
77+
let columnCount;
78+
let columnWidths;
79+
let indexOfLastPinnedColumn;
7680

77-
if (typeof level !== 'undefined') {
81+
if (typeof firstDataElement !== 'undefined') {
7882
let maxLevel = 0;
7983

8084
data.forEach((r) => {
@@ -84,9 +88,25 @@ export class IgxExcelExporterService extends IgxBaseExporter {
8488
if (maxLevel > 7) {
8589
throw Error('Can create an outline of up to eight levels!');
8690
}
91+
92+
if (firstDataElement.type === ExportRecordType.HierarchicalGridRecord) {
93+
columnCount = data
94+
.map(a => this._ownersMap.get(a.owner).columns.length + a.level)
95+
.sort((a,b) => b - a)[0];
96+
97+
rootKeys = this._ownersMap.get(firstDataElement.owner).columns.map(c => c.header);
98+
} else {
99+
const defaultOwner = this._ownersMap.get(DEFAULT_OWNER);
100+
const columns = defaultOwner.columns.filter(col => !col.skip);
101+
columnWidths = defaultOwner.columnWidths;
102+
indexOfLastPinnedColumn = defaultOwner.indexOfLastPinnedColumn;
103+
columnCount = columns.length;
104+
rootKeys = columns.map(c => c.header);
105+
}
87106
}
88107

89-
const worksheetData = new WorksheetData(data, this.columnWidthList, options, this._indexOfLastPinnedColumn, this._sort);
108+
const worksheetData =
109+
new WorksheetData(data, options, this._sort, columnCount, rootKeys, indexOfLastPinnedColumn, columnWidths);
90110

91111
this._xlsx = new (JSZip as any).default();
92112

0 commit comments

Comments
 (0)