diff --git a/CHANGELOG.md b/CHANGELOG.md index f834ec8b..69b6c641 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Features / Enhancements - Updated date and time input timezone (#452) +- Added dashboard variables support in button titles (#479) ## 4.4.0 (2024-08-29) diff --git a/provisioning/dashboards/panels.json b/provisioning/dashboards/panels.json index e0645722..a4c3d6bf 100644 --- a/provisioning/dashboards/panels.json +++ b/provisioning/dashboards/panels.json @@ -24,7 +24,6 @@ "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 3, "links": [], "liveNow": false, "panels": [ @@ -117,7 +116,7 @@ }, "updateEnabled": "auto" }, - "pluginVersion": "4.3.0", + "pluginVersion": "4.5.0", "title": "No Elements", "type": "volkovlabs-form-panel" }, @@ -233,7 +232,7 @@ }, "updateEnabled": "auto" }, - "pluginVersion": "4.3.0", + "pluginVersion": "4.5.0", "title": "Data Manipulation Form", "type": "volkovlabs-form-panel" }, @@ -288,20 +287,20 @@ "backgroundColor": "purple", "foregroundColor": "yellow", "icon": "process", - "text": "Reset", - "variant": "hidden" + "text": "Reset $var", + "variant": "primary" }, "resetAction": { "code": "console.log(context.panel.data, context.panel.response, context.panel.initial, context.panel.elements);", - "confirm": false, + "confirm": true, "getPayload": "return {\n rawSql: '',\n format: 'table',\n}", "mode": "initial", "payload": {} }, "saveDefault": { "icon": "save", - "text": "Save Default", - "variant": "hidden" + "text": "Save Default $var", + "variant": "secondary" }, "submit": { "backgroundColor": "purple", @@ -309,7 +308,7 @@ "icon": "cloud-upload", "orientation": "center", "size": "md", - "text": "Submit", + "text": "Submit $var", "variant": "destructive" }, "sync": true, @@ -325,7 +324,7 @@ }, "updateEnabled": "auto" }, - "pluginVersion": "4.3.0", + "pluginVersion": "4.5.0", "title": "Buttons only", "type": "volkovlabs-form-panel" }, @@ -532,7 +531,7 @@ "width": 0, "widthMode": "auto" }, - "pluginVersion": "4.3.0", + "pluginVersion": "4.5.0", "title": "Single Form", "type": "volkovlabs-form-panel" }, @@ -670,7 +669,7 @@ }, "updateEnabled": "auto" }, - "pluginVersion": "4.3.0", + "pluginVersion": "4.5.0", "title": "Read Only Updated", "type": "volkovlabs-form-panel" }, @@ -841,7 +840,7 @@ }, "updateEnabled": "auto" }, - "pluginVersion": "4.3.0", + "pluginVersion": "4.5.0", "title": "Control Panel", "type": "volkovlabs-form-panel" }, @@ -939,7 +938,7 @@ }, "updateEnabled": "auto" }, - "pluginVersion": "4.3.0", + "pluginVersion": "4.5.0", "title": "No Initial Request and Custom Button", "type": "volkovlabs-form-panel" }, @@ -1141,7 +1140,7 @@ "width": 0, "widthMode": "auto" }, - "pluginVersion": "4.3.0", + "pluginVersion": "4.5.0", "title": "Split Form", "type": "volkovlabs-form-panel" } @@ -1153,9 +1152,9 @@ "list": [ { "current": { - "selected": true, - "text": "test", - "value": "test" + "selected": false, + "text": "test2", + "value": "test2" }, "hide": 0, "label": "Var", @@ -1163,11 +1162,11 @@ "options": [ { "selected": true, - "text": "test", - "value": "test" + "text": "test2", + "value": "test2" } ], - "query": "test", + "query": "test2", "skipUrlSync": false, "type": "textbox" } diff --git a/src/components/CustomButtonsRow/CustomButtonsRow.tsx b/src/components/CustomButtonsRow/CustomButtonsRow.tsx index 2a46e14e..05b66f98 100644 --- a/src/components/CustomButtonsRow/CustomButtonsRow.tsx +++ b/src/components/CustomButtonsRow/CustomButtonsRow.tsx @@ -71,6 +71,7 @@ export const CustomButtonsRow: React.FC = ({ elements, initial, replaceVa executeCustomCode={executeCustomCode} initial={initial} elements={elements} + replaceVariables={replaceVariables} /> ); } diff --git a/src/components/FormElement/FormElement.tsx b/src/components/FormElement/FormElement.tsx index 8070218a..baa232e9 100644 --- a/src/components/FormElement/FormElement.tsx +++ b/src/components/FormElement/FormElement.tsx @@ -82,11 +82,12 @@ interface Props { */ export const FormElement: React.FC = ({ element, - onChange, - highlightClass, - executeCustomCode, elements, + executeCustomCode, + highlightClass, initial, + onChange, + replaceVariables, timeZone, }) => { /** @@ -156,6 +157,7 @@ export const FormElement: React.FC = ({ diff --git a/src/components/FormElement/components/CustomButtonElement/CustomButtonElement.tsx b/src/components/FormElement/components/CustomButtonElement/CustomButtonElement.tsx index 37a05991..a538390e 100644 --- a/src/components/FormElement/components/CustomButtonElement/CustomButtonElement.tsx +++ b/src/components/FormElement/components/CustomButtonElement/CustomButtonElement.tsx @@ -1,3 +1,4 @@ +import { InterpolateFunction } from '@grafana/data'; import { Button, InlineField, useTheme2 } from '@grafana/ui'; import React, { useCallback } from 'react'; @@ -35,12 +36,23 @@ interface Props { * @type {[id: string]: unknown} */ initial: { [id: string]: unknown }; + + /** + * Template variables interpolation function + */ + replaceVariables: InterpolateFunction; } /** * Custom Button Element */ -export const CustomButtonElement: React.FC = ({ element, executeCustomCode, elements, initial }) => { +export const CustomButtonElement: React.FC = ({ + element, + executeCustomCode, + elements, + initial, + replaceVariables, +}) => { /** * Styles and Theme */ @@ -75,7 +87,7 @@ export const CustomButtonElement: React.FC = ({ element, executeCustomCod size={element.size} data-testid={TEST_IDS.formElements.fieldCustomButton(element.id)} > - {element.buttonLabel} + {replaceVariables(element.buttonLabel)} ); diff --git a/src/components/FormElements/FormElements.test.tsx b/src/components/FormElements/FormElements.test.tsx index ac5f1631..7a71067e 100644 --- a/src/components/FormElements/FormElements.test.tsx +++ b/src/components/FormElements/FormElements.test.tsx @@ -17,6 +17,12 @@ jest.useFakeTimers(); */ jest.mock('@volkovlabs/components'); +/** + * Replace variables + */ +const replaceVariablesMock = (code: string) => code; +const replaceVariables = jest.fn(replaceVariablesMock) as any; + /** * Form Elements */ @@ -34,7 +40,12 @@ describe('Form Elements', () => { */ const getComponent = ({ options = {}, ...restProps }: any) => { return ( - + ); }; @@ -258,6 +269,34 @@ describe('Form Elements', () => { expect(selectors.fieldSliderInput()).toBeInTheDocument(); }); + it('Should render default only for test', async () => { + const options = { + submit: {}, + initial: { highlightColor: false }, + update: {}, + reset: {}, + elements: [{ id: 'none', type: 'none', value: 0 }], + }; + + render(getComponent({ options, onChangeElement })); + expect(selectors.element(true, 'none', 'none')).toBeInTheDocument(); + expect(selectors.fieldSlider(true)).not.toBeInTheDocument(); + expect(selectors.fieldCheckboxListContainer(true)).not.toBeInTheDocument(); + expect(selectors.fieldCode(true)).not.toBeInTheDocument(); + expect(selectors.fieldDateTime(true)).not.toBeInTheDocument(); + expect(selectors.fieldDisabled(true)).not.toBeInTheDocument(); + expect(selectors.fieldDisabledTextarea(true)).not.toBeInTheDocument(); + expect(selectors.fieldFile(true)).not.toBeInTheDocument(); + expect(selectors.fieldNumber(true)).not.toBeInTheDocument(); + expect(selectors.fieldPassword(true)).not.toBeInTheDocument(); + expect(selectors.fieldRadioContainer(true)).not.toBeInTheDocument(); + expect(selectors.fieldSelect(true)).not.toBeInTheDocument(); + expect(selectors.fieldString(true)).not.toBeInTheDocument(); + expect(selectors.fieldSliderInput(true)).not.toBeInTheDocument(); + expect(selectors.fieldTextarea(true)).not.toBeInTheDocument(); + expect(selectors.fieldTime(true)).not.toBeInTheDocument(); + }); + it('Should find component with Radio', async () => { const options = { submit: {}, diff --git a/src/components/FormPanel/FormPanel.test.tsx b/src/components/FormPanel/FormPanel.test.tsx index e86d5615..93e78c9a 100644 --- a/src/components/FormPanel/FormPanel.test.tsx +++ b/src/components/FormPanel/FormPanel.test.tsx @@ -552,8 +552,10 @@ describe('Panel', () => { /** * Check if replace variables called for get payload function + * replaceVariables called for buttons titles + * number of calls increased */ - expect(replaceVariables).toHaveBeenCalledTimes(1); + expect(replaceVariables).toHaveBeenCalledTimes(13); }); it('Should update elements with query result', async () => { @@ -830,8 +832,10 @@ describe('Panel', () => { /** * Check if replace variables called for get payload function + * replaceVariables called for buttons titles + * number of calls increased */ - expect(replaceVariables).toHaveBeenCalledTimes(1); + expect(replaceVariables).toHaveBeenCalledTimes(13); }); it('Should not update elements if datasource is unspecified', async () => { @@ -2051,7 +2055,12 @@ describe('Panel', () => { }); expect(replaceVariables).toHaveBeenCalledWith(defaultOptions.resetAction.code); - expect(replaceVariables).toHaveBeenCalledTimes(1); + + /** + * replaceVariables called for buttons titles + * number of calls increased + */ + expect(replaceVariables).toHaveBeenCalledTimes(9); expect(appEventsMock.publish).toHaveBeenCalledWith({ type: AppEvents.alertSuccess.name, payload: 'success', @@ -2156,8 +2165,10 @@ describe('Panel', () => { /** * Check if replace variables called for get payload function + * replaceVariables called for buttons titles + * number of calls increased */ - expect(replaceVariables).toHaveBeenCalledTimes(1); + expect(replaceVariables).toHaveBeenCalledTimes(9); }); it('Should not run reset datasource request if datasource not specified', async () => { @@ -2245,8 +2256,10 @@ describe('Panel', () => { /** * Check if replace variables called for get payload function + * replaceVariables called for buttons titles + * number of calls increased */ - expect(replaceVariables).toHaveBeenCalledTimes(0); + expect(replaceVariables).toHaveBeenCalledTimes(4); }); it('Should show reset datasource request error', async () => { diff --git a/src/components/FormPanel/FormPanel.tsx b/src/components/FormPanel/FormPanel.tsx index 3ddc94b2..4ca57508 100644 --- a/src/components/FormPanel/FormPanel.tsx +++ b/src/components/FormPanel/FormPanel.tsx @@ -997,7 +997,7 @@ export const FormPanel: React.FC = ({ size={options.buttonGroup.size} data-testid={TEST_IDS.panel.buttonSubmit} > - {options.submit.text} + {replaceVariables(options.submit.text)} )} @@ -1027,7 +1027,7 @@ export const FormPanel: React.FC = ({ size={options.buttonGroup.size} data-testid={TEST_IDS.panel.buttonReset} > - {options.reset.text} + {replaceVariables(options.reset.text)} )} @@ -1041,7 +1041,7 @@ export const FormPanel: React.FC = ({ data-testid={TEST_IDS.panel.buttonSaveDefault} title="Save values in the dashboard. Requires to Save dashboard." > - {options.saveDefault.text} + {replaceVariables(options.saveDefault.text)} )} @@ -1140,13 +1140,13 @@ export const FormPanel: React.FC = ({ )} } - confirmText={options.confirmModal.confirm} + confirmText={replaceVariables(options.confirmModal.confirm)} onConfirm={() => { updateRequest(); setUpdateConfirmation(false); }} onDismiss={() => setUpdateConfirmation(false)} - dismissText={options.confirmModal.cancel} + dismissText={replaceVariables(options.confirmModal.cancel)} /> `data-testid form-elements element-${elementId}-${elementType}`, + default: 'data-testid form-elements default-none', fieldNumber: 'data-testid form-elements field-number', fieldString: 'data-testid form-elements field-string', fieldFile: 'data-testid form-elements field-file',