From db77d464ee37eda573351e89d4c5acc9b5648649 Mon Sep 17 00:00:00 2001 From: Ghislain B Date: Wed, 27 Jan 2021 18:44:38 -0500 Subject: [PATCH] feat(formatters): add grid option to auto add custom editor formatter (#248) * feat(formatters): add grid option to auto add custom editor formatter --- .../src/examples/example12.ts | 32 ++--------------- .../src/interfaces/gridOption.interface.ts | 9 +++++ .../__tests__/slick-vanilla-grid.spec.ts | 25 ++++++++++++- .../__tests__/slick-vanilla-utilities.spec.ts | 36 +++++++++++++++++++ .../components/slick-vanilla-grid-bundle.ts | 11 ++++++ .../src/components/slick-vanilla-utilities.ts | 25 +++++++++++++ 6 files changed, 107 insertions(+), 31 deletions(-) create mode 100644 packages/vanilla-bundle/src/components/__tests__/slick-vanilla-utilities.spec.ts create mode 100644 packages/vanilla-bundle/src/components/slick-vanilla-utilities.ts diff --git a/examples/webpack-demo-vanilla-bundle/src/examples/example12.ts b/examples/webpack-demo-vanilla-bundle/src/examples/example12.ts index f6aa0a25f..2575f19ec 100644 --- a/examples/webpack-demo-vanilla-bundle/src/examples/example12.ts +++ b/examples/webpack-demo-vanilla-bundle/src/examples/example12.ts @@ -71,7 +71,7 @@ function checkItemIsEditable(dataContext, columnDef, grid) { return isEditable; } -const customEditableInputFormatter = (_row, _cell, value, columnDef, dataContext, grid) => { +const customEditableInputFormatter: Formatter = (_row, _cell, value, columnDef, dataContext, grid) => { const isEditableLine = checkItemIsEditable(dataContext, columnDef, grid); value = (value === null || value === undefined) ? '' : value; return isEditableLine ? `
${value}
` : value; @@ -351,14 +351,12 @@ export class Example12 { }, ]; - // automatically add a Custom Formatter with blue background for any Editable Fields - this.autoAddCustomEditorFormatter(this.columnDefinitions, customEditableInputFormatter); - this.gridOptions = { useSalesforceDefaultGridOptions: true, datasetIdPropertyName: 'id', eventNamingStyle: EventNamingStyle.lowerCase, editable: true, + autoAddCustomEditorFormatter: customEditableInputFormatter, enableAddRow: true, // <-- this flag is required to work with the (create & clone) modal types enableCellNavigation: true, asyncEditorLoading: false, @@ -577,32 +575,6 @@ export class Example12 { } } - /** - * Instead of manually adding a Custom Formatter on every column definition that is editable, let's do it in an automated way - * We'll loop through all column definitions and add a Formatter (blue background) when necessary - * Note however that if there's already a Formatter on that column definition, we need to turn it into a Formatters.multiple - */ - autoAddCustomEditorFormatter(columnDefinitions: Column[], customFormatter: Formatter) { - if (Array.isArray(columnDefinitions)) { - for (const columnDef of columnDefinitions) { - if (columnDef.editor) { - if (columnDef.formatter && columnDef.formatter !== Formatters.multiple) { - const prevFormatter = columnDef.formatter; - columnDef.formatter = Formatters.multiple; - columnDef.params = { ...columnDef.params, formatters: [prevFormatter, customFormatter] }; - } else if (columnDef.formatter && columnDef.formatter === Formatters.multiple) { - if (!columnDef.params) { - columnDef.params = {}; - } - columnDef.params.formatters = [...columnDef.params.formatters, customFormatter]; - } else { - columnDef.formatter = customFormatter; - } - } - } - } - } - toggleGridEditReadonly() { // first need undo all edits this.undoAllEdits(); diff --git a/packages/common/src/interfaces/gridOption.interface.ts b/packages/common/src/interfaces/gridOption.interface.ts index 334c90f38..bee23f1d7 100644 --- a/packages/common/src/interfaces/gridOption.interface.ts +++ b/packages/common/src/interfaces/gridOption.interface.ts @@ -15,6 +15,7 @@ import { ExcelCopyBufferOption, ExcelExportOption, ExternalResource, + Formatter, FormatterOption, GridMenu, GridState, @@ -51,6 +52,14 @@ export interface GridOption { /** Defaults to 40, which is the delay before the asynchronous post renderer start cleanup execution */ asyncPostRenderCleanupDelay?: number; + /** + * Automatically add a Custom Formatter on all column definitions that have an Editor. + * Instead of manually adding a Custom Formatter on every column definitions that are editables, let's ask the system to do it in an easier automated way. + * It will loop through all column definitions and add an Custom Editor Formatter when necessary, + * also note that if there's already a Formatter on the column definition it will automatically use the Formatters.multiple and add the custom formatter into the `params: formatters: {}}` + */ + autoAddCustomEditorFormatter?: Formatter; + /** Defaults to false, when enabled will try to commit the current edit without focusing on the next row. If a custom editor is implemented and the grid cannot auto commit, you must use this option to implement it yourself */ autoCommitEdit?: boolean; diff --git a/packages/vanilla-bundle/src/components/__tests__/slick-vanilla-grid.spec.ts b/packages/vanilla-bundle/src/components/__tests__/slick-vanilla-grid.spec.ts index 84905940d..d74b1feb8 100644 --- a/packages/vanilla-bundle/src/components/__tests__/slick-vanilla-grid.spec.ts +++ b/packages/vanilla-bundle/src/components/__tests__/slick-vanilla-grid.spec.ts @@ -35,11 +35,13 @@ import { TreeDataService, TranslaterService, SlickEditorLock, + Formatter, } from '@slickgrid-universal/common'; import { GraphqlService, GraphqlPaginatedResult, GraphqlServiceApi, GraphqlServiceOption } from '@slickgrid-universal/graphql'; import { SlickCompositeEditorComponent } from '@slickgrid-universal/composite-editor-component'; import * as backendUtilities from '@slickgrid-universal/common/dist/commonjs/services/backend-utilities'; import * as utilities from '@slickgrid-universal/common/dist/commonjs/services/utilities'; +import * as slickVanillaUtilities from '../slick-vanilla-utilities'; import { SlickVanillaGridBundle } from '../slick-vanilla-grid-bundle'; import { EventPubSubService } from '../../services/eventPubSub.service'; @@ -55,10 +57,12 @@ const mockExecuteBackendProcess = jest.fn(); const mockRefreshBackendDataset = jest.fn(); const mockBackendError = jest.fn(); const mockConvertParentChildArray = jest.fn(); +const mockAutoAddCustomEditorFormatter = jest.fn(); (backendUtilities.executeBackendProcessesCallback as any) = mockExecuteBackendProcess; (backendUtilities.refreshBackendDataset as any) = mockRefreshBackendDataset; (backendUtilities.onBackendError as any) = mockBackendError; +(slickVanillaUtilities.autoAddEditorFormatterToColumnsWithEditor as any) = mockAutoAddCustomEditorFormatter; declare const Slick: any; const slickEventHandler = new MockSlickEventHandler() as unknown as SlickEventHandler; @@ -377,6 +381,12 @@ describe('Slick-Vanilla-Grid-Bundle Component instantiated via Constructor', () }); describe('initialization method', () => { + const customEditableInputFormatter: Formatter = (_row, _cell, value, columnDef) => { + const isEditableLine = !!columnDef.editor; + value = (value === null || value === undefined) ? '' : value; + return isEditableLine ? `
${value}
` : value; + }; + afterEach(() => { jest.clearAllMocks(); }); @@ -420,6 +430,17 @@ describe('Slick-Vanilla-Grid-Bundle Component instantiated via Constructor', () expect(resizerSpy).toHaveBeenCalledWith(); }); + describe('autoAddCustomEditorFormatter grid option', () => { + it('should initialize the grid and automatically add custom Editor Formatter when provided in the grid options', () => { + const autoAddFormatterSpy = jest.spyOn(slickVanillaUtilities, 'autoAddEditorFormatterToColumnsWithEditor'); + + component.gridOptions = { autoAddCustomEditorFormatter: customEditableInputFormatter }; + component.initialization(divContainer, slickEventHandler); + + expect(autoAddFormatterSpy).toHaveBeenCalledWith([{ id: 'name', field: 'name', editor: undefined, internalColumnEditor: {} }], customEditableInputFormatter); + }); + }); + describe('columns definitions changed', () => { it('should expect "translateColumnHeaders" being called when "enableTranslate" is set', () => { const translateSpy = jest.spyOn(extensionServiceStub, 'translateColumnHeaders'); @@ -448,10 +469,11 @@ describe('Slick-Vanilla-Grid-Bundle Component instantiated via Constructor', () const renderSpy = jest.spyOn(extensionServiceStub, 'renderColumnHeaders'); const eventSpy = jest.spyOn(eventPubSubService, 'publish'); const addPubSubSpy = jest.spyOn(component.translaterService, 'addPubSubMessaging'); + const autoAddFormatterSpy = jest.spyOn(slickVanillaUtilities, 'autoAddEditorFormatterToColumnsWithEditor'); const mockColDefs = [{ id: 'name', field: 'name', editor: undefined, internalColumnEditor: {} }]; + component.gridOptions = { enableTranslate: false, autoAddCustomEditorFormatter: customEditableInputFormatter }; component.columnDefinitions = mockColDefs; - component.gridOptions = { enableTranslate: false }; component.initialization(divContainer, slickEventHandler); expect(translateSpy).not.toHaveBeenCalled(); @@ -460,6 +482,7 @@ describe('Slick-Vanilla-Grid-Bundle Component instantiated via Constructor', () expect(eventSpy).toHaveBeenCalledTimes(4); expect(updateSpy).toHaveBeenCalledWith(mockColDefs); expect(renderSpy).toHaveBeenCalledWith(mockColDefs, true); + expect(autoAddFormatterSpy).toHaveBeenCalledWith([{ id: 'name', field: 'name', editor: undefined, internalColumnEditor: {} }], customEditableInputFormatter); }); }); diff --git a/packages/vanilla-bundle/src/components/__tests__/slick-vanilla-utilities.spec.ts b/packages/vanilla-bundle/src/components/__tests__/slick-vanilla-utilities.spec.ts new file mode 100644 index 000000000..8a68099e8 --- /dev/null +++ b/packages/vanilla-bundle/src/components/__tests__/slick-vanilla-utilities.spec.ts @@ -0,0 +1,36 @@ +import { Column, Editors, Formatter, Formatters } from '@slickgrid-universal/common'; +import { autoAddEditorFormatterToColumnsWithEditor } from '../slick-vanilla-utilities'; + + +describe('Slick-Vanilla-Grid-Bundle / Utilies', () => { + describe('autoAddEditorFormatterToColumnsWithEditor', () => { + let columnDefinitions: Column[]; + const customEditableInputFormatter: Formatter = (_row, _cell, value, columnDef) => { + const isEditableLine = !!columnDef.editor; + value = (value === null || value === undefined) ? '' : value; + return isEditableLine ? `
${value}
` : value; + }; + + beforeEach(() => { + columnDefinitions = [ + { id: 'firstName', field: 'firstName', editor: { model: Editors.text } }, + { id: 'lastName', field: 'lastName', editor: { model: Editors.text }, formatter: Formatters.multiple, params: { formatters: [Formatters.italic, Formatters.bold] } }, + { id: 'age', field: 'age', type: 'number', formatter: Formatters.multiple }, + { id: 'address', field: 'address.street', editor: { model: Editors.longText }, formatter: Formatters.complexObject }, + { id: 'zip', field: 'address.zip', type: 'number', formatter: Formatters.complexObject }, + ]; + }); + + it('should have custom editor formatter with correct structure', () => { + autoAddEditorFormatterToColumnsWithEditor(columnDefinitions, customEditableInputFormatter); + + expect(columnDefinitions).toEqual([ + { id: 'firstName', field: 'firstName', editor: { model: Editors.text }, formatter: customEditableInputFormatter }, + { id: 'lastName', field: 'lastName', editor: { model: Editors.text }, formatter: Formatters.multiple, params: { formatters: [Formatters.italic, Formatters.bold, customEditableInputFormatter] } }, + { id: 'age', field: 'age', type: 'number', formatter: Formatters.multiple }, + { id: 'address', field: 'address.street', editor: { model: Editors.longText }, formatter: Formatters.multiple, params: { formatters: [Formatters.complexObject, customEditableInputFormatter] } }, + { id: 'zip', field: 'address.zip', type: 'number', formatter: Formatters.complexObject }, + ]); + }); + }); +}); \ No newline at end of file diff --git a/packages/vanilla-bundle/src/components/slick-vanilla-grid-bundle.ts b/packages/vanilla-bundle/src/components/slick-vanilla-grid-bundle.ts index cc99e0a94..e2bb1d84b 100644 --- a/packages/vanilla-bundle/src/components/slick-vanilla-grid-bundle.ts +++ b/packages/vanilla-bundle/src/components/slick-vanilla-grid-bundle.ts @@ -83,6 +83,7 @@ import { SlickFooterComponent } from './slick-footer.component'; import { SlickPaginationComponent } from './slick-pagination.component'; import { SlickerGridInstance } from '../interfaces/slickerGridInstance.interface'; import { UniversalContainerService } from '../services/universalContainer.service'; +import { autoAddEditorFormatterToColumnsWithEditor } from './slick-vanilla-utilities'; // using external non-typed js libraries declare const Slick: SlickNamespace; @@ -495,6 +496,11 @@ export class SlickVanillaGridBundle { // then take back "editor.model" and make it the new "editor" so that SlickGrid Editor Factory still works this._columnDefinitions = this.swapInternalEditorToSlickGridFactoryEditor(this._columnDefinitions); + // if the user wants to automatically add a Custom Editor Formatter, we need to call the auto add function again + if (this._gridOptions.autoAddCustomEditorFormatter) { + autoAddEditorFormatterToColumnsWithEditor(this._columnDefinitions, this._gridOptions.autoAddCustomEditorFormatter); + } + // save reference for all columns before they optionally become hidden/visible this.sharedService.allColumns = this._columnDefinitions; this.sharedService.visibleColumns = this._columnDefinitions; @@ -1071,6 +1077,11 @@ export class SlickVanillaGridBundle { // map/swap the internal library Editor to the SlickGrid Editor factory newColumnDefinitions = this.swapInternalEditorToSlickGridFactoryEditor(newColumnDefinitions); + // if the user wants to automatically add a Custom Editor Formatter, we need to call the auto add function again + if (this._gridOptions.autoAddCustomEditorFormatter) { + autoAddEditorFormatterToColumnsWithEditor(newColumnDefinitions, this._gridOptions.autoAddCustomEditorFormatter); + } + if (this._gridOptions.enableTranslate) { this.extensionService.translateColumnHeaders(false, newColumnDefinitions); } else { diff --git a/packages/vanilla-bundle/src/components/slick-vanilla-utilities.ts b/packages/vanilla-bundle/src/components/slick-vanilla-utilities.ts new file mode 100644 index 000000000..a41c22359 --- /dev/null +++ b/packages/vanilla-bundle/src/components/slick-vanilla-utilities.ts @@ -0,0 +1,25 @@ +import { Column, Formatter, Formatters } from '@slickgrid-universal/common'; + +/** + * Automatically add a Custom Formatter on all column definitions that have an Editor. + * Instead of manually adding a Custom Formatter on every column definitions that are editables, let's ask the system to do it in an easier automated way. + * It will loop through all column definitions and add an Custom Editor Formatter when necessary, + * also note that if there's already a Formatter on the column definition it will automatically use the Formatters.multiple and add the custom formatter into the `params: formatters: {}}` + */ +export function autoAddEditorFormatterToColumnsWithEditor(columnDefinitions: Column[], customEditableFormatter: Formatter) { + if (Array.isArray(columnDefinitions)) { + for (const columnDef of columnDefinitions) { + if (columnDef.editor) { + if (columnDef.formatter && columnDef.formatter !== Formatters.multiple) { + const prevFormatter = columnDef.formatter; + columnDef.formatter = Formatters.multiple; + columnDef.params = { ...columnDef.params, formatters: [prevFormatter, customEditableFormatter] }; + } else if (columnDef.formatter && columnDef.formatter === Formatters.multiple && columnDef.params) { + columnDef.params.formatters = [...columnDef.params.formatters, customEditableFormatter]; + } else { + columnDef.formatter = customEditableFormatter; + } + } + } + } +}