From 8c51587b0b3a761676ed5821b728a2f12f16c952 Mon Sep 17 00:00:00 2001 From: vitPinchuk Date: Thu, 31 Oct 2024 15:28:24 +0300 Subject: [PATCH 01/11] e2e test updated --- package-lock.json | 4 +- package.json | 1 + provisioning/dashboards/datasource.json | 373 ++++++++++++++- provisioning/dashboards/e2e.json | 369 ++++++++++++++- provisioning/dashboards/panels.json | 117 ++++- provisioning/dashboards/validation.json | 28 +- .../SelectElement/SelectElement.tsx | 1 + test/panel.spec.ts | 444 +++++++++++++++++- test/utils/form.ts | 296 ++++++++++++ test/utils/index.ts | 3 + test/utils/modal.ts | 55 +++ test/utils/selectors.ts | 51 ++ 12 files changed, 1650 insertions(+), 92 deletions(-) create mode 100644 test/utils/form.ts create mode 100644 test/utils/index.ts create mode 100644 test/utils/modal.ts create mode 100644 test/utils/selectors.ts diff --git a/package-lock.json b/package-lock.json index cf92c6ce..4b45a2ca 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "business-forms", - "version": "4.8.0", + "version": "4.9.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "business-forms", - "version": "4.8.0", + "version": "4.9.0", "license": "Apache-2.0", "dependencies": { "@emotion/css": "^11.13.0", diff --git a/package.json b/package.json index cc689e26..6c9c2f7d 100644 --- a/package.json +++ b/package.json @@ -75,6 +75,7 @@ "stop": "docker compose down", "test": "jest --watch --onlyChanged", "test:e2e": "npx playwright test", + "test:e2e:dev": "npx playwright test --ui", "test:e2e:docker": "docker compose --profile e2e up --exit-code-from test", "test:ci": "jest --maxWorkers 4 --coverage", "upgrade": "npm upgrade --save" diff --git a/provisioning/dashboards/datasource.json b/provisioning/dashboards/datasource.json index 1f4253e1..93e0eb52 100644 --- a/provisioning/dashboards/datasource.json +++ b/provisioning/dashboards/datasource.json @@ -18,8 +18,8 @@ "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, + "id": 1, "links": [], - "liveNow": false, "panels": [ { "datasource": { @@ -27,9 +27,13 @@ "type": "marcusolsson-static-datasource", "uid": "P1D2C73DC01F2359B" }, + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, "gridPos": { "h": 8, - "w": 17, + "w": 14, "x": 0, "y": 0 }, @@ -124,7 +128,7 @@ }, "updateEnabled": "auto" }, - "pluginVersion": "4.7.0", + "pluginVersion": "4.9.0", "targets": [ { "datasource": { @@ -172,10 +176,14 @@ "type": "marcusolsson-static-datasource", "uid": "P1D2C73DC01F2359B" }, + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, "gridPos": { - "h": 25, - "w": 7, - "x": 17, + "h": 8, + "w": 10, + "x": 14, "y": 0 }, "id": 5, @@ -318,7 +326,12 @@ }, "updateEnabled": "auto" }, - "pluginVersion": "4.7.0", + "pluginVersion": "4.9.0", + "targets": [ + { + "refId": "A" + } + ], "title": "Initial patchFormValue", "type": "volkovlabs-form-panel" }, @@ -376,6 +389,8 @@ "filter": true, "groupSelection": false, "header": true, + "isUseLocalTime": false, + "minimizeOutputFormat": "text", "padding": 10, "persistent": false, "saveSelectedGroup": false, @@ -390,7 +405,13 @@ "tabsInOrder": true, "variable": "device" }, - "pluginVersion": "3.5.0", + "pluginVersion": "3.6.0", + "targets": [ + { + "refId": "A" + } + ], + "title": "", "type": "volkovlabs-variable-panel" }, { @@ -398,9 +419,13 @@ "type": "postgres", "uid": "PCC52D03280B7034C" }, + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, "gridPos": { "h": 9, - "w": 13, + "w": 10, "x": 4, "y": 8 }, @@ -650,7 +675,7 @@ }, "updateEnabled": "auto" }, - "pluginVersion": "4.7.0", + "pluginVersion": "4.9.0", "targets": [ { "datasource": { @@ -689,9 +714,308 @@ "type": "postgres", "uid": "PCC52D03280B7034C" }, + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, "gridPos": { "h": 8, - "w": 13, + "w": 10, + "x": 14, + "y": 8 + }, + "id": 8, + "options": { + "buttonGroup": { + "orientation": "center", + "size": "md" + }, + "confirmModal": { + "body": "Please confirm to update changed values", + "cancel": "Cancel", + "columns": { + "include": ["name", "oldValue", "newValue"], + "name": "Label", + "newValue": "New Value", + "oldValue": "Old Value" + }, + "confirm": "Confirm", + "elementDisplayMode": "modified", + "title": "Confirm update request" + }, + "elementValueChanged": "", + "elements": [ + { + "fieldName": "max", + "id": "max", + "labelWidth": 10, + "options": [], + "queryField": { + "label": "A:max", + "refId": "A", + "value": "max" + }, + "section": "current", + "title": "Max", + "tooltip": "", + "type": "disabled", + "uid": "8285e945-b1cf-4dd6-9efd-8e322196222a", + "unit": "", + "value": 0, + "width": 20 + }, + { + "fieldName": "min", + "id": "min", + "labelWidth": 10, + "options": [], + "queryField": { + "label": "A:min", + "refId": "A", + "value": "min" + }, + "section": "current", + "title": "Min", + "tooltip": "", + "type": "disabled", + "uid": "6d3ca353-178b-4a7a-b23d-e548cf3d2437", + "unit": "", + "value": 0, + "width": 20 + }, + { + "fieldName": "speed", + "id": "speed", + "labelWidth": 10, + "options": [], + "queryField": { + "label": "A:speed", + "refId": "A", + "value": "speed" + }, + "section": "current", + "title": "Speed", + "tooltip": "", + "type": "disabled", + "uid": "b7c38a6c-6c59-41db-a439-1964d20e660b", + "unit": "", + "value": 0, + "width": 20 + }, + { + "fieldName": "max", + "id": "max", + "labelWidth": 10, + "queryField": { + "label": "A:max", + "refId": "A", + "value": "max" + }, + "section": "new", + "title": "Max", + "tooltip": "", + "type": "number", + "uid": "3d3cf24f-7611-482d-b27b-b55621745dda", + "unit": "", + "value": 0, + "width": 20 + }, + { + "fieldName": "min", + "id": "min", + "labelWidth": 10, + "queryField": { + "label": "A:min", + "refId": "A", + "value": "min" + }, + "section": "new", + "title": "Min", + "tooltip": "", + "type": "number", + "uid": "bc2c6772-5bbd-456d-9fe2-f1824c373c68", + "unit": "", + "value": 0, + "width": 20 + }, + { + "fieldName": "speed", + "id": "speed", + "labelWidth": 10, + "max": 100, + "min": 0, + "queryField": { + "label": "A:speed", + "refId": "A", + "value": "speed" + }, + "section": "new", + "step": 1, + "title": "Speed", + "tooltip": "", + "type": "slider", + "uid": "1918dc47-f686-4ca2-aeb3-7f21c360f621", + "unit": "", + "value": 0 + } + ], + "initial": { + "code": "const payload = {};\n\ncontext.panel.elements.forEach((element) => {\n if (!element.value) {\n return;\n }\n\n payload[element.id] = element.value;\n})\n\nconsole.log(payload);\ncontext.panel.setInitial(payload);\n\nreturn;", + "contentType": "application/json", + "datasource": "PostgreSQL", + "getPayload": "", + "highlight": true, + "highlightColor": "red", + "method": "datasource", + "payload": { + "editorMode": "code", + "format": "table", + "rawQuery": true, + "rawSql": "select * from configuration where name ='$device';", + "refId": "A", + "sql": { + "columns": [ + { + "parameters": [], + "type": "function" + } + ], + "groupBy": [ + { + "property": { + "type": "string" + }, + "type": "groupBy" + } + ], + "limit": 50 + } + } + }, + "layout": { + "orientation": "horizontal", + "padding": 10, + "sectionVariant": "default", + "sections": [ + { + "id": "current", + "name": "Current Values" + }, + { + "id": "new", + "name": "New Values" + } + ], + "variant": "split" + }, + "reset": { + "backgroundColor": "purple", + "foregroundColor": "yellow", + "icon": "process", + "text": "Reset", + "variant": "secondary" + }, + "resetAction": { + "code": "console.log(context.panel.data, context.panel.response, context.panel.initial, context.panel.elements);", + "confirm": false, + "getPayload": "return {\n rawSql: '',\n format: 'table',\n}", + "mode": "initial", + "payload": {} + }, + "saveDefault": { + "icon": "save", + "text": "Save Default", + "variant": "hidden" + }, + "submit": { + "backgroundColor": "purple", + "foregroundColor": "yellow", + "icon": "cloud-upload", + "text": "Submit", + "variant": "primary" + }, + "sync": true, + "update": { + "code": "if (context.panel.response && context.panel.response.state === 'Done') {\n context.grafana.notifySuccess(['Update', 'Values updated successfully.']);\n context.panel.initialRequest();\n} else {\n context.grafana.notifyError(['Update', 'An error occurred updating values.']);\n}", + "confirm": true, + "contentType": "application/json", + "datasource": "PostgreSQL", + "getPayload": "const payload = {};\n\ncontext.panel.elements.forEach((element) => {\n if (!element.value) {\n return;\n }\n\n payload[element.id] = element.value;\n})\n\n/**\n * Data Source payload\n */\nreturn payload;", + "method": "datasource", + "payload": { + "editorMode": "code", + "format": "table", + "rawQuery": true, + "rawSql": "update\n configuration\nset\n min = ${payload.min},\n max = ${payload.max},\n speed = ${payload.speed}\n test = ${payload.speed}\nwhere\n name = '$device'", + "refId": "A", + "sql": { + "columns": [ + { + "parameters": [], + "type": "function" + } + ], + "groupBy": [ + { + "property": { + "type": "string" + }, + "type": "groupBy" + } + ], + "limit": 50 + } + }, + "payloadMode": "custom" + }, + "updateEnabled": "auto" + }, + "pluginVersion": "4.9.0", + "targets": [ + { + "datasource": { + "type": "postgres", + "uid": "PCC52D03280B7034C" + }, + "editorMode": "code", + "format": "table", + "rawQuery": true, + "rawSql": "select * from configuration where name ='$device';", + "refId": "A", + "sql": { + "columns": [ + { + "parameters": [], + "type": "function" + } + ], + "groupBy": [ + { + "property": { + "type": "string" + }, + "type": "groupBy" + } + ], + "limit": 50 + } + } + ], + "title": "Invalid Payload", + "type": "volkovlabs-form-panel" + }, + { + "datasource": { + "type": "postgres", + "uid": "PCC52D03280B7034C" + }, + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 10, "x": 4, "y": 17 }, @@ -918,7 +1242,7 @@ }, "updateEnabled": "auto" }, - "pluginVersion": "4.7.0", + "pluginVersion": "4.9.0", "targets": [ { "datasource": { @@ -957,6 +1281,10 @@ "type": "postgres", "uid": "PCC52D03280B7034C" }, + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, "gridPos": { "h": 17, "w": 20, @@ -1235,7 +1563,7 @@ }, "updateEnabled": "auto" }, - "pluginVersion": "4.7.0", + "pluginVersion": "4.9.0", "targets": [ { "datasource": { @@ -1266,7 +1594,7 @@ } } ], - "title": "Data Source", + "title": "Data Source Options", "type": "volkovlabs-form-panel" }, { @@ -1274,6 +1602,10 @@ "type": "postgres", "uid": "PCC52D03280B7034C" }, + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, "gridPos": { "h": 17, "w": 20, @@ -1566,7 +1898,7 @@ }, "updateEnabled": "auto" }, - "pluginVersion": "4.7.0", + "pluginVersion": "4.9.0", "targets": [ { "datasource": { @@ -1597,18 +1929,18 @@ } } ], - "title": "Data Source", + "title": "Data Source Options 2", "type": "volkovlabs-form-panel" } ], + "preload": false, "refresh": "", - "schemaVersion": 39, + "schemaVersion": 40, "tags": [], "templating": { "list": [ { "current": { - "selected": false, "text": "device1", "value": "device1" }, @@ -1617,15 +1949,12 @@ "uid": "PCC52D03280B7034C" }, "definition": "select distinct name from configuration;", - "hide": 0, "includeAll": false, - "multi": false, "name": "device", "options": [], "query": "select distinct name from configuration;", "refresh": 1, "regex": "", - "skipUrlSync": false, "sort": 1, "type": "query" } @@ -1639,6 +1968,6 @@ "timezone": "", "title": "Configuration", "uid": "d185bf58-9e0a-4373-befa-3905a18af42e", - "version": 2, + "version": 5, "weekStart": "" } diff --git a/provisioning/dashboards/e2e.json b/provisioning/dashboards/e2e.json index 7d2950b8..dec41b06 100644 --- a/provisioning/dashboards/e2e.json +++ b/provisioning/dashboards/e2e.json @@ -18,17 +18,21 @@ "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, + "id": 2, "links": [], - "liveNow": false, "panels": [ { "datasource": { "type": "datasource", "uid": "grafana" }, + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, "gridPos": { - "h": 21, - "w": 17, + "h": 16, + "w": 12, "x": 0, "y": 0 }, @@ -55,7 +59,7 @@ "elements": [ { "id": "dateTime", - "labelWidth": 10, + "labelWidth": 15, "title": "DateTime", "type": "datetime", "uid": "69baa91c-9674-455e-896b-83e866a68b36", @@ -64,7 +68,7 @@ { "id": "time", "isUseLocalTime": false, - "labelWidth": 10, + "labelWidth": 15, "section": "", "title": "Time", "tooltip": "", @@ -75,7 +79,7 @@ }, { "id": "date", - "labelWidth": 10, + "labelWidth": 15, "section": "", "title": "Date", "tooltip": "", @@ -86,7 +90,7 @@ }, { "id": "amount", - "labelWidth": 10, + "labelWidth": 15, "title": "Amount", "type": "number", "uid": "5ec96b3b-1360-48fb-aea0-6aecfd5a6bfb", @@ -95,7 +99,7 @@ }, { "id": "updated", - "labelWidth": 10, + "labelWidth": 15, "title": "Updated", "type": "boolean", "uid": "251b9dc7-7fed-4df1-981f-122cd141c51c", @@ -103,7 +107,7 @@ }, { "id": "name", - "labelWidth": 10, + "labelWidth": 15, "title": "Name", "type": "string", "uid": "67e7fa0e-8038-4b14-80d0-9e2ccc3336ff", @@ -111,7 +115,7 @@ }, { "id": "step", - "labelWidth": 10, + "labelWidth": 15, "max": 6, "min": 0, "step": 1, @@ -124,7 +128,7 @@ { "allowCustomValue": false, "id": "select", - "labelWidth": 10, + "labelWidth": 15, "options": [ { "id": "max", @@ -149,7 +153,7 @@ }, { "id": "radio", - "labelWidth": 10, + "labelWidth": 15, "options": [ { "id": "plus", @@ -166,28 +170,227 @@ "type": "radio", "uid": "9dbab8b8-92ca-4bd0-b7b4-9e5f47911de8" }, + { + "id": "password", + "labelWidth": 15, + "title": "Password", + "type": "password", + "uid": "0b021eb5-f8b3-4e8d-9387-9693d6a7df8c" + }, { "id": "disabled", - "labelWidth": 10, + "labelWidth": 15, "options": [], "title": "Disabled", "type": "disabled", "uid": "79758a94-6654-4dbe-a44e-a311cba6a4e5" }, { - "id": "password", - "labelWidth": 10, - "title": "Password", - "type": "password", - "uid": "0b021eb5-f8b3-4e8d-9387-9693d6a7df8c" + "id": "link", + "labelWidth": 15, + "linkText": "https://volkovlabs.io/", + "section": "", + "target": "_blank", + "title": "link", + "tooltip": "", + "type": "link", + "uid": "69a27982-9e3d-47c1-afe2-7c825d5fa12d", + "unit": "", + "value": "" + }, + { + "allowCustomValue": false, + "id": "checkbox", + "labelWidth": 15, + "options": [ + { + "id": "1", + "label": "1", + "type": "number", + "value": 1 + }, + { + "id": "2", + "label": "2", + "type": "number", + "value": 2 + } + ], + "optionsSource": "Custom", + "section": "", + "title": "checkbox", + "tooltip": "", + "type": "checkboxList", + "uid": "4722ca3f-aca3-4fb2-976c-71b3ddc31797", + "unit": "", + "value": [] + } + ], + "height": 0, + "heightMode": "auto", + "initial": { + "code": "console.log(context.panel.response)\nconsole.log(context)", + "contentType": "application/json", + "getPayload": "return {\n rawSql: '',\n format: 'table',\n}", + "header": [ + { + "name": "X-Key", + "value": "123" + } + ], + "highlight": true, + "highlightColor": "red", + "method": "GET", + "payload": {}, + "payloadMode": "all", + "url": "http://localhost:3001/${var}" + }, + "layout": { + "orientation": "horizontal", + "padding": 10, + "sectionVariant": "default", + "variant": "single" + }, + "name": "data", + "reset": { + "backgroundColor": "purple", + "foregroundColor": "yellow", + "icon": "process", + "text": "Reset", + "variant": "secondary" + }, + "resetAction": { + "code": "if (response && response.ok) {\n notifySuccess(['Update', 'Values updated successfully.']);\n locationService.reload();\n} else {\n notifyError(['Update', 'An error occured updating values.']);\n}", + "confirm": false, + "getPayload": "return {\n rawSql: '',\n format: 'table',\n}", + "mode": "initial", + "payload": {} + }, + "saveDefault": { + "icon": "save", + "text": "Save Default", + "variant": "hidden" + }, + "submit": { + "backgroundColor": "purple", + "foregroundColor": "yellow", + "icon": "cloud-upload", + "orientation": "center", + "size": "md", + "text": "Update", + "variant": "primary" + }, + "sync": true, + "update": { + "code": "if (context.panel.response && context.panel.response.ok) {\n context.grafana.notifySuccess(['Update', 'Values updated successfully.']);\n context.panel.initialRequest();\n} else {\n context.grafana.notifyError(['Update', `An error occured updating values: ${response.status}`]);\n}", + "confirm": true, + "contentType": "application/json", + "getPayload": "const payload = {};\n\nelements.forEach((element) => {\n if (!element.value) {\n return;\n }\n\n payload[element.id] = element.value;\n})\n\nreturn payload;\n\n/**\n * Data Source payload\n */ \nreturn {\n rawSql: '',\n format: 'table',\n};", + "header": [ + { + "name": "X-Key", + "value": "123" + } + ], + "method": "POST", + "payload": {}, + "payloadMode": "all", + "url": "http://localhost:3001/${var}" + }, + "updateEnabled": "auto", + "width": 0, + "widthMode": "auto" + }, + "pluginVersion": "4.9.0", + "targets": [ + { + "refId": "A" + } + ], + "title": "Single Form", + "type": "volkovlabs-form-panel" + }, + { + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, + "gridPos": { + "h": 21, + "w": 12, + "x": 12, + "y": 0 + }, + "id": 3, + "options": { + "buttonGroup": { + "orientation": "center", + "size": "md" + }, + "confirmModal": { + "body": "Please confirm to update changed values", + "cancel": "Cancel", + "columns": { + "include": ["name", "oldValue", "newValue"], + "name": "Label", + "newValue": "New Value", + "oldValue": "Old Value" + }, + "confirm": "Confirm", + "elementDisplayMode": "modified", + "title": "Confirm update request" + }, + "elementValueChanged": "", + "elements": [ + { + "accept": "", + "id": "file", + "labelWidth": 15, + "multiple": true, + "section": "", + "title": "file", + "tooltip": "", + "type": "file", + "uid": "d2c7fbe1-e246-40ff-8470-b2a2ff7af762", + "unit": "", + "value": [] }, { "id": "text", - "labelWidth": 10, + "labelWidth": 15, "rows": 4, "title": "Text", "type": "textarea", "uid": "ff544458-ce27-4e30-bac5-58a7f8fbe955" + }, + { + "id": "readOnlyTextArea", + "labelWidth": 15, + "rows": 10, + "section": "", + "title": "Disbled Textbox", + "tooltip": "", + "type": "disabledTextarea", + "uid": "7da514d1-8989-45b6-b42d-6919149573bf", + "unit": "", + "value": "" + }, + { + "height": 200, + "id": "code", + "labelWidth": 15, + "language": "javascript", + "section": "", + "title": "code", + "tooltip": "", + "type": "code", + "uid": "29fd6236-b227-4a10-abfb-dd47a5c66798", + "unit": "", + "value": "" } ], "height": 0, @@ -265,23 +468,138 @@ "width": 0, "widthMode": "auto" }, - "pluginVersion": "4.7.0", - "title": "Single Form", + "pluginVersion": "4.9.0", + "targets": [ + { + "refId": "A" + } + ], + "title": "Boxes", + "type": "volkovlabs-form-panel" + }, + { + "datasource": { + "type": "marcusolsson-static-datasource", + "uid": "P1D2C73DC01F2359B" + }, + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 12, + "x": 0, + "y": 16 + }, + "id": 4, + "options": { + "buttonGroup": { + "orientation": "center", + "size": "md" + }, + "confirmModal": { + "body": "Please confirm to update changed values", + "cancel": "Cancel", + "columns": { + "include": ["name", "oldValue", "newValue"], + "name": "Label", + "newValue": "New Value", + "oldValue": "Old Value" + }, + "confirm": "Confirm", + "elementDisplayMode": "modified", + "title": "Confirm update request" + }, + "elementValueChanged": "", + "elements": [ + { + "hidden": false, + "id": "Name", + "labelWidth": 10, + "section": "", + "title": "Name", + "tooltip": "", + "type": "string", + "uid": "86564314-5234-4cc3-8e0c-f29d0c4c4bf7", + "unit": "", + "value": "" + } + ], + "initial": { + "code": "console.log(context.panel.data, context.panel.response, context.panel.initial, context.panel.elements);\n\nreturn;\n\n/**\n * Data Source\n * Requires form elements to be defined\n */\nconst dataQuery = context.utils.toDataQueryResponse(context.panel.response);\nconsole.log(dataQuery);", + "contentType": "application/json", + "getPayload": "return {}", + "highlight": false, + "highlightColor": "red", + "method": "GET", + "payload": {} + }, + "layout": { + "orientation": "horizontal", + "padding": 10, + "sectionVariant": "default", + "variant": "single" + }, + "reset": { + "backgroundColor": "purple", + "foregroundColor": "yellow", + "icon": "process", + "text": "Reset", + "variant": "hidden" + }, + "resetAction": { + "code": "if (context.panel.response) {\n context.grafana.notifySuccess(['Update', 'Values updated successfully.']);\n context.grafana.locationService.reload();\n} else {\n context.grafana.notifyError(['Update', 'An error occurred updating values.']);\n}", + "confirm": false, + "getPayload": "return {}", + "mode": "initial", + "payload": {} + }, + "saveDefault": { + "icon": "save", + "text": "Save Default", + "variant": "hidden" + }, + "submit": { + "backgroundColor": "purple", + "foregroundColor": "yellow", + "icon": "cloud-upload", + "text": "Submit", + "variant": "primary" + }, + "sync": true, + "update": { + "code": "if (context.panel.response) {\n context.grafana.notifySuccess(['Update', 'Values updated successfully.']);\n context.grafana.locationService.reload();\n} else {\n context.grafana.notifyError(['Update', 'An error occurred updating values.']);\n}", + "confirm": false, + "contentType": "application/json", + "getPayload": "const payload = {};\n\ncontext.panel.elements.forEach((element) => {\n if (!element.value) {\n return;\n }\n\n payload[element.id] = element.value;\n})\n\nreturn payload;", + "method": "-", + "payload": "const payload = {};\n\ncontext.panel.elements.forEach((element) => {\n if (!element.value) {\n return;\n }\n\n payload[element.id] = element.value;\n})\n\nreturn payload;", + "payloadMode": "all" + }, + "updateEnabled": "auto" + }, + "pluginVersion": "4.9.0", + "targets": [ + { + "refId": "A" + } + ], + "title": "Empty URL", "type": "volkovlabs-form-panel" } ], + "preload": false, "refresh": "", - "schemaVersion": 39, + "schemaVersion": 40, "tags": [], "templating": { "list": [ { "current": { - "selected": false, "text": "test", "value": "test" }, - "hide": 0, "label": "Var", "name": "var", "options": [ @@ -292,7 +610,6 @@ } ], "query": "test", - "skipUrlSync": false, "type": "textbox" } ] @@ -305,6 +622,6 @@ "timezone": "", "title": "E2E", "uid": "ddab9faf-2c15-431a-a047-9a7a6a0aed71", - "version": 1, + "version": 2, "weekStart": "" } diff --git a/provisioning/dashboards/panels.json b/provisioning/dashboards/panels.json index bc5a29ba..1d17da64 100644 --- a/provisioning/dashboards/panels.json +++ b/provisioning/dashboards/panels.json @@ -24,15 +24,18 @@ "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 4, + "id": 3, "links": [], - "liveNow": false, "panels": [ { "datasource": { "type": "datasource", "uid": "grafana" }, + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, "gridPos": { "h": 6, "w": 9, @@ -117,7 +120,12 @@ }, "updateEnabled": "auto" }, - "pluginVersion": "4.5.0", + "pluginVersion": "4.9.0", + "targets": [ + { + "refId": "A" + } + ], "title": "No Elements", "type": "volkovlabs-form-panel" }, @@ -126,6 +134,10 @@ "type": "datasource", "uid": "grafana" }, + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, "gridPos": { "h": 12, "w": 15, @@ -233,7 +245,12 @@ }, "updateEnabled": "auto" }, - "pluginVersion": "4.5.0", + "pluginVersion": "4.9.0", + "targets": [ + { + "refId": "A" + } + ], "title": "Data Manipulation Form", "type": "volkovlabs-form-panel" }, @@ -242,6 +259,10 @@ "type": "datasource", "uid": "grafana" }, + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, "gridPos": { "h": 6, "w": 9, @@ -325,7 +346,12 @@ }, "updateEnabled": "auto" }, - "pluginVersion": "4.5.0", + "pluginVersion": "4.9.0", + "targets": [ + { + "refId": "A" + } + ], "title": "Buttons only", "type": "volkovlabs-form-panel" }, @@ -334,6 +360,10 @@ "type": "datasource", "uid": "grafana" }, + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, "gridPos": { "h": 13, "w": 9, @@ -410,7 +440,7 @@ "layout": { "orientation": "vertical", "padding": 10, - "sectionVariant": "default", + "sectionVariant": "collapsable", "sections": [ { "id": "Section 1", @@ -464,8 +494,13 @@ }, "updateEnabled": "auto" }, - "pluginVersion": "4.5.0", - "title": "Replace Variables", + "pluginVersion": "4.9.0", + "targets": [ + { + "refId": "A" + } + ], + "title": "Sections", "type": "volkovlabs-form-panel" }, { @@ -473,6 +508,10 @@ "type": "marcusolsson-static-datasource", "uid": "P1D2C73DC01F2359B" }, + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, "gridPos": { "h": 7, "w": 15, @@ -602,7 +641,12 @@ }, "updateEnabled": "auto" }, - "pluginVersion": "4.5.0", + "pluginVersion": "4.9.0", + "targets": [ + { + "refId": "A" + } + ], "title": "Read Only Updated", "type": "volkovlabs-form-panel" }, @@ -611,6 +655,10 @@ "type": "marcusolsson-static-datasource", "uid": "P1D2C73DC01F2359B" }, + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, "gridPos": { "h": 8, "w": 15, @@ -681,6 +729,7 @@ "width": 0 }, { + "allowCustomValue": false, "id": "type", "labelWidth": 10, "options": [ @@ -773,7 +822,12 @@ }, "updateEnabled": "auto" }, - "pluginVersion": "4.5.0", + "pluginVersion": "4.9.0", + "targets": [ + { + "refId": "A" + } + ], "title": "Control Panel", "type": "volkovlabs-form-panel" }, @@ -782,6 +836,10 @@ "type": "datasource", "uid": "grafana" }, + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, "gridPos": { "h": 13, "w": 9, @@ -843,6 +901,7 @@ "value": 4 }, { + "allowCustomValue": false, "id": "select", "options": [ { @@ -980,7 +1039,12 @@ "width": 0, "widthMode": "auto" }, - "pluginVersion": "4.5.0", + "pluginVersion": "4.9.0", + "targets": [ + { + "refId": "A" + } + ], "title": "Single Form", "type": "volkovlabs-form-panel" }, @@ -989,6 +1053,10 @@ "type": "datasource", "uid": "grafana" }, + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, "gridPos": { "h": 9, "w": 15, @@ -1046,6 +1114,7 @@ "unit": "" }, { + "allowCustomValue": false, "id": "select", "options": [ { @@ -1182,7 +1251,12 @@ "width": 0, "widthMode": "auto" }, - "pluginVersion": "4.5.0", + "pluginVersion": "4.9.0", + "targets": [ + { + "refId": "A" + } + ], "title": "Split Form", "type": "volkovlabs-form-panel" }, @@ -1191,6 +1265,10 @@ "type": "datasource", "uid": "grafana" }, + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, "gridPos": { "h": 4, "w": 9, @@ -1280,23 +1358,27 @@ }, "updateEnabled": "auto" }, - "pluginVersion": "4.5.0", + "pluginVersion": "4.9.0", + "targets": [ + { + "refId": "A" + } + ], "title": "No Initial Request and Custom Button", "type": "volkovlabs-form-panel" } ], + "preload": false, "refresh": "", - "schemaVersion": 39, + "schemaVersion": 40, "tags": [], "templating": { "list": [ { "current": { - "selected": false, "text": "test2", "value": "test2" }, - "hide": 0, "label": "Var", "name": "var", "options": [ @@ -1307,7 +1389,6 @@ } ], "query": "test2", - "skipUrlSync": false, "type": "textbox" } ] @@ -1320,6 +1401,6 @@ "timezone": "", "title": "Panels", "uid": "O4tc_E6Gz", - "version": 4, + "version": 1, "weekStart": "" } diff --git a/provisioning/dashboards/validation.json b/provisioning/dashboards/validation.json index 7f2be9b9..c9d08615 100644 --- a/provisioning/dashboards/validation.json +++ b/provisioning/dashboards/validation.json @@ -18,15 +18,18 @@ "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 5, + "id": 7, "links": [], - "liveNow": false, "panels": [ { "datasource": { "type": "marcusolsson-static-datasource", "uid": "P1D2C73DC01F2359B" }, + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, "gridPos": { "h": 8, "w": 12, @@ -55,6 +58,7 @@ "elementValueChanged": "let elements = context.panel.elements;\n\n/**\n * Reset room and bench values on facility change\n */\nif (context.element.id === 'facility') {\n elements = elements.map((element) => {\n if (element.id === 'room' || element.id === 'bench') {\n return {\n ...element,\n value: null,\n }\n }\n\n return element;\n })\n}\n\n/**\n * Reset bench value on room change\n */\nif (context.element.id === 'room') {\n elements = elements.map((element) => {\n if (element.id === 'bench') {\n return {\n ...element,\n value: null,\n }\n }\n\n return element;\n })\n}\n\n/**\n * Update form elements\n */\ncontext.panel.onChangeElements(elements);\n\n/**\n * Caclulate form validation state\n */\nconst isFormValid = elements.every((element) => !!element.value);\n\n/**\n * Enable/Disable submit button\n */\nif (isFormValid) {\n context.panel.enableSubmit();\n} else {\n context.panel.disableSubmit();\n}", "elements": [ { + "allowCustomValue": false, "disableIf": "", "id": "facility", "labelWidth": 10, @@ -83,6 +87,20 @@ "value": "" }, { + "disableIf": "const facility = context.panel.elements.find((element) => element.id === 'facility')\n\nreturn !facility?.value", + "id": "number", + "labelWidth": 10, + "section": "", + "showIf": "", + "title": "Number", + "tooltip": "", + "type": "number", + "uid": "fc06941b-31fa-4d09-95ca-89a43e543ea9", + "unit": "", + "value": 0 + }, + { + "allowCustomValue": false, "disableIf": "const facility = context.panel.elements.find((element) => element.id === 'facility')\n\nreturn !facility?.value", "id": "room", "labelWidth": 10, @@ -110,6 +128,7 @@ "value": "" }, { + "allowCustomValue": false, "disableIf": "const facility = context.panel.elements.find((element) => element.id === 'facility')\nconst room = context.panel.elements.find((element) => element.id === 'room')\n\nreturn !facility?.value || !room?.value", "id": "bench", "labelWidth": 10, @@ -190,7 +209,7 @@ }, "updateEnabled": "manual" }, - "pluginVersion": "4.1.0", + "pluginVersion": "4.9.0", "targets": [ { "datasource": { @@ -204,8 +223,9 @@ "type": "volkovlabs-form-panel" } ], + "preload": false, "refresh": "", - "schemaVersion": 39, + "schemaVersion": 40, "tags": [], "templating": { "list": [] diff --git a/src/components/FormElement/components/SelectElement/SelectElement.tsx b/src/components/FormElement/components/SelectElement/SelectElement.tsx index fbf254d2..4f428d6c 100644 --- a/src/components/FormElement/components/SelectElement/SelectElement.tsx +++ b/src/components/FormElement/components/SelectElement/SelectElement.tsx @@ -47,6 +47,7 @@ export const SelectElement: React.FC = ({ element, onChange, highlightCla key={element.id} isMulti={element.type === FormElementType.MULTISELECT} aria-label={TEST_IDS.formElements.fieldSelect} + data-testid={TEST_IDS.formElements.fieldSelect} value={element.value !== undefined ? element.value : null} allowCustomValue={element.allowCustomValue} onChange={(event) => { diff --git a/test/panel.spec.ts b/test/panel.spec.ts index c7465ec9..b9bb9d08 100644 --- a/test/panel.spec.ts +++ b/test/panel.spec.ts @@ -1,5 +1,6 @@ import { test, expect } from '@grafana/plugin-e2e'; import { TEST_IDS } from '../src/constants/tests'; +import { ModalHelper, PanelHelper } from './utils'; test.describe('Data Manipulation Panel', () => { test('Check grafana version', async ({ grafanaVersion }) => { @@ -7,25 +8,428 @@ test.describe('Data Manipulation Panel', () => { expect(grafanaVersion).toEqual(grafanaVersion); }); - test('Should display a Form', async ({ gotoDashboardPage, dashboardPage, page }) => { - /** - * Go To E2E dashboard - * return dashboardPage - */ - await gotoDashboardPage({ uid: 'ddab9faf-2c15-431a-a047-9a7a6a0aed71' }); - - /** - * Find panel by title with chart - * Should be visible - */ - await expect(dashboardPage.getPanelByTitle('Single Form').locator).toBeVisible(); - - await page.waitForTimeout(2500); - /** - * Check and compare image - */ - await expect( - dashboardPage.getPanelByTitle('Single Form').locator.getByTestId(TEST_IDS.panel.root) - ).toHaveScreenshot('actual-screenshot.png', { maxDiffPixelRatio: 0.1 }); + test.describe('Base', () => { + test('Should display a Form panels with all elements', async ({ gotoDashboardPage, readProvisionedDashboard }) => { + /** + * Go To Panels dashboard e2e.json + * return dashboardPage + */ + const dashboard = await readProvisionedDashboard({ fileName: 'e2e.json' }); + const dashboardPage = await gotoDashboardPage({ uid: dashboard.uid }); + + /** + * Check Presence + */ + const panel = new PanelHelper(dashboardPage, 'Single Form'); + + await panel.checkIfNoErrors(); + await panel.checkPresence(); + + const elements = panel.getElements(); + + /** + * Check all elements Presence + */ + await elements.checkPresence(); + await elements.checkElementPresence('dateTime', 'datetime'); + await elements.checkElementPresence('time', 'time'); + await elements.checkElementPresence('date', 'date'); + await elements.checkElementPresence('amount', 'number'); + await elements.checkElementPresence('updated', 'boolean'); + await elements.checkElementPresence('name', 'string'); + await elements.checkElementPresence('step', 'slider'); + await elements.checkElementPresence('select', 'select'); + await elements.checkElementPresence('radio', 'radio'); + await elements.checkElementPresence('password', 'password'); + await elements.checkElementPresence('disabled', 'disabled'); + await elements.checkElementPresence('link', 'link'); + await elements.checkElementPresence('checkbox', 'checkboxList'); + + const panelBoxes = new PanelHelper(dashboardPage, 'Boxes'); + await panelBoxes.checkIfNoErrors(); + await panelBoxes.checkPresence(); + + const boxesElements = panelBoxes.getElements(); + await boxesElements.checkPresence(); + await boxesElements.checkElementPresence('file', 'file'); + await boxesElements.checkElementPresence('text', 'textarea'); + await boxesElements.checkElementPresence('readOnlyTextArea', 'disabledTextarea'); + await boxesElements.checkElementPresence('code', 'code'); + }); + + test('Should add empty Form panel', async ({ gotoDashboardPage, readProvisionedDashboard }) => { + /** + * Go To Panels dashboard e2e.json + * return dashboardPage + */ + const dashboard = await readProvisionedDashboard({ fileName: 'e2e.json' }); + const dashboardPage = await gotoDashboardPage({ uid: dashboard.uid }); + + /** + * Add new visualization + */ + const editPage = await dashboardPage.addPanel(); + await editPage.setVisualization('Business Forms'); + await editPage.setPanelTitle('Business Forms Empty'); + await editPage.backToDashboard(); + + /** + * Should add empty visualization without errors + */ + const panel = new PanelHelper(dashboardPage, 'Business Forms Empty'); + await panel.checkIfNoErrors(); + await panel.checkPresence(); + await panel.checkAlertMessage(); + }); + + test('Should add Form panel with base element', async ({ gotoDashboardPage, readProvisionedDashboard, page }) => { + /** + * Go To Panels dashboard e2e.json + * return dashboardPage + */ + const dashboard = await readProvisionedDashboard({ fileName: 'e2e.json' }); + const dashboardPage = await gotoDashboardPage({ uid: dashboard.uid }); + + /** + * Add new visualization + */ + const editPage = await dashboardPage.addPanel(); + await editPage.setVisualization('Business Forms'); + await editPage.setPanelTitle('Business Form New'); + + const panel = new PanelHelper(dashboardPage, 'Business Form New'); + const editor = panel.getPanelEditor(page.locator('body'), editPage); + + await editor.addElement('string', 'Name', 'String input'); + + /** + * Apply changes and return to dashboard + */ + await editPage.backToDashboard(); + + /** + * Check Presence + */ + await panel.checkPresence(); + await panel.checkIfNoErrors(); + + const elements = panel.getElements(); + + await elements.checkPresence(); + await elements.checkElementPresence('string', 'string'); + }); + }); + + test.describe('Initial request', () => { + test('Should set initial values from Datasource', async ({ gotoDashboardPage, readProvisionedDashboard }) => { + /** + * Go To Panels dashboard datasource.json + * return dashboardPage + */ + const dashboard = await readProvisionedDashboard({ fileName: 'datasource.json' }); + const dashboardPage = await gotoDashboardPage({ uid: dashboard.uid }); + + /** + * Check Presence + */ + const panel = new PanelHelper(dashboardPage, 'Data Source'); + + await panel.checkIfNoErrors(); + await panel.checkPresence(); + await panel.checkSectionPresence('Current Values'); + + const elements = panel.getElements(); + const disabledMaxElement = await elements.getDisabledElement('max', 'disabled'); + const disabledMinElement = await elements.getDisabledElement('min', 'disabled'); + const disabledSpeedElement = await elements.getDisabledElement('speed', 'disabled'); + + await disabledMaxElement.checkValue('100'); + await disabledMinElement.checkValue('11'); + await disabledSpeedElement.checkValue('54'); + }); + + test('Should set initial values from Query', async ({ gotoDashboardPage, readProvisionedDashboard }) => { + /** + * Go To Panels dashboard datasource.json + * return dashboardPage + */ + const variableParams = new URLSearchParams(); + variableParams.set('var-device', 'device2'); + + const dashboard = await readProvisionedDashboard({ fileName: 'datasource.json' }); + const dashboardPage = await gotoDashboardPage({ uid: dashboard.uid, queryParams: variableParams }); + + /** + * Check Presence + */ + const panel = new PanelHelper(dashboardPage, 'Query'); + + await panel.checkIfNoErrors(); + await panel.checkPresence(); + await panel.checkSectionPresence('Current Values'); + + const elements = panel.getElements(); + const disabledMaxElement = await elements.getDisabledElement('max', 'disabled'); + const disabledMinElement = await elements.getDisabledElement('min', 'disabled'); + const disabledSpeedElement = await elements.getDisabledElement('speed', 'disabled'); + + await disabledMaxElement.checkValue('60'); + await disabledMinElement.checkValue('0'); + await disabledSpeedElement.checkValue('10'); + + variableParams.set('var-device', 'device1'); + await gotoDashboardPage({ uid: dashboard.uid, queryParams: variableParams }); + + await disabledMaxElement.checkValue('100'); + await disabledMinElement.checkValue('11'); + await disabledSpeedElement.checkValue('54'); + }); + + test('Should display error for get initial request without URL', async ({ + gotoDashboardPage, + readProvisionedDashboard, + }) => { + /** + * Go To Panels dashboard e2e.json + * return dashboardPage + */ + const dashboard = await readProvisionedDashboard({ fileName: 'e2e.json' }); + const dashboardPage = await gotoDashboardPage({ uid: dashboard.uid }); + + /** + * Check Presence + */ + const panel = new PanelHelper(dashboardPage, 'Empty URL'); + await panel.checkPresence(); + await panel.checkErrorMessage(); + }); + }); + + test.describe('Update', () => { + test('Should update values', async ({ gotoDashboardPage, readProvisionedDashboard }) => { + /** + * Go To Panels dashboard datasource.json + * return dashboardPage + */ + + const dashboard = await readProvisionedDashboard({ fileName: 'datasource.json' }); + const dashboardPage = await gotoDashboardPage({ uid: dashboard.uid }); + + /** + * Check Presence + */ + const panel = new PanelHelper(dashboardPage, 'Data Source'); + + await panel.checkIfNoErrors(); + await panel.checkPresence(); + await panel.checkSectionPresence('Current Values'); + await panel.checkSectionPresence('New Values'); + + const elements = panel.getElements(); + const disabledMaxElement = await elements.getDisabledElement('max', 'disabled'); + const submitButton = panel.getSubmitButton(); + + await disabledMaxElement.checkValue('100'); + await submitButton.checkPresence(); + await submitButton.checkIsDisabled(); + + const numberMaxElement = await elements.getNumberElement('max', 'number'); + await numberMaxElement.checkValue('100'); + await numberMaxElement.setValue('125'); + + await submitButton.checkIsNotDisabled(); + await submitButton.submit(); + + const confirmModal = new ModalHelper(dashboardPage); + await confirmModal.checkPresence(); + await confirmModal.confirmButtonCheckPresence(); + await confirmModal.cancelButtonCheckPresence(); + await confirmModal.updateValues(); + + await submitButton.checkIsDisabled(); + await numberMaxElement.checkValue('125'); + await disabledMaxElement.checkValue('125'); + + /** + * Return to initial + */ + await numberMaxElement.setValue('100'); + await submitButton.submit(); + await confirmModal.updateValues(); + }); + + test('Should not update values if cancel', async ({ gotoDashboardPage, readProvisionedDashboard }) => { + /** + * Go To Panels dashboard datasource.json + * return dashboardPage + */ + + const dashboard = await readProvisionedDashboard({ fileName: 'datasource.json' }); + const dashboardPage = await gotoDashboardPage({ uid: dashboard.uid }); + + /** + * Check Presence + */ + const panel = new PanelHelper(dashboardPage, 'Data Source'); + + await panel.checkIfNoErrors(); + await panel.checkPresence(); + + const elements = panel.getElements(); + const disabledMaxElement = await elements.getDisabledElement('max', 'disabled'); + + const submitButton = panel.getSubmitButton(); + + await disabledMaxElement.checkValue('100'); + await submitButton.checkPresence(); + await submitButton.checkIsDisabled(); + + const numberMaxElement = await elements.getNumberElement('max', 'number'); + await numberMaxElement.setValue('125'); + + await submitButton.submit(); + + const confirmModal = new ModalHelper(dashboardPage); + + await confirmModal.cancelButtonCheckPresence(); + await confirmModal.cancelUpdateValues(); + await confirmModal.checkNotPresence(); + await disabledMaxElement.checkValue('100'); + }); + + test('Should reset values without update it', async ({ gotoDashboardPage, readProvisionedDashboard }) => { + /** + * Go To Panels dashboard datasource.json + * return dashboardPage + */ + + const dashboard = await readProvisionedDashboard({ fileName: 'datasource.json' }); + const dashboardPage = await gotoDashboardPage({ uid: dashboard.uid }); + + /** + * Check Presence + */ + const panel = new PanelHelper(dashboardPage, 'Data Source'); + + await panel.checkIfNoErrors(); + await panel.checkPresence(); + + const elements = panel.getElements(); + const disabledMaxElement = await elements.getDisabledElement('max', 'disabled'); + + const resetButton = panel.getResetButton(); + + await disabledMaxElement.checkValue('100'); + + const numberMaxElement = await elements.getNumberElement('max', 'number'); + const numberMinElement = await elements.getNumberElement('min', 'number'); + await numberMaxElement.checkValue('100'); + await numberMinElement.checkValue('11'); + + await numberMaxElement.setValue('115'); + await numberMinElement.setValue('15'); + + await resetButton.reset(); + + await numberMaxElement.checkValue('100'); + await numberMinElement.checkValue('11'); + }); + + test('Should display error for invalid update request', async ({ gotoDashboardPage, readProvisionedDashboard }) => { + /** + * Go To Panels dashboard datasource.json + * return dashboardPage + */ + const dashboard = await readProvisionedDashboard({ fileName: 'datasource.json' }); + const dashboardPage = await gotoDashboardPage({ uid: dashboard.uid }); + + /** + * Check Presence + */ + const panel = new PanelHelper(dashboardPage, 'Invalid Payload'); + + await panel.checkPresence(); + + const elements = panel.getElements(); + const numberMaxElement = await elements.getNumberElement('max', 'number'); + await numberMaxElement.setValue('125'); + + const submitButton = panel.getSubmitButton(); + await submitButton.submit(); + + const confirmModal = new ModalHelper(dashboardPage); + await confirmModal.updateValues(); + + await panel.checkErrorMessage(); + }); + }); + + test.describe('Element change', () => { + test('Should call element change function', async ({ gotoDashboardPage, readProvisionedDashboard }) => { + /** + * Go To Panels dashboard validation.json + * return dashboardPage + */ + + const dashboard = await readProvisionedDashboard({ fileName: 'validation.json' }); + const dashboardPage = await gotoDashboardPage({ uid: dashboard.uid }); + dashboardPage.getByGrafanaSelector; + /** + * Check Presence + */ + const panel = new PanelHelper(dashboardPage, 'Reset and Validate Elements'); + + await panel.checkIfNoErrors(); + await panel.checkPresence(); + + const elements = panel.getElements(); + const facilityElement = await elements.getSelectElement('facility', 'select', dashboardPage); + const numberElement = await elements.getNumberElement('number', 'number'); + + await numberElement.isDisabled(); + + await facilityElement.setValue(1); + + await numberElement.isNotDisabled(); + }); + }); + + test.describe('Sections', () => { + test('Should keep section state after refresh dashboard', async ({ + gotoDashboardPage, + readProvisionedDashboard, + }) => { + /** + * Go To Panels dashboard panels.json + * return dashboardPage + */ + const dashboard = await readProvisionedDashboard({ fileName: 'panels.json' }); + const dashboardPage = await gotoDashboardPage({ uid: dashboard.uid }); + + /** + * Check Presence + */ + const panel = new PanelHelper(dashboardPage, 'Sections'); + + await panel.checkIfNoErrors(); + await panel.checkPresence(); + + const elements = panel.getElements(); + await elements.checkElementNotPresence('element1', 'string'); + await panel.checkSectionPresence('Section 1'); + + /** + * Open section + */ + await panel.openSection('Section 1'); + await elements.checkElementPresence('element1', 'string'); + + await dashboardPage.refreshDashboard(); + + /** + * Element in open section should be visible + */ + await elements.checkElementPresence('element1', 'string'); + }); }); }); diff --git a/test/utils/form.ts b/test/utils/form.ts new file mode 100644 index 00000000..8e96cc3c --- /dev/null +++ b/test/utils/form.ts @@ -0,0 +1,296 @@ +import { Locator } from '@playwright/test'; +import { DashboardPage, expect, Panel, PanelEditPage } from '@grafana/plugin-e2e'; +import { getLocatorSelectors, LocatorSelectors } from './selectors'; +import { TEST_IDS } from '../../src/constants/tests'; + +const getElementsSelector = getLocatorSelectors(TEST_IDS.formElements); + +/** + * Disabled Element Helper + */ +class DisabledElementHelper { + private readonly locator: Locator; + + constructor(parentLocator: Locator) { + this.locator = parentLocator.getByTestId(TEST_IDS.formElements.fieldDisabled); + } + + private getMsg(message: string): string { + return `DisabledElement: ${message}`; + } + + public get() { + return this.locator; + } + + public async checkValue(text: string) { + return expect(this.get(), this.getMsg('Check Text')).toHaveValue(text); + } +} + +/** + * Number Element Helper + */ +class NumberElementHelper { + private readonly locator: Locator; + + constructor(parentLocator: Locator) { + this.locator = parentLocator.getByTestId(TEST_IDS.formElements.fieldNumber); + } + + private getMsg(message: string): string { + return `NumberElement: ${message}`; + } + + public get() { + return this.locator; + } + + public async checkValue(text: string) { + return expect(this.get(), this.getMsg('Check value')).toHaveValue(text); + } + + public async setValue(value: string) { + await this.get().fill(value); + return this.get().blur(); + } + + public async isDisabled() { + return expect(this.get(), this.getMsg('Number element is Disabled')).toBeDisabled(); + } + + public async isNotDisabled() { + return expect(this.get(), this.getMsg('Number element is not Disabled')).not.toBeDisabled(); + } +} + +/** + * Select Element Helper + */ +class SelectElementHelper { + private readonly locator: Locator; + private readonly page: DashboardPage; + + constructor(parentLocator: Locator, page: DashboardPage) { + this.locator = parentLocator.getByTestId(TEST_IDS.formElements.fieldSelect); + this.page = page; + } + + public get() { + return this.locator; + } + + public async setValue(fieldKey) { + await this.get().click(); + return this.page.getByGrafanaSelector(this.page.ctx.selectors.components.Select.option).getByText(fieldKey).click(); + } +} + +/** + * Elements Helper + */ +class ElementsHelper { + public selectors: LocatorSelectors; + + constructor(public readonly locator: Locator) { + this.selectors = this.getSelectors(locator); + } + + private getMsg(msg: string): string { + return `Elements: ${msg}`; + } + + private getSelectors(locator: Locator) { + return getElementsSelector(locator); + } + + public async checkPresence() { + return expect(this.selectors.root(), this.getMsg('Elements Container Presence')).toBeVisible(); + } + + public async checkElementPresence(elementId: string, elementType: string) { + return expect( + this.selectors.element(elementId, elementType), + this.getMsg(`Element ${elementId} Presence`) + ).toBeVisible(); + } + + public async checkElementNotPresence(elementId: string, elementType: string) { + return expect( + this.selectors.element(elementId, elementType), + this.getMsg(`Element ${elementId} not Presence`) + ).not.toBeVisible(); + } + + public async getElement(elementId: string, elementType: string) { + return this.selectors.element(elementId, elementType); + } + + public async getDisabledElement(elementId: string, elementType: string) { + const element = await this.getElement(elementId, elementType); + return new DisabledElementHelper(element); + } + + public async getNumberElement(elementId: string, elementType: string) { + const element = await this.getElement(elementId, elementType); + return new NumberElementHelper(element); + } + + public async getSelectElement(elementId: string, elementType: string, dashboardPage: DashboardPage) { + const element = await this.getElement(elementId, elementType); + return new SelectElementHelper(element, dashboardPage); + } +} + +/** + * Submit Button Helper + */ +class SubmitButtonHelper { + private readonly locator: Locator; + + constructor(parentLocator: Locator) { + this.locator = parentLocator.getByTestId(TEST_IDS.panel.buttonSubmit); + } + + private getMsg(message: string): string { + return `Submit Button: ${message}`; + } + + public get() { + return this.locator; + } + + public async checkPresence() { + return expect(this.get(), this.getMsg(`Check Submit button Presence`)).toBeVisible(); + } + + public async checkIsDisabled() { + return expect(this.get(), this.getMsg(`Check Submit button disabled`)).toBeDisabled(); + } + + public async checkIsNotDisabled() { + return expect(this.get(), this.getMsg(`Check Submit button not disabled`)).not.toBeDisabled(); + } + + public async submit() { + return this.get().click(); + } +} + +/** + * Reset Button Helper + */ +class ResetButtonHelper { + private readonly locator: Locator; + + constructor(parentLocator: Locator) { + this.locator = parentLocator.getByTestId(TEST_IDS.panel.buttonReset); + } + + private getMsg(message: string): string { + return `Reset Button: ${message}`; + } + + public get() { + return this.locator; + } + + public async checkPresence() { + return expect(this.get(), this.getMsg(`Check Reset button Presence`)).toBeVisible(); + } + + public async reset() { + return this.get().click(); + } +} + +/** + * Panel Editor Helper + */ +class PanelEditorHelper { + private readonly elementsEditorSelectors: LocatorSelectors; + + constructor( + private readonly locator: Locator, + private readonly editPage: PanelEditPage + ) { + this.elementsEditorSelectors = getLocatorSelectors(TEST_IDS.formElementsEditor)(this.locator); + } + + public async addElement(id: string, label: string, type: string) { + await this.elementsEditorSelectors.newElementId().fill(id); + await this.elementsEditorSelectors.newElementLabel().fill(label); + await this.elementsEditorSelectors.newElementType().click(); + await this.editPage + .getByGrafanaSelector(this.editPage.ctx.selectors.components.Select.option) + .getByText(type) + .click(); + await this.elementsEditorSelectors.buttonAddElement().click(); + await this.elementsEditorSelectors.buttonSaveChanges().click(); + } +} + +/** + * Panel Helper + */ +export class PanelHelper { + private readonly locator: Locator; + private readonly panel: Panel; + private readonly title: string; + private readonly selectors: LocatorSelectors; + + constructor(dashboardPage: DashboardPage, panelTitle: string) { + this.panel = dashboardPage.getPanelByTitle(panelTitle); + this.title = panelTitle; + this.locator = this.panel.locator; + this.selectors = getLocatorSelectors(TEST_IDS.panel)(this.locator); + } + + private getMsg(msg: string): string { + return `Panel: ${msg}`; + } + + public get() { + return this.locator; + } + + public getElements() { + return new ElementsHelper(this.locator); + } + + public getPanelEditor(locator: Locator, editPage: PanelEditPage) { + return new PanelEditorHelper(locator, editPage); + } + + public getSubmitButton() { + return new SubmitButtonHelper(this.locator); + } + + public getResetButton() { + return new ResetButtonHelper(this.locator); + } + + public async checkIfNoErrors() { + return expect(this.panel.getErrorIcon(), this.getMsg('Check If No Errors')).not.toBeVisible(); + } + + public async checkPresence() { + return expect(this.selectors.root(), this.getMsg(`Check ${this.title} Presence`)).toBeVisible(); + } + + public async checkAlertMessage() { + return expect(this.selectors.infoMessage(), this.getMsg(`Check Alert Message`)).toBeVisible(); + } + + public async checkSectionPresence(name: string) { + return expect(this.selectors.splitLayoutContent(name), this.getMsg(`Check ${name} Section`)).toBeVisible(); + } + + public async openSection(name: string) { + return this.selectors.splitLayoutContent(name).click(); + } + + public async checkErrorMessage() { + return expect(this.selectors.errorMessage(), this.getMsg(`Check Error Message`)).toBeVisible(); + } +} diff --git a/test/utils/index.ts b/test/utils/index.ts new file mode 100644 index 00000000..6b66b53c --- /dev/null +++ b/test/utils/index.ts @@ -0,0 +1,3 @@ +export * from './selectors'; +export * from './form'; +export * from './modal'; diff --git a/test/utils/modal.ts b/test/utils/modal.ts new file mode 100644 index 00000000..b326ba16 --- /dev/null +++ b/test/utils/modal.ts @@ -0,0 +1,55 @@ +import { Locator } from '@playwright/test'; +import { DashboardPage, expect } from '@grafana/plugin-e2e'; +import { getLocatorSelectors, LocatorSelectors } from './selectors'; +import { TEST_IDS } from '../../src/constants/tests'; + +/** + * Modal Helper + */ +export class ModalHelper { + private readonly locator: Locator; + private readonly selectors: LocatorSelectors; + + constructor(dashboardPage: DashboardPage) { + this.locator = dashboardPage.ctx.page.getByRole('dialog'); + this.selectors = getLocatorSelectors(TEST_IDS.panel)(this.locator); + } + + private getMsg(msg: string): string { + return `Panel: ${msg}`; + } + + public get() { + return this.locator; + } + + public async checkPresence() { + return expect(this.get(), this.getMsg(`Check Modal Presence`)).toBeVisible(); + } + + public async checkNotPresence() { + return expect(this.get(), this.getMsg(`Check Modal Not Presence`)).not.toBeVisible(); + } + + public async confirmButtonCheckPresence() { + return expect( + this.get().getByRole('button', { name: 'Confirm' }), + this.getMsg(`Check Confirm button Presence`) + ).toBeVisible(); + } + + public async cancelButtonCheckPresence() { + return expect( + this.get().getByRole('button', { name: 'Cancel' }), + this.getMsg(`Check Cancel button Presence`) + ).toBeVisible(); + } + + public async updateValues() { + return this.get().getByRole('button', { name: 'Confirm' }).click(); + } + + public async cancelUpdateValues() { + return this.get().getByRole('button', { name: 'Cancel' }).click(); + } +} diff --git a/test/utils/selectors.ts b/test/utils/selectors.ts new file mode 100644 index 00000000..0da35dc4 --- /dev/null +++ b/test/utils/selectors.ts @@ -0,0 +1,51 @@ +import { Locator } from '@playwright/test'; + +/** + * Selector + */ +type LocatorSelector = (...args: TArgs) => ReturnType<() => Locator>; + +/** + * Check If Selector Object + */ +type IsSelectorObject = TCandidate extends { + selector: (...args: unknown[]) => void; + apply: (...args: unknown[]) => void; +} + ? TCandidate & { selector: TCandidate['selector']; apply: TCandidate['apply'] } + : never; + +/** + * Selectors + */ +export type LocatorSelectors = { + [K in keyof T]: T[K] extends (...args: infer Args) => void + ? LocatorSelector + : T[K] extends IsSelectorObject + ? LocatorSelector> + : LocatorSelector<[]>; +}; + +export const getLocatorSelectors = + >( + selectors: TSelectors + ): ((locator: Locator) => LocatorSelectors) => + (locator) => { + return Object.entries(selectors).reduce((acc, [key, selector]) => { + const getElement = (...args: unknown[]): Locator => { + const getValue = typeof selector === 'object' && 'selector' in selector! ? selector.selector : selector; + const value = typeof getValue === 'function' ? getValue(...args) : getValue; + + if (value.startsWith('data-testid')) { + return locator.getByTestId(value); + } + + return locator.getByLabel(value); + }; + + return { + ...acc, + [key]: getElement, + }; + }, {} as LocatorSelectors); + }; From 280426dae82e54a3a56b60113202eff0443ae543 Mon Sep 17 00:00:00 2001 From: vitPinchuk Date: Thu, 31 Oct 2024 15:40:02 +0300 Subject: [PATCH 02/11] set correct initial values --- test/panel.spec.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/panel.spec.ts b/test/panel.spec.ts index b9bb9d08..a30664aa 100644 --- a/test/panel.spec.ts +++ b/test/panel.spec.ts @@ -144,7 +144,7 @@ test.describe('Data Manipulation Panel', () => { const disabledSpeedElement = await elements.getDisabledElement('speed', 'disabled'); await disabledMaxElement.checkValue('100'); - await disabledMinElement.checkValue('11'); + await disabledMinElement.checkValue('10'); await disabledSpeedElement.checkValue('54'); }); @@ -181,7 +181,7 @@ test.describe('Data Manipulation Panel', () => { await gotoDashboardPage({ uid: dashboard.uid, queryParams: variableParams }); await disabledMaxElement.checkValue('100'); - await disabledMinElement.checkValue('11'); + await disabledMinElement.checkValue('10'); await disabledSpeedElement.checkValue('54'); }); @@ -324,7 +324,7 @@ test.describe('Data Manipulation Panel', () => { const numberMaxElement = await elements.getNumberElement('max', 'number'); const numberMinElement = await elements.getNumberElement('min', 'number'); await numberMaxElement.checkValue('100'); - await numberMinElement.checkValue('11'); + await numberMinElement.checkValue('10'); await numberMaxElement.setValue('115'); await numberMinElement.setValue('15'); @@ -332,7 +332,7 @@ test.describe('Data Manipulation Panel', () => { await resetButton.reset(); await numberMaxElement.checkValue('100'); - await numberMinElement.checkValue('11'); + await numberMinElement.checkValue('10'); }); test('Should display error for invalid update request', async ({ gotoDashboardPage, readProvisionedDashboard }) => { From d096ed7b3cbfbf9a1a2c2c8c93de210e22ed650b Mon Sep 17 00:00:00 2001 From: vitPinchuk Date: Thu, 31 Oct 2024 16:43:19 +0300 Subject: [PATCH 03/11] update to min version --- README.md | 2 +- docker-compose.yml | 2 +- provisioning/dashboards/datasource.json | 980 ++++++++++++------------ provisioning/dashboards/e2e.json | 242 +++--- src/plugin.json | 2 +- 5 files changed, 583 insertions(+), 645 deletions(-) diff --git a/README.md b/README.md index 70a7fbcb..bed24dac 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ The Business Forms panel is a conceptually new plugin for Grafana. It is the fir ## Requirements -- Business Forms panel 4.X requires **Grafana 10** or **Grafana 11**. +- Business Forms panel 4.X requires **Grafana 10.3** or **Grafana 11**. - Data Manipulation panel 3.X requires **Grafana 9** or **Grafana 10**. - Data Manipulation panel 2.X requires **Grafana 9** or **Grafana 8.5**. - Data Manipulation panel 1.X requires **Grafana 8**. diff --git a/docker-compose.yml b/docker-compose.yml index 927be309..48967dd1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -69,7 +69,7 @@ services: - main grafana-dep: - image: grafana/grafana:10.0.0 + image: grafana/grafana:10.3.0 ports: - 3000:3000/tcp environment: diff --git a/provisioning/dashboards/datasource.json b/provisioning/dashboards/datasource.json index 93e0eb52..30da6fd2 100644 --- a/provisioning/dashboards/datasource.json +++ b/provisioning/dashboards/datasource.json @@ -18,175 +18,94 @@ "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 1, "links": [], + "liveNow": false, "panels": [ { "datasource": { - "default": true, "type": "marcusolsson-static-datasource", "uid": "P1D2C73DC01F2359B" }, "fieldConfig": { - "defaults": {}, + "defaults": { + "custom": { + "thresholdsStyle": { + "mode": "color", + "thresholds": [] + } + }, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, "overrides": [] }, "gridPos": { - "h": 8, - "w": 14, + "h": 35, + "w": 4, "x": 0, "y": 0 }, - "id": 4, + "id": 1, "options": { - "buttonGroup": { - "orientation": "center", - "size": "md" - }, - "confirmModal": { - "body": "Please confirm to update changed values", - "cancel": "Cancel", - "columns": { - "include": ["name", "oldValue", "newValue"], - "name": "Label", - "newValue": "New Value", - "oldValue": "Old Value" - }, - "confirm": "Confirm", - "elementDisplayMode": "modified", - "title": "Confirm update request" - }, - "elementValueChanged": "", - "elements": [ - { - "hidden": false, - "id": "String", - "labelWidth": 10, - "queryField": { - "label": "undefined:val-1 category", - "value": "category" - }, - "section": "", - "title": "String", - "tooltip": "", - "type": "string", - "uid": "0a15f927-e435-4678-8341-10f0e5caa3fb", - "unit": "", - "value": "" - } - ], - "initial": { - "code": "", - "contentType": "application/json", - "getPayload": "return {}", - "highlight": false, - "highlightColor": "red", - "method": "query", - "payload": {} - }, - "layout": { - "orientation": "horizontal", - "padding": 10, - "sectionVariant": "default", - "variant": "single" - }, - "reset": { - "backgroundColor": "purple", - "foregroundColor": "yellow", - "icon": "process", - "text": "Reset", - "variant": "hidden" - }, - "resetAction": { - "code": "if (context.panel.response) {\n context.grafana.notifySuccess(['Update', 'Values updated successfully.']);\n context.grafana.locationService.reload();\n} else {\n context.grafana.notifyError(['Update', 'An error occurred updating values.']);\n}", - "confirm": false, - "getPayload": "return {}", - "mode": "initial", - "payload": {} - }, - "saveDefault": { - "icon": "save", - "text": "Save Default", - "variant": "hidden" - }, - "submit": { - "backgroundColor": "purple", - "foregroundColor": "yellow", - "icon": "cloud-upload", - "text": "Submit", - "variant": "primary" - }, - "sync": true, - "update": { - "code": "", - "confirm": false, - "contentType": "application/json", - "getPayload": "const payload = {};\n\ncontext.panel.elements.forEach((element) => {\n if (!element.value) {\n return;\n }\n\n payload[element.id] = element.value;\n})\n\nreturn payload;", - "method": "-", - "payload": {}, - "payloadMode": "all" + "alwaysVisibleFilter": false, + "autoScroll": true, + "collapsedByDefault": false, + "customValue": false, + "displayMode": "table", + "emptyValue": false, + "favorites": { + "addQuery": {}, + "datasource": "", + "deleteQuery": {}, + "enabled": true, + "getQuery": {}, + "storage": "browser" }, - "updateEnabled": "auto" + "filter": true, + "groupSelection": false, + "header": true, + "isUseLocalTime": false, + "minimizeOutputFormat": "text", + "padding": 10, + "persistent": false, + "saveSelectedGroup": false, + "saveSelectedGroupKey": "", + "showGroupTotal": false, + "showLabel": false, + "showName": false, + "showResetButton": false, + "showTotal": false, + "statusSort": false, + "sticky": true, + "tabsInOrder": true, + "variable": "device" }, - "pluginVersion": "4.9.0", - "targets": [ - { - "datasource": { - "type": "marcusolsson-static-datasource", - "uid": "P1D2C73DC01F2359B" - }, - "frame": { - "fields": [ - { - "config": {}, - "name": "value", - "type": "string", - "values": ["val-1", "val-2", "val-3"] - }, - { - "config": {}, - "name": "category", - "type": "string", - "values": ["cat-1", "cat-2", "cat-3"] - } - ], - "meta": {}, - "name": "A" - }, - "refId": "A" - } - ], - "title": "Transform data - for Query Fields", - "transformations": [ - { - "id": "partitionByValues", - "options": { - "fields": ["value"], - "keepFields": true, - "naming": { - "asLabels": false - } - } - } - ], - "type": "volkovlabs-form-panel" + "pluginVersion": "3.6.0", + "type": "volkovlabs-variable-panel" }, { "datasource": { - "type": "marcusolsson-static-datasource", - "uid": "P1D2C73DC01F2359B" - }, - "fieldConfig": { - "defaults": {}, - "overrides": [] + "type": "postgres", + "uid": "PCC52D03280B7034C" }, "gridPos": { - "h": 8, + "h": 9, "w": 10, - "x": 14, + "x": 4, "y": 0 }, - "id": 5, + "id": 2, "options": { "buttonGroup": { "orientation": "center", @@ -208,96 +127,179 @@ "elementValueChanged": "", "elements": [ { - "hidden": false, - "id": "speed", + "fieldName": "max", + "id": "max", "labelWidth": 10, - "section": "", - "title": "speed", + "options": [], + "queryField": { + "label": "A:max", + "refId": "A", + "value": "max" + }, + "section": "current", + "title": "Max", "tooltip": "", - "type": "string", - "uid": "5fbcaea6-13d1-46b0-9f2f-a4ae21a95166", + "type": "disabled", + "uid": "8285e945-b1cf-4dd6-9efd-8e322196222a", "unit": "", - "value": "" + "value": 0, + "width": 20 }, { - "allowCustomValue": false, - "id": "max", + "fieldName": "min", + "id": "min", "labelWidth": 10, "options": [], - "optionsSource": "Custom", - "section": "", - "title": "max", + "queryField": { + "label": "A:min", + "refId": "A", + "value": "min" + }, + "section": "current", + "title": "Min", "tooltip": "", "type": "disabled", - "uid": "15d744cd-1e4c-41b4-b703-51e560bf8065", + "uid": "6d3ca353-178b-4a7a-b23d-e548cf3d2437", "unit": "", - "value": "" + "value": 0, + "width": 20 }, { - "allowCustomValue": false, - "id": "min", + "fieldName": "speed", + "id": "speed", "labelWidth": 10, "options": [], - "optionsSource": "Custom", - "section": "", - "title": "min", + "queryField": { + "label": "A:speed", + "refId": "A", + "value": "speed" + }, + "section": "current", + "title": "Speed", "tooltip": "", "type": "disabled", - "uid": "79a97013-7957-4426-b5e5-fb5143d91357", + "uid": "b7c38a6c-6c59-41db-a439-1964d20e660b", "unit": "", - "value": "" + "value": 0, + "width": 20 }, { - "id": "maxArea", + "fieldName": "max", + "id": "max", "labelWidth": 10, - "rows": 10, - "section": "", - "title": "maxArea", + "queryField": { + "label": "A:max", + "refId": "A", + "value": "max" + }, + "section": "new", + "title": "Max", "tooltip": "", - "type": "disabledTextarea", - "uid": "965c7503-f229-4549-b5df-24182e42ab1b", + "type": "number", + "uid": "3d3cf24f-7611-482d-b27b-b55621745dda", "unit": "", - "value": "" + "value": 0, + "width": 20 }, { - "id": "minArea", + "fieldName": "min", + "id": "min", "labelWidth": 10, - "rows": 10, - "section": "", - "title": "minArea", + "queryField": { + "label": "A:min", + "refId": "A", + "value": "min" + }, + "section": "new", + "title": "Min", "tooltip": "", - "type": "disabledTextarea", - "uid": "c9c4c436-d3e6-4f27-9d3a-91c3b9c2d5dd", + "type": "number", + "uid": "bc2c6772-5bbd-456d-9fe2-f1824c373c68", "unit": "", - "value": "" + "value": 0, + "width": 20 + }, + { + "fieldName": "speed", + "id": "speed", + "labelWidth": 10, + "max": 100, + "min": 0, + "queryField": { + "label": "A:speed", + "refId": "A", + "value": "speed" + }, + "section": "new", + "step": 1, + "title": "Speed", + "tooltip": "", + "type": "slider", + "uid": "1918dc47-f686-4ca2-aeb3-7f21c360f621", + "unit": "", + "value": 0 } ], "initial": { - "code": "context.panel.patchFormValue({\n \"speed\": \"15\",\n \"max\": 220,\n \"min\": 230,\n \"maxArea\": 250,\n \"minArea\": 260\n});\n", + "code": "const payload = {};\n\ncontext.panel.elements.forEach((element) => {\n if (!element.value) {\n return;\n }\n\n payload[element.id] = element.value;\n})\n\nconsole.log(payload);\ncontext.panel.setInitial(payload);\n\nreturn;", "contentType": "application/json", - "getPayload": "return {}", - "highlight": false, + "datasource": "PostgreSQL", + "getPayload": "", + "highlight": true, "highlightColor": "red", - "method": "-", - "payload": {} + "method": "datasource", + "payload": { + "editorMode": "code", + "format": "table", + "rawQuery": true, + "rawSql": "select * from configuration where name ='$device';", + "refId": "A", + "sql": { + "columns": [ + { + "parameters": [], + "type": "function" + } + ], + "groupBy": [ + { + "property": { + "type": "string" + }, + "type": "groupBy" + } + ], + "limit": 50 + } + } }, "layout": { "orientation": "horizontal", "padding": 10, "sectionVariant": "default", - "variant": "single" + "sections": [ + { + "id": "current", + "name": "Current Values" + }, + { + "id": "new", + "name": "New Values" + } + ], + "variant": "split" }, "reset": { "backgroundColor": "purple", "foregroundColor": "yellow", "icon": "process", "text": "Reset", - "variant": "hidden" + "variant": "secondary" }, "resetAction": { - "code": "if (context.panel.response) {\n context.grafana.notifySuccess(['Update', 'Values updated successfully.']);\n context.grafana.locationService.reload();\n} else {\n context.grafana.notifyError(['Update', 'An error occurred updating values.']);\n}", + "code": "console.log(context.panel.data, context.panel.response, context.panel.initial, context.panel.elements);", "confirm": false, - "getPayload": "return {}", + "getPayload": "return {\n rawSql: '',\n format: 'table',\n}", "mode": "initial", "payload": {} }, @@ -321,115 +323,80 @@ "datasource": "PostgreSQL", "getPayload": "const payload = {};\n\ncontext.panel.elements.forEach((element) => {\n if (!element.value) {\n return;\n }\n\n payload[element.id] = element.value;\n})\n\n/**\n * Data Source payload\n */\nreturn payload;", "method": "datasource", - "payload": {}, - "payloadMode": "custom" - }, - "updateEnabled": "auto" - }, - "pluginVersion": "4.9.0", - "targets": [ - { - "refId": "A" - } - ], - "title": "Initial patchFormValue", - "type": "volkovlabs-form-panel" - }, - { - "datasource": { - "type": "marcusolsson-static-datasource", - "uid": "P1D2C73DC01F2359B" - }, - "fieldConfig": { - "defaults": { - "custom": { - "thresholdsStyle": { - "mode": "color", - "thresholds": [] - } - }, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 35, - "w": 4, - "x": 0, - "y": 8 - }, - "id": 1, - "options": { - "alwaysVisibleFilter": false, - "autoScroll": true, - "collapsedByDefault": false, - "customValue": false, - "displayMode": "table", - "emptyValue": false, - "favorites": { - "addQuery": {}, - "datasource": "", - "deleteQuery": {}, - "enabled": true, - "getQuery": {}, - "storage": "browser" + "payload": { + "editorMode": "code", + "format": "table", + "rawQuery": true, + "rawSql": "update configuration set min=${payload.min}, max=${payload.max}, speed=${payload.speed} where name='$device'", + "refId": "A", + "sql": { + "columns": [ + { + "parameters": [], + "type": "function" + } + ], + "groupBy": [ + { + "property": { + "type": "string" + }, + "type": "groupBy" + } + ], + "limit": 50 + } + }, + "payloadMode": "custom" }, - "filter": true, - "groupSelection": false, - "header": true, - "isUseLocalTime": false, - "minimizeOutputFormat": "text", - "padding": 10, - "persistent": false, - "saveSelectedGroup": false, - "saveSelectedGroupKey": "", - "showGroupTotal": false, - "showLabel": false, - "showName": false, - "showResetButton": false, - "showTotal": false, - "statusSort": false, - "sticky": true, - "tabsInOrder": true, - "variable": "device" + "updateEnabled": "auto" }, - "pluginVersion": "3.6.0", + "pluginVersion": "4.9.0", "targets": [ { - "refId": "A" + "datasource": { + "type": "postgres", + "uid": "PCC52D03280B7034C" + }, + "editorMode": "code", + "format": "table", + "rawQuery": true, + "rawSql": "select * from configuration where name ='$device';", + "refId": "A", + "sql": { + "columns": [ + { + "parameters": [], + "type": "function" + } + ], + "groupBy": [ + { + "property": { + "type": "string" + }, + "type": "groupBy" + } + ], + "limit": 50 + } } ], - "title": "", - "type": "volkovlabs-variable-panel" + "title": "Data Source", + "type": "volkovlabs-form-panel" }, { "datasource": { "type": "postgres", "uid": "PCC52D03280B7034C" }, - "fieldConfig": { - "defaults": {}, - "overrides": [] - }, "gridPos": { - "h": 9, + "h": 8, "w": 10, - "x": 4, - "y": 8 + "x": 14, + "y": 0 }, - "id": 2, + "id": 3, "options": { "buttonGroup": { "orientation": "center", @@ -565,37 +532,14 @@ } ], "initial": { - "code": "const payload = {};\n\ncontext.panel.elements.forEach((element) => {\n if (!element.value) {\n return;\n }\n\n payload[element.id] = element.value;\n})\n\nconsole.log(payload);\ncontext.panel.setInitial(payload);\n\nreturn;", + "code": "", "contentType": "application/json", "datasource": "PostgreSQL", - "getPayload": "", + "getPayload": "return {\n rawSql: \"select * from configuration where name ='$device';\",\n format: 'table',\n}", "highlight": true, "highlightColor": "red", - "method": "datasource", - "payload": { - "editorMode": "code", - "format": "table", - "rawQuery": true, - "rawSql": "select * from configuration where name ='$device';", - "refId": "A", - "sql": { - "columns": [ - { - "parameters": [], - "type": "function" - } - ], - "groupBy": [ - { - "property": { - "type": "string" - }, - "type": "groupBy" - } - ], - "limit": 50 - } - } + "method": "query", + "payload": {} }, "layout": { "orientation": "horizontal", @@ -641,7 +585,7 @@ }, "sync": true, "update": { - "code": "if (context.panel.response && context.panel.response.state === 'Done') {\n context.grafana.notifySuccess(['Update', 'Values updated successfully.']);\n context.panel.initialRequest();\n} else {\n context.grafana.notifyError(['Update', 'An error occurred updating values.']);\n}", + "code": "if (context.panel.response && context.panel.response.state === 'Done') {\n context.grafana.notifySuccess(['Update', 'Values updated successfully.']);\n context.panel.initialRequest();\n} else {\n context.grafana.notifyError(['Update', 'An error occured updating values.']);\n}", "confirm": true, "contentType": "application/json", "datasource": "PostgreSQL", @@ -706,7 +650,162 @@ } } ], - "title": "Data Source", + "title": "Query", + "type": "volkovlabs-form-panel" + }, + { + "datasource": { + "type": "marcusolsson-static-datasource", + "uid": "P1D2C73DC01F2359B" + }, + "gridPos": { + "h": 17, + "w": 10, + "x": 14, + "y": 8 + }, + "id": 5, + "options": { + "buttonGroup": { + "orientation": "center", + "size": "md" + }, + "confirmModal": { + "body": "Please confirm to update changed values", + "cancel": "Cancel", + "columns": { + "include": ["name", "oldValue", "newValue"], + "name": "Label", + "newValue": "New Value", + "oldValue": "Old Value" + }, + "confirm": "Confirm", + "elementDisplayMode": "modified", + "title": "Confirm update request" + }, + "elementValueChanged": "", + "elements": [ + { + "hidden": false, + "id": "speed", + "labelWidth": 10, + "section": "", + "title": "speed", + "tooltip": "", + "type": "string", + "uid": "5fbcaea6-13d1-46b0-9f2f-a4ae21a95166", + "unit": "", + "value": "" + }, + { + "allowCustomValue": false, + "id": "max", + "labelWidth": 10, + "options": [], + "optionsSource": "Custom", + "section": "", + "title": "max", + "tooltip": "", + "type": "disabled", + "uid": "15d744cd-1e4c-41b4-b703-51e560bf8065", + "unit": "", + "value": "" + }, + { + "allowCustomValue": false, + "id": "min", + "labelWidth": 10, + "options": [], + "optionsSource": "Custom", + "section": "", + "title": "min", + "tooltip": "", + "type": "disabled", + "uid": "79a97013-7957-4426-b5e5-fb5143d91357", + "unit": "", + "value": "" + }, + { + "id": "maxArea", + "labelWidth": 10, + "rows": 10, + "section": "", + "title": "maxArea", + "tooltip": "", + "type": "disabledTextarea", + "uid": "965c7503-f229-4549-b5df-24182e42ab1b", + "unit": "", + "value": "" + }, + { + "id": "minArea", + "labelWidth": 10, + "rows": 10, + "section": "", + "title": "minArea", + "tooltip": "", + "type": "disabledTextarea", + "uid": "c9c4c436-d3e6-4f27-9d3a-91c3b9c2d5dd", + "unit": "", + "value": "" + } + ], + "initial": { + "code": "context.panel.patchFormValue({\n \"speed\": \"15\",\n \"max\": 220,\n \"min\": 230,\n \"maxArea\": 250,\n \"minArea\": 260\n});\n", + "contentType": "application/json", + "getPayload": "return {}", + "highlight": false, + "highlightColor": "red", + "method": "-", + "payload": {} + }, + "layout": { + "orientation": "horizontal", + "padding": 10, + "sectionVariant": "default", + "variant": "single" + }, + "reset": { + "backgroundColor": "purple", + "foregroundColor": "yellow", + "icon": "process", + "text": "Reset", + "variant": "hidden" + }, + "resetAction": { + "code": "if (context.panel.response) {\n context.grafana.notifySuccess(['Update', 'Values updated successfully.']);\n context.grafana.locationService.reload();\n} else {\n context.grafana.notifyError(['Update', 'An error occurred updating values.']);\n}", + "confirm": false, + "getPayload": "return {}", + "mode": "initial", + "payload": {} + }, + "saveDefault": { + "icon": "save", + "text": "Save Default", + "variant": "hidden" + }, + "submit": { + "backgroundColor": "purple", + "foregroundColor": "yellow", + "icon": "cloud-upload", + "text": "Submit", + "variant": "primary" + }, + "sync": true, + "update": { + "code": "if (context.panel.response && context.panel.response.state === 'Done') {\n context.grafana.notifySuccess(['Update', 'Values updated successfully.']);\n context.panel.initialRequest();\n} else {\n context.grafana.notifyError(['Update', 'An error occurred updating values.']);\n}", + "confirm": true, + "contentType": "application/json", + "datasource": "PostgreSQL", + "getPayload": "const payload = {};\n\ncontext.panel.elements.forEach((element) => {\n if (!element.value) {\n return;\n }\n\n payload[element.id] = element.value;\n})\n\n/**\n * Data Source payload\n */\nreturn payload;", + "method": "datasource", + "payload": {}, + "payloadMode": "custom" + }, + "updateEnabled": "auto" + }, + "pluginVersion": "4.9.0", + "title": "Initial patchFormValue", "type": "volkovlabs-form-panel" }, { @@ -714,15 +813,11 @@ "type": "postgres", "uid": "PCC52D03280B7034C" }, - "fieldConfig": { - "defaults": {}, - "overrides": [] - }, "gridPos": { "h": 8, "w": 10, - "x": 14, - "y": 8 + "x": 4, + "y": 9 }, "id": 8, "options": { @@ -1006,12 +1101,9 @@ }, { "datasource": { - "type": "postgres", - "uid": "PCC52D03280B7034C" - }, - "fieldConfig": { - "defaults": {}, - "overrides": [] + "default": true, + "type": "marcusolsson-static-datasource", + "uid": "P1D2C73DC01F2359B" }, "gridPos": { "h": 8, @@ -1019,7 +1111,7 @@ "x": 4, "y": 17 }, - "id": 3, + "id": 4, "options": { "buttonGroup": { "orientation": "center", @@ -1041,125 +1133,27 @@ "elementValueChanged": "", "elements": [ { - "fieldName": "max", - "id": "max", - "labelWidth": 10, - "options": [], - "queryField": { - "label": "A:max", - "refId": "A", - "value": "max" - }, - "section": "current", - "title": "Max", - "tooltip": "", - "type": "disabled", - "uid": "8285e945-b1cf-4dd6-9efd-8e322196222a", - "unit": "", - "value": 0, - "width": 20 - }, - { - "fieldName": "min", - "id": "min", - "labelWidth": 10, - "options": [], - "queryField": { - "label": "A:min", - "refId": "A", - "value": "min" - }, - "section": "current", - "title": "Min", - "tooltip": "", - "type": "disabled", - "uid": "6d3ca353-178b-4a7a-b23d-e548cf3d2437", - "unit": "", - "value": 0, - "width": 20 - }, - { - "fieldName": "speed", - "id": "speed", - "labelWidth": 10, - "options": [], - "queryField": { - "label": "A:speed", - "refId": "A", - "value": "speed" - }, - "section": "current", - "title": "Speed", - "tooltip": "", - "type": "disabled", - "uid": "b7c38a6c-6c59-41db-a439-1964d20e660b", - "unit": "", - "value": 0, - "width": 20 - }, - { - "fieldName": "max", - "id": "max", - "labelWidth": 10, - "queryField": { - "label": "A:max", - "refId": "A", - "value": "max" - }, - "section": "new", - "title": "Max", - "tooltip": "", - "type": "number", - "uid": "3d3cf24f-7611-482d-b27b-b55621745dda", - "unit": "", - "value": 0, - "width": 20 - }, - { - "fieldName": "min", - "id": "min", - "labelWidth": 10, - "queryField": { - "label": "A:min", - "refId": "A", - "value": "min" - }, - "section": "new", - "title": "Min", - "tooltip": "", - "type": "number", - "uid": "bc2c6772-5bbd-456d-9fe2-f1824c373c68", - "unit": "", - "value": 0, - "width": 20 - }, - { - "fieldName": "speed", - "id": "speed", + "hidden": false, + "id": "String", "labelWidth": 10, - "max": 100, - "min": 0, "queryField": { - "label": "A:speed", - "refId": "A", - "value": "speed" + "label": "undefined:val-1 category", + "value": "category" }, - "section": "new", - "step": 1, - "title": "Speed", + "section": "", + "title": "String", "tooltip": "", - "type": "slider", - "uid": "1918dc47-f686-4ca2-aeb3-7f21c360f621", + "type": "string", + "uid": "0a15f927-e435-4678-8341-10f0e5caa3fb", "unit": "", - "value": 0 + "value": "" } ], "initial": { "code": "", "contentType": "application/json", - "datasource": "PostgreSQL", - "getPayload": "return {\n rawSql: \"select * from configuration where name ='$device';\",\n format: 'table',\n}", - "highlight": true, + "getPayload": "return {}", + "highlight": false, "highlightColor": "red", "method": "query", "payload": {} @@ -1168,29 +1162,19 @@ "orientation": "horizontal", "padding": 10, "sectionVariant": "default", - "sections": [ - { - "id": "current", - "name": "Current Values" - }, - { - "id": "new", - "name": "New Values" - } - ], - "variant": "split" + "variant": "single" }, "reset": { "backgroundColor": "purple", "foregroundColor": "yellow", "icon": "process", "text": "Reset", - "variant": "secondary" + "variant": "hidden" }, "resetAction": { - "code": "console.log(context.panel.data, context.panel.response, context.panel.initial, context.panel.elements);", + "code": "if (context.panel.response) {\n context.grafana.notifySuccess(['Update', 'Values updated successfully.']);\n context.grafana.locationService.reload();\n} else {\n context.grafana.notifyError(['Update', 'An error occurred updating values.']);\n}", "confirm": false, - "getPayload": "return {\n rawSql: '',\n format: 'table',\n}", + "getPayload": "return {}", "mode": "initial", "payload": {} }, @@ -1208,37 +1192,13 @@ }, "sync": true, "update": { - "code": "if (context.panel.response && context.panel.response.state === 'Done') {\n context.grafana.notifySuccess(['Update', 'Values updated successfully.']);\n context.panel.initialRequest();\n} else {\n context.grafana.notifyError(['Update', 'An error occured updating values.']);\n}", - "confirm": true, + "code": "", + "confirm": false, "contentType": "application/json", - "datasource": "PostgreSQL", - "getPayload": "const payload = {};\n\ncontext.panel.elements.forEach((element) => {\n if (!element.value) {\n return;\n }\n\n payload[element.id] = element.value;\n})\n\n/**\n * Data Source payload\n */\nreturn payload;", - "method": "datasource", - "payload": { - "editorMode": "code", - "format": "table", - "rawQuery": true, - "rawSql": "update configuration set min=${payload.min}, max=${payload.max}, speed=${payload.speed} where name='$device'", - "refId": "A", - "sql": { - "columns": [ - { - "parameters": [], - "type": "function" - } - ], - "groupBy": [ - { - "property": { - "type": "string" - }, - "type": "groupBy" - } - ], - "limit": 50 - } - }, - "payloadMode": "custom" + "getPayload": "const payload = {};\n\ncontext.panel.elements.forEach((element) => {\n if (!element.value) {\n return;\n }\n\n payload[element.id] = element.value;\n})\n\nreturn payload;", + "method": "-", + "payload": {}, + "payloadMode": "all" }, "updateEnabled": "auto" }, @@ -1246,34 +1206,43 @@ "targets": [ { "datasource": { - "type": "postgres", - "uid": "PCC52D03280B7034C" + "type": "marcusolsson-static-datasource", + "uid": "P1D2C73DC01F2359B" }, - "editorMode": "code", - "format": "table", - "rawQuery": true, - "rawSql": "select * from configuration where name ='$device';", - "refId": "A", - "sql": { - "columns": [ + "frame": { + "fields": [ { - "parameters": [], - "type": "function" - } - ], - "groupBy": [ + "config": {}, + "name": "value", + "type": "string", + "values": ["val-1", "val-2", "val-3"] + }, { - "property": { - "type": "string" - }, - "type": "groupBy" + "config": {}, + "name": "category", + "type": "string", + "values": ["cat-1", "cat-2", "cat-3"] } ], - "limit": 50 + "meta": {}, + "name": "A" + }, + "refId": "A" + } + ], + "title": "Transform data - for Query Fields", + "transformations": [ + { + "id": "partitionByValues", + "options": { + "fields": ["value"], + "keepFields": true, + "naming": { + "asLabels": false + } } } ], - "title": "Query", "type": "volkovlabs-form-panel" }, { @@ -1281,10 +1250,6 @@ "type": "postgres", "uid": "PCC52D03280B7034C" }, - "fieldConfig": { - "defaults": {}, - "overrides": [] - }, "gridPos": { "h": 17, "w": 20, @@ -1602,10 +1567,6 @@ "type": "postgres", "uid": "PCC52D03280B7034C" }, - "fieldConfig": { - "defaults": {}, - "overrides": [] - }, "gridPos": { "h": 17, "w": 20, @@ -1933,14 +1894,14 @@ "type": "volkovlabs-form-panel" } ], - "preload": false, "refresh": "", - "schemaVersion": 40, + "schemaVersion": 39, "tags": [], "templating": { "list": [ { "current": { + "selected": false, "text": "device1", "value": "device1" }, @@ -1949,12 +1910,15 @@ "uid": "PCC52D03280B7034C" }, "definition": "select distinct name from configuration;", + "hide": 0, "includeAll": false, + "multi": false, "name": "device", "options": [], "query": "select distinct name from configuration;", "refresh": 1, "regex": "", + "skipUrlSync": false, "sort": 1, "type": "query" } @@ -1968,6 +1932,6 @@ "timezone": "", "title": "Configuration", "uid": "d185bf58-9e0a-4373-befa-3905a18af42e", - "version": 5, + "version": 1, "weekStart": "" } diff --git a/provisioning/dashboards/e2e.json b/provisioning/dashboards/e2e.json index dec41b06..d68b8416 100644 --- a/provisioning/dashboards/e2e.json +++ b/provisioning/dashboards/e2e.json @@ -18,18 +18,14 @@ "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 2, "links": [], + "liveNow": false, "panels": [ { "datasource": { "type": "datasource", "uid": "grafana" }, - "fieldConfig": { - "defaults": {}, - "overrides": [] - }, "gridPos": { "h": 16, "w": 12, @@ -302,28 +298,121 @@ "widthMode": "auto" }, "pluginVersion": "4.9.0", - "targets": [ - { - "refId": "A" - } - ], "title": "Single Form", "type": "volkovlabs-form-panel" }, + { + "datasource": { + "type": "marcusolsson-static-datasource", + "uid": "P1D2C73DC01F2359B" + }, + "gridPos": { + "h": 6, + "w": 12, + "x": 12, + "y": 0 + }, + "id": 4, + "options": { + "buttonGroup": { + "orientation": "center", + "size": "md" + }, + "confirmModal": { + "body": "Please confirm to update changed values", + "cancel": "Cancel", + "columns": { + "include": ["name", "oldValue", "newValue"], + "name": "Label", + "newValue": "New Value", + "oldValue": "Old Value" + }, + "confirm": "Confirm", + "elementDisplayMode": "modified", + "title": "Confirm update request" + }, + "elementValueChanged": "", + "elements": [ + { + "hidden": false, + "id": "Name", + "labelWidth": 10, + "section": "", + "title": "Name", + "tooltip": "", + "type": "string", + "uid": "86564314-5234-4cc3-8e0c-f29d0c4c4bf7", + "unit": "", + "value": "" + } + ], + "initial": { + "code": "console.log(context.panel.data, context.panel.response, context.panel.initial, context.panel.elements);\n\nreturn;\n\n/**\n * Data Source\n * Requires form elements to be defined\n */\nconst dataQuery = context.utils.toDataQueryResponse(context.panel.response);\nconsole.log(dataQuery);", + "contentType": "application/json", + "getPayload": "return {}", + "highlight": false, + "highlightColor": "red", + "method": "GET", + "payload": {} + }, + "layout": { + "orientation": "horizontal", + "padding": 10, + "sectionVariant": "default", + "variant": "single" + }, + "reset": { + "backgroundColor": "purple", + "foregroundColor": "yellow", + "icon": "process", + "text": "Reset", + "variant": "hidden" + }, + "resetAction": { + "code": "if (context.panel.response) {\n context.grafana.notifySuccess(['Update', 'Values updated successfully.']);\n context.grafana.locationService.reload();\n} else {\n context.grafana.notifyError(['Update', 'An error occurred updating values.']);\n}", + "confirm": false, + "getPayload": "return {}", + "mode": "initial", + "payload": {} + }, + "saveDefault": { + "icon": "save", + "text": "Save Default", + "variant": "hidden" + }, + "submit": { + "backgroundColor": "purple", + "foregroundColor": "yellow", + "icon": "cloud-upload", + "text": "Submit", + "variant": "primary" + }, + "sync": true, + "update": { + "code": "if (context.panel.response) {\n context.grafana.notifySuccess(['Update', 'Values updated successfully.']);\n context.grafana.locationService.reload();\n} else {\n context.grafana.notifyError(['Update', 'An error occurred updating values.']);\n}", + "confirm": false, + "contentType": "application/json", + "getPayload": "const payload = {};\n\ncontext.panel.elements.forEach((element) => {\n if (!element.value) {\n return;\n }\n\n payload[element.id] = element.value;\n})\n\nreturn payload;", + "method": "-", + "payload": "const payload = {};\n\ncontext.panel.elements.forEach((element) => {\n if (!element.value) {\n return;\n }\n\n payload[element.id] = element.value;\n})\n\nreturn payload;", + "payloadMode": "all" + }, + "updateEnabled": "auto" + }, + "pluginVersion": "4.9.0", + "title": "Empty URL", + "type": "volkovlabs-form-panel" + }, { "datasource": { "type": "datasource", "uid": "grafana" }, - "fieldConfig": { - "defaults": {}, - "overrides": [] - }, "gridPos": { "h": 21, "w": 12, "x": 12, - "y": 0 + "y": 6 }, "id": 3, "options": { @@ -469,129 +558,12 @@ "widthMode": "auto" }, "pluginVersion": "4.9.0", - "targets": [ - { - "refId": "A" - } - ], "title": "Boxes", "type": "volkovlabs-form-panel" - }, - { - "datasource": { - "type": "marcusolsson-static-datasource", - "uid": "P1D2C73DC01F2359B" - }, - "fieldConfig": { - "defaults": {}, - "overrides": [] - }, - "gridPos": { - "h": 6, - "w": 12, - "x": 0, - "y": 16 - }, - "id": 4, - "options": { - "buttonGroup": { - "orientation": "center", - "size": "md" - }, - "confirmModal": { - "body": "Please confirm to update changed values", - "cancel": "Cancel", - "columns": { - "include": ["name", "oldValue", "newValue"], - "name": "Label", - "newValue": "New Value", - "oldValue": "Old Value" - }, - "confirm": "Confirm", - "elementDisplayMode": "modified", - "title": "Confirm update request" - }, - "elementValueChanged": "", - "elements": [ - { - "hidden": false, - "id": "Name", - "labelWidth": 10, - "section": "", - "title": "Name", - "tooltip": "", - "type": "string", - "uid": "86564314-5234-4cc3-8e0c-f29d0c4c4bf7", - "unit": "", - "value": "" - } - ], - "initial": { - "code": "console.log(context.panel.data, context.panel.response, context.panel.initial, context.panel.elements);\n\nreturn;\n\n/**\n * Data Source\n * Requires form elements to be defined\n */\nconst dataQuery = context.utils.toDataQueryResponse(context.panel.response);\nconsole.log(dataQuery);", - "contentType": "application/json", - "getPayload": "return {}", - "highlight": false, - "highlightColor": "red", - "method": "GET", - "payload": {} - }, - "layout": { - "orientation": "horizontal", - "padding": 10, - "sectionVariant": "default", - "variant": "single" - }, - "reset": { - "backgroundColor": "purple", - "foregroundColor": "yellow", - "icon": "process", - "text": "Reset", - "variant": "hidden" - }, - "resetAction": { - "code": "if (context.panel.response) {\n context.grafana.notifySuccess(['Update', 'Values updated successfully.']);\n context.grafana.locationService.reload();\n} else {\n context.grafana.notifyError(['Update', 'An error occurred updating values.']);\n}", - "confirm": false, - "getPayload": "return {}", - "mode": "initial", - "payload": {} - }, - "saveDefault": { - "icon": "save", - "text": "Save Default", - "variant": "hidden" - }, - "submit": { - "backgroundColor": "purple", - "foregroundColor": "yellow", - "icon": "cloud-upload", - "text": "Submit", - "variant": "primary" - }, - "sync": true, - "update": { - "code": "if (context.panel.response) {\n context.grafana.notifySuccess(['Update', 'Values updated successfully.']);\n context.grafana.locationService.reload();\n} else {\n context.grafana.notifyError(['Update', 'An error occurred updating values.']);\n}", - "confirm": false, - "contentType": "application/json", - "getPayload": "const payload = {};\n\ncontext.panel.elements.forEach((element) => {\n if (!element.value) {\n return;\n }\n\n payload[element.id] = element.value;\n})\n\nreturn payload;", - "method": "-", - "payload": "const payload = {};\n\ncontext.panel.elements.forEach((element) => {\n if (!element.value) {\n return;\n }\n\n payload[element.id] = element.value;\n})\n\nreturn payload;", - "payloadMode": "all" - }, - "updateEnabled": "auto" - }, - "pluginVersion": "4.9.0", - "targets": [ - { - "refId": "A" - } - ], - "title": "Empty URL", - "type": "volkovlabs-form-panel" } ], - "preload": false, "refresh": "", - "schemaVersion": 40, + "schemaVersion": 39, "tags": [], "templating": { "list": [ @@ -600,6 +572,7 @@ "text": "test", "value": "test" }, + "hide": 0, "label": "Var", "name": "var", "options": [ @@ -610,6 +583,7 @@ } ], "query": "test", + "skipUrlSync": false, "type": "textbox" } ] @@ -622,6 +596,6 @@ "timezone": "", "title": "E2E", "uid": "ddab9faf-2c15-431a-a047-9a7a6a0aed71", - "version": 2, + "version": 1, "weekStart": "" } diff --git a/src/plugin.json b/src/plugin.json index 9ea673bb..f04afabf 100644 --- a/src/plugin.json +++ b/src/plugin.json @@ -1,7 +1,7 @@ { "$schema": "https://raw.githubusercontent.com/grafana/grafana/master/docs/sources/developers/plugins/plugin.schema.json", "dependencies": { - "grafanaDependency": ">=10.0.0", + "grafanaDependency": ">=10.3.0", "plugins": [] }, "id": "volkovlabs-form-panel", From 3fcc2024a132228f04fe34c4e261270cfb3b6459 Mon Sep 17 00:00:00 2001 From: vitPinchuk Date: Thu, 31 Oct 2024 16:44:35 +0300 Subject: [PATCH 04/11] remove screen --- .../actual-screenshot-run-tests-linux.png | Bin 31035 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 test/panel.spec.ts-snapshots/actual-screenshot-run-tests-linux.png diff --git a/test/panel.spec.ts-snapshots/actual-screenshot-run-tests-linux.png b/test/panel.spec.ts-snapshots/actual-screenshot-run-tests-linux.png deleted file mode 100644 index 829535f806a20fcecde6b1ecf665c121dac7e274..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 31035 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 Date: Sat, 9 Nov 2024 09:16:54 +0300 Subject: [PATCH 05/11] formatting --- test/panel.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/panel.spec.ts b/test/panel.spec.ts index a30664aa..01bca916 100644 --- a/test/panel.spec.ts +++ b/test/panel.spec.ts @@ -373,7 +373,7 @@ test.describe('Data Manipulation Panel', () => { const dashboard = await readProvisionedDashboard({ fileName: 'validation.json' }); const dashboardPage = await gotoDashboardPage({ uid: dashboard.uid }); - dashboardPage.getByGrafanaSelector; + /** * Check Presence */ From 2f527de21cb6dea0610ddeff48d333fc48056147 Mon Sep 17 00:00:00 2001 From: vitPinchuk Date: Sat, 9 Nov 2024 10:12:13 +0300 Subject: [PATCH 06/11] rewrite button helpers --- test/panel.spec.ts | 31 ++++++++++++----------- test/utils/form.ts | 61 ++++++++++++++-------------------------------- 2 files changed, 33 insertions(+), 59 deletions(-) diff --git a/test/panel.spec.ts b/test/panel.spec.ts index 01bca916..21cf023d 100644 --- a/test/panel.spec.ts +++ b/test/panel.spec.ts @@ -1,5 +1,4 @@ import { test, expect } from '@grafana/plugin-e2e'; -import { TEST_IDS } from '../src/constants/tests'; import { ModalHelper, PanelHelper } from './utils'; test.describe('Data Manipulation Panel', () => { @@ -227,18 +226,18 @@ test.describe('Data Manipulation Panel', () => { const elements = panel.getElements(); const disabledMaxElement = await elements.getDisabledElement('max', 'disabled'); - const submitButton = panel.getSubmitButton(); + const buttons = panel.getButtons(); await disabledMaxElement.checkValue('100'); - await submitButton.checkPresence(); - await submitButton.checkIsDisabled(); + await buttons.checkSubmitButtonPresence(); + await buttons.checkSubmitButtonIsDisabled(); const numberMaxElement = await elements.getNumberElement('max', 'number'); await numberMaxElement.checkValue('100'); await numberMaxElement.setValue('125'); - await submitButton.checkIsNotDisabled(); - await submitButton.submit(); + await buttons.checkSubmitButtonIsNotDisabled(); + await buttons.submit(); const confirmModal = new ModalHelper(dashboardPage); await confirmModal.checkPresence(); @@ -246,7 +245,7 @@ test.describe('Data Manipulation Panel', () => { await confirmModal.cancelButtonCheckPresence(); await confirmModal.updateValues(); - await submitButton.checkIsDisabled(); + await buttons.checkSubmitButtonPresence(); await numberMaxElement.checkValue('125'); await disabledMaxElement.checkValue('125'); @@ -254,7 +253,7 @@ test.describe('Data Manipulation Panel', () => { * Return to initial */ await numberMaxElement.setValue('100'); - await submitButton.submit(); + await buttons.submit(); await confirmModal.updateValues(); }); @@ -278,16 +277,16 @@ test.describe('Data Manipulation Panel', () => { const elements = panel.getElements(); const disabledMaxElement = await elements.getDisabledElement('max', 'disabled'); - const submitButton = panel.getSubmitButton(); + const buttons = panel.getButtons(); await disabledMaxElement.checkValue('100'); - await submitButton.checkPresence(); - await submitButton.checkIsDisabled(); + await buttons.checkSubmitButtonPresence(); + await buttons.checkSubmitButtonIsDisabled(); const numberMaxElement = await elements.getNumberElement('max', 'number'); await numberMaxElement.setValue('125'); - await submitButton.submit(); + await buttons.submit(); const confirmModal = new ModalHelper(dashboardPage); @@ -317,7 +316,7 @@ test.describe('Data Manipulation Panel', () => { const elements = panel.getElements(); const disabledMaxElement = await elements.getDisabledElement('max', 'disabled'); - const resetButton = panel.getResetButton(); + const buttons = panel.getButtons(); await disabledMaxElement.checkValue('100'); @@ -329,7 +328,7 @@ test.describe('Data Manipulation Panel', () => { await numberMaxElement.setValue('115'); await numberMinElement.setValue('15'); - await resetButton.reset(); + await buttons.reset(); await numberMaxElement.checkValue('100'); await numberMinElement.checkValue('10'); @@ -354,8 +353,8 @@ test.describe('Data Manipulation Panel', () => { const numberMaxElement = await elements.getNumberElement('max', 'number'); await numberMaxElement.setValue('125'); - const submitButton = panel.getSubmitButton(); - await submitButton.submit(); + const buttons = panel.getButtons(); + await buttons.submit(); const confirmModal = new ModalHelper(dashboardPage); await confirmModal.updateValues(); diff --git a/test/utils/form.ts b/test/utils/form.ts index 8e96cc3c..cbf66379 100644 --- a/test/utils/form.ts +++ b/test/utils/form.ts @@ -143,64 +143,43 @@ class ElementsHelper { } /** - * Submit Button Helper + * Buttons Helper */ -class SubmitButtonHelper { +class ButtonsHelper { private readonly locator: Locator; + private readonly selectors: LocatorSelectors; constructor(parentLocator: Locator) { - this.locator = parentLocator.getByTestId(TEST_IDS.panel.buttonSubmit); + this.locator = parentLocator; + this.selectors = getLocatorSelectors(TEST_IDS.panel)(this.locator); } private getMsg(message: string): string { - return `Submit Button: ${message}`; + return `Buttons: ${message}`; } - public get() { - return this.locator; - } - - public async checkPresence() { - return expect(this.get(), this.getMsg(`Check Submit button Presence`)).toBeVisible(); + public async checkSubmitButtonPresence() { + return expect(this.selectors.buttonSubmit(), this.getMsg(`Check Submit button Presence`)).toBeVisible(); } - public async checkIsDisabled() { - return expect(this.get(), this.getMsg(`Check Submit button disabled`)).toBeDisabled(); + public async checkSubmitButtonIsDisabled() { + return expect(this.selectors.buttonSubmit(), this.getMsg(`Check Submit button disabled`)).toBeDisabled(); } - public async checkIsNotDisabled() { - return expect(this.get(), this.getMsg(`Check Submit button not disabled`)).not.toBeDisabled(); + public async checkSubmitButtonIsNotDisabled() { + return expect(this.selectors.buttonSubmit(), this.getMsg(`Check Submit button not disabled`)).not.toBeDisabled(); } public async submit() { - return this.get().click(); + return this.selectors.buttonSubmit().click(); } -} - -/** - * Reset Button Helper - */ -class ResetButtonHelper { - private readonly locator: Locator; - constructor(parentLocator: Locator) { - this.locator = parentLocator.getByTestId(TEST_IDS.panel.buttonReset); - } - - private getMsg(message: string): string { - return `Reset Button: ${message}`; - } - - public get() { - return this.locator; - } - - public async checkPresence() { - return expect(this.get(), this.getMsg(`Check Reset button Presence`)).toBeVisible(); + public async checkResetButtonPresence() { + return expect(this.selectors.buttonReset(), this.getMsg(`Check Reset button Presence`)).toBeVisible(); } public async reset() { - return this.get().click(); + return this.selectors.buttonReset().click(); } } @@ -262,12 +241,8 @@ export class PanelHelper { return new PanelEditorHelper(locator, editPage); } - public getSubmitButton() { - return new SubmitButtonHelper(this.locator); - } - - public getResetButton() { - return new ResetButtonHelper(this.locator); + public getButtons() { + return new ButtonsHelper(this.locator); } public async checkIfNoErrors() { From b57a418cfe6fbfdb29dda2fe95c35572ec2d632a Mon Sep 17 00:00:00 2001 From: vitPinchuk Date: Sat, 9 Nov 2024 10:40:04 +0300 Subject: [PATCH 07/11] add sectionsHelper --- test/panel.spec.ts | 20 ++++++++++++++------ test/utils/form.ts | 37 +++++++++++++++++++++++++++++-------- 2 files changed, 43 insertions(+), 14 deletions(-) diff --git a/test/panel.spec.ts b/test/panel.spec.ts index 21cf023d..eb8e6610 100644 --- a/test/panel.spec.ts +++ b/test/panel.spec.ts @@ -135,7 +135,9 @@ test.describe('Data Manipulation Panel', () => { await panel.checkIfNoErrors(); await panel.checkPresence(); - await panel.checkSectionPresence('Current Values'); + + const sections = panel.getSections(); + await sections.checkSectionPresence('Current Values'); const elements = panel.getElements(); const disabledMaxElement = await elements.getDisabledElement('max', 'disabled'); @@ -165,7 +167,9 @@ test.describe('Data Manipulation Panel', () => { await panel.checkIfNoErrors(); await panel.checkPresence(); - await panel.checkSectionPresence('Current Values'); + + const sections = panel.getSections(); + await sections.checkSectionPresence('Current Values'); const elements = panel.getElements(); const disabledMaxElement = await elements.getDisabledElement('max', 'disabled'); @@ -221,8 +225,10 @@ test.describe('Data Manipulation Panel', () => { await panel.checkIfNoErrors(); await panel.checkPresence(); - await panel.checkSectionPresence('Current Values'); - await panel.checkSectionPresence('New Values'); + + const sections = panel.getSections(); + await sections.checkSectionPresence('Current Values'); + await sections.checkSectionPresence('New Values'); const elements = panel.getElements(); const disabledMaxElement = await elements.getDisabledElement('max', 'disabled'); @@ -415,12 +421,14 @@ test.describe('Data Manipulation Panel', () => { const elements = panel.getElements(); await elements.checkElementNotPresence('element1', 'string'); - await panel.checkSectionPresence('Section 1'); + + const sections = panel.getSections(); + await sections.checkSectionPresence('Section 1'); /** * Open section */ - await panel.openSection('Section 1'); + await sections.openSection('Section 1'); await elements.checkElementPresence('element1', 'string'); await dashboardPage.refreshDashboard(); diff --git a/test/utils/form.ts b/test/utils/form.ts index cbf66379..9edb3ebd 100644 --- a/test/utils/form.ts +++ b/test/utils/form.ts @@ -183,6 +183,31 @@ class ButtonsHelper { } } +/** + * Buttons Helper + */ +class SectionsHelper { + private readonly locator: Locator; + private readonly selectors: LocatorSelectors; + + constructor(parentLocator: Locator) { + this.locator = parentLocator; + this.selectors = getLocatorSelectors(TEST_IDS.panel)(this.locator); + } + + private getMsg(message: string): string { + return `Sections: ${message}`; + } + + public async checkSectionPresence(name: string) { + return expect(this.selectors.splitLayoutContent(name), this.getMsg(`Check ${name} Section`)).toBeVisible(); + } + + public async openSection(name: string) { + return this.selectors.splitLayoutContent(name).click(); + } +} + /** * Panel Editor Helper */ @@ -245,6 +270,10 @@ export class PanelHelper { return new ButtonsHelper(this.locator); } + public getSections() { + return new SectionsHelper(this.locator); + } + public async checkIfNoErrors() { return expect(this.panel.getErrorIcon(), this.getMsg('Check If No Errors')).not.toBeVisible(); } @@ -257,14 +286,6 @@ export class PanelHelper { return expect(this.selectors.infoMessage(), this.getMsg(`Check Alert Message`)).toBeVisible(); } - public async checkSectionPresence(name: string) { - return expect(this.selectors.splitLayoutContent(name), this.getMsg(`Check ${name} Section`)).toBeVisible(); - } - - public async openSection(name: string) { - return this.selectors.splitLayoutContent(name).click(); - } - public async checkErrorMessage() { return expect(this.selectors.errorMessage(), this.getMsg(`Check Error Message`)).toBeVisible(); } From bfbd37a1edeeeaa7b57b6ded853b68db47abaf00 Mon Sep 17 00:00:00 2001 From: vitPinchuk Date: Sat, 9 Nov 2024 11:24:41 +0300 Subject: [PATCH 08/11] update sections helper with elements count --- test/panel.spec.ts | 5 +++++ test/utils/form.ts | 8 ++++++++ 2 files changed, 13 insertions(+) diff --git a/test/panel.spec.ts b/test/panel.spec.ts index eb8e6610..d9d429de 100644 --- a/test/panel.spec.ts +++ b/test/panel.spec.ts @@ -138,6 +138,7 @@ test.describe('Data Manipulation Panel', () => { const sections = panel.getSections(); await sections.checkSectionPresence('Current Values'); + await sections.checkElementsCountInSection('Current Values', 3); const elements = panel.getElements(); const disabledMaxElement = await elements.getDisabledElement('max', 'disabled'); @@ -170,6 +171,7 @@ test.describe('Data Manipulation Panel', () => { const sections = panel.getSections(); await sections.checkSectionPresence('Current Values'); + await sections.checkElementsCountInSection('Current Values', 3); const elements = panel.getElements(); const disabledMaxElement = await elements.getDisabledElement('max', 'disabled'); @@ -229,6 +231,8 @@ test.describe('Data Manipulation Panel', () => { const sections = panel.getSections(); await sections.checkSectionPresence('Current Values'); await sections.checkSectionPresence('New Values'); + await sections.checkElementsCountInSection('Current Values', 3); + await sections.checkElementsCountInSection('New Values', 3); const elements = panel.getElements(); const disabledMaxElement = await elements.getDisabledElement('max', 'disabled'); @@ -429,6 +433,7 @@ test.describe('Data Manipulation Panel', () => { * Open section */ await sections.openSection('Section 1'); + await sections.checkElementsCountInSection('Section 1', 1); await elements.checkElementPresence('element1', 'string'); await dashboardPage.refreshDashboard(); diff --git a/test/utils/form.ts b/test/utils/form.ts index 9edb3ebd..b59a4581 100644 --- a/test/utils/form.ts +++ b/test/utils/form.ts @@ -203,6 +203,14 @@ class SectionsHelper { return expect(this.selectors.splitLayoutContent(name), this.getMsg(`Check ${name} Section`)).toBeVisible(); } + public async checkElementsCountInSection(name: string, count: number) { + const section = this.selectors.splitLayoutContent(name); + const elementsContainer = getElementsSelector(section); + const elements = await elementsContainer.root().locator('label').all(); + + return expect(elements, this.getMsg('Check Body Rows Count')).toHaveLength(count); + } + public async openSection(name: string) { return this.selectors.splitLayoutContent(name).click(); } From 24674f085cd6937af3f89a8af9cbe1be68589df5 Mon Sep 17 00:00:00 2001 From: vitPinchuk Date: Sat, 9 Nov 2024 11:44:08 +0300 Subject: [PATCH 09/11] base element helper added --- test/utils/form.ts | 70 ++++++++++++++++++++-------------------------- 1 file changed, 30 insertions(+), 40 deletions(-) diff --git a/test/utils/form.ts b/test/utils/form.ts index b59a4581..3033b1d5 100644 --- a/test/utils/form.ts +++ b/test/utils/form.ts @@ -8,78 +8,68 @@ const getElementsSelector = getLocatorSelectors(TEST_IDS.formElements); /** * Disabled Element Helper */ -class DisabledElementHelper { - private readonly locator: Locator; +class BaseElementHelper { + protected readonly locator: Locator; - constructor(parentLocator: Locator) { - this.locator = parentLocator.getByTestId(TEST_IDS.formElements.fieldDisabled); + constructor(locator: Locator) { + this.locator = locator; } private getMsg(message: string): string { - return `DisabledElement: ${message}`; + return `Element: ${message}`; } public get() { return this.locator; } - public async checkValue(text: string) { - return expect(this.get(), this.getMsg('Check Text')).toHaveValue(text); - } -} - -/** - * Number Element Helper - */ -class NumberElementHelper { - private readonly locator: Locator; - - constructor(parentLocator: Locator) { - this.locator = parentLocator.getByTestId(TEST_IDS.formElements.fieldNumber); - } - - private getMsg(message: string): string { - return `NumberElement: ${message}`; - } - - public get() { - return this.locator; + public async setValue(value: string) { + await this.get().fill(value); + return this.get().blur(); } public async checkValue(text: string) { return expect(this.get(), this.getMsg('Check value')).toHaveValue(text); } - public async setValue(value: string) { - await this.get().fill(value); - return this.get().blur(); - } - public async isDisabled() { - return expect(this.get(), this.getMsg('Number element is Disabled')).toBeDisabled(); + return expect(this.get(), this.getMsg('Element is Disabled')).toBeDisabled(); } public async isNotDisabled() { - return expect(this.get(), this.getMsg('Number element is not Disabled')).not.toBeDisabled(); + return expect(this.get(), this.getMsg('Element is not Disabled')).not.toBeDisabled(); + } +} + +/** + * Disabled Element Helper + */ +class DisabledElementHelper extends BaseElementHelper { + constructor(parentLocator: Locator) { + super(parentLocator.getByTestId(TEST_IDS.formElements.fieldDisabled)); + } +} + +/** + * Number Element Helper + */ +class NumberElementHelper extends BaseElementHelper { + constructor(parentLocator: Locator) { + super(parentLocator.getByTestId(TEST_IDS.formElements.fieldNumber)); } } /** * Select Element Helper */ -class SelectElementHelper { - private readonly locator: Locator; +class SelectElementHelper extends BaseElementHelper { private readonly page: DashboardPage; constructor(parentLocator: Locator, page: DashboardPage) { - this.locator = parentLocator.getByTestId(TEST_IDS.formElements.fieldSelect); + super(parentLocator.getByTestId(TEST_IDS.formElements.fieldSelect)); this.page = page; } - public get() { - return this.locator; - } - public async setValue(fieldKey) { await this.get().click(); return this.page.getByGrafanaSelector(this.page.ctx.selectors.components.Select.option).getByText(fieldKey).click(); From e5a025f4d7c74683ef52da58126fbc8824465348 Mon Sep 17 00:00:00 2001 From: vitPinchuk Date: Mon, 11 Nov 2024 13:20:31 +0300 Subject: [PATCH 10/11] use FormElementType instead string --- .../CustomButtonsRow/CustomButtonsRow.tsx | 4 +- .../ElementEditor/ElementEditor.tsx | 8 ++-- .../ElementOptionsEditor.tsx | 12 ++---- .../ElementSections/ElementSections.test.tsx | 13 ++---- src/components/FormElement/FormElement.tsx | 4 +- .../BooleanElement/BooleanElement.tsx | 4 +- .../CheckboxListElement.tsx | 4 +- .../components/CodeElement/CodeElement.tsx | 4 +- .../CustomButtonElement.tsx | 11 ++++- .../DateTimeElement/DateTimeElement.tsx | 4 +- .../DisabledElement/DisabledElement.tsx | 4 +- .../DisabledTextAreaElement.tsx | 4 +- .../components/FileElement/FileElement.tsx | 4 +- .../components/LinkElement/LinkElement.tsx | 4 +- .../NumberElement/NumberElement.tsx | 4 +- .../PasswordElement/PasswordElement.tsx | 4 +- .../components/RadioElement/RadioElement.tsx | 4 +- .../SelectElement/SelectElement.tsx | 4 +- .../SliderElement/SliderElement.tsx | 4 +- .../StringElement/StringElement.tsx | 4 +- .../TextAreaElement/TextAreaElement.tsx | 4 +- .../components/TimeElement/TimeElement.tsx | 4 +- .../FormElements/FormElements.test.tsx | 7 ++-- .../FormElementsEditor.test.tsx | 8 ++-- src/components/FormPanel/FormPanel.test.tsx | 2 +- src/components/FormPanel/FormPanel.tsx | 2 +- src/constants/default.ts | 3 +- src/constants/form-element.ts | 28 +------------ src/migration.test.ts | 4 +- src/migration.ts | 4 +- src/types/form-element.ts | 28 ++++++++++++- src/utils/form-element.test.ts | 4 +- src/utils/form-element.ts | 2 +- src/utils/request.test.ts | 3 +- src/utils/request.ts | 4 +- test/panel.spec.ts | 41 ++++++++++--------- test/utils/form.ts | 4 +- 37 files changed, 132 insertions(+), 128 deletions(-) diff --git a/src/components/CustomButtonsRow/CustomButtonsRow.tsx b/src/components/CustomButtonsRow/CustomButtonsRow.tsx index 05b66f98..ebb7e81b 100644 --- a/src/components/CustomButtonsRow/CustomButtonsRow.tsx +++ b/src/components/CustomButtonsRow/CustomButtonsRow.tsx @@ -1,8 +1,8 @@ import { InterpolateFunction, PanelData } from '@grafana/data'; import React, { useMemo } from 'react'; -import { FormElementType } from '../../constants'; -import { CustomButtonShow, ExecuteCustomCodeParams, LocalFormElement } from '../../types'; +import { CustomButtonShow, ExecuteCustomCodeParams, FormElementType, LocalFormElement } from '@/types'; + import { CustomButtonElement } from '../FormElement/components'; /** diff --git a/src/components/ElementEditor/ElementEditor.tsx b/src/components/ElementEditor/ElementEditor.tsx index 41641a45..0277d753 100644 --- a/src/components/ElementEditor/ElementEditor.tsx +++ b/src/components/ElementEditor/ElementEditor.tsx @@ -22,7 +22,6 @@ import { CUSTOM_BUTTON_VARIANT_OPTIONS, CUSTOM_VALUE_OPTIONS, FORM_ELEMENT_TYPE_OPTIONS, - FormElementType, LINK_TARGET_OPTIONS, OPTIONS_SOURCE_OPTIONS, OptionsSource, @@ -30,15 +29,16 @@ import { STRING_ELEMENT_OPTIONS, TEST_IDS, TIME_TRANSFORMATION_OPTIONS, -} from '../../constants'; -import { ButtonVariant, CodeLanguage, LocalFormElement } from '../../types'; +} from '@/constants'; +import { ButtonVariant, CodeLanguage, FormElementType, LocalFormElement } from '@/types'; import { formatNumberValue, getElementWithNewType, getOptionsWithTestId, isFormElementType, toNumberValue, -} from '../../utils'; +} from '@/utils'; + import { ElementDateEditor } from '../ElementDateEditor'; import { ElementOptionsEditor } from '../ElementOptionsEditor'; import { ElementQueryOptionsEditor } from '../ElementQueryOptionsEditor'; diff --git a/src/components/ElementOptionsEditor/ElementOptionsEditor.tsx b/src/components/ElementOptionsEditor/ElementOptionsEditor.tsx index 3a88e18c..265bda31 100644 --- a/src/components/ElementOptionsEditor/ElementOptionsEditor.tsx +++ b/src/components/ElementOptionsEditor/ElementOptionsEditor.tsx @@ -4,14 +4,10 @@ import { DragDropContext, Draggable, DraggingStyle, Droppable, DropResult, NotDr import { Collapse } from '@volkovlabs/components'; import React, { ChangeEvent, useCallback, useState } from 'react'; -import { - FORM_ELEMENT_OPTION_DEFAULT, - FormElementType, - ICON_OPTIONS, - SELECT_ELEMENT_OPTIONS, - TEST_IDS, -} from '../../constants'; -import { reorder } from '../../utils'; +import { FORM_ELEMENT_OPTION_DEFAULT, ICON_OPTIONS, SELECT_ELEMENT_OPTIONS, TEST_IDS } from '@/constants'; +import { FormElementType } from '@/types'; +import { reorder } from '@/utils'; + import { getStyles } from './ElementOptionsEditor.styles'; /** diff --git a/src/components/ElementSections/ElementSections.test.tsx b/src/components/ElementSections/ElementSections.test.tsx index 851bf855..30d30fcf 100644 --- a/src/components/ElementSections/ElementSections.test.tsx +++ b/src/components/ElementSections/ElementSections.test.tsx @@ -1,15 +1,10 @@ import { fireEvent, render, screen } from '@testing-library/react'; import React from 'react'; -import { - FORM_ELEMENT_DEFAULT, - FormElementType, - LayoutOrientation, - LayoutVariant, - SectionVariant, -} from '../../constants'; -import { PanelOptions, UpdateEnabledMode } from '../../types'; -import { getFormElementsSectionSelectors, normalizeElementsForLocalState } from '../../utils'; +import { FORM_ELEMENT_DEFAULT, LayoutOrientation, LayoutVariant, SectionVariant } from '@/constants'; +import { FormElementType, PanelOptions, UpdateEnabledMode } from '@/types'; +import { getFormElementsSectionSelectors, normalizeElementsForLocalState } from '@/utils'; + import { ElementSections } from './ElementSections'; /** diff --git a/src/components/FormElement/FormElement.tsx b/src/components/FormElement/FormElement.tsx index 48ea516f..10b2a045 100644 --- a/src/components/FormElement/FormElement.tsx +++ b/src/components/FormElement/FormElement.tsx @@ -3,8 +3,8 @@ import { InterpolateFunction } from '@grafana/data'; import { InlineFieldRow, InlineLabel, useStyles2 } from '@grafana/ui'; import React from 'react'; -import { FormElementType, TEST_IDS } from '@/constants'; -import { CustomButtonShow, ExecuteCustomCodeParams, LocalFormElement } from '@/types'; +import { TEST_IDS } from '@/constants'; +import { CustomButtonShow, ExecuteCustomCodeParams, FormElementType, LocalFormElement } from '@/types'; import { BooleanElement, diff --git a/src/components/FormElement/components/BooleanElement/BooleanElement.tsx b/src/components/FormElement/components/BooleanElement/BooleanElement.tsx index ea18e8f2..4e329137 100644 --- a/src/components/FormElement/components/BooleanElement/BooleanElement.tsx +++ b/src/components/FormElement/components/BooleanElement/BooleanElement.tsx @@ -1,8 +1,8 @@ import { InlineField, RadioButtonGroup } from '@grafana/ui'; import React from 'react'; -import { BOOLEAN_ELEMENT_OPTIONS, FormElementType, TEST_IDS } from '@/constants'; -import { FormElementByType, LocalFormElement } from '@/types'; +import { BOOLEAN_ELEMENT_OPTIONS, TEST_IDS } from '@/constants'; +import { FormElementByType, FormElementType, LocalFormElement } from '@/types'; import { applyLabelStyles, applyWidth } from '@/utils'; /** diff --git a/src/components/FormElement/components/CheckboxListElement/CheckboxListElement.tsx b/src/components/FormElement/components/CheckboxListElement/CheckboxListElement.tsx index 9f71c17c..bb2cb248 100644 --- a/src/components/FormElement/components/CheckboxListElement/CheckboxListElement.tsx +++ b/src/components/FormElement/components/CheckboxListElement/CheckboxListElement.tsx @@ -2,8 +2,8 @@ import { cx } from '@emotion/css'; import { Checkbox, InlineField, useStyles2, useTheme2 } from '@grafana/ui'; import React from 'react'; -import { FormElementType, TEST_IDS } from '@/constants'; -import { FormElementByType, LocalFormElement } from '@/types'; +import { TEST_IDS } from '@/constants'; +import { FormElementByType, FormElementType, LocalFormElement } from '@/types'; import { applyLabelStyles, applyWidth } from '@/utils'; import { getStyles } from './CheckboxListElement.style'; diff --git a/src/components/FormElement/components/CodeElement/CodeElement.tsx b/src/components/FormElement/components/CodeElement/CodeElement.tsx index bb131fd4..73dd3d50 100644 --- a/src/components/FormElement/components/CodeElement/CodeElement.tsx +++ b/src/components/FormElement/components/CodeElement/CodeElement.tsx @@ -2,8 +2,8 @@ import { InlineField } from '@grafana/ui'; import { AutosizeCodeEditor } from '@volkovlabs/components'; import React, { useMemo } from 'react'; -import { FormElementType, TEST_IDS } from '@/constants'; -import { CodeLanguage, FormElementByType, LocalFormElement } from '@/types'; +import { TEST_IDS } from '@/constants'; +import { CodeLanguage, FormElementByType, FormElementType, LocalFormElement } from '@/types'; import { applyLabelStyles, applyWidth } from '@/utils'; /** diff --git a/src/components/FormElement/components/CustomButtonElement/CustomButtonElement.tsx b/src/components/FormElement/components/CustomButtonElement/CustomButtonElement.tsx index c2fa76f7..5d7b5be2 100644 --- a/src/components/FormElement/components/CustomButtonElement/CustomButtonElement.tsx +++ b/src/components/FormElement/components/CustomButtonElement/CustomButtonElement.tsx @@ -2,8 +2,15 @@ import { InterpolateFunction } from '@grafana/data'; import { Button, InlineField, useTheme2 } from '@grafana/ui'; import React, { useCallback } from 'react'; -import { FormElementType, TEST_IDS } from '@/constants'; -import { ButtonVariant, CustomButtonShow, ExecuteCustomCodeParams, FormElementByType, LocalFormElement } from '@/types'; +import { TEST_IDS } from '@/constants'; +import { + ButtonVariant, + CustomButtonShow, + ExecuteCustomCodeParams, + FormElementByType, + FormElementType, + LocalFormElement, +} from '@/types'; import { applyLabelStyles, applyWidth, getButtonVariant } from '@/utils'; /** diff --git a/src/components/FormElement/components/DateTimeElement/DateTimeElement.tsx b/src/components/FormElement/components/DateTimeElement/DateTimeElement.tsx index 1e0256cf..b4808938 100644 --- a/src/components/FormElement/components/DateTimeElement/DateTimeElement.tsx +++ b/src/components/FormElement/components/DateTimeElement/DateTimeElement.tsx @@ -2,8 +2,8 @@ import { DateTime, dateTime } from '@grafana/data'; import { DatePickerWithInput, DateTimePicker, InlineField } from '@grafana/ui'; import React from 'react'; -import { FormElementType, TEST_IDS } from '@/constants'; -import { FormElementByType, LocalFormElement } from '@/types'; +import { TEST_IDS } from '@/constants'; +import { FormElementByType, FormElementType, LocalFormElement } from '@/types'; import { applyLabelStyles, applyWidth } from '@/utils'; /** diff --git a/src/components/FormElement/components/DisabledElement/DisabledElement.tsx b/src/components/FormElement/components/DisabledElement/DisabledElement.tsx index fa576735..4a46b909 100644 --- a/src/components/FormElement/components/DisabledElement/DisabledElement.tsx +++ b/src/components/FormElement/components/DisabledElement/DisabledElement.tsx @@ -1,8 +1,8 @@ import { InlineField, Input } from '@grafana/ui'; import React from 'react'; -import { FormElementType, TEST_IDS } from '@/constants'; -import { FormElementByType, LocalFormElement } from '@/types'; +import { TEST_IDS } from '@/constants'; +import { FormElementByType, FormElementType, LocalFormElement } from '@/types'; import { applyLabelStyles, applyWidth } from '@/utils'; /** diff --git a/src/components/FormElement/components/DisabledTextAreaElement/DisabledTextAreaElement.tsx b/src/components/FormElement/components/DisabledTextAreaElement/DisabledTextAreaElement.tsx index 5b13f7cb..9364c348 100644 --- a/src/components/FormElement/components/DisabledTextAreaElement/DisabledTextAreaElement.tsx +++ b/src/components/FormElement/components/DisabledTextAreaElement/DisabledTextAreaElement.tsx @@ -1,8 +1,8 @@ import { InlineField, TextArea } from '@grafana/ui'; import React from 'react'; -import { FormElementType, TEST_IDS } from '@/constants'; -import { FormElementByType, LocalFormElement } from '@/types'; +import { TEST_IDS } from '@/constants'; +import { FormElementByType, FormElementType, LocalFormElement } from '@/types'; import { applyLabelStyles, applyWidth } from '@/utils'; /** diff --git a/src/components/FormElement/components/FileElement/FileElement.tsx b/src/components/FormElement/components/FileElement/FileElement.tsx index d87605a9..ba20e872 100644 --- a/src/components/FormElement/components/FileElement/FileElement.tsx +++ b/src/components/FormElement/components/FileElement/FileElement.tsx @@ -1,8 +1,8 @@ import { FileDropzone, InlineField } from '@grafana/ui'; import React from 'react'; -import { FormElementType, TEST_IDS } from '@/constants'; -import { FormElementByType, LocalFormElement } from '@/types'; +import { TEST_IDS } from '@/constants'; +import { FormElementByType, FormElementType, LocalFormElement } from '@/types'; import { applyAcceptedFiles, applyLabelStyles, applyWidth } from '@/utils'; /** diff --git a/src/components/FormElement/components/LinkElement/LinkElement.tsx b/src/components/FormElement/components/LinkElement/LinkElement.tsx index d7e0ab53..1482e513 100644 --- a/src/components/FormElement/components/LinkElement/LinkElement.tsx +++ b/src/components/FormElement/components/LinkElement/LinkElement.tsx @@ -1,8 +1,8 @@ import { InlineField, TextLink, useStyles2, useTheme2 } from '@grafana/ui'; import React from 'react'; -import { FormElementType, TEST_IDS } from '@/constants'; -import { FormElementByType, LinkTarget, LocalFormElement } from '@/types'; +import { TEST_IDS } from '@/constants'; +import { FormElementByType, FormElementType, LinkTarget, LocalFormElement } from '@/types'; import { applyLabelStyles, applyWidth } from '@/utils'; import { getStyles } from './LinkElement.style'; diff --git a/src/components/FormElement/components/NumberElement/NumberElement.tsx b/src/components/FormElement/components/NumberElement/NumberElement.tsx index d90a5892..1d1065e9 100644 --- a/src/components/FormElement/components/NumberElement/NumberElement.tsx +++ b/src/components/FormElement/components/NumberElement/NumberElement.tsx @@ -2,8 +2,8 @@ import { InlineField } from '@grafana/ui'; import { NumberInput } from '@volkovlabs/components'; import React from 'react'; -import { FormElementType, TEST_IDS } from '@/constants'; -import { FormElementByType, LocalFormElement } from '@/types'; +import { TEST_IDS } from '@/constants'; +import { FormElementByType, FormElementType, LocalFormElement } from '@/types'; import { applyLabelStyles, applyWidth, formatNumberValue } from '@/utils'; /** diff --git a/src/components/FormElement/components/PasswordElement/PasswordElement.tsx b/src/components/FormElement/components/PasswordElement/PasswordElement.tsx index 5cbcb944..98630647 100644 --- a/src/components/FormElement/components/PasswordElement/PasswordElement.tsx +++ b/src/components/FormElement/components/PasswordElement/PasswordElement.tsx @@ -1,8 +1,8 @@ import { InlineField, Input } from '@grafana/ui'; import React, { ChangeEvent } from 'react'; -import { FormElementType, TEST_IDS } from '@/constants'; -import { FormElementByType, LocalFormElement } from '@/types'; +import { TEST_IDS } from '@/constants'; +import { FormElementByType, FormElementType, LocalFormElement } from '@/types'; import { applyLabelStyles, applyWidth } from '@/utils'; /** diff --git a/src/components/FormElement/components/RadioElement/RadioElement.tsx b/src/components/FormElement/components/RadioElement/RadioElement.tsx index 3bcf6b35..5ce195b2 100644 --- a/src/components/FormElement/components/RadioElement/RadioElement.tsx +++ b/src/components/FormElement/components/RadioElement/RadioElement.tsx @@ -1,8 +1,8 @@ import { InlineField, RadioButtonGroup } from '@grafana/ui'; import React from 'react'; -import { FormElementType, TEST_IDS } from '@/constants'; -import { FormElementByType, LocalFormElement } from '@/types'; +import { TEST_IDS } from '@/constants'; +import { FormElementByType, FormElementType, LocalFormElement } from '@/types'; import { applyLabelStyles, applyWidth } from '@/utils'; /** diff --git a/src/components/FormElement/components/SelectElement/SelectElement.tsx b/src/components/FormElement/components/SelectElement/SelectElement.tsx index 4f428d6c..5cc6e94c 100644 --- a/src/components/FormElement/components/SelectElement/SelectElement.tsx +++ b/src/components/FormElement/components/SelectElement/SelectElement.tsx @@ -1,8 +1,8 @@ import { InlineField, Select } from '@grafana/ui'; import React from 'react'; -import { FormElementType, TEST_IDS } from '@/constants'; -import { FormElementByType, LocalFormElement } from '@/types'; +import { TEST_IDS } from '@/constants'; +import { FormElementByType, FormElementType, LocalFormElement } from '@/types'; import { applyLabelStyles, applyWidth } from '@/utils'; /** diff --git a/src/components/FormElement/components/SliderElement/SliderElement.tsx b/src/components/FormElement/components/SliderElement/SliderElement.tsx index 8ba0db40..6e2ac21d 100644 --- a/src/components/FormElement/components/SliderElement/SliderElement.tsx +++ b/src/components/FormElement/components/SliderElement/SliderElement.tsx @@ -3,8 +3,8 @@ import { InlineField, Input, useStyles2 } from '@grafana/ui'; import Slider from 'rc-slider'; import React from 'react'; -import { FormElementType, TEST_IDS } from '@/constants'; -import { FormElementByType, LocalFormElement } from '@/types'; +import { TEST_IDS } from '@/constants'; +import { FormElementByType, FormElementType, LocalFormElement } from '@/types'; import { applyLabelStyles, applyWidth } from '@/utils'; import { getStyles } from './SliderElement.style'; diff --git a/src/components/FormElement/components/StringElement/StringElement.tsx b/src/components/FormElement/components/StringElement/StringElement.tsx index 3ae7787d..d69198fc 100644 --- a/src/components/FormElement/components/StringElement/StringElement.tsx +++ b/src/components/FormElement/components/StringElement/StringElement.tsx @@ -2,8 +2,8 @@ import { cx } from '@emotion/css'; import { InlineField, Input, useStyles2 } from '@grafana/ui'; import React, { ChangeEvent } from 'react'; -import { FormElementType, TEST_IDS } from '@/constants'; -import { FormElementByType, LocalFormElement } from '@/types'; +import { TEST_IDS } from '@/constants'; +import { FormElementByType, FormElementType, LocalFormElement } from '@/types'; import { applyLabelStyles, applyWidth } from '@/utils'; import { getStyles } from './StringElement.style'; diff --git a/src/components/FormElement/components/TextAreaElement/TextAreaElement.tsx b/src/components/FormElement/components/TextAreaElement/TextAreaElement.tsx index 44cc5692..0f031c66 100644 --- a/src/components/FormElement/components/TextAreaElement/TextAreaElement.tsx +++ b/src/components/FormElement/components/TextAreaElement/TextAreaElement.tsx @@ -1,8 +1,8 @@ import { InlineField, TextArea } from '@grafana/ui'; import React, { ChangeEvent } from 'react'; -import { FormElementType, TEST_IDS } from '@/constants'; -import { FormElementByType, LocalFormElement } from '@/types'; +import { TEST_IDS } from '@/constants'; +import { FormElementByType, FormElementType, LocalFormElement } from '@/types'; import { applyLabelStyles, applyWidth } from '@/utils'; /** diff --git a/src/components/FormElement/components/TimeElement/TimeElement.tsx b/src/components/FormElement/components/TimeElement/TimeElement.tsx index 6ebba517..85c2234e 100644 --- a/src/components/FormElement/components/TimeElement/TimeElement.tsx +++ b/src/components/FormElement/components/TimeElement/TimeElement.tsx @@ -2,8 +2,8 @@ import { DateTime, dateTimeForTimeZone, getTimeZone } from '@grafana/data'; import { InlineField, TimeOfDayPicker } from '@grafana/ui'; import React, { useCallback } from 'react'; -import { FormElementType, TEST_IDS } from '@/constants'; -import { FormElementByType, LocalFormElement } from '@/types'; +import { TEST_IDS } from '@/constants'; +import { FormElementByType, FormElementType, LocalFormElement } from '@/types'; import { applyWidth } from '@/utils'; /** diff --git a/src/components/FormElements/FormElements.test.tsx b/src/components/FormElements/FormElements.test.tsx index ac08176f..cf68d138 100644 --- a/src/components/FormElements/FormElements.test.tsx +++ b/src/components/FormElements/FormElements.test.tsx @@ -2,9 +2,10 @@ import { toDataFrame } from '@grafana/data'; import { act, fireEvent, render, screen, within } from '@testing-library/react'; import React from 'react'; -import { FORM_ELEMENT_DEFAULT, FormElementType, OptionsSource } from '../../constants'; -import { ButtonVariant, CustomButtonShow, LinkTarget } from '../../types'; -import { getFormElementsSelectors, normalizeElementsForLocalState } from '../../utils'; +import { FORM_ELEMENT_DEFAULT, OptionsSource } from '@/constants'; +import { ButtonVariant, CustomButtonShow, FormElementType, LinkTarget } from '@/types'; +import { getFormElementsSelectors, normalizeElementsForLocalState } from '@/utils'; + import { FormElements } from './FormElements'; /** diff --git a/src/components/FormElementsEditor/FormElementsEditor.test.tsx b/src/components/FormElementsEditor/FormElementsEditor.test.tsx index b534e3c0..0cea780c 100644 --- a/src/components/FormElementsEditor/FormElementsEditor.test.tsx +++ b/src/components/FormElementsEditor/FormElementsEditor.test.tsx @@ -8,15 +8,15 @@ import { CUSTOM_BUTTON_DEFAULT, FORM_ELEMENT_DEFAULT, FORM_ELEMENT_OPTION_DEFAULT, - FormElementType, NUMBER_DEFAULT, OptionsSource, RequestMethod, SELECT_DEFAULT, SLIDER_DEFAULT, -} from '../../constants'; -import { ButtonVariant, CodeLanguage, LinkTarget } from '../../types'; -import { getFormElementsEditorSelectors } from '../../utils'; +} from '@/constants'; +import { ButtonVariant, CodeLanguage, FormElementType, LinkTarget } from '@/types'; +import { getFormElementsEditorSelectors } from '@/utils'; + import { FormElementsEditor } from './FormElementsEditor'; /** diff --git a/src/components/FormPanel/FormPanel.test.tsx b/src/components/FormPanel/FormPanel.test.tsx index c910c58a..0781f733 100644 --- a/src/components/FormPanel/FormPanel.test.tsx +++ b/src/components/FormPanel/FormPanel.test.tsx @@ -10,7 +10,6 @@ import { CONFIRM_MODAL_DEFAULT, ContentType, FORM_ELEMENT_DEFAULT, - FormElementType, LayoutOrientation, LayoutVariant, PayloadMode, @@ -24,6 +23,7 @@ import { ButtonVariant, CustomButtonShow, FormElement, + FormElementType, LocalFormElement, ModalColumnName, PanelOptions, diff --git a/src/components/FormPanel/FormPanel.tsx b/src/components/FormPanel/FormPanel.tsx index d350ba53..ec0b8aaa 100644 --- a/src/components/FormPanel/FormPanel.tsx +++ b/src/components/FormPanel/FormPanel.tsx @@ -29,7 +29,6 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { ConfirmationElementDisplayMode, ContentType, - FormElementType, LayoutVariant, LoadingMode, PayloadMode, @@ -41,6 +40,7 @@ import { useFormElements, useMutableState } from '@/hooks'; import { ButtonVariant, FormElement, + FormElementType, LocalFormElement, ModalColumnName, PanelOptions, diff --git a/src/constants/default.ts b/src/constants/default.ts index bc97aa9c..062fbfa9 100644 --- a/src/constants/default.ts +++ b/src/constants/default.ts @@ -7,6 +7,7 @@ import { CustomButtonOptions, CustomButtonShow, FormElement, + FormElementType, ModalColumnName, ModalOptions, NumberOptions, @@ -15,7 +16,7 @@ import { TextareaOptions, } from '../types'; import { CODE_EDITOR_CONFIG } from './code-editor'; -import { FormElementType, OptionsSource } from './form-element'; +import { OptionsSource } from './form-element'; import { ConfirmationElementDisplayMode } from './request'; /** diff --git a/src/constants/form-element.ts b/src/constants/form-element.ts index 429d0f8f..f389b82c 100644 --- a/src/constants/form-element.ts +++ b/src/constants/form-element.ts @@ -1,35 +1,9 @@ import { SelectableValue } from '@grafana/data'; import { getAvailableIcons } from '@grafana/ui'; -import { ButtonSize, ButtonVariant, CustomButtonShow, LinkTarget } from '../types'; +import { ButtonSize, ButtonVariant, CustomButtonShow, FormElementType, LinkTarget } from '../types'; import { TEST_IDS } from './tests'; -/** - * Form Element Type - */ -export const enum FormElementType { - BOOLEAN = 'boolean', - CODE = 'code', - BUTTON = 'button', - DATE = 'date', - DATETIME = 'datetime', - DISABLED = 'disabled', - DISABLED_TEXTAREA = 'disabledTextarea', - FILE = 'file', - LINK = 'link', - MULTISELECT = 'multiselect', - NUMBER = 'number', - PASSWORD = 'password', - RADIO = 'radio', - SECRET = 'secret', - SELECT = 'select', - SLIDER = 'slider', - STRING = 'string', - TEXTAREA = 'textarea', - TIME = 'time', - CHECKBOX_LIST = 'checkboxList', -} - /** * Form Element Type Options */ diff --git a/src/migration.test.ts b/src/migration.test.ts index 9195a27b..282b29cd 100644 --- a/src/migration.test.ts +++ b/src/migration.test.ts @@ -1,8 +1,8 @@ import { getBackendSrv } from '@grafana/runtime'; -import { FormElementType, PayloadMode } from './constants'; +import { PayloadMode } from './constants'; import { getMigratedOptions } from './migration'; -import { PanelOptions } from './types'; +import { FormElementType, PanelOptions } from './types'; /** * Mock @grafana/runtime diff --git a/src/migration.ts b/src/migration.ts index a34cfbd8..6fbce179 100644 --- a/src/migration.ts +++ b/src/migration.ts @@ -3,8 +3,8 @@ import { getBackendSrv } from '@grafana/runtime'; import { set } from 'lodash'; import semver from 'semver'; -import { FormElementType, PayloadMode } from './constants'; -import { LayoutOptions, LayoutSection, PanelOptions, RequestOptions } from './types'; +import { PayloadMode } from './constants'; +import { FormElementType, LayoutOptions, LayoutSection, PanelOptions, RequestOptions } from './types'; /** * Outdated Request Options diff --git a/src/types/form-element.ts b/src/types/form-element.ts index 2d5111a3..5c6b00cf 100644 --- a/src/types/form-element.ts +++ b/src/types/form-element.ts @@ -1,9 +1,35 @@ import { DataQueryResponse, IconName, InterpolateFunction, PanelData, SelectableValue } from '@grafana/data'; import { FetchResponse } from '@grafana/runtime'; -import { FormElementType, OptionsSource } from '../constants'; +import { OptionsSource } from '../constants'; import { ButtonSize, ButtonVariant, CodeLanguage } from '../types'; +/** + * Form Element Type + */ +export const enum FormElementType { + BOOLEAN = 'boolean', + CODE = 'code', + BUTTON = 'button', + DATE = 'date', + DATETIME = 'datetime', + DISABLED = 'disabled', + DISABLED_TEXTAREA = 'disabledTextarea', + FILE = 'file', + LINK = 'link', + MULTISELECT = 'multiselect', + NUMBER = 'number', + PASSWORD = 'password', + RADIO = 'radio', + SECRET = 'secret', + SELECT = 'select', + SLIDER = 'slider', + STRING = 'string', + TEXTAREA = 'textarea', + TIME = 'time', + CHECKBOX_LIST = 'checkboxList', +} + /** * Query Field */ diff --git a/src/utils/form-element.test.ts b/src/utils/form-element.test.ts index 3dfd2422..053637a1 100644 --- a/src/utils/form-element.test.ts +++ b/src/utils/form-element.test.ts @@ -1,8 +1,8 @@ /* eslint-disable no-console */ import { css, keyframes } from '@emotion/css'; -import { FormElementType, OptionsSource } from '../constants'; -import { ButtonVariant } from '../types'; +import { OptionsSource } from '../constants'; +import { ButtonVariant, FormElementType } from '../types'; import { applyAcceptedFiles, applyLabelStyles, diff --git a/src/utils/form-element.ts b/src/utils/form-element.ts index b310c1d9..0f6a2c78 100644 --- a/src/utils/form-element.ts +++ b/src/utils/form-element.ts @@ -6,7 +6,6 @@ import { v4 as uuidv4 } from 'uuid'; import { CODE_DEFAULT, CUSTOM_BUTTON_DEFAULT, - FormElementType, NUMBER_DEFAULT, OptionsSource, SELECT_DEFAULT, @@ -18,6 +17,7 @@ import { DisableIfHelper, FormElement, FormElementByType, + FormElementType, GetOptionsHelper, LayoutSection, LinkTarget, diff --git a/src/utils/request.test.ts b/src/utils/request.test.ts index 76480d57..f1b5dbc9 100644 --- a/src/utils/request.test.ts +++ b/src/utils/request.test.ts @@ -1,4 +1,5 @@ -import { FormElementType, PayloadMode } from '@/constants'; +import { PayloadMode } from '@/constants'; +import { FormElementType } from '@/types'; import { getPayloadForRequest, toFormData, toJson } from './request'; diff --git a/src/utils/request.ts b/src/utils/request.ts index 3d8dc6da..69e166e8 100644 --- a/src/utils/request.ts +++ b/src/utils/request.ts @@ -1,7 +1,7 @@ import { InterpolateFunction } from '@grafana/data'; -import { FormElementType, PayloadMode } from '../constants'; -import { LocalFormElement, RequestOptions } from '../types'; +import { PayloadMode } from '../constants'; +import { FormElementType, LocalFormElement, RequestOptions } from '../types'; import { getPayloadCodeParameters } from './code-parameters'; /** diff --git a/test/panel.spec.ts b/test/panel.spec.ts index d9d429de..840f216a 100644 --- a/test/panel.spec.ts +++ b/test/panel.spec.ts @@ -1,5 +1,6 @@ import { test, expect } from '@grafana/plugin-e2e'; import { ModalHelper, PanelHelper } from './utils'; +import { FormElementType } from '../src/types/form-element'; test.describe('Data Manipulation Panel', () => { test('Check grafana version', async ({ grafanaVersion }) => { @@ -30,19 +31,19 @@ test.describe('Data Manipulation Panel', () => { * Check all elements Presence */ await elements.checkPresence(); - await elements.checkElementPresence('dateTime', 'datetime'); - await elements.checkElementPresence('time', 'time'); - await elements.checkElementPresence('date', 'date'); - await elements.checkElementPresence('amount', 'number'); - await elements.checkElementPresence('updated', 'boolean'); - await elements.checkElementPresence('name', 'string'); - await elements.checkElementPresence('step', 'slider'); - await elements.checkElementPresence('select', 'select'); - await elements.checkElementPresence('radio', 'radio'); - await elements.checkElementPresence('password', 'password'); - await elements.checkElementPresence('disabled', 'disabled'); - await elements.checkElementPresence('link', 'link'); - await elements.checkElementPresence('checkbox', 'checkboxList'); + await elements.checkElementPresence('dateTime', FormElementType.DATETIME); + await elements.checkElementPresence('time', FormElementType.TIME); + await elements.checkElementPresence('date', FormElementType.DATE); + await elements.checkElementPresence('amount', FormElementType.NUMBER); + await elements.checkElementPresence('updated', FormElementType.BOOLEAN); + await elements.checkElementPresence('name', FormElementType.STRING); + await elements.checkElementPresence('step', FormElementType.SLIDER); + await elements.checkElementPresence('select', FormElementType.SELECT); + await elements.checkElementPresence('radio', FormElementType.RADIO); + await elements.checkElementPresence('password', FormElementType.PASSWORD); + await elements.checkElementPresence('disabled', FormElementType.DISABLED); + await elements.checkElementPresence('link', FormElementType.LINK); + await elements.checkElementPresence('checkbox', FormElementType.CHECKBOX_LIST); const panelBoxes = new PanelHelper(dashboardPage, 'Boxes'); await panelBoxes.checkIfNoErrors(); @@ -50,10 +51,10 @@ test.describe('Data Manipulation Panel', () => { const boxesElements = panelBoxes.getElements(); await boxesElements.checkPresence(); - await boxesElements.checkElementPresence('file', 'file'); - await boxesElements.checkElementPresence('text', 'textarea'); - await boxesElements.checkElementPresence('readOnlyTextArea', 'disabledTextarea'); - await boxesElements.checkElementPresence('code', 'code'); + await boxesElements.checkElementPresence('file', FormElementType.FILE); + await boxesElements.checkElementPresence('text', FormElementType.TEXTAREA); + await boxesElements.checkElementPresence('readOnlyTextArea', FormElementType.DISABLED_TEXTAREA); + await boxesElements.checkElementPresence('code', FormElementType.CODE); }); test('Should add empty Form panel', async ({ gotoDashboardPage, readProvisionedDashboard }) => { @@ -115,7 +116,7 @@ test.describe('Data Manipulation Panel', () => { const elements = panel.getElements(); await elements.checkPresence(); - await elements.checkElementPresence('string', 'string'); + await elements.checkElementPresence('string', FormElementType.STRING); }); }); @@ -434,14 +435,14 @@ test.describe('Data Manipulation Panel', () => { */ await sections.openSection('Section 1'); await sections.checkElementsCountInSection('Section 1', 1); - await elements.checkElementPresence('element1', 'string'); + await elements.checkElementPresence('element1', FormElementType.STRING); await dashboardPage.refreshDashboard(); /** * Element in open section should be visible */ - await elements.checkElementPresence('element1', 'string'); + await elements.checkElementPresence('element1', FormElementType.STRING); }); }); }); diff --git a/test/utils/form.ts b/test/utils/form.ts index 3033b1d5..62b484fd 100644 --- a/test/utils/form.ts +++ b/test/utils/form.ts @@ -3,6 +3,8 @@ import { DashboardPage, expect, Panel, PanelEditPage } from '@grafana/plugin-e2e import { getLocatorSelectors, LocatorSelectors } from './selectors'; import { TEST_IDS } from '../../src/constants/tests'; +import { FormElementType } from '../../src/types/form-element'; + const getElementsSelector = getLocatorSelectors(TEST_IDS.formElements); /** @@ -98,7 +100,7 @@ class ElementsHelper { return expect(this.selectors.root(), this.getMsg('Elements Container Presence')).toBeVisible(); } - public async checkElementPresence(elementId: string, elementType: string) { + public async checkElementPresence(elementId: string, elementType: FormElementType) { return expect( this.selectors.element(elementId, elementType), this.getMsg(`Element ${elementId} Presence`) From 276e10dce985a5bfcea4116d82092e2e2c8cfb96 Mon Sep 17 00:00:00 2001 From: Mikhail Volkov Date: Mon, 11 Nov 2024 22:06:22 -0500 Subject: [PATCH 11/11] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 67ba3f1a..aee4e09b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - Removed DatasourceResponseError moved to external Components (#535) - Added HTML, Markdown to supported Code Editor languages (#543) - Updated options to use datasource ID instead of name (#539) +- Updated E2E tests (#538) ## 4.8.0 (2024-10-25)