Skip to content

Commit

Permalink
Add dashboard variables support in button titles (#479)
Browse files Browse the repository at this point in the history
* add replace variables for button titles

* Updates

---------

Co-authored-by: Mikhail Volkov <mikhail@volkovlabs.io>
  • Loading branch information
vitPinchuk and mikhail-vl authored Sep 3, 2024
1 parent 4b5cd75 commit d102e13
Show file tree
Hide file tree
Showing 9 changed files with 105 additions and 37 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
41 changes: 20 additions & 21 deletions provisioning/dashboards/panels.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"id": 3,
"links": [],
"liveNow": false,
"panels": [
Expand Down Expand Up @@ -117,7 +116,7 @@
},
"updateEnabled": "auto"
},
"pluginVersion": "4.3.0",
"pluginVersion": "4.5.0",
"title": "No Elements",
"type": "volkovlabs-form-panel"
},
Expand Down Expand Up @@ -233,7 +232,7 @@
},
"updateEnabled": "auto"
},
"pluginVersion": "4.3.0",
"pluginVersion": "4.5.0",
"title": "Data Manipulation Form",
"type": "volkovlabs-form-panel"
},
Expand Down Expand Up @@ -288,28 +287,28 @@
"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",
"foregroundColor": "yellow",
"icon": "cloud-upload",
"orientation": "center",
"size": "md",
"text": "Submit",
"text": "Submit $var",
"variant": "destructive"
},
"sync": true,
Expand All @@ -325,7 +324,7 @@
},
"updateEnabled": "auto"
},
"pluginVersion": "4.3.0",
"pluginVersion": "4.5.0",
"title": "Buttons only",
"type": "volkovlabs-form-panel"
},
Expand Down Expand Up @@ -532,7 +531,7 @@
"width": 0,
"widthMode": "auto"
},
"pluginVersion": "4.3.0",
"pluginVersion": "4.5.0",
"title": "Single Form",
"type": "volkovlabs-form-panel"
},
Expand Down Expand Up @@ -670,7 +669,7 @@
},
"updateEnabled": "auto"
},
"pluginVersion": "4.3.0",
"pluginVersion": "4.5.0",
"title": "Read Only Updated",
"type": "volkovlabs-form-panel"
},
Expand Down Expand Up @@ -841,7 +840,7 @@
},
"updateEnabled": "auto"
},
"pluginVersion": "4.3.0",
"pluginVersion": "4.5.0",
"title": "Control Panel",
"type": "volkovlabs-form-panel"
},
Expand Down Expand Up @@ -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"
},
Expand Down Expand Up @@ -1141,7 +1140,7 @@
"width": 0,
"widthMode": "auto"
},
"pluginVersion": "4.3.0",
"pluginVersion": "4.5.0",
"title": "Split Form",
"type": "volkovlabs-form-panel"
}
Expand All @@ -1153,21 +1152,21 @@
"list": [
{
"current": {
"selected": true,
"text": "test",
"value": "test"
"selected": false,
"text": "test2",
"value": "test2"
},
"hide": 0,
"label": "Var",
"name": "var",
"options": [
{
"selected": true,
"text": "test",
"value": "test"
"text": "test2",
"value": "test2"
}
],
"query": "test",
"query": "test2",
"skipUrlSync": false,
"type": "textbox"
}
Expand Down
1 change: 1 addition & 0 deletions src/components/CustomButtonsRow/CustomButtonsRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ export const CustomButtonsRow: React.FC<Props> = ({ elements, initial, replaceVa
executeCustomCode={executeCustomCode}
initial={initial}
elements={elements}
replaceVariables={replaceVariables}
/>
);
}
Expand Down
8 changes: 5 additions & 3 deletions src/components/FormElement/FormElement.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,12 @@ interface Props {
*/
export const FormElement: React.FC<Props> = ({
element,
onChange,
highlightClass,
executeCustomCode,
elements,
executeCustomCode,
highlightClass,
initial,
onChange,
replaceVariables,
timeZone,
}) => {
/**
Expand Down Expand Up @@ -156,6 +157,7 @@ export const FormElement: React.FC<Props> = ({
<CustomButtonElement
element={element}
executeCustomCode={executeCustomCode}
replaceVariables={replaceVariables}
elements={elements}
initial={initial}
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { InterpolateFunction } from '@grafana/data';
import { Button, InlineField, useTheme2 } from '@grafana/ui';
import React, { useCallback } from 'react';

Expand Down Expand Up @@ -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<Props> = ({ element, executeCustomCode, elements, initial }) => {
export const CustomButtonElement: React.FC<Props> = ({
element,
executeCustomCode,
elements,
initial,
replaceVariables,
}) => {
/**
* Styles and Theme
*/
Expand Down Expand Up @@ -75,7 +87,7 @@ export const CustomButtonElement: React.FC<Props> = ({ element, executeCustomCod
size={element.size}
data-testid={TEST_IDS.formElements.fieldCustomButton(element.id)}
>
{element.buttonLabel}
{replaceVariables(element.buttonLabel)}
</Button>
);

Expand Down
41 changes: 40 additions & 1 deletion src/components/FormElements/FormElements.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand All @@ -34,7 +40,12 @@ describe('Form Elements', () => {
*/
const getComponent = ({ options = {}, ...restProps }: any) => {
return (
<FormElements options={options} elements={normalizeElementsForLocalState(options.elements)} {...restProps} />
<FormElements
replaceVariables={replaceVariables}
options={options}
elements={normalizeElementsForLocalState(options.elements)}
{...restProps}
/>
);
};

Expand Down Expand Up @@ -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: {},
Expand Down
23 changes: 18 additions & 5 deletions src/components/FormPanel/FormPanel.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 () => {
Expand Down Expand Up @@ -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 () => {
Expand Down Expand Up @@ -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',
Expand Down Expand Up @@ -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 () => {
Expand Down Expand Up @@ -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 () => {
Expand Down
10 changes: 5 additions & 5 deletions src/components/FormPanel/FormPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -997,7 +997,7 @@ export const FormPanel: React.FC<Props> = ({
size={options.buttonGroup.size}
data-testid={TEST_IDS.panel.buttonSubmit}
>
{options.submit.text}
{replaceVariables(options.submit.text)}
</Button>
)}

Expand Down Expand Up @@ -1027,7 +1027,7 @@ export const FormPanel: React.FC<Props> = ({
size={options.buttonGroup.size}
data-testid={TEST_IDS.panel.buttonReset}
>
{options.reset.text}
{replaceVariables(options.reset.text)}
</Button>
)}

Expand All @@ -1041,7 +1041,7 @@ export const FormPanel: React.FC<Props> = ({
data-testid={TEST_IDS.panel.buttonSaveDefault}
title="Save values in the dashboard. Requires to Save dashboard."
>
{options.saveDefault.text}
{replaceVariables(options.saveDefault.text)}
</Button>
)}
</div>
Expand Down Expand Up @@ -1140,13 +1140,13 @@ export const FormPanel: React.FC<Props> = ({
)}
</div>
}
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)}
/>

<ConfirmModal
Expand Down
1 change: 1 addition & 0 deletions src/constants/tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export const TEST_IDS = {
root: 'data-testid form-elements',
element: (elementId: string, elementType: string) =>
`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',
Expand Down

0 comments on commit d102e13

Please sign in to comment.