Skip to content

Commit

Permalink
fix(editors): add saveOutputType to finally have proper save format (#17
Browse files Browse the repository at this point in the history
)

* fix(editors): add saveOutputType to finally have proper editor save format output

- we now have 3 available types to use with the Date Editors formatting
1. `type` (data input format),
2. `outputType` (picker display format)
3. `saveOutputType` (save format)
  • Loading branch information
ghiscoding authored Jul 18, 2020
1 parent 42e7e3d commit ebfd715
Show file tree
Hide file tree
Showing 12 changed files with 88 additions and 36 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
"mocha": "^8.0.1",
"mochawesome": "^6.1.1",
"ts-jest": "^26.1.1",
"typescript": "^3.9.6"
"typescript": "^3.9.7"
},
"engines": {
"node": ">=12.13.1",
Expand Down
3 changes: 1 addition & 2 deletions packages/common/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,7 @@
"nodemon": "^2.0.4",
"npm-run-all": "^4.1.5",
"postcss-cli": "^7.1.1",
"rimraf": "^3.0.2",
"typescript": "^3.9.6"
"rimraf": "^3.0.2"
},
"engines": {
"node": ">=12.13.1",
Expand Down
48 changes: 38 additions & 10 deletions packages/common/src/editors/__tests__/dateEditor.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,36 +195,64 @@ describe('DateEditor', () => {

expect(editor.isValueChanged()).toBe(false);
});

it('should return False when input date is invalid', () => {
mockItemData = { id: 1, startDate: '1900-02-32', isActive: true };
mockColumn.type = FieldType.dateUs;
mockColumn.internalColumnEditor.editorOptions = { allowInput: true }; // change to allow input value only for testing purposes

editor = new DateEditor(editorArguments);
editor.loadValue(mockItemData);
const editorInputElm = divContainer.querySelector<HTMLInputElement>('input.flatpickr-alt-input');
editorInputElm.value = '1900-02-32';
editorInputElm.dispatchEvent(new (window.window as any).KeyboardEvent('keydown', { keyCode: 13, bubbles: true, cancelable: true }));

expect(editor.isValueChanged()).toBe(false);
});
});

describe('applyValue method', () => {
it('should apply the value to the startDate property when it passes validation', () => {
it('should apply the value to the startDate property with ISO format when no "outputType" is defined and when it passes validation', () => {
mockColumn.internalColumnEditor.validator = null;
mockColumn.type = FieldType.dateTimeIsoAmPm;
mockColumn.type = FieldType.date;
mockItemData = { id: 1, startDate: '2001-04-05T11:33:42.000Z', isActive: true };

const newDate = '2001-01-02T16:02:02.000+05:00';
const newDate = new Date(Date.UTC(2001, 0, 2, 16, 2, 2, 0));
editor = new DateEditor(editorArguments);
editor.applyValue(mockItemData, newDate);

expect(mockItemData).toEqual({ id: 1, startDate: moment(newDate, 'YYYY-MM-DD hh:mm:ss a').toDate(), isActive: true });
expect(mockItemData).toEqual({ id: 1, startDate: moment(newDate).format('YYYY-MM-DD'), isActive: true });
});

it('should apply the value to the startDate property with a field having dot notation (complex object) that passes validation', () => {
it('should apply the value to the startDate property with "outputType" format with a field having dot notation (complex object) that passes validation', () => {
mockColumn.internalColumnEditor.validator = null;
mockColumn.type = FieldType.dateTimeIsoAmPm;
mockColumn.type = FieldType.date;
mockColumn.outputType = FieldType.dateTimeIsoAmPm;
mockColumn.field = 'employee.startDate';
mockItemData = { id: 1, employee: { startDate: new Date(Date.UTC(2001, 0, 2, 16, 2, 2, 0)) }, isActive: true };
mockItemData = { id: 1, employee: { startDate: '2001-04-05T11:33:42.000Z' }, isActive: true };

const newDate = new Date(Date.UTC(2001, 0, 2, 16, 2, 2, 0));
editor = new DateEditor(editorArguments);
editor.applyValue(mockItemData, newDate);

expect(mockItemData).toEqual({ id: 1, employee: { startDate: moment(newDate).format('YYYY-MM-DD hh:mm:ss a') }, isActive: true });
});

it('should apply the value to the startDate property with output format defined by "saveOutputType" when it passes validation', () => {
mockColumn.internalColumnEditor.validator = null;
mockColumn.type = FieldType.date;
mockColumn.saveOutputType = FieldType.dateTimeIsoAmPm;
mockItemData = { id: 1, startDate: '2001-04-05T11:33:42.000Z', isActive: true };

const newDate = '2001-01-02T16:02:02.000+05:00';
const newDate = new Date(Date.UTC(2001, 0, 2, 16, 2, 2, 0));
editor = new DateEditor(editorArguments);
editor.applyValue(mockItemData, newDate);

expect(mockItemData).toEqual({ id: 1, employee: { startDate: moment(newDate, 'YYYY-MM-DD hh:mm:ss a').toDate() }, isActive: true });
expect(mockItemData).toEqual({ id: 1, startDate: moment(newDate).format('YYYY-MM-DD hh:mm:ss a'), isActive: true });
});

it('should return item data with an empty string in its value when it fails the custom validation', () => {
mockColumn.internalColumnEditor.validator = (value: any, args: EditorArgs) => {
mockColumn.internalColumnEditor.validator = (value: any) => {
if (value.length > 10) {
return { valid: false, msg: 'Must be at least 10 chars long.' };
}
Expand Down
21 changes: 13 additions & 8 deletions packages/common/src/editors/dateEditor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ export class DateEditor implements Editor {
const title = this.columnEditor && this.columnEditor.title || '';
const gridOptions = (this.args.grid.getOptions() || {}) as GridOption;
this.defaultDate = (this.args.item) ? this.args.item[this.columnDef.field] : null;
const inputFormat = mapFlatpickrDateFormatWithFieldType(this.columnDef.type || FieldType.dateUtc);
const outputFormat = mapFlatpickrDateFormatWithFieldType(this.columnDef.outputType || this.columnDef.type || FieldType.dateUtc);
let currentLocale = this._translaterService && this._translaterService.getCurrentLocale && this._translaterService.getCurrentLocale() || gridOptions.locale || 'en';
if (currentLocale.length > 2) {
Expand All @@ -105,12 +106,10 @@ export class DateEditor implements Editor {
defaultDate: this.defaultDate as string,
altInput: true,
altFormat: outputFormat,
dateFormat: outputFormat,
dateFormat: inputFormat,
closeOnSelect: false,
locale: (currentLocale !== 'en') ? this.loadFlatpickrLocale(currentLocale) : 'en',
onChange: (selectedDates: Date[] | Date, dateStr: string, instance: any) => {
this.save();
},
onChange: () => this.save(),
errorHandler: () => {
// do nothing, Flatpickr is a little too sensitive and will throw an error when provided date is lower than minDate so just disregard the error completely
}
Expand Down Expand Up @@ -176,11 +175,12 @@ export class DateEditor implements Editor {
const fieldName = this.columnDef && this.columnDef.field;
if (fieldName !== undefined) {
const outputTypeFormat = mapMomentDateFormatWithFieldType((this.columnDef && (this.columnDef.outputType || this.columnDef.type)) || FieldType.dateUtc);
const saveTypeFormat = mapMomentDateFormatWithFieldType((this.columnDef && (this.columnDef.saveOutputType || this.columnDef.outputType || this.columnDef.type)) || FieldType.dateUtc);
const isComplexObject = fieldName.indexOf('.') > 0; // is the field a complex object, "address.streetNumber"

// validate the value before applying it (if not valid we'll set an empty string)
const validation = this.validate(state);
const newValue = (validation && validation.valid) ? moment(state, outputTypeFormat).toDate() : '';
const newValue = (validation && validation.valid) ? moment(state, outputTypeFormat).format(saveTypeFormat) : '';

// set the new value to the item datacontext
if (isComplexObject) {
Expand All @@ -193,9 +193,13 @@ export class DateEditor implements Editor {

isValueChanged(): boolean {
const elmValue = this._$input.val();
const inputFormat = mapMomentDateFormatWithFieldType(this.columnDef?.type || FieldType.dateIso);
const outputTypeFormat = mapMomentDateFormatWithFieldType((this.columnDef && (this.columnDef.outputType || this.columnDef.type)) || FieldType.dateUtc);
const elmDateStr = elmValue ? moment(elmValue, outputTypeFormat, false).format(outputTypeFormat) : '';
const orgDateStr = this.originalDate ? moment(this.originalDate, outputTypeFormat, false).format(outputTypeFormat) : '';
const elmDateStr = elmValue ? moment(elmValue, inputFormat, false).format(outputTypeFormat) : '';
const orgDateStr = this.originalDate ? moment(this.originalDate, inputFormat, false).format(outputTypeFormat) : '';
if (elmDateStr === 'Invalid date' || orgDateStr === 'Invalid date') {
return false;
}

return (!(elmDateStr === '' && orgDateStr === '')) && (elmDateStr !== orgDateStr);
}
Expand Down Expand Up @@ -236,8 +240,9 @@ export class DateEditor implements Editor {
return '';
}

const inputFormat = mapMomentDateFormatWithFieldType(this.columnDef?.type || FieldType.dateIso);
const outputTypeFormat = mapMomentDateFormatWithFieldType((this.columnDef && (this.columnDef.outputType || this.columnDef.type)) || FieldType.dateIso);
const value = moment(domValue, outputTypeFormat, false).format(outputTypeFormat);
const value = moment(domValue, inputFormat, false).format(outputTypeFormat);

return value;
}
Expand Down
11 changes: 10 additions & 1 deletion packages/common/src/interfaces/column.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,9 +171,18 @@ export interface Column<T = any> {
/** an event that can be used for executing an action after a cell click */
onCellClick?: (e: SlickEventData, args: OnEventArgs) => void;

/** column output type */
/**
* Column output type (e.g. Date Picker, the output format that we will see in the picker)
* NOTE: this is only currently used by the Editors/Filters with a Date Picker
*/
outputType?: FieldType;

/**
* Column Editor save format type (e.g. which date format to use when saving after choosing a date from the Date Editor picker)
* NOTE: this is only currently used by the Date Editor (date picker)
*/
saveOutputType?: FieldType;

/** if you want to pass custom paramaters to your Formatter/Editor or anything else */
params?: any | any[];

Expand Down
3 changes: 1 addition & 2 deletions packages/excel-export/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@
"devDependencies": {
"cross-env": "^7.0.2",
"npm-run-all": "^4.1.5",
"rimraf": "^3.0.2",
"typescript": "^3.9.6"
"rimraf": "^3.0.2"
}
}
3 changes: 1 addition & 2 deletions packages/file-export/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@
"@types/text-encoding-utf-8": "^1.0.1",
"cross-env": "^7.0.2",
"npm-run-all": "^4.1.5",
"rimraf": "^3.0.2",
"typescript": "^3.9.6"
"rimraf": "^3.0.2"
}
}
1 change: 0 additions & 1 deletion packages/vanilla-bundle/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@
"npm-run-all": "^4.1.5",
"rimraf": "^3.0.2",
"ts-loader": "^7.0.5",
"typescript": "^3.9.6",
"webpack": "^4.43.0"
}
}
2 changes: 2 additions & 0 deletions packages/vanilla-bundle/src/salesforce-global-grid-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { GridOption, EventNamingStyle } from '@slickgrid-universal/common';

/** Global Grid Options Defaults for Salesforce */
export const SalesforceGlobalGridOptions: GridOption = {
autoEdit: true, // true single click (false for double-click)
autoCommitEdit: true,
datasetIdPropertyName: 'Id',
enableExport: true,
enableDeepCopyDatasetOnPageLoad: true,
Expand Down
1 change: 0 additions & 1 deletion packages/web-demo-vanilla-bundle/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@
"style-loader": "^1.2.1",
"ts-loader": "^7.0.5",
"ts-node": "^8.10.2",
"typescript": "^3.9.6",
"url-loader": "^4.1.0",
"webpack": "^4.43.0",
"webpack-cli": "^3.3.12",
Expand Down
21 changes: 17 additions & 4 deletions packages/web-demo-vanilla-bundle/src/examples/example07.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ export class Example7 {
this.initializeGrid();
this.dataset = this.loadData(500);
const gridContainerElm = document.querySelector<HTMLDivElement>(`.grid7`);
gridContainerElm.addEventListener('oncellchange', this.handleOnCellChange.bind(this));
gridContainerElm.addEventListener('onvalidationerror', this.handleValidationError.bind(this));
gridContainerElm.addEventListener('onslickergridcreated', this.handleOnSlickerGridCreated.bind(this));
this.slickgridLwc = new Slicker.GridBundle(gridContainerElm, this.columnDefinitions, { ...ExampleGridOptions, ...this.gridOptions }, this.dataset);
}
Expand All @@ -47,11 +49,11 @@ export class Example7 {
{ id: 'percentComplete', name: '% Complete', field: 'percentComplete', sortable: true, editor: { model: Editors.slider, minValue: 0, maxValue: 100, }, },
{
id: 'start', name: 'Start', field: 'start', formatter: Formatters.dateIso,
editor: { model: Editors.date }, type: FieldType.dateIso, outputType: FieldType.dateIso,
editor: { model: Editors.date }, type: FieldType.date,/* outputType: FieldType.dateUs, */ saveOutputType: FieldType.dateUtc,
},
{
id: 'finish', name: 'Finish', field: 'finish', formatter: Formatters.dateIso,
editor: { model: Editors.date }, type: FieldType.dateIso, outputType: FieldType.dateIso,
editor: { model: Editors.date }, type: FieldType.dateIso, saveOutputType: FieldType.dateUtc,
},
{ id: 'effort-driven', name: 'Completed', field: 'effortDriven', formatter: Formatters.checkmarkMaterial, editor: { model: Editors.checkbox } }
];
Expand Down Expand Up @@ -114,8 +116,8 @@ export class Example7 {
title: 'Task ' + i,
duration: Math.round(Math.random() * 25),
percentComplete: Math.round(Math.random() * 100),
start: '2009-01-01',
finish: '2009-01-05',
start: new Date(2009, 0, 1),
finish: new Date(2009, 0, 5),
effortDriven: (i % 5 === 0)
};
}
Expand Down Expand Up @@ -162,6 +164,17 @@ export class Example7 {
this.slickgridLwc.dataset = this.dataset; // update dataset and re-render the grid
}

handleOnCellChange(event) {
console.log('onCellChanged', event.detail, event.detail.args.item.start);
}

handleValidationError(event) {
console.log('handleValidationError', event.detail);
const args = event.detail && event.detail.args;
if (args.validationResults) {
alert(args.validationResults.msg);
}
}

handleOnSlickerGridCreated(event) {
this.slickerGridInstance = event && event.detail;
Expand Down
8 changes: 4 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -11913,10 +11913,10 @@ typedarray@^0.0.6:
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=

typescript@^3.9.6:
version "3.9.6"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.6.tgz#8f3e0198a34c3ae17091b35571d3afd31999365a"
integrity sha512-Pspx3oKAPJtjNwE92YS05HQoY7z2SFyOpHo9MqJor3BXAGNaPUs83CuVp9VISFkSjyRfiTpmKuAYGJB7S7hOxw==
typescript@^3.9.7:
version "3.9.7"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.7.tgz#98d600a5ebdc38f40cb277522f12dc800e9e25fa"
integrity sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==

uglify-js@^3.1.4:
version "3.8.0"
Expand Down

0 comments on commit ebfd715

Please sign in to comment.