diff --git a/src/mixins/ScreenBase.js b/src/mixins/ScreenBase.js index dca6a885d..53d200e26 100644 --- a/src/mixins/ScreenBase.js +++ b/src/mixins/ScreenBase.js @@ -155,21 +155,17 @@ export default { }, setValue(name, value, object = this, defaults = object) { if (object && value !== undefined) { - const splittedName = name.split('.'); - splittedName.forEach((attr, index) => { + const parsedName = name.split('.'); - let isLastElement, setValue; - const originalValue = get(object, attr); + for (const attr of parsedName) { + let setValue; + let index = parsedName.indexOf(attr); + let isLastElement = index === parsedName.length - 1; - if (index === splittedName.length - 1) { - isLastElement = true; - } else { - isLastElement = false; - } + const originalValue = get(object, attr); if (isLastElement) { setValue = value; - } else { setValue = originalValue; @@ -177,7 +173,7 @@ export default { // Check defaults setValue = get(defaults, attr); } - + if (!setValue) { // Still no value? Set empty object setValue = {}; @@ -193,9 +189,10 @@ export default { attr, setValue ); + object = get(object, attr); defaults = get(defaults, attr); - }); + } } }, validationMessage(validation) { diff --git a/src/mixins/computedFields.js b/src/mixins/computedFields.js index 4560996ce..0e6d3961d 100644 --- a/src/mixins/computedFields.js +++ b/src/mixins/computedFields.js @@ -1,23 +1,18 @@ -import _ from 'lodash'; import { Parser } from 'expr-eval'; export default { methods: { evaluateExpression(expression, type) { - let value = null; - const self = this; + let value = null; try { - //monitor if variable belongs to data (defined variables) or vdata (external variables) - //in this way the event is not executed again when the variable is update - const data = new Proxy(Object.assign({}, this), { + // Monitor if variable belongs to data (defined variables) or + // vdata (external variables)in this way the event is not + // executed again when the variable is update + const data = new Proxy(Object.assign({}, self, self.vdata), { get(data, name) { - if (data[name] === undefined || !_.isEqual(data[name]), self.vdata[name]) { - return self.vdata[name]; - } else { - return data[name]; - } + return undefined === data[name] ? self.vdata[name] : data[name]; }, set() { throw 'You are not allowed to set properties from inside an expression'; diff --git a/src/mixins/extensions/ComputedFields.js b/src/mixins/extensions/ComputedFields.js index d1be25643..e4d4fb152 100644 --- a/src/mixins/extensions/ComputedFields.js +++ b/src/mixins/extensions/ComputedFields.js @@ -3,14 +3,28 @@ import computedFields from '../computedFields'; export default { methods: { computedFields(screen, definition) { + // Add computed fields screen.mixins.push(computedFields); - definition.computed.forEach(computed => { + + for (const computed of definition.computed) { screen.computed[computed.property] = { - get: new Function(`return this.evaluateExpression(${JSON.stringify(computed.formula)}, ${JSON.stringify(computed.type)});`), - set() {}, + get: (() => { + const formula = JSON.stringify(computed.formula); + const type = JSON.stringify(computed.type); + + return new Function(`return this.evaluateExpression(${formula}, ${type});`); + })(), + set() { + // Do nothing (as it's not allowed) + }, }; - this.addWatch(screen, computed.property, `this.setValue(${JSON.stringify(computed.property)}, value, this.vdata);`); - }); + + this.addWatch( + screen, + computed.property, + `this.setValue(${JSON.stringify(computed.property)}, value, this.vdata);` + ); + } }, }, mounted() { diff --git a/tests/e2e/fixtures/FOUR-4853.json b/tests/e2e/fixtures/FOUR-4853.json new file mode 100644 index 000000000..e255c579e --- /dev/null +++ b/tests/e2e/fixtures/FOUR-4853.json @@ -0,0 +1,486 @@ +{ + "type": "screen_package", + "version": "2", + "screens": [ + { + "id": 14, + "screen_category_id": "1", + "title": "FOUR-4853", + "description": "test", + "type": "FORM", + "config": [ + { + "name": "FOUR-4853", + "items": [ + { + "label": "Line Input", + "config": { + "icon": "far fa-square", + "name": "foo.two", + "type": "text", + "label": "foo.two", + "helper": null, + "readonly": false, + "dataFormat": "string", + "validation": [], + "placeholder": null + }, + "component": "FormInput", + "inspector": [ + { + "type": "FormInput", + "field": "name", + "config": { + "name": "Variable Name", + "label": "Variable Name", + "helper": "A variable name is a symbolic name to reference information.", + "validation": "regex:\/^(?:[A-Za-z])(?:[0-9A-Z_.a-z])*[^.]$\/|required|not_in:null,break,case,catch,continue,debugger,default,delete,do,else,finally,for,function,if,in,instanceof,new,return,switch,this,throw,try,typeof,var,void,while,with,class,const,enum,export,extends,import,super,true,false" + } + }, + { + "type": "FormInput", + "field": "label", + "config": { + "label": "Label", + "helper": "The label describes the field's name" + } + }, + { + "type": "FormMultiselect", + "field": "dataFormat", + "config": { + "name": "Data Type", + "label": "Data Type", + "helper": "The data type specifies what kind of data is stored in the variable.", + "options": [ + { + "value": "string", + "content": "Text" + }, + { + "value": "int", + "content": "Integer" + }, + { + "value": "currency", + "content": "Currency" + }, + { + "value": "percentage", + "content": "Percentage" + }, + { + "value": "float", + "content": "Decimal" + }, + { + "value": "datetime", + "content": "Datetime" + }, + { + "value": "date", + "content": "Date" + }, + { + "value": "password", + "content": "Password" + } + ], + "validation": "required" + } + }, + { + "type": { + "extends": { + "props": [ + "label", + "error", + "options", + "helper", + "name", + "value", + "selectedControl" + ], + "mixins": [ + { + "props": { + "validation": { + "type": null + }, + "validationData": { + "type": null + }, + "validationField": { + "type": null + }, + "validationMessages": { + "type": null + } + }, + "watch": { + "validationData": { + "deep": true + } + }, + "methods": [], + "computed": [] + } + ], + "methods": [], + "computed": [], + "_compiled": true, + "inheritAttrs": false, + "staticRenderFns": [] + }, + "computed": [], + "_compiled": true, + "staticRenderFns": [] + }, + "field": "dataMask", + "config": { + "name": "Data Format", + "label": "Data Format", + "helper": "The data format for the selected type." + } + }, + { + "type": "ValidationSelect", + "field": "validation", + "config": { + "label": "Validation Rules", + "helper": "The validation rules needed for this field" + } + }, + { + "type": "FormInput", + "field": "placeholder", + "config": { + "label": "Placeholder Text", + "helper": "The placeholder is what is shown in the field when no value is provided yet" + } + }, + { + "type": "FormInput", + "field": "helper", + "config": { + "label": "Helper Text", + "helper": "Help text is meant to provide additional guidance on the field's value" + } + }, + { + "type": "FormCheckbox", + "field": "readonly", + "config": { + "label": "Read Only", + "helper": null + } + }, + { + "type": "ColorSelect", + "field": "color", + "config": { + "label": "Text Color", + "helper": "Set the element's text color", + "options": [ + { + "value": "text-primary", + "content": "primary" + }, + { + "value": "text-secondary", + "content": "secondary" + }, + { + "value": "text-success", + "content": "success" + }, + { + "value": "text-danger", + "content": "danger" + }, + { + "value": "text-warning", + "content": "warning" + }, + { + "value": "text-info", + "content": "info" + }, + { + "value": "text-light", + "content": "light" + }, + { + "value": "text-dark", + "content": "dark" + } + ] + } + }, + { + "type": "ColorSelect", + "field": "bgcolor", + "config": { + "label": "Background Color", + "helper": "Set the element's background color", + "options": [ + { + "value": "alert alert-primary", + "content": "primary" + }, + { + "value": "alert alert-secondary", + "content": "secondary" + }, + { + "value": "alert alert-success", + "content": "success" + }, + { + "value": "alert alert-danger", + "content": "danger" + }, + { + "value": "alert alert-warning", + "content": "warning" + }, + { + "value": "alert alert-info", + "content": "info" + }, + { + "value": "alert alert-light", + "content": "light" + }, + { + "value": "alert alert-dark", + "content": "dark" + } + ] + } + }, + { + "type": { + "props": [ + "value", + "helper" + ], + "watch": { + "value": { + "immediate": true + } + }, + "methods": [], + "_scopeId": "data-v-172d71e1", + "computed": { + "effectiveValue": [] + }, + "_compiled": true, + "components": { + "MonacoEditor": { + "name": "monaco-editor", + "_Ctor": [], + "props": { + "amdRequire": [] + }, + "extends": { + "name": "MonacoEditor", + "model": { + "event": "change" + }, + "props": { + "theme": { + "default": "vs" + }, + "value": { + "required": true + }, + "options": [], + "language": [], + "original": [], + "amdRequire": [], + "diffEditor": { + "default": false + } + }, + "watch": { + "options": { + "deep": true, + "user": true + } + }, + "methods": [] + }, + "methods": [] + } + }, + "staticRenderFns": [] + }, + "field": "defaultValue", + "config": { + "label": "Default Value", + "helper": "The default value is pre populated using the existing request data. This feature will allow you to modify the value displayed on screen load if needed." + } + }, + { + "type": "FormInput", + "field": "conditionalHide", + "config": { + "label": "Visibility Rule", + "helper": "This control is hidden until this expression is true" + } + }, + { + "type": "FormInput", + "field": "customFormatter", + "config": { + "label": "Custom Format String", + "helper": "Use the Mask Pattern format
Date ##\/##\/####
SSN ###-##-####
Phone (###) ###-####", + "validation": null + } + }, + { + "type": "FormInput", + "field": "customCssSelector", + "config": { + "label": "CSS Selector Name", + "helper": "Use this in your custom css rules", + "validation": "regex: [-?[_a-zA-Z]+[_-a-zA-Z0-9]*]" + } + }, + { + "type": "FormInput", + "field": "ariaLabel", + "config": { + "label": "Aria Label", + "helper": "Attribute designed to help assistive technology (e.g. screen readers) attach a label" + } + }, + { + "type": "FormInput", + "field": "tabindex", + "config": { + "label": "Tab Order", + "helper": "Order in which a user will move focus from one control to another by pressing the Tab key", + "validation": "regex: [0-9]*" + } + } + ], + "editor-control": "FormInput", + "editor-component": "FormInput" + }, + { + "label": "Rich Text", + "config": { + "icon": "fas fa-pencil-ruler", + "label": null, + "content": "

output: {{ output }}<\/p>", + "interactive": true, + "renderVarHtml": false + }, + "component": "FormHtmlViewer", + "inspector": [ + { + "type": "FormTextArea", + "field": "content", + "config": { + "rows": 5, + "label": "Content", + "value": null, + "helper": "The HTML text to display" + } + }, + { + "type": "FormCheckbox", + "field": "renderVarHtml", + "config": { + "label": "Render HTML from a Variable", + "value": null, + "helper": null + } + }, + { + "type": "FormInput", + "field": "conditionalHide", + "config": { + "label": "Visibility Rule", + "helper": "This control is hidden until this expression is true" + } + }, + { + "type": "FormInput", + "field": "customFormatter", + "config": { + "label": "Custom Format String", + "helper": "Use the Mask Pattern format
Date ##\/##\/####
SSN ###-##-####
Phone (###) ###-####", + "validation": null + } + }, + { + "type": "FormInput", + "field": "customCssSelector", + "config": { + "label": "CSS Selector Name", + "helper": "Use this in your custom css rules", + "validation": "regex: [-?[_a-zA-Z]+[_-a-zA-Z0-9]*]" + } + }, + { + "type": "FormInput", + "field": "ariaLabel", + "config": { + "label": "Aria Label", + "helper": "Attribute designed to help assistive technology (e.g. screen readers) attach a label" + } + }, + { + "type": "FormInput", + "field": "tabindex", + "config": { + "label": "Tab Order", + "helper": "Order in which a user will move focus from one control to another by pressing the Tab key", + "validation": "regex: [0-9]*" + } + } + ], + "editor-control": "FormHtmlEditor", + "editor-component": "FormHtmlEditor" + } + ] + } + ], + "computed": [ + { + "id": 2, + "name": "test", + "type": "javascript", + "formula": "return this.foo.one;", + "property": "output" + } + ], + "custom_css": null, + "created_at": "2021-12-17T21:24:43+00:00", + "updated_at": "2021-12-17T21:26:25+00:00", + "status": "ACTIVE", + "key": null, + "watchers": [], + "categories": [ + { + "id": 1, + "name": "Uncategorized", + "status": "ACTIVE", + "is_system": 0, + "created_at": "2021-12-17T21:02:03+00:00", + "updated_at": "2021-12-17T21:02:03+00:00", + "pivot": { + "assignable_id": 14, + "category_id": 1, + "category_type": "ProcessMaker\\Models\\ScreenCategory" + } + } + ] + } + ], + "screen_categories": [], + "scripts": [] +} \ No newline at end of file diff --git a/tests/e2e/fixtures/FOUR-5086.json b/tests/e2e/fixtures/FOUR-5086.json new file mode 100644 index 000000000..1bcd738f0 --- /dev/null +++ b/tests/e2e/fixtures/FOUR-5086.json @@ -0,0 +1,3826 @@ +{ + "type": "screen_package", + "version": "2", + "screens": [ + { + "id": 2, + "screen_category_id": "1", + "title": "Screen Form Task Start", + "description": "Screen with calcs", + "type": "FORM", + "config": [ + { + "name": "Screen Form Task Start", + "items": [ + { + "label": "Rich Text", + "config": { + "icon": "fas fa-pencil-ruler", + "label": null, + "content": "