Skip to content

Commit

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

- 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 committed Jul 18, 2020
1 parent a18609b commit cc8c31d
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 31 deletions.
4 changes: 3 additions & 1 deletion src/app/examples/grid-editor.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,9 @@ export class GridEditorComponent implements OnInit {
filter: { model: Filters.compoundDate },
formatter: Formatters.dateIso,
exportWithFormatter: true,
type: FieldType.date,
type: FieldType.date, // dataset cell input format
// outputType: FieldType.dateUs, // date picker format
saveOutputType: FieldType.dateUtc, // save output date formattype: FieldType.date,
editor: {
model: Editors.date,
// override any of the Flatpickr options through "editorOptions"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -210,36 +210,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, 3, 5, 16, 11, 33, 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
45 changes: 26 additions & 19 deletions src/app/modules/angular-slickgrid/editors/dateEditor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ export class DateEditor implements Editor {
const placeholder = this.columnEditor && this.columnEditor.placeholder || '';
const title = this.columnEditor && this.columnEditor.title || '';
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._translate && this._translate.currentLang || this.gridOptions.locale || 'en';
if (currentLocale && currentLocale.length > 2) {
Expand All @@ -106,12 +107,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 @@ -174,26 +173,33 @@ export class DateEditor implements Editor {

applyValue(item: any, state: any) {
const fieldName = this.columnDef && this.columnDef.field;
const outputTypeFormat = mapMomentDateFormatWithFieldType((this.columnDef && (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() : '';

// set the new value to the item datacontext
if (isComplexObject) {
setDeepValue(item, fieldName, newValue);
} else {
item[fieldName] = newValue;
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).format(saveTypeFormat) : '';

// set the new value to the item datacontext
if (isComplexObject) {
setDeepValue(item, fieldName, newValue);
} else {
item[fieldName] = newValue;
}
}
}

isValueChanged() {
const elmValue = this._$input.val();
const inputFormat = mapMomentDateFormatWithFieldType(this.columnDef && 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 @@ -232,8 +238,9 @@ export class DateEditor implements Editor {
return '';
}

const inputFormat = mapMomentDateFormatWithFieldType(this.columnDef && 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 src/app/modules/angular-slickgrid/models/column.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,9 +169,18 @@ export interface Column<T = any> {
/** an event that can be used for triggering an action after a cell click */
onCellClick?: (e: KeyboardEvent | MouseEvent, 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

0 comments on commit cc8c31d

Please sign in to comment.