Skip to content

Commit

Permalink
feat(exports): add optional file MIME type to Excel export service (#849
Browse files Browse the repository at this point in the history
)
  • Loading branch information
ghiscoding authored Dec 18, 2022
1 parent 27a18c4 commit 05402e5
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 3 deletions.
8 changes: 8 additions & 0 deletions packages/common/src/interfaces/excelExportOption.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@ export interface ExcelExportOption {
/** file type format, .xls/.xlsx (this will provide the extension) */
format?: FileType.xls | FileType.xlsx;

/**
* file MIME type could be provided by the user.
* - when undefined it will detect the type depending on its extension unless user defines it.
* - user could also be set to an empty string, which in this case would lead to an empty MIME type:
* - ie Salesforce restricts Excel MIME types, however we can go around this issue by not providing any MIME type
*/
mimeType?: string;

/** The column header title (at A0 in Excel) of the Group by. If nothing is provided it will use "Group By" (which is a translated value of GROUP_BY i18n) */
groupingColumnHeaderTitle?: string;

Expand Down
38 changes: 38 additions & 0 deletions packages/excel-export/src/excelExport.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
SortComparers,
SortDirectionNumber,
} from '@slickgrid-universal/common';
import * as ExcelBuilder from 'excel-builder-webpacker';
import { ContainerServiceStub } from '../../../test/containerServiceStub';
import { TranslateServiceStub } from '../../../test/translateServiceStub';
import { ExcelExportService } from './excelExport.service';
Expand Down Expand Up @@ -143,6 +144,10 @@ describe('ExcelExportService', () => {
jest.spyOn(gridStub, 'getColumns').mockReturnValue(mockColumns);
});

afterEach(() => {
jest.clearAllMocks();
});

it('should throw an error when trying call exportToExcel" without a grid and/or dataview object initialized', async () => {
try {
service.init(null as any, container);
Expand Down Expand Up @@ -174,6 +179,7 @@ describe('ExcelExportService', () => {
});

it('should call "URL.createObjectURL" with a Blob and xlsx file when browser is not IE11 (basically any other browser) when exporting as xlsx', async () => {
const excelBuilderSpy = jest.spyOn(ExcelBuilder.Builder, 'createFile');
const optionExpectation = { filename: 'export.xlsx', format: FileType.xlsx };
const pubSubSpy = jest.spyOn(pubSubServiceStub, 'publish');
const spyUrlCreate = jest.spyOn(URL, 'createObjectURL');
Expand All @@ -184,8 +190,40 @@ describe('ExcelExportService', () => {
expect(result).toBeTruthy();
expect(pubSubSpy).toHaveBeenCalledWith(`onAfterExportToExcel`, optionExpectation);
expect(spyUrlCreate).toHaveBeenCalledWith(mockExcelBlob);
expect(excelBuilderSpy).toHaveBeenCalledWith(expect.anything(), { type: 'blob', mimeType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
});

it('should call "URL.createObjectURL" with a Blob and xlsx file without any MIME type when providing an empty string as a mime type', async () => {
const excelBuilderSpy = jest.spyOn(ExcelBuilder.Builder, 'createFile');
mockGridOptions.excelExportOptions = { mimeType: '' };
const optionExpectation = { filename: 'export.xlsx', format: FileType.xlsx };
const pubSubSpy = jest.spyOn(pubSubServiceStub, 'publish');
const spyUrlCreate = jest.spyOn(URL, 'createObjectURL');

service.init(gridStub, container);
const result = await service.exportToExcel(mockExportExcelOptions);

expect(result).toBeTruthy();
expect(pubSubSpy).toHaveBeenCalledWith(`onAfterExportToExcel`, optionExpectation);
expect(spyUrlCreate).toHaveBeenCalledWith(mockExcelBlob);
expect(excelBuilderSpy).toHaveBeenCalledWith(expect.anything(), { type: 'blob' });
});

it('should call "URL.createObjectURL" with a Blob and expect same mime type when provided', async () => {
const excelBuilderSpy = jest.spyOn(ExcelBuilder.Builder, 'createFile');
mockGridOptions.excelExportOptions = { mimeType: 'application/some-excel-format' };
const optionExpectation = { filename: 'export.xlsx', format: FileType.xlsx };
const pubSubSpy = jest.spyOn(pubSubServiceStub, 'publish');
const spyUrlCreate = jest.spyOn(URL, 'createObjectURL');

service.init(gridStub, container);
const result = await service.exportToExcel(mockExportExcelOptions);

expect(result).toBeTruthy();
expect(pubSubSpy).toHaveBeenCalledWith(`onAfterExportToExcel`, optionExpectation);
expect(spyUrlCreate).toHaveBeenCalledWith(mockExcelBlob);
expect(excelBuilderSpy).toHaveBeenCalledWith(expect.anything(), { type: 'blob', mimeType: 'application/some-excel-format' });
});

it('should call "msSaveOrOpenBlob" with a Blob and xlsx file when browser is IE11 when exporting as xlsx', async () => {
const optionExpectation = { filename: 'export.xlsx', format: FileType.xlsx };
Expand Down
13 changes: 10 additions & 3 deletions packages/excel-export/src/excelExport.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,9 +169,16 @@ export class ExcelExportService implements ExternalResource, BaseExcelExportServ
this._workbook.addWorksheet(this._sheet);

// using ExcelBuilder.Builder.createFile with WebPack but ExcelBuilder.createFile with RequireJS/SystemJS
const createFileFn = ExcelBuilder.Builder && ExcelBuilder.Builder.createFile ? ExcelBuilder.Builder.createFile : ExcelBuilder.createFile;
const mimeType = this._fileFormat === FileType.xls ? 'application/vnd.ms-excel' : 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet ';
const excelBlob = await createFileFn(this._workbook, { type: 'blob', mimeType });
const createFileFn = ExcelBuilder.Builder?.createFile ?? ExcelBuilder.createFile;

// MIME type could be undefined, if that's the case we'll detect the type by its file extension
// user could also provide its own mime type, if however an empty string is provided we will consider to be without any MIME type)
let mimeType = this._excelExportOptions?.mimeType;
if (mimeType === undefined) {
mimeType = this._fileFormat === FileType.xls ? 'application/vnd.ms-excel' : 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
}
const createFileOptions = mimeType === '' ? { type: 'blob' } : { type: 'blob', mimeType };
const excelBlob = await createFileFn(this._workbook, createFileOptions);
const downloadOptions = {
filename: `${this._excelExportOptions.filename}.${this._fileFormat}`,
format: this._fileFormat
Expand Down

0 comments on commit 05402e5

Please sign in to comment.