Skip to content

Commit

Permalink
feat(formatters): add grid option to auto add custom editor formatter (
Browse files Browse the repository at this point in the history
…#248)

* feat(formatters): add grid option to auto add custom editor formatter
  • Loading branch information
ghiscoding authored Jan 27, 2021
1 parent e466f62 commit db77d46
Show file tree
Hide file tree
Showing 6 changed files with 107 additions and 31 deletions.
32 changes: 2 additions & 30 deletions examples/webpack-demo-vanilla-bundle/src/examples/example12.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 ? `<div class="editing-field">${value}</div>` : value;
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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();
Expand Down
9 changes: 9 additions & 0 deletions packages/common/src/interfaces/gridOption.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
ExcelCopyBufferOption,
ExcelExportOption,
ExternalResource,
Formatter,
FormatterOption,
GridMenu,
GridState,
Expand Down Expand Up @@ -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;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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;
Expand Down Expand Up @@ -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 ? `<div class="editing-field">${value}</div>` : value;
};

afterEach(() => {
jest.clearAllMocks();
});
Expand Down Expand Up @@ -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');
Expand Down Expand Up @@ -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();
Expand All @@ -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);
});
});

Expand Down
Original file line number Diff line number Diff line change
@@ -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 ? `<div class="editing-field">${value}</div>` : 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 },
]);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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 {
Expand Down
25 changes: 25 additions & 0 deletions packages/vanilla-bundle/src/components/slick-vanilla-utilities.ts
Original file line number Diff line number Diff line change
@@ -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;
}
}
}
}
}

0 comments on commit db77d46

Please sign in to comment.