Skip to content

Commit 0a87cde

Browse files
authored
fix(export): add checks for missing data - 20.1.x (#16724)
* fix(export): add checks for missing data * test(exporter): empty data and summaries should not throw an error
1 parent 83ed77e commit 0a87cde

5 files changed

Lines changed: 135 additions & 29 deletions

File tree

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

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,9 @@ import { IgxHierarchicalGridExportComponent,
4242
IgxHierarchicalGridMCHCollapsibleComponent,
4343
IgxHierarchicalGridMultiColumnHeaderIslandsExportComponent,
4444
IgxHierarchicalGridMultiColumnHeadersExportComponent,
45-
IgxHierarchicalGridSummariesExportComponent
45+
IgxHierarchicalGridSummariesExportComponent,
46+
IgxHierarchicalGridEmptyDataExportComponent,
47+
IgxHierarchicalGridMissingChildDataExportComponent
4648
} from '../../test-utils/hierarchical-grid-components.spec';
4749
import { IgxHierarchicalGridComponent } from '../../grids/hierarchical-grid/public_api';
4850
import { IgxHierarchicalRowComponent } from '../../grids/hierarchical-grid/hierarchical-row.component';
@@ -77,6 +79,8 @@ describe('Excel Exporter', () => {
7779
IgxPivotGridTestComplexHierarchyComponent,
7880
IgxTreeGridSummariesKeyComponent,
7981
IgxHierarchicalGridSummariesExportComponent,
82+
IgxHierarchicalGridEmptyDataExportComponent,
83+
IgxHierarchicalGridMissingChildDataExportComponent,
8084
GroupedGridWithSummariesComponent,
8185
GridCurrencySummariesComponent,
8286
GridUserMeetingDataComponent,
@@ -930,6 +934,41 @@ describe('Excel Exporter', () => {
930934

931935
await exportAndVerify(hGrid, options, actualData.exportHierarchicalDataWithSkippedRows);
932936
});
937+
938+
it('should export hierarchical grid with empty data without throwing error', async () => {
939+
fix = TestBed.createComponent(IgxHierarchicalGridEmptyDataExportComponent);
940+
fix.detectChanges();
941+
942+
hGrid = fix.componentInstance.hGrid;
943+
options = createExportOptions('HierarchicalGridEmptyDataExcelExport');
944+
945+
await expectAsync(getExportedData(hGrid, options)).toBeResolved();
946+
});
947+
948+
it('should export hierarchical grid with empty data and summaries without throwing error', async () => {
949+
fix = TestBed.createComponent(IgxHierarchicalGridEmptyDataExportComponent);
950+
fix.detectChanges();
951+
952+
hGrid = fix.componentInstance.hGrid;
953+
const artistColumn = hGrid.columns.find(col => col.field === 'Artist');
954+
artistColumn.hasSummary = true;
955+
fix.detectChanges();
956+
957+
options = createExportOptions('HierarchicalGridEmptyDataWithSummariesExcelExport');
958+
options.exportSummaries = true;
959+
960+
await expectAsync(getExportedData(hGrid, options)).toBeResolved();
961+
});
962+
963+
it('should export hierarchical grid with missing child data key without throwing error', async () => {
964+
fix = TestBed.createComponent(IgxHierarchicalGridMissingChildDataExportComponent);
965+
fix.detectChanges();
966+
967+
hGrid = fix.componentInstance.hGrid;
968+
options = createExportOptions('HierarchicalGridMissingChildDataExcelExport');
969+
970+
await expectAsync(getExportedData(hGrid, options)).toBeResolved();
971+
});
933972
});
934973

935974
describe('', () => {
@@ -1494,7 +1533,7 @@ describe('Excel Exporter', () => {
14941533
fix.componentInstance.data = SALES_DATA;
14951534
fix.componentInstance.pivotConfigHierarchy = {
14961535
rows: [
1497-
{
1536+
{
14981537
memberName: 'All_Srep Code Alts',
14991538
enabled: true,
15001539
width: '150px',

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

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,10 @@ export class IgxExcelExporterService extends IgxBaseExporter {
7777
const firstDataElement = data[0];
7878
const isHierarchicalGrid = firstDataElement?.type === ExportRecordType.HierarchicalGridRecord;
7979
const isPivotGrid = firstDataElement?.type === ExportRecordType.PivotGridRecord;
80+
const ownersKeys = Array.from(this._ownersMap.keys());
81+
const firstKey = ownersKeys[0];
82+
const isHierarchicalGridByMap = firstKey && typeof firstKey !== 'string';
83+
const filterColumns = (columns) => columns.filter(col => col.field !== GRID_LEVEL_COL && !col.skip && col.headerType === ExportHeaderType.ColumnHeader);
8084

8185
let rootKeys;
8286
let columnCount;
@@ -113,20 +117,30 @@ export class IgxExcelExporterService extends IgxBaseExporter {
113117
rootKeys = this._ownersMap.get(firstDataElement.owner).columns.filter(c => !c.skip).map(c => c.field);
114118
defaultOwner = this._ownersMap.get(firstDataElement.owner);
115119
} else {
116-
defaultOwner = this._ownersMap.get(DEFAULT_OWNER);
117-
const columns = defaultOwner.columns.filter(col => col.field !== GRID_LEVEL_COL && !col.skip && col.headerType === ExportHeaderType.ColumnHeader);
118-
119-
columnWidths = defaultOwner.columnWidths;
120-
indexOfLastPinnedColumn = defaultOwner.indexOfLastPinnedColumn;
121-
columnCount = isPivotGrid ? columns.length + this.pivotGridFilterFieldsCount : columns.length;
122-
rootKeys = columns.map(c => c.field);
120+
// Check if this is actually a hierarchical grid (when data only contains summary records)
121+
defaultOwner = isHierarchicalGridByMap
122+
? this._ownersMap.get(firstKey)
123+
: this._ownersMap.get(DEFAULT_OWNER) || this._ownersMap.get(firstKey);
124+
125+
if (defaultOwner) {
126+
const columns = filterColumns(defaultOwner.columns);
127+
128+
columnWidths = defaultOwner.columnWidths;
129+
indexOfLastPinnedColumn = defaultOwner.indexOfLastPinnedColumn;
130+
columnCount = isPivotGrid ? columns.length + this.pivotGridFilterFieldsCount : columns.length;
131+
rootKeys = columns.map(c => c.field);
132+
}
123133
}
124134
} else {
125-
const ownersKeys = Array.from(this._ownersMap.keys());
135+
// For hierarchical grids with empty data, use the grid instance; otherwise try DEFAULT_OWNER first
136+
defaultOwner = isHierarchicalGridByMap
137+
? this._ownersMap.get(firstKey)
138+
: this._ownersMap.get(DEFAULT_OWNER) || this._ownersMap.get(firstKey);
126139

127-
defaultOwner = this._ownersMap.get(ownersKeys[0]);
128-
columnWidths = defaultOwner.columnWidths;
129-
columnCount = defaultOwner.columns.filter(col => col.field !== GRID_LEVEL_COL && !col.skip && col.headerType === ExportHeaderType.ColumnHeader).length;
140+
if (defaultOwner) {
141+
columnWidths = defaultOwner.columnWidths;
142+
columnCount = filterColumns(defaultOwner.columns).length;
143+
}
130144
}
131145

132146
const worksheetData =

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -551,9 +551,15 @@ export class WorksheetFile implements IExcelFile {
551551
}
552552

553553
private getSummaryFunction(type: string, key: string, dimensionMapKey: any, recordLevel: number, col: IColumnInfo): string {
554-
const dimensionMap = dimensionMapKey ? this.hierarchicalDimensionMap.get(dimensionMapKey) : this.dimensionMap;
554+
const dimensionMap = dimensionMapKey ? (this.hierarchicalDimensionMap.get(dimensionMapKey) ?? this.dimensionMap) : this.dimensionMap;
555+
if (!dimensionMap) {
556+
return '';
557+
}
555558
const dimensions = dimensionMap.get(key);
556559
const levelDimensions = dimensionMap.get(GRID_LEVEL_COL);
560+
if (!dimensions || !levelDimensions) {
561+
return '';
562+
}
557563

558564
let func = '';
559565
let funcType = '';

projects/igniteui-angular/src/lib/services/exporter-common/base-export-service.ts

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,8 @@ export abstract class IgxBaseExporter {
258258
const childLayoutList = grid.childLayoutList;
259259

260260
for (const island of childLayoutList) {
261-
this.mapHierarchicalGridColumns(island, grid.data[0]);
261+
const gridData = grid.data && grid.data.length > 0 ? grid.data[0] : {};
262+
this.mapHierarchicalGridColumns(island, gridData);
262263
}
263264
} else if (grid.type === 'pivot') {
264265
this.pivotGridColumns = [];
@@ -1227,32 +1228,37 @@ export abstract class IgxBaseExporter {
12271228
let keyData;
12281229

12291230
if (island.autoGenerate) {
1230-
keyData = gridData[island.key];
1231-
const islandKeys = island.children.map(i => i.key);
1231+
keyData = gridData && gridData[island.key] ? gridData[island.key] : undefined;
1232+
const islandKeys = island.children && island.children.length > 0 ? island.children.map(i => i.key) : [];
12321233

1233-
const islandData = keyData.map(i => {
1234-
const newItem = {};
1234+
if (keyData && Array.isArray(keyData) && keyData.length > 0) {
1235+
const islandData = keyData.map(i => {
1236+
const newItem = {};
12351237

1236-
Object.keys(i).map(k => {
1237-
if (!islandKeys.includes(k)) {
1238-
newItem[k] = i[k];
1239-
}
1240-
});
1238+
Object.keys(i).map(k => {
1239+
if (!islandKeys.includes(k)) {
1240+
newItem[k] = i[k];
1241+
}
1242+
});
12411243

1242-
return newItem;
1243-
});
1244+
return newItem;
1245+
});
12441246

1245-
columnList = this.getAutoGeneratedColumns(islandData);
1247+
columnList = this.getAutoGeneratedColumns(islandData);
1248+
} else {
1249+
// If no data available, create empty column list
1250+
columnList = this.getAutoGeneratedColumns([{}]);
1251+
}
12461252
} else {
12471253
const islandColumnList = island.columns;
12481254
columnList = this.getColumns(islandColumnList);
12491255
}
12501256

12511257
this._ownersMap.set(island, columnList);
12521258

1253-
if (island.children.length > 0) {
1259+
if (island.children && island.children.length > 0) {
12541260
for (const childIsland of island.children) {
1255-
const islandKeyData = keyData !== undefined ? keyData[0] : {};
1261+
const islandKeyData = keyData && Array.isArray(keyData) && keyData.length > 0 ? keyData[0] : {};
12561262
this.mapHierarchicalGridColumns(childIsland, islandKeyData);
12571263
}
12581264
}

projects/igniteui-angular/src/lib/test-utils/hierarchical-grid-components.spec.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -754,3 +754,44 @@ export class IgxHierarchicalGridDefaultComponent {
754754
this.data = SampleTestData.hierarchicalGridSingersFullData();
755755
}
756756
}
757+
758+
@Component({
759+
template: `
760+
<igx-hierarchical-grid [data]="data" [height]="'1200px'" [width]="'700px'" #hierarchicalGrid>
761+
<igx-column field="Artist" [sortable]="true"></igx-column>
762+
<igx-column field="Debut" [sortable]="true" dataType="number"></igx-column>
763+
<igx-column field="GrammyNominations" header="Grammy Nominations" [sortable]="true"></igx-column>
764+
<igx-column field="GrammyAwards" header="Grammy Awards" [sortable]="true"></igx-column>
765+
766+
<igx-row-island [key]="'Albums'" [autoGenerate]="true">
767+
</igx-row-island>
768+
</igx-hierarchical-grid>
769+
`,
770+
imports: [IgxHierarchicalGridComponent, IgxColumnComponent, IgxRowIslandComponent]
771+
})
772+
export class IgxHierarchicalGridEmptyDataExportComponent {
773+
@ViewChild('hierarchicalGrid', { read: IgxHierarchicalGridComponent, static: true }) public hGrid: IgxHierarchicalGridComponent;
774+
public data = [];
775+
}
776+
777+
@Component({
778+
template: `
779+
<igx-hierarchical-grid [data]="data" [height]="'1200px'" [width]="'700px'" #hierarchicalGrid>
780+
<igx-column field="Artist" [sortable]="true"></igx-column>
781+
<igx-column field="Debut" [sortable]="true" dataType="number"></igx-column>
782+
<igx-column field="GrammyNominations" header="Grammy Nominations" [sortable]="true"></igx-column>
783+
<igx-column field="GrammyAwards" header="Grammy Awards" [sortable]="true"></igx-column>
784+
785+
<igx-row-island [key]="'Albums'" [autoGenerate]="true">
786+
</igx-row-island>
787+
</igx-hierarchical-grid>
788+
`,
789+
imports: [IgxHierarchicalGridComponent, IgxColumnComponent, IgxRowIslandComponent]
790+
})
791+
export class IgxHierarchicalGridMissingChildDataExportComponent {
792+
@ViewChild('hierarchicalGrid', { read: IgxHierarchicalGridComponent, static: true }) public hGrid: IgxHierarchicalGridComponent;
793+
// Data without the 'Albums' key that the row island expects
794+
public data = [
795+
{ Artist: 'Artist1', Debut: 2000, GrammyNominations: 5, GrammyAwards: 2 }
796+
];
797+
}

0 commit comments

Comments
 (0)