Skip to content

Commit b20aedd

Browse files
onlyexeptiongedinakovakdinev
authored
fix(excel-export): sucessfully export hgrid with skipped columns (#10377)
* fix(excel-export): sucessfully export hgrid with skipped columns * fix(exporter): do not include dimensions in expected output if hGrid * fix(exporter): decrease maxLevel with 1 for every column group skipped * fix(exporter): improve logic for removing canceled columns Co-authored-by: Galina Edinakova <[email protected]> Co-authored-by: Konstantin Dinev <[email protected]>
1 parent 2c8198a commit b20aedd

File tree

6 files changed

+217
-32
lines changed

6 files changed

+217
-32
lines changed

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

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -829,6 +829,22 @@ describe('Excel Exporter', () => {
829829

830830
await exportAndVerify(hGrid, options, actualData.exportHierarchicalDataWithFrozenHeaders);
831831
});
832+
833+
it('should export hierarchical grid with skipped columns', async () => {
834+
exporter.columnExporting.subscribe((args: IColumnExportingEventArgs) => {
835+
if (args.header === 'Debut' ||
836+
args.header === 'Billboard Review' ||
837+
args.header === 'Album' ||
838+
args.header === 'Tickets Sold' ||
839+
args.header === 'Released') {
840+
args.cancel = true;
841+
}
842+
});
843+
844+
fix.detectChanges();
845+
846+
await exportAndVerify(hGrid, options, actualData.exportHierarchicalDataWithSkippedColumns);
847+
});
832848
});
833849

834850
describe('', () => {
@@ -840,6 +856,57 @@ describe('Excel Exporter', () => {
840856
options = createExportOptions('HierarchicalGridMCHExcelExport');
841857
await exportAndVerify(hGrid, options, actualData.exportHierarchicalDataWithMultiColumnHeaders);
842858
});
859+
860+
it('should export hierarchical grid with multi column headers and skipped column', async () => {
861+
const fix = TestBed.createComponent(IgxHierarchicalGridMultiColumnHeadersExportComponent);
862+
fix.detectChanges();
863+
864+
const hGrid = fix.componentInstance.hGrid;
865+
options = createExportOptions('HierarchicalGridMCHExcelExport');
866+
867+
exporter.columnExporting.subscribe((args: IColumnExportingEventArgs) => {
868+
if (args.field === 'ContactTitle') {
869+
args.cancel = true;
870+
}
871+
});
872+
fix.detectChanges();
873+
874+
await exportAndVerify(hGrid, options, actualData.exportMultiColumnHeadersDataWithSkippedColumn);
875+
});
876+
877+
it('should export hierarchical grid with multi column headers and skipped parent multi column header', async () => {
878+
const fix = TestBed.createComponent(IgxHierarchicalGridMultiColumnHeadersExportComponent);
879+
fix.detectChanges();
880+
881+
const hGrid = fix.componentInstance.hGrid;
882+
options = createExportOptions('HierarchicalGridMCHExcelExport');
883+
884+
exporter.columnExporting.subscribe((args: IColumnExportingEventArgs) => {
885+
if (args.header === 'Address Information') {
886+
args.cancel = true;
887+
}
888+
});
889+
fix.detectChanges();
890+
891+
await exportAndVerify(hGrid, options, actualData.exportMultiColumnHeadersDataWithSkippedParentMCH);
892+
});
893+
894+
it('should export empty file when all parent multi column headers are skipped', async () => {
895+
const fix = TestBed.createComponent(IgxHierarchicalGridMultiColumnHeadersExportComponent);
896+
fix.detectChanges();
897+
898+
const hGrid = fix.componentInstance.hGrid;
899+
options = createExportOptions('HierarchicalGridMCHExcelExport');
900+
901+
exporter.columnExporting.subscribe((args: IColumnExportingEventArgs) => {
902+
if (args.header === 'General Information' || args.header === 'Address Information' || args.field === 'CustomerID') {
903+
args.cancel = true;
904+
}
905+
});
906+
fix.detectChanges();
907+
908+
await exportAndVerify(hGrid, options, actualData.exportMultiColumnHeadersDataWithAllParentsSkipped);
909+
});
843910
});
844911

845912
describe('', () => {

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,10 +109,10 @@ export class IgxExcelExporterService extends IgxBaseExporter {
109109

110110
if (isHierarchicalGrid) {
111111
columnCount = data
112-
.map(a => this._ownersMap.get(a.owner).columns.length + a.level)
112+
.map(a => this._ownersMap.get(a.owner).columns.filter(c => !c.skip).length + a.level)
113113
.sort((a,b) => b - a)[0];
114114

115-
rootKeys = this._ownersMap.get(firstDataElement.owner).columns.map(c => c.field);
115+
rootKeys = this._ownersMap.get(firstDataElement.owner).columns.filter(c => !c.skip).map(c => c.field);
116116
defaultOwner = this._ownersMap.get(firstDataElement.owner);
117117
} else {
118118
defaultOwner = this._ownersMap.get(DEFAULT_OWNER);

projects/igniteui-angular/src/lib/services/excel/jszip-helper.spec.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,11 +163,13 @@ export class JSZipFiles {
163163
`xmlns:x14ac="http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac">${ sheetData }` +
164164
`<pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/>${tablePart}</worksheet>`;
165165
} else {
166+
const dimensionsPart = isHGrid ? '' : '<dimension ref="A1"/>';
167+
166168
return `<?xml version="1.0" encoding="UTF-8"?>
167169
<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" ` +
168170
`xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" ` +
169171
`xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="x14ac" ` +
170-
`xmlns:x14ac="http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac"><dimension ref="A1"/>` +
172+
`xmlns:x14ac="http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac">${dimensionsPart}` +
171173
`<sheetViews><sheetView tabSelected="1" workbookViewId="0"></sheetView></sheetViews><sheetFormatPr defaultRowHeight="15" ` +
172174
`x14ac:dyDescent="0.25"/><sheetData/><pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" ` +
173175
`footer="0.3"/></worksheet>`;

projects/igniteui-angular/src/lib/services/excel/test-data.service.spec.ts

Lines changed: 65 additions & 0 deletions
Large diffs are not rendered by default.

projects/igniteui-angular/src/lib/services/excel/worksheet-data.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ export class WorksheetData {
3030
}
3131

3232
public get isEmpty(): boolean {
33-
return !this.rowCount || !this.columnCount;
33+
return !this.rowCount || !this.columnCount || this.owner.columns.every(c => c.skip);
3434
}
3535

3636
public get isSpecialData(): boolean {

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

Lines changed: 79 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ export interface IColumnInfo {
5454
level?: number;
5555
exportIndex?: number;
5656
pinnedIndex?: number;
57+
columnGroupParent?: ColumnType;
58+
columnGroup?: ColumnType;
5759
}
5860
/**
5961
* rowExporting event arguments
@@ -307,8 +309,18 @@ export abstract class IgxBaseExporter {
307309
shouldReorderColumns = true;
308310
}
309311

310-
if (column.skip && index <= indexOfLastPinnedColumn) {
311-
skippedPinnedColumnsCount++;
312+
if (column.skip) {
313+
if (index <= indexOfLastPinnedColumn) {
314+
skippedPinnedColumnsCount++;
315+
}
316+
317+
this.calculateColumnSpans(column, mapRecord, column.columnSpan);
318+
319+
const nonSkippedColumns = mapRecord.columns.filter(c => !c.skip);
320+
321+
if (nonSkippedColumns.length > 0) {
322+
this._ownersMap.get(key).maxLevel = nonSkippedColumns.sort((a, b) => b.level - a.level)[0].level;
323+
}
312324
}
313325

314326
if (this._sort && this._sort.fieldName === column.field) {
@@ -344,35 +356,72 @@ export abstract class IgxBaseExporter {
344356
});
345357
}
346358

359+
private calculateColumnSpans(column: IColumnInfo, mapRecord: IColumnList, span: number) {
360+
if (column.headerType === HeaderType.MultiColumnHeader && column.skip) {
361+
const columnGroupChildren = mapRecord.columns.filter(c => c.columnGroupParent === column.columnGroup);
362+
363+
columnGroupChildren.forEach(cgc => {
364+
if (cgc.headerType === HeaderType.MultiColumnHeader) {
365+
cgc.columnSpan = 0;
366+
cgc.columnGroupParent = null;
367+
cgc.skip = true;
368+
369+
this.calculateColumnSpans(cgc, mapRecord, cgc.columnSpan);
370+
} else {
371+
cgc.skip = true;
372+
}
373+
});
374+
}
375+
376+
const targetCol = mapRecord.columns.filter(c => column.columnGroupParent !== null && c.columnGroup === column.columnGroupParent)[0];
377+
if (targetCol !== undefined) {
378+
targetCol.columnSpan -= span;
379+
380+
if (targetCol.columnGroupParent !== null) {
381+
this.calculateColumnSpans(targetCol, mapRecord, span);
382+
}
383+
384+
if (targetCol.columnSpan === 0) {
385+
targetCol.skip = true;
386+
}
387+
}
388+
}
389+
347390
private exportRow(data: IExportRecord[], record: IExportRecord, index: number, isSpecialData: boolean) {
348-
if (!isSpecialData && record.type !== ExportRecordType.HeaderRecord) {
391+
if (!isSpecialData) {
349392
const owner = record.owner === undefined ? DEFAULT_OWNER : record.owner;
393+
const ownerCols = this._ownersMap.get(owner).columns;
394+
395+
if (record.type !== ExportRecordType.HeaderRecord) {
396+
const columns = ownerCols
397+
.filter(c => c.headerType !== HeaderType.MultiColumnHeader && !c.skip)
398+
.sort((a, b) => a.startIndex - b.startIndex)
399+
.sort((a, b) => a.pinnedIndex - b.pinnedIndex);
400+
401+
record.data = columns.reduce((a, e) => {
402+
if (!e.skip) {
403+
let rawValue = resolveNestedPath(record.data, e.field);
404+
405+
const shouldApplyFormatter = e.formatter && !e.skipFormatter && record.type !== ExportRecordType.GroupedRecord;
406+
407+
if (e.dataType === 'date' &&
408+
!(rawValue instanceof Date) &&
409+
!shouldApplyFormatter &&
410+
rawValue !== undefined &&
411+
rawValue !== null) {
412+
rawValue = new Date(rawValue);
413+
} else if (e.dataType === 'string' && rawValue instanceof Date) {
414+
rawValue = rawValue.toString();
415+
}
350416

351-
const columns = this._ownersMap.get(owner).columns
352-
.filter(c => c.headerType !== HeaderType.MultiColumnHeader)
353-
.sort((a, b) => a.startIndex - b.startIndex)
354-
.sort((a, b) => a.pinnedIndex - b.pinnedIndex);
355-
356-
record.data = columns.reduce((a, e) => {
357-
if (!e.skip) {
358-
let rawValue = resolveNestedPath(record.data, e.field);
359-
360-
const shouldApplyFormatter = e.formatter && !e.skipFormatter && record.type !== ExportRecordType.GroupedRecord;
361-
362-
if (e.dataType === 'date' &&
363-
!(rawValue instanceof Date) &&
364-
!shouldApplyFormatter &&
365-
rawValue !== undefined &&
366-
rawValue !== null) {
367-
rawValue = new Date(rawValue);
368-
} else if (e.dataType === 'string' && rawValue instanceof Date) {
369-
rawValue = rawValue.toString();
417+
a[e.field] = shouldApplyFormatter ? e.formatter(rawValue) : rawValue;
370418
}
371-
372-
a[e.field] = shouldApplyFormatter ? e.formatter(rawValue) : rawValue;
373-
}
374-
return a;
375-
}, {});
419+
return a;
420+
}, {});
421+
} else {
422+
const filteredHeaders = ownerCols.filter(c => c.skip).map(c => c.header ? c.header : c.field);
423+
record.data = record.data.filter(d => filteredHeaders.indexOf(d) === -1);
424+
}
376425
}
377426

378427
const rowArgs = {
@@ -860,7 +909,9 @@ export abstract class IgxBaseExporter {
860909
Number.MAX_VALUE :
861910
!column.hidden ?
862911
column.grid.pinnedColumns.indexOf(column)
863-
: NaN
912+
: NaN,
913+
columnGroupParent: column.parent ? column.parent : null,
914+
columnGroup: isMultiColHeader ? column : null
864915
};
865916

866917
if (this.options.ignoreColumnsOrder) {

0 commit comments

Comments
 (0)