diff --git a/package-lock.json b/package-lock.json index 93c06ac9..b8ea654b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "@chantouchsek/validatorjs": "1.2.3", "@storybook/addon-docs": "^7.6.13", "axios-extensions": "^3.1.6", + "flatted": "^3.3.3", "lodash": "^4.17.21", "lru-cache": "^10.0.1", "moment": "^2.30.1", @@ -13580,10 +13581,9 @@ } }, "node_modules/flatted": { - "version": "3.2.9", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", - "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", - "dev": true + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==" }, "node_modules/flow-parser": { "version": "0.228.0", diff --git a/package.json b/package.json index 5413fa7d..3959ad7f 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "@chantouchsek/validatorjs": "1.2.3", "@storybook/addon-docs": "^7.6.13", "axios-extensions": "^3.1.6", + "flatted": "^3.3.3", "lodash": "^4.17.21", "lru-cache": "^10.0.1", "moment": "^2.30.1", diff --git a/src/components/renderer/form-button.vue b/src/components/renderer/form-button.vue index 45e3a7cd..9d09e717 100644 --- a/src/components/renderer/form-button.vue +++ b/src/components/renderer/form-button.vue @@ -20,6 +20,8 @@ import Mustache from 'mustache'; import { mapActions, mapState } from "vuex"; import { getValidPath } from '@/mixins'; import Worker from "@/workers/worker.js?worker&inline"; +import { findRootScreen } from "@/mixins/DataReference"; +import { stringify } from 'flatted'; export default { mixins: [getValidPath], @@ -112,45 +114,50 @@ export default { }); return; } + if (this.event === 'pageNavigate') { + // Run handler for page navigate + await this.runHandler(); + } this.$emit(this.event, this.eventData); if (this.event === 'pageNavigate') { this.$emit('page-navigate', this.eventData); } }, - async runHandler() { + runHandler() { if (this.handler) { - try { - const data = this.getScreenDataReference( - null, - (screen, name, value) => { - // Enable the data reference to be updated by the handler - screen.$set(screen.vdata, name, value); - } - ); + return new Promise((resolve, reject) => { + try { + const rootScreen = findRootScreen(this); + const data = rootScreen.vdata; + const scope = this.transientData; - const rawData = data[Symbol.for("__v_raw")]; + const worker = new Worker(); + // Send the handler code to the worker + worker.postMessage({ + fn: this.handler, + dataRefs: stringify({data, scope}), + }); - const worker = new Worker(); - // Send the handler code to the worker - worker.postMessage({ - fn: this.handler, - data: rawData, - }); - - // Listen for the result from the worker - worker.onmessage = (e) => { - if (e.data.error) { - console.error("Worker error:", e.data.error); - } else if (e.data.result) { - // Update the data with the result - Object.keys(e.data.result).forEach(key => { - rawData[key] = e.data.result[key]; - }); - } - }; - } catch (error) { - console.error("❌ There is an error in the button handler", error); - } + // Listen for the result from the worker + worker.onmessage = (e) => { + if (e.data.error) { + reject(e.data.error); + } else if (e.data.result) { + // Update the data with the result + Object.keys(e.data.result).forEach(key => { + if (key === '_root') { + Object.assign(data, e.data.result[key]); + } else { + scope[key] = e.data.result[key]; + } + }); + resolve(); + } + }; + } catch (error) { + console.error("❌ There is an error in the button handler", error); + } + }); } } }, diff --git a/src/workers/worker.js b/src/workers/worker.js index ab1eb3fd..df13a0e9 100644 --- a/src/workers/worker.js +++ b/src/workers/worker.js @@ -1,6 +1,9 @@ // worker.js +import { parse } from 'flatted'; + self.onmessage = async function (e) { - const { fn, data } = e.data; + const { fn, dataRefs } = e.data; + const { data, scope, parent } = parse(dataRefs); try { // Validate inputs @@ -18,15 +21,15 @@ self.onmessage = async function (e) { // Use Function constructor with explicit parameter and body // eslint-disable-next-line no-new-func - const userFunc = new Function('data', functionBody); - const result = isAsync ? await userFunc(data) : userFunc(data); + const userFunc = new Function('data', 'parent', functionBody); + const result = isAsync ? await userFunc.apply(scope, [data, parent]) : userFunc.apply(scope, [data, parent]); self.postMessage({ result }); } catch (error) { - console.error('Error executing handler:', error); + console.error('❌ Error executing handler:', error); self.postMessage({ - error: error.message, + error: error.message || error.toString(), stack: error.stack }); } diff --git a/tests/e2e/fixtures/button_click_handler_worker.json b/tests/e2e/fixtures/button_click_handler_worker.json new file mode 100644 index 00000000..0f806228 --- /dev/null +++ b/tests/e2e/fixtures/button_click_handler_worker.json @@ -0,0 +1,2626 @@ +{ + "type": "screen_package", + "version": "2", + "screens": [ + { + "id": 1, + "screen_category_id": "1", + "title": "ClickHandler", + "description": "ClickHandler", + "type": "FORM", + "config": [ + { + "name": "Default", + "items": [ + { + "uuid": "305baccb-7167-4930-acaf-e296100dff7b", + "config": { + "name": "form_record_list_1", + "icon": "fas fa-th-list", + "label": "New Record List", + "editable": true, + "fields": { + "dataSource": "provideData", + "jsonData": "[]", + "optionsList": [], + "showOptionCard": false, + "showRemoveWarning": false, + "showJsonEditor": false, + "editIndex": null, + "removeIndex": null + }, + "form": "1", + "source": { + "collectionFields": [], + "collectionFieldsColumns": [], + "pmql": null, + "sourceOptions": "Variable", + "variableStore": null, + "dataSelectionOptions": "no-selection", + "singleField": null + } + }, + "inspector": [ + { + "type": "FormInput", + "field": "name", + "config": { + "label": "Variable Name", + "name": "Variable Name", + "validation": "dot_notation|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", + "helper": "A variable name is a symbolic name to reference information." + } + }, + { + "type": "FormInput", + "field": "label", + "config": { + "label": "List Label", + "helper": "The label describes this record list" + } + }, + { + "type": "collectionDataSource", + "field": "source", + "config": { + "label": "Source of Record List", + "helper": "A record list can display the data of a defined variable or a collection" + } + }, + { + "type": "FormCheckbox", + "field": "editable", + "config": { + "label": "Editable?", + "helper": "Should records be editable/removable and can new records be added" + }, + "if": "hideControl" + }, + { + "type": "ColumnSetup", + "field": "fields", + "config": { + "label": "Columns", + "helper": "List of columns to display in the record list" + } + }, + { + "type": "FormMultiselect", + "field": "paginationOption", + "config": { + "icon": "fas", + "label": "Pagination", + "options": [ + { + "content": "No Pagination (show all)", + "value": 0 + }, + { + "content": "5 items per page", + "value": 5 + }, + { + "content": "10 items per page", + "value": 10 + }, + { + "content": "15 items per page", + "value": 15 + }, + { + "content": "25 items per page", + "value": 25 + }, + { + "content": "50 items per page", + "value": 50 + } + ], + "helper": "" + } + }, + { + "type": "PageSelect", + "field": "form", + "config": { + "label": "Record Form", + "helper": "The form to use for adding/editing records" + }, + "if": "hideControl" + }, + { + "type": "collectionDesignerMode", + "field": "designerMode", + "config": { + "label": "Table Style", + "helper": "" + } + }, + { + "type": "ColorSelectRecord", + "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": "ColorSelectRecord", + "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": "ColorSelectModern", + "field": "bgcolormodern", + "config": { + "label": "", + "helper": "", + "options": [ + { + "value": "alert alert-primary", + "content": "primary" + }, + { + "value": "alert alert-success", + "content": "success" + }, + { + "value": "alert alert-warning", + "content": "warning" + }, + { + "value": "alert alert-secondary", + "content": "secondary" + } + ] + } + }, + { + "type": "FormInput", + "field": "conditionalHide", + "config": { + "label": "Visibility Rule", + "helper": "This control is hidden until this expression is true" + } + }, + { + "type": "DeviceVisibility", + "field": "deviceVisibility", + "config": { + "label": "Device Visibility", + "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": "" + } + }, + { + "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]*" + } + }, + { + "type": "EncryptedConfig", + "field": "encryptedConfig", + "config": { + "label": "Encrypted", + "helper": "" + } + } + ], + "component": "FormRecordList", + "editor-component": "FormText", + "editor-control": "FormText", + "label": "Record List" + }, + { + "uuid": "e4447496-0239-4d6f-9d83-26c3053d54ec", + "config": { + "name": "form_record_list_1", + "icon": "fas fa-redo", + "settings": { + "type": "existing", + "varname": "form_record_list_1", + "times": "3", + "add": true + }, + "label": "" + }, + "inspector": [ + { + "type": "LoopInspector", + "field": "settings", + "config": { + "label": "", + "helper": "" + } + }, + { + "type": "FormInput", + "field": "conditionalHide", + "config": { + "label": "Visibility Rule", + "helper": "This control is hidden until this expression is true" + } + }, + { + "type": "DeviceVisibility", + "field": "deviceVisibility", + "config": { + "label": "Device Visibility", + "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": "" + } + }, + { + "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]*" + } + }, + { + "type": "EncryptedConfig", + "field": "encryptedConfig", + "config": { + "label": "Encrypted", + "helper": "" + } + } + ], + "component": "FormLoop", + "editor-component": "Loop", + "editor-control": "Loop", + "label": "Loop", + "items": [ + { + "uuid": "7e7670f3-12e2-4a9e-b683-fa4c2abe2b84", + "config": { + "icon": "fas fa-share-square", + "label": "x Delete row", + "variant": "danger", + "event": "submit", + "loading": false, + "loadingLabel": "Loading...", + "defaultSubmit": true, + "name": "delete_row", + "fieldValue": null, + "tooltip": {}, + "handler": "const form_record_list_1 = data.form_record_list_1;\nconst index = form_record_list_1.indexOf(this);\nform_record_list_1.splice(index, 1);\n\nreturn {\n name: \"deleted\",\n _root: {\n form_record_list_1\n }\n}" + }, + "inspector": [ + { + "type": "FormInput", + "field": "label", + "config": { + "label": "Label", + "helper": "The label describes the button's text" + } + }, + { + "type": "FormInput", + "field": "name", + "config": { + "label": "Variable Name", + "name": "Variable Name", + "helper": "A variable name is a symbolic name to reference information.", + "validation": "regex:/^(?:[A-Za-z])(?:[0-9A-Z_.a-z])*(? Date ##/##/####
SSN ###-##-####
Phone (###) ###-####", + "validation": "" + } + }, + { + "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]*" + } + }, + { + "type": "EncryptedConfig", + "field": "encryptedConfig", + "config": { + "label": "Encrypted", + "helper": "" + } + } + ], + "component": "FormButton", + "editor-component": "FormButton", + "editor-control": "FormSubmit", + "label": "Submit Button" + }, + { + "uuid": "693b4380-e319-4c5f-a1a5-18eea510d067", + "config": { + "icon": "fas fa-share-square", + "label": "Error", + "variant": "warning", + "event": "submit", + "loading": false, + "loadingLabel": "Loading...", + "defaultSubmit": true, + "name": "handle_error", + "fieldValue": null, + "tooltip": { + "variant": "primary" + }, + "handler": "throw new Error(\"Testing error\")" + }, + "inspector": [ + { + "type": "FormInput", + "field": "label", + "config": { + "label": "Label", + "helper": "The label describes the button's text" + } + }, + { + "type": "FormInput", + "field": "name", + "config": { + "label": "Variable Name", + "name": "Variable Name", + "helper": "A variable name is a symbolic name to reference information.", + "validation": "regex:/^(?:[A-Za-z])(?:[0-9A-Z_.a-z])*(? Date ##/##/####
SSN ###-##-####
Phone (###) ###-####", + "validation": "" + } + }, + { + "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]*" + } + }, + { + "type": "EncryptedConfig", + "field": "encryptedConfig", + "config": { + "label": "Encrypted", + "helper": "" + } + }, + { + "type": "FormInput", + "field": "conditionalHide", + "config": { + "label": "Visibility Rule", + "helper": "This control is hidden until this expression is true" + } + }, + { + "type": "DeviceVisibility", + "field": "deviceVisibility", + "config": { + "label": "Device Visibility", + "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": "" + } + }, + { + "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]*" + } + }, + { + "type": "EncryptedConfig", + "field": "encryptedConfig", + "config": { + "label": "Encrypted", + "helper": "" + } + }, + { + "type": "FormInput", + "field": "conditionalHide", + "config": { + "label": "Visibility Rule", + "helper": "This control is hidden until this expression is true" + } + }, + { + "type": "DeviceVisibility", + "field": "deviceVisibility", + "config": { + "label": "Device Visibility", + "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": "" + } + }, + { + "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]*" + } + }, + { + "type": "EncryptedConfig", + "field": "encryptedConfig", + "config": { + "label": "Encrypted", + "helper": "" + } + } + ], + "component": "FormButton", + "editor-component": "FormButton", + "editor-control": "FormSubmit", + "label": "Submit Button" + }, + { + "uuid": "3a02685d-598b-4aad-a0e6-cd6919d09cab", + "config": { + "icon": "far fa-square", + "label": "name", + "name": "name", + "placeholder": "", + "validation": [], + "helper": null, + "type": "text", + "dataFormat": "string", + "readonly": false + }, + "inspector": [ + { + "type": "FormInput", + "field": "name", + "config": { + "label": "Variable Name", + "name": "Variable Name", + "validation": "dot_notation|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", + "helper": "A variable name is a symbolic name to reference information." + } + }, + { + "type": "FormInput", + "field": "label", + "config": { + "label": "Label", + "helper": "The label describes the field's name" + } + }, + { + "type": "FormMultiselect", + "field": "dataFormat", + "config": { + "label": "Data Type", + "name": "Data Type", + "helper": "The data type specifies what kind of data is stored in the variable.", + "validation": "required", + "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" + } + ] + } + }, + { + "type": "SelectDataTypeMask", + "field": "dataMask", + "config": { + "label": "Data Format", + "name": "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": "" + } + }, + { + "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": "default-value-editor", + "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": "DeviceVisibility", + "field": "deviceVisibility", + "config": { + "label": "Device Visibility", + "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": "" + } + }, + { + "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]*" + } + }, + { + "type": "EncryptedConfig", + "field": "encryptedConfig", + "config": { + "label": "Encrypted", + "helper": "" + } + } + ], + "component": "FormInput", + "editor-component": "FormInput", + "editor-control": "FormInput", + "label": "Line Input" + }, + { + "uuid": "fa288d55-b472-4c23-a228-c4342cb6002d", + "config": { + "icon": "fas fa-share-square", + "label": "change and submit", + "variant": "primary", + "event": "submit", + "loading": false, + "loadingLabel": "Loading...", + "defaultSubmit": true, + "name": "change_and_submit", + "fieldValue": null, + "tooltip": {}, + "handler": "return {\n name: \"last change and submit\"\n}" + }, + "inspector": [ + { + "type": "FormInput", + "field": "label", + "config": { + "label": "Label", + "helper": "The label describes the button's text" + } + }, + { + "type": "FormInput", + "field": "name", + "config": { + "label": "Variable Name", + "name": "Variable Name", + "helper": "A variable name is a symbolic name to reference information.", + "validation": "regex:/^(?:[A-Za-z])(?:[0-9A-Z_.a-z])*(? Date ##/##/####
SSN ###-##-####
Phone (###) ###-####", + "validation": "" + } + }, + { + "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]*" + } + }, + { + "type": "EncryptedConfig", + "field": "encryptedConfig", + "config": { + "label": "Encrypted", + "helper": "" + } + } + ], + "component": "FormButton", + "editor-component": "FormButton", + "editor-control": "FormSubmit", + "label": "Submit Button" + } + ], + "container": true + } + ], + "order": 1 + }, + { + "name": "test", + "order": 2, + "items": [ + { + "uuid": "f539505a-65cc-40d2-8257-666bf44970cc", + "config": { + "icon": "far fa-square", + "label": "name", + "name": "name", + "placeholder": "", + "validation": [], + "helper": null, + "type": "text", + "dataFormat": "string", + "readonly": false + }, + "inspector": [ + { + "type": "FormInput", + "field": "name", + "config": { + "label": "Variable Name", + "name": "Variable Name", + "validation": "dot_notation|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", + "helper": "A variable name is a symbolic name to reference information." + } + }, + { + "type": "FormInput", + "field": "label", + "config": { + "label": "Label", + "helper": "The label describes the field's name" + } + }, + { + "type": "FormMultiselect", + "field": "dataFormat", + "config": { + "label": "Data Type", + "name": "Data Type", + "helper": "The data type specifies what kind of data is stored in the variable.", + "validation": "required", + "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" + } + ] + } + }, + { + "type": "SelectDataTypeMask", + "field": "dataMask", + "config": { + "label": "Data Format", + "name": "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": "" + } + }, + { + "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": "default-value-editor", + "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": "DeviceVisibility", + "field": "deviceVisibility", + "config": { + "label": "Device Visibility", + "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": "" + } + }, + { + "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]*" + } + }, + { + "type": "EncryptedConfig", + "field": "encryptedConfig", + "config": { + "label": "Encrypted", + "helper": "" + } + } + ], + "component": "FormInput", + "editor-component": "FormInput", + "editor-control": "FormInput", + "label": "Line Input" + }, + { + "uuid": "a50d6f9b-5798-49ab-8d0e-103930cdf000", + "config": { + "icon": "fas fa-share-square", + "label": "New Submit", + "variant": "secondary", + "event": "submit", + "loading": false, + "loadingLabel": "Loading...", + "defaultSubmit": true, + "name": "record_list_button_with_handler", + "fieldValue": null, + "tooltip": {}, + "handler": "return {\n name: \"value changed by handler\"\n}" + }, + "inspector": [ + { + "type": "FormInput", + "field": "label", + "config": { + "label": "Label", + "helper": "The label describes the button's text" + } + }, + { + "type": "FormInput", + "field": "name", + "config": { + "label": "Variable Name", + "name": "Variable Name", + "helper": "A variable name is a symbolic name to reference information.", + "validation": "regex:/^(?:[A-Za-z])(?:[0-9A-Z_.a-z])*(? Date ##/##/####
SSN ###-##-####
Phone (###) ###-####", + "validation": "" + } + }, + { + "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]*" + } + }, + { + "type": "EncryptedConfig", + "field": "encryptedConfig", + "config": { + "label": "Encrypted", + "helper": "" + } + } + ], + "component": "FormButton", + "editor-component": "FormButton", + "editor-control": "FormSubmit", + "label": "Submit Button" + } + ] + } + ], + "computed": [ + { + "id": 1, + "name": "padre is a circular reference", + "type": "javascript", + "formula": "return this._parent;", + "property": "padre" + } + ], + "custom_css": null, + "created_at": "2020-06-17T22:30:48+00:00", + "updated_at": "2020-08-03T21:11:40+00:00", + "status": "ACTIVE", + "key": null, + "watchers": [], + "categories": [ + { + "id": 1, + "name": "Uncategorized", + "status": "ACTIVE", + "is_system": 0, + "created_at": "2020-07-01T20:06:18+00:00", + "updated_at": "2020-07-01T20:06:18+00:00", + "pivot": { + "assignable_id": 5, + "category_id": 1, + "category_type": "ProcessMaker\\Models\\ScreenCategory" + } + } + ] + } + ], + "screen_categories": [], + "scripts": [] +} \ No newline at end of file diff --git a/tests/e2e/specs/ButtonClickHandlerWorker.spec.js b/tests/e2e/specs/ButtonClickHandlerWorker.spec.js new file mode 100644 index 00000000..a91009f7 --- /dev/null +++ b/tests/e2e/specs/ButtonClickHandlerWorker.spec.js @@ -0,0 +1,62 @@ +describe("Button click handler", () => { + + before(() => { + cy.visit("/", { + onBeforeLoad(win) { + // stub console.error + cy.stub(win.console, 'error').as('consoleError') + } + }); + }); + + it.only("Test circular reference and click handlers", () => { + cy.loadFromJson("button_click_handler_worker.json", 0); + cy.wait(1000); + + cy.get("[data-cy=mode-preview]").click(); + // Add new row in record list (data-cy="add-row") + cy.get("[data-cy=preview-content] [data-cy=add-row]").click(); + // Fill the first input + cy.get("[data-cy=preview-content] [data-cy=screen-field-form_record_list_1] [data-cy=modal-add] [name=name]").clear().type("12345678"); + cy.get("[data-cy=preview-content] [data-cy=screen-field-form_record_list_1] [data-cy=modal-add] [name=name]").should( + "have.value", + "12345678" + ); + // Click on the first button + cy.get("[data-cy=preview-content] [data-cy=screen-field-form_record_list_1] [data-cy=modal-add] [name=record_list_button_with_handler]").click(); + cy.get("[data-cy=preview-content] [data-cy=screen-field-form_record_list_1] [data-cy=modal-add] [name=name]").should( + "have.value", + "value changed by handler" + ); + // Click on OK + cy.get("[data-cy=preview-content] [data-cy=screen-field-form_record_list_1] [data-cy=modal-add] button.btn-primary").click(); + + // Add new row (data-cy="loop-form_record_list_1-add") + cy.get("[data-cy=preview-content] [data-cy=loop-form_record_list_1-add]").click(); + + // Click on first dete row + cy.get("[data-cy=preview-content] [name=delete_row]").eq(0).click(); + + // Click on first handle error + cy.get("[data-cy=preview-content] [name=handle_error]").eq(0).click(); + // Check the error message in console + cy.get('@consoleError') + .should('have.been.called') + .and('have.been.calledWith', 'Testing error'); + + // Click on first change and submit + cy.get("[data-cy=preview-content] [name=change_and_submit]").eq(0).click(); + + // Check the data of the screen + cy.assertPreviewData({ + "form_record_list_1": [ + { + "delete_row": null, + "handle_error": null, + "name": "last change and submit", + "change_and_submit": null + } + ] + }); + }); +});