Skip to content

Commit

Permalink
fix(editors): Composite Editor modal compo should work w/complex obje…
Browse files Browse the repository at this point in the history
…cts (#298)

- the composite editor component was partially working with complex object and this PR adds all missing piece for it to work entirely
- we should be able to call `changeFormInputValue` with column and/or columnId and assume that the formValues will still be updated even with complex object(s)
- add a new `changeFormValue` to update the `formValues` object directly without passing by an editor
- also add a new and small dependency `deep-assign` since it was discovered that we can use spread to do deep assignment, this small lib does it nicely, else expose it through the Slicker.Utilities on the Vanilla Bundle
  • Loading branch information
ghiscoding authored Mar 30, 2021
1 parent 78f2e18 commit 721a6c5
Show file tree
Hide file tree
Showing 23 changed files with 264 additions and 80 deletions.
1 change: 1 addition & 0 deletions packages/common/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
"not dead"
],
"dependencies": {
"assign-deep": "^1.0.1",
"dequal": "^2.0.2",
"dompurify": "^2.2.7",
"flatpickr": "^4.6.9",
Expand Down
22 changes: 15 additions & 7 deletions packages/common/src/editors/autoCompleteEditor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,11 @@ export class AutoCompleteEditor implements Editor {
return customStructure;
}

/** Getter for the item data context object */
get dataContext(): any {
return this.args.item;
}

get editorOptions(): AutocompleteOption {
return this.columnEditor?.editorOptions || {};
}
Expand Down Expand Up @@ -225,7 +230,7 @@ export class AutoCompleteEditor implements Editor {
return this._$input.val();
}

setValue(inputValue: any, isApplyingValue = false) {
setValue(inputValue: any, isApplyingValue = false, triggerOnCompositeEditorChange = true) {
let label = inputValue;
// if user provided a custom structure, we will serialize the value returned from the object with custom structure
if (inputValue && inputValue.hasOwnProperty(this.labelName)) {
Expand All @@ -242,7 +247,7 @@ export class AutoCompleteEditor implements Editor {

// if it's set by a Composite Editor, then also trigger a change for it
const compositeEditorOptions = this.args.compositeEditorOptions;
if (compositeEditorOptions) {
if (compositeEditorOptions && triggerOnCompositeEditorChange) {
this.handleChangeOnCompositeEditor(null, compositeEditorOptions, 'system');
}
}
Expand Down Expand Up @@ -274,7 +279,10 @@ export class AutoCompleteEditor implements Editor {

// set the new value to the item datacontext
if (isComplexObject) {
setDeepValue(item, fieldName, newValue);
// when it's a complex object, user could override the object path (where the editable object is located)
// else we use the path provided in the Field Column Definition
const objectPath = this.columnEditor?.complexObjectPath ?? fieldName ?? '';
setDeepValue(item, objectPath, newValue);
} else {
item[fieldName] = newValue;
}
Expand Down Expand Up @@ -417,21 +425,21 @@ export class AutoCompleteEditor implements Editor {
/** when it's a Composite Editor, we'll check if the Editor is editable (by checking onBeforeEditCell) and if not Editable we'll disable the Editor */
protected applyInputUsabilityState() {
const activeCell = this.grid.getActiveCell();
const isCellEditable = this.grid.onBeforeEditCell.notify({ ...activeCell, item: this.args.item, column: this.args.column, grid: this.grid });
const isCellEditable = this.grid.onBeforeEditCell.notify({ ...activeCell, item: this.dataContext, column: this.args.column, grid: this.grid });
this.disable(isCellEditable === false);
}

protected handleChangeOnCompositeEditor(event: Event | null, compositeEditorOptions: CompositeEditorOption, triggeredBy: 'user' | 'system' = 'user', isCalledByClearValue = false) {
const activeCell = this.grid.getActiveCell();
const column = this.args.column;
const columnId = this.columnDef?.id ?? '';
const item = this.args.item;
const item = this.dataContext;
const grid = this.grid;
const newValue = this.serializeValue();

// when valid, we'll also apply the new value to the dataContext item object
if (this.validate().valid) {
this.applyValue(this.args.item, newValue);
this.applyValue(this.dataContext, newValue);
}
this.applyValue(compositeEditorOptions.formValues, newValue);

Expand Down Expand Up @@ -558,7 +566,7 @@ export class AutoCompleteEditor implements Editor {

// user could also override the collection
if (this.columnEditor?.collectionOverride) {
const overrideArgs: CollectionOverrideArgs = { column: this.columnDef, dataContext: this.args.item, grid: this.grid, originalCollections: this.collection };
const overrideArgs: CollectionOverrideArgs = { column: this.columnDef, dataContext: this.dataContext, grid: this.grid, originalCollections: this.collection };
if (this.args.compositeEditorOptions) {
const { formValues, modalType } = this.args.compositeEditorOptions;
overrideArgs.compositeEditorOptions = { formValues, modalType };
Expand Down
21 changes: 14 additions & 7 deletions packages/common/src/editors/checkboxEditor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ export class CheckboxEditor implements Editor {
return this.columnDef && this.columnDef.internalColumnEditor || {};
}

/** Getter for the item data context object */
get dataContext(): any {
return this.args.item;
}

/** Getter for the Editor DOM Element */
get editorDomElement(): any {
return this._input;
Expand Down Expand Up @@ -148,7 +153,7 @@ export class CheckboxEditor implements Editor {
return this._input?.checked ?? false;
}

setValue(val: boolean | string, isApplyingValue = false) {
setValue(val: boolean | string, isApplyingValue = false, triggerOnCompositeEditorChange = true) {
const isChecked = val ? true : false;
if (this._input) {
this._input.checked = isChecked;
Expand All @@ -159,7 +164,7 @@ export class CheckboxEditor implements Editor {

// if it's set by a Composite Editor, then also trigger a change for it
const compositeEditorOptions = this.args.compositeEditorOptions;
if (compositeEditorOptions) {
if (compositeEditorOptions && triggerOnCompositeEditorChange) {
this.handleChangeOnCompositeEditor(null, compositeEditorOptions, 'system');
}
}
Expand All @@ -175,8 +180,10 @@ export class CheckboxEditor implements Editor {
const newValue = (validation && validation.valid) ? state : '';

// set the new value to the item datacontext
if (isComplexObject) {
setDeepValue(item, fieldName, newValue);
if (isComplexObject) {// when it's a complex object, user could override the object path (where the editable object is located)
// else we use the path provided in the Field Column Definition
const objectPath = this.columnEditor?.complexObjectPath ?? fieldName ?? '';
setDeepValue(item, objectPath, newValue);
} else {
item[fieldName] = newValue;
}
Expand Down Expand Up @@ -280,21 +287,21 @@ export class CheckboxEditor implements Editor {
/** when it's a Composite Editor, we'll check if the Editor is editable (by checking onBeforeEditCell) and if not Editable we'll disable the Editor */
protected applyInputUsabilityState() {
const activeCell = this.grid.getActiveCell();
const isCellEditable = this.grid.onBeforeEditCell.notify({ ...activeCell, item: this.args.item, column: this.args.column, grid: this.grid });
const isCellEditable = this.grid.onBeforeEditCell.notify({ ...activeCell, item: this.dataContext, column: this.args.column, grid: this.grid });
this.disable(isCellEditable === false);
}

protected handleChangeOnCompositeEditor(event: Event | null, compositeEditorOptions: CompositeEditorOption, triggeredBy: 'user' | 'system' = 'user', isCalledByClearValue = false) {
const activeCell = this.grid.getActiveCell();
const column = this.args.column;
const columnId = this.columnDef?.id ?? '';
const item = this.args.item;
const item = this.dataContext;
const grid = this.grid;
const newValue = this.serializeValue();

// when valid, we'll also apply the new value to the dataContext item object
if (this.validate().valid) {
this.applyValue(this.args.item, newValue);
this.applyValue(this.dataContext, newValue);
}
this.applyValue(compositeEditorOptions.formValues, newValue);

Expand Down
20 changes: 14 additions & 6 deletions packages/common/src/editors/dateEditor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,11 @@ export class DateEditor implements Editor {
return this.columnDef && this.columnDef.internalColumnEditor || {};
}

/** Getter for the item data context object */
get dataContext(): any {
return this.args.item;
}

/** Getter for the Editor DOM Element */
get editorDomElement(): any {
return this._$input;
Expand Down Expand Up @@ -255,15 +260,15 @@ export class DateEditor implements Editor {
return this._$input.val();
}

setValue(val: string, isApplyingValue = false) {
setValue(val: string, isApplyingValue = false, triggerOnCompositeEditorChange = true) {
this.flatInstance.setDate(val);

if (isApplyingValue) {
this.applyValue(this.args.item, this.serializeValue());

// if it's set by a Composite Editor, then also trigger a change for it
const compositeEditorOptions = this.args.compositeEditorOptions;
if (compositeEditorOptions) {
if (compositeEditorOptions && triggerOnCompositeEditorChange) {
this.handleChangeOnCompositeEditor(compositeEditorOptions, 'system');
}
}
Expand All @@ -282,7 +287,10 @@ export class DateEditor implements Editor {

// set the new value to the item datacontext
if (isComplexObject) {
setDeepValue(item, fieldName, newValue);
// when it's a complex object, user could override the object path (where the editable object is located)
// else we use the path provided in the Field Column Definition
const objectPath = this.columnEditor?.complexObjectPath ?? fieldName ?? '';
setDeepValue(item, objectPath, newValue);
} else {
item[fieldName] = newValue;
}
Expand Down Expand Up @@ -409,7 +417,7 @@ export class DateEditor implements Editor {
/** when it's a Composite Editor, we'll check if the Editor is editable (by checking onBeforeEditCell) and if not Editable we'll disable the Editor */
protected applyInputUsabilityState() {
const activeCell = this.grid.getActiveCell();
const isCellEditable = this.grid.onBeforeEditCell.notify({ ...activeCell, item: this.args.item, column: this.args.column, grid: this.grid });
const isCellEditable = this.grid.onBeforeEditCell.notify({ ...activeCell, item: this.dataContext, column: this.args.column, grid: this.grid });
this.disable(isCellEditable === false);
}

Expand All @@ -431,13 +439,13 @@ export class DateEditor implements Editor {
const activeCell = this.grid.getActiveCell();
const column = this.args.column;
const columnId = this.columnDef?.id ?? '';
const item = this.args.item;
const item = this.dataContext;
const grid = this.grid;
const newValue = this.serializeValue();

// when valid, we'll also apply the new value to the dataContext item object
if (this.validate().valid) {
this.applyValue(this.args.item, newValue);
this.applyValue(this.dataContext, newValue);
}
this.applyValue(compositeEditorOptions.formValues, newValue);

Expand Down
16 changes: 12 additions & 4 deletions packages/common/src/editors/dualInputEditor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,11 @@ export class DualInputEditor implements Editor {
return this.columnDef && this.columnDef.internalColumnEditor || {};
}

/** Getter for the item data context object */
get dataContext(): any {
return this.args.item;
}

/** Getter for the Editor DOM Element */
get editorDomElement(): { leftInput: HTMLInputElement, rightInput: HTMLInputElement } {
return { leftInput: this._leftInput, rightInput: this._rightInput };
Expand Down Expand Up @@ -278,7 +283,10 @@ export class DualInputEditor implements Editor {
if (isComplexObject) {
const newValueFromComplex = getDescendantProperty(state, fieldNameToUse);
const newValue = (validation && validation.valid) ? newValueFromComplex : '';
setDeepValue(item, fieldName, newValue);
// when it's a complex object, user could override the object path (where the editable object is located)
// else we use the path provided in the Field Column Definition
const objectPath = this.columnEditor?.complexObjectPath ?? fieldName ?? '';
setDeepValue(item, objectPath, newValue);
} else if (fieldName) {
item[fieldName] = (validation && validation.valid) ? state[fieldName] : '';
}
Expand Down Expand Up @@ -501,7 +509,7 @@ export class DualInputEditor implements Editor {
/** when it's a Composite Editor, we'll check if the Editor is editable (by checking onBeforeEditCell) and if not Editable we'll disable the Editor */
protected applyInputUsabilityState() {
const activeCell = this.grid.getActiveCell();
const isCellEditable = this.grid.onBeforeEditCell.notify({ ...activeCell, item: this.args.item, column: this.args.column, grid: this.grid });
const isCellEditable = this.grid.onBeforeEditCell.notify({ ...activeCell, item: this.dataContext, column: this.args.column, grid: this.grid });
this.disable(isCellEditable === false);
}

Expand All @@ -510,13 +518,13 @@ export class DualInputEditor implements Editor {
const column = this.args.column;
const leftInputId = this.columnEditor.params?.leftInput?.field ?? '';
const rightInputId = this.columnEditor.params?.rightInput?.field ?? '';
const item = this.args.item;
const item = this.dataContext;
const grid = this.grid;
const newValues = this.serializeValue();

// when valid, we'll also apply the new value to the dataContext item object
if (this.validate().valid) {
this.applyValue(this.args.item, newValues);
this.applyValue(this.dataContext, newValues);
}
this.applyValue(compositeEditorOptions.formValues, newValues);

Expand Down
20 changes: 14 additions & 6 deletions packages/common/src/editors/floatEditor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ export class FloatEditor implements Editor {
return this.columnDef && this.columnDef.internalColumnEditor || {};
}

/** Getter for the item data context object */
get dataContext(): any {
return this.args.item;
}

/** Getter for the Editor DOM Element */
get editorDomElement(): any {
return this._input;
Expand Down Expand Up @@ -181,7 +186,7 @@ export class FloatEditor implements Editor {
return this._input?.value || '';
}

setValue(value: number | string, isApplyingValue = false) {
setValue(value: number | string, isApplyingValue = false, triggerOnCompositeEditorChange = true) {
if (this._input) {
this._input.value = `${value}`;

Expand All @@ -190,7 +195,7 @@ export class FloatEditor implements Editor {

// if it's set by a Composite Editor, then also trigger a change for it
const compositeEditorOptions = this.args.compositeEditorOptions;
if (compositeEditorOptions) {
if (compositeEditorOptions && triggerOnCompositeEditorChange) {
this.handleChangeOnCompositeEditor(null, compositeEditorOptions, 'system');
}
}
Expand All @@ -207,7 +212,10 @@ export class FloatEditor implements Editor {

// set the new value to the item datacontext
if (isComplexObject) {
setDeepValue(item, fieldName, newValue);
// when it's a complex object, user could override the object path (where the editable object is located)
// else we use the path provided in the Field Column Definition
const objectPath = this.columnEditor?.complexObjectPath ?? fieldName ?? '';
setDeepValue(item, objectPath, newValue);
} else {
item[fieldName] = newValue;
}
Expand Down Expand Up @@ -326,21 +334,21 @@ export class FloatEditor implements Editor {
/** when it's a Composite Editor, we'll check if the Editor is editable (by checking onBeforeEditCell) and if not Editable we'll disable the Editor */
protected applyInputUsabilityState() {
const activeCell = this.grid.getActiveCell();
const isCellEditable = this.grid.onBeforeEditCell.notify({ ...activeCell, item: this.args.item, column: this.args.column, grid: this.grid });
const isCellEditable = this.grid.onBeforeEditCell.notify({ ...activeCell, item: this.dataContext, column: this.args.column, grid: this.grid });
this.disable(isCellEditable === false);
}

protected handleChangeOnCompositeEditor(event: Event | null, compositeEditorOptions: CompositeEditorOption, triggeredBy: 'user' | 'system' = 'user', isCalledByClearValue = false) {
const activeCell = this.grid.getActiveCell();
const column = this.args.column;
const columnId = this.columnDef?.id ?? '';
const item = this.args.item;
const item = this.dataContext;
const grid = this.grid;
const newValue = this.serializeValue();

// when valid, we'll also apply the new value to the dataContext item object
if (this.validate().valid) {
this.applyValue(this.args.item, newValue);
this.applyValue(this.dataContext, newValue);
}
this.applyValue(compositeEditorOptions.formValues, newValue);

Expand Down
20 changes: 14 additions & 6 deletions packages/common/src/editors/integerEditor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ export class IntegerEditor implements Editor {
return this.columnDef && this.columnDef.internalColumnEditor || {};
}

/** Getter for the item data context object */
get dataContext(): any {
return this.args.item;
}

/** Getter for the Editor DOM Element */
get editorDomElement(): any {
return this._input;
Expand Down Expand Up @@ -157,11 +162,11 @@ export class IntegerEditor implements Editor {
return this._input?.value || '';
}

setValue(value: number | string, isApplyingValue = false) {
setValue(value: number | string, isApplyingValue = false, triggerOnCompositeEditorChange = true) {
if (this._input) {
this._input.value = `${value}`;

if (isApplyingValue) {
if (isApplyingValue && triggerOnCompositeEditorChange) {
this.applyValue(this.args.item, this.serializeValue());

// if it's set by a Composite Editor, then also trigger a change for it
Expand All @@ -184,7 +189,10 @@ export class IntegerEditor implements Editor {

// set the new value to the item datacontext
if (isComplexObject) {
setDeepValue(item, fieldName, newValue);
// when it's a complex object, user could override the object path (where the editable object is located)
// else we use the path provided in the Field Column Definition
const objectPath = this.columnEditor?.complexObjectPath ?? fieldName ?? '';
setDeepValue(item, objectPath, newValue);
} else {
item[fieldName] = newValue;
}
Expand Down Expand Up @@ -289,21 +297,21 @@ export class IntegerEditor implements Editor {
/** when it's a Composite Editor, we'll check if the Editor is editable (by checking onBeforeEditCell) and if not Editable we'll disable the Editor */
protected applyInputUsabilityState() {
const activeCell = this.grid.getActiveCell();
const isCellEditable = this.grid.onBeforeEditCell.notify({ ...activeCell, item: this.args.item, column: this.args.column, grid: this.grid });
const isCellEditable = this.grid.onBeforeEditCell.notify({ ...activeCell, item: this.dataContext, column: this.args.column, grid: this.grid });
this.disable(isCellEditable === false);
}

protected handleChangeOnCompositeEditor(event: Event | null, compositeEditorOptions: CompositeEditorOption, triggeredBy: 'user' | 'system' = 'user', isCalledByClearValue = false) {
const activeCell = this.grid.getActiveCell();
const column = this.args.column;
const columnId = this.columnDef?.id ?? '';
const item = this.args.item;
const item = this.dataContext;
const grid = this.grid;
const newValue = this.serializeValue();

// when valid, we'll also apply the new value to the dataContext item object
if (this.validate().valid) {
this.applyValue(this.args.item, newValue);
this.applyValue(this.dataContext, newValue);
}
this.applyValue(compositeEditorOptions.formValues, newValue);

Expand Down
Loading

0 comments on commit 721a6c5

Please sign in to comment.