-
-
Notifications
You must be signed in to change notification settings - Fork 29
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: export to file/excel should also have tree indentation
- Loading branch information
1 parent
8b468f0
commit 8c4c2b8
Showing
11 changed files
with
228 additions
and
26 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
132 changes: 132 additions & 0 deletions
132
packages/common/src/formatters/__tests__/treeExportFormatter.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
import { Column, SlickDataView, GridOption, SlickGrid } from '../../interfaces/index'; | ||
import { treeExportFormatter } from '../treeExportFormatter'; | ||
|
||
const dataViewStub = { | ||
getIdxById: jest.fn(), | ||
getItemByIdx: jest.fn(), | ||
getIdPropertyName: jest.fn(), | ||
} as unknown as SlickDataView; | ||
|
||
const gridStub = { | ||
getData: jest.fn(), | ||
getOptions: jest.fn(), | ||
} as unknown as SlickGrid; | ||
|
||
describe('Tree Export Formatter', () => { | ||
let dataset: any[]; | ||
let mockGridOptions: GridOption; | ||
|
||
beforeEach(() => { | ||
dataset = [ | ||
{ id: 0, firstName: 'John', lastName: 'Smith', fullName: 'John Smith', email: '[email protected]', address: { zip: 123456 }, parentId: null, indent: 0 }, | ||
{ id: 1, firstName: 'Jane', lastName: 'Doe', fullName: 'Jane Doe', email: '[email protected]', address: { zip: 222222 }, parentId: 0, indent: 1 }, | ||
{ id: 2, firstName: 'Bob', lastName: 'Cane', fullName: 'Bob Cane', email: '[email protected]', address: { zip: 333333 }, parentId: 1, indent: 2, __collapsed: true }, | ||
{ id: 3, firstName: 'Barbara', lastName: 'Cane', fullName: 'Barbara Cane', email: '[email protected]', address: { zip: 444444 }, parentId: null, indent: 0, __collapsed: true }, | ||
{ id: 4, firstName: 'Anonymous', lastName: 'Doe', fullName: 'Anonymous < Doe', email: '[email protected]', address: { zip: 556666 }, parentId: null, indent: 0, __collapsed: true }, | ||
]; | ||
mockGridOptions = { | ||
treeDataOptions: { levelPropName: 'indent' } | ||
} as GridOption; | ||
jest.spyOn(gridStub, 'getOptions').mockReturnValue(mockGridOptions); | ||
}); | ||
|
||
it('should throw an error when oarams are mmissing', () => { | ||
expect(() => treeExportFormatter(1, 1, 'blah', {} as Column, {}, gridStub)) | ||
.toThrowError('You must provide valid "treeDataOptions" in your Grid Options and it seems that there are no tree level found in this row'); | ||
}); | ||
|
||
it('should return empty string when DataView is not correctly formed', () => { | ||
const output = treeExportFormatter(1, 1, '', {} as Column, dataset[1], gridStub); | ||
expect(output).toBe(''); | ||
}); | ||
|
||
it('should return empty string when value is null', () => { | ||
const output = treeExportFormatter(1, 1, null, {} as Column, dataset[1], gridStub); | ||
expect(output).toBe(''); | ||
}); | ||
|
||
it('should return empty string when value is undefined', () => { | ||
const output = treeExportFormatter(1, 1, undefined, {} as Column, dataset[1], gridStub); | ||
expect(output).toBe(''); | ||
}); | ||
|
||
it('should return empty string when item is undefined', () => { | ||
const output = treeExportFormatter(1, 1, 'blah', {} as Column, undefined, gridStub); | ||
expect(output).toBe(''); | ||
}); | ||
|
||
it('should return a span without any icon and ', () => { | ||
jest.spyOn(gridStub, 'getData').mockReturnValue(dataViewStub); | ||
jest.spyOn(dataViewStub, 'getIdxById').mockReturnValue(1); | ||
jest.spyOn(dataViewStub, 'getItemByIdx').mockReturnValue(dataset[0]); | ||
|
||
const output = treeExportFormatter(1, 1, dataset[0]['firstName'], {} as Column, dataset[0], gridStub); | ||
expect(output).toBe(`John`); | ||
}); | ||
|
||
it('should return a span without any icon and 15px indentation of a tree level 1', () => { | ||
jest.spyOn(gridStub, 'getData').mockReturnValue(dataViewStub); | ||
jest.spyOn(dataViewStub, 'getIdxById').mockReturnValue(1); | ||
jest.spyOn(dataViewStub, 'getItemByIdx').mockReturnValue(dataset[1]); | ||
|
||
const output = treeExportFormatter(1, 1, dataset[1]['firstName'], {} as Column, dataset[1], gridStub); | ||
expect(output).toBe(`. Jane`); | ||
}); | ||
|
||
it('should return a span without any icon and 30px indentation of a tree level 2', () => { | ||
jest.spyOn(gridStub, 'getData').mockReturnValue(dataViewStub); | ||
jest.spyOn(dataViewStub, 'getIdxById').mockReturnValue(1); | ||
jest.spyOn(dataViewStub, 'getItemByIdx').mockReturnValue(dataset[1]); | ||
|
||
const output = treeExportFormatter(1, 1, dataset[2]['firstName'], {} as Column, dataset[2], gridStub); | ||
expect(output).toBe(`. Bob`); | ||
}); | ||
|
||
it('should return a span with expanded icon and 15px indentation of a tree level 1 when current item is greater than next item', () => { | ||
jest.spyOn(gridStub, 'getData').mockReturnValue(dataViewStub); | ||
jest.spyOn(dataViewStub, 'getIdxById').mockReturnValue(1); | ||
jest.spyOn(dataViewStub, 'getItemByIdx').mockReturnValue(dataset[2]); | ||
|
||
const output = treeExportFormatter(1, 1, dataset[1]['firstName'], {} as Column, dataset[1], gridStub); | ||
expect(output).toBe(`⮟ Jane`); | ||
}); | ||
|
||
it('should return a span with collapsed icon and 0px indentation of a tree level 0 when current item is lower than next item', () => { | ||
jest.spyOn(gridStub, 'getData').mockReturnValue(dataViewStub); | ||
jest.spyOn(dataViewStub, 'getIdxById').mockReturnValue(1); | ||
jest.spyOn(dataViewStub, 'getItemByIdx').mockReturnValue(dataset[1]); | ||
|
||
const output = treeExportFormatter(1, 1, dataset[3]['firstName'], {} as Column, dataset[3], gridStub); | ||
expect(output).toBe(`⮞ Barbara`); | ||
}); | ||
|
||
it('should execute "queryFieldNameGetterFn" callback to get field name to use when it is defined', () => { | ||
jest.spyOn(gridStub, 'getData').mockReturnValue(dataViewStub); | ||
jest.spyOn(dataViewStub, 'getIdxById').mockReturnValue(1); | ||
jest.spyOn(dataViewStub, 'getItemByIdx').mockReturnValue(dataset[1]); | ||
|
||
const mockColumn = { id: 'firstName', field: 'firstName', queryFieldNameGetterFn: (dataContext) => 'fullName' } as Column; | ||
const output = treeExportFormatter(1, 1, null, mockColumn as Column, dataset[3], gridStub); | ||
expect(output).toBe(`⮞ Barbara Cane`); | ||
}); | ||
|
||
it('should execute "queryFieldNameGetterFn" callback to get field name and also apply html encoding when output value includes a character that should be encoded', () => { | ||
jest.spyOn(gridStub, 'getData').mockReturnValue(dataViewStub); | ||
jest.spyOn(dataViewStub, 'getIdxById').mockReturnValue(2); | ||
jest.spyOn(dataViewStub, 'getItemByIdx').mockReturnValue(dataset[2]); | ||
|
||
const mockColumn = { id: 'firstName', field: 'firstName', queryFieldNameGetterFn: (dataContext) => 'fullName' } as Column; | ||
const output = treeExportFormatter(1, 1, null, mockColumn as Column, dataset[4], gridStub); | ||
expect(output).toBe(`⮞ Anonymous < Doe`); | ||
}); | ||
|
||
it('should execute "queryFieldNameGetterFn" callback to get field name, which has (.) dot notation reprensenting complex object', () => { | ||
jest.spyOn(gridStub, 'getData').mockReturnValue(dataViewStub); | ||
jest.spyOn(dataViewStub, 'getIdxById').mockReturnValue(1); | ||
jest.spyOn(dataViewStub, 'getItemByIdx').mockReturnValue(dataset[1]); | ||
|
||
const mockColumn = { id: 'zip', field: 'zip', queryFieldNameGetterFn: (dataContext) => 'address.zip' } as Column; | ||
const output = treeExportFormatter(1, 1, null, mockColumn as Column, dataset[3], gridStub); | ||
expect(output).toBe(`⮞ 444444`); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import { SlickDataView, Formatter } from './../interfaces/index'; | ||
import { addWhiteSpaces, getDescendantProperty } from '../services/utilities'; | ||
|
||
/** Formatter that must be use with a Tree Data column */ | ||
export const treeExportFormatter: Formatter = (_row, _cell, value, columnDef, dataContext, grid) => { | ||
const dataView = grid?.getData<SlickDataView>(); | ||
const gridOptions = grid?.getOptions(); | ||
const treeDataOptions = gridOptions?.treeDataOptions; | ||
const treeLevelPropName = treeDataOptions?.levelPropName ?? '__treeLevel'; | ||
const indentMarginLeft = treeDataOptions?.exportIndentMarginLeft ?? 4; | ||
const groupCollapsedSymbol = gridOptions?.excelExportOptions?.groupCollapsedSymbol ?? '⮞'; | ||
const groupExpandedSymbol = gridOptions?.excelExportOptions?.groupExpandedSymbol ?? '⮟'; | ||
let outputValue = value; | ||
|
||
if (typeof columnDef.queryFieldNameGetterFn === 'function') { | ||
const fieldName = columnDef.queryFieldNameGetterFn(dataContext); | ||
if (fieldName?.indexOf('.') >= 0) { | ||
outputValue = getDescendantProperty(dataContext, fieldName); | ||
} else { | ||
outputValue = dataContext.hasOwnProperty(fieldName) ? dataContext[fieldName] : value; | ||
} | ||
} | ||
if (outputValue === null || outputValue === undefined || dataContext === undefined) { | ||
return ''; | ||
} | ||
|
||
if (!dataContext.hasOwnProperty(treeLevelPropName)) { | ||
throw new Error('You must provide valid "treeDataOptions" in your Grid Options and it seems that there are no tree level found in this row'); | ||
} | ||
|
||
if (dataView?.getItemByIdx) { | ||
const identifierPropName = dataView.getIdPropertyName() || 'id'; | ||
const treeLevel = dataContext[treeLevelPropName] || 0; | ||
const spacer = addWhiteSpaces(indentMarginLeft * treeLevel); | ||
const idx = dataView.getIdxById(dataContext[identifierPropName]); | ||
const nextItemRow = dataView.getItemByIdx((idx || 0) + 1); | ||
|
||
if (nextItemRow?.[treeLevelPropName] > treeLevel) { | ||
if (dataContext.__collapsed) { | ||
return `${groupCollapsedSymbol} ${spacer} ${outputValue}`; | ||
} else { | ||
return `${groupExpandedSymbol} ${spacer} ${outputValue}`; | ||
} | ||
} | ||
return treeLevel === 0 ? outputValue : `.${spacer} ${outputValue}`; | ||
} | ||
return ''; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters