From 2736fe0568252ee55e0fcdafecfc32c0e0adedc0 Mon Sep 17 00:00:00 2001 From: Victor Marin <36818606+mdvictor@users.noreply.github.com> Date: Mon, 25 Nov 2024 12:21:25 +0200 Subject: [PATCH] Dashboards: Add the option to toggle custom value options in the groupBy variable (#96892) * allow options list locking in groupBy var * refactor * fix * fix lint --- .../grafana-data/src/types/templateVars.ts | 1 + .../sceneVariablesSetToVariables.test.ts | 2 ++ .../sceneVariablesSetToVariables.ts | 1 + .../components/GroupByVariableForm.test.tsx | 20 +++++++++++++++++++ .../components/GroupByVariableForm.tsx | 15 +++++++++++++- .../editors/GroupByVariableEditor.test.tsx | 7 ++++++- .../editors/GroupByVariableEditor.tsx | 9 ++++++++- .../dashboard-scene/utils/variables.test.ts | 2 ++ .../dashboard-scene/utils/variables.ts | 1 + 9 files changed, 55 insertions(+), 3 deletions(-) diff --git a/packages/grafana-data/src/types/templateVars.ts b/packages/grafana-data/src/types/templateVars.ts index e31319b9675..56b391a92b8 100644 --- a/packages/grafana-data/src/types/templateVars.ts +++ b/packages/grafana-data/src/types/templateVars.ts @@ -77,6 +77,7 @@ export interface GroupByVariableModel extends VariableWithOptions { type: 'groupby'; datasource: DataSourceRef | null; multi: true; + allowCustomValue?: boolean; } export interface VariableOption { diff --git a/public/app/features/dashboard-scene/serialization/sceneVariablesSetToVariables.test.ts b/public/app/features/dashboard-scene/serialization/sceneVariablesSetToVariables.test.ts index b6d4a0ee672..838e01764bc 100644 --- a/public/app/features/dashboard-scene/serialization/sceneVariablesSetToVariables.test.ts +++ b/public/app/features/dashboard-scene/serialization/sceneVariablesSetToVariables.test.ts @@ -652,6 +652,7 @@ describe('sceneVariablesSetToVariables', () => { name: 'test', label: 'test-label', description: 'test-desc', + allowCustomValue: true, datasource: { uid: 'fake-std', type: 'fake-std' }, defaultOptions: [ { @@ -673,6 +674,7 @@ describe('sceneVariablesSetToVariables', () => { expect(result).toHaveLength(1); expect(result[0]).toMatchInlineSnapshot(` { + "allowCustomValue": true, "current": { "text": [], "value": [], diff --git a/public/app/features/dashboard-scene/serialization/sceneVariablesSetToVariables.ts b/public/app/features/dashboard-scene/serialization/sceneVariablesSetToVariables.ts index 5c8dd721129..3e1a1d21d05 100644 --- a/public/app/features/dashboard-scene/serialization/sceneVariablesSetToVariables.ts +++ b/public/app/features/dashboard-scene/serialization/sceneVariablesSetToVariables.ts @@ -171,6 +171,7 @@ export function sceneVariablesSetToVariables(set: SceneVariables, keepQueryOptio // @ts-expect-error value: variable.state.value, }, + allowCustomValue: variable.state.allowCustomValue, }); } else if (sceneUtils.isAdHocVariable(variable)) { variables.push({ diff --git a/public/app/features/dashboard-scene/settings/variables/components/GroupByVariableForm.test.tsx b/public/app/features/dashboard-scene/settings/variables/components/GroupByVariableForm.test.tsx index a959b73e5bd..81eda3d14fe 100644 --- a/public/app/features/dashboard-scene/settings/variables/components/GroupByVariableForm.test.tsx +++ b/public/app/features/dashboard-scene/settings/variables/components/GroupByVariableForm.test.tsx @@ -38,8 +38,11 @@ jest.mock('@grafana/runtime/src/services/dataSourceSrv', () => ({ describe('GroupByVariableForm', () => { const onDataSourceChangeMock = jest.fn(); const onDefaultOptionsChangeMock = jest.fn(); + const onAllowCustomValueChangeMock = jest.fn(); const defaultProps: GroupByVariableFormProps = { + allowCustomValue: true, + onAllowCustomValueChange: onAllowCustomValueChangeMock, onDataSourceChange: onDataSourceChangeMock, onDefaultOptionsChange: onDefaultOptionsChangeMock, }; @@ -55,6 +58,23 @@ describe('GroupByVariableForm', () => { jest.clearAllMocks(); }); + it('should render the form with allow custom value true', async () => { + const mockOnAllowCustomValueChange = jest.fn(); + const { + renderer: { getByTestId }, + } = setup({ + allowCustomValue: true, + onAllowCustomValueChange: mockOnAllowCustomValueChange, + }); + + const allowCustomValueCheckbox = getByTestId( + selectors.pages.Dashboard.Settings.Variables.Edit.General.selectionOptionsAllowCustomValueSwitch + ); + + expect(allowCustomValueCheckbox).toBeInTheDocument(); + expect(allowCustomValueCheckbox).toBeChecked(); + }); + it('should call onDataSourceChange when changing the datasource', async () => { const { renderer: { getByTestId }, diff --git a/public/app/features/dashboard-scene/settings/variables/components/GroupByVariableForm.tsx b/public/app/features/dashboard-scene/settings/variables/components/GroupByVariableForm.tsx index 20fd841727b..a0472f464c1 100644 --- a/public/app/features/dashboard-scene/settings/variables/components/GroupByVariableForm.tsx +++ b/public/app/features/dashboard-scene/settings/variables/components/GroupByVariableForm.tsx @@ -1,4 +1,4 @@ -import { useCallback } from 'react'; +import { FormEvent, useCallback } from 'react'; import { DataSourceInstanceSettings, MetricFindValue, readCSV } from '@grafana/data'; import { selectors } from '@grafana/e2e-selectors'; @@ -6,6 +6,7 @@ import { DataSourceRef } from '@grafana/schema'; import { Alert, CodeEditor, Field, Switch } from '@grafana/ui'; import { DataSourcePicker } from 'app/features/datasources/components/picker/DataSourcePicker'; +import { VariableCheckboxField } from './VariableCheckboxField'; import { VariableLegend } from './VariableLegend'; export interface GroupByVariableFormProps { @@ -14,6 +15,8 @@ export interface GroupByVariableFormProps { onDefaultOptionsChange: (options?: MetricFindValue[]) => void; infoText?: string; defaultOptions?: MetricFindValue[]; + allowCustomValue: boolean; + onAllowCustomValueChange: (event: FormEvent) => void; } export function GroupByVariableForm({ @@ -22,6 +25,8 @@ export function GroupByVariableForm({ infoText, onDataSourceChange, onDefaultOptionsChange, + allowCustomValue, + onAllowCustomValueChange, }: GroupByVariableFormProps) { const updateDefaultOptions = useCallback( (csvContent: string) => { @@ -76,6 +81,14 @@ export function GroupByVariableForm({ showLineNumbers={true} /> )} + + ); } diff --git a/public/app/features/dashboard-scene/settings/variables/editors/GroupByVariableEditor.test.tsx b/public/app/features/dashboard-scene/settings/variables/editors/GroupByVariableEditor.test.tsx index 689db0d1abc..cbafdb5c7fc 100644 --- a/public/app/features/dashboard-scene/settings/variables/editors/GroupByVariableEditor.test.tsx +++ b/public/app/features/dashboard-scene/settings/variables/editors/GroupByVariableEditor.test.tsx @@ -38,13 +38,18 @@ jest.mock('@grafana/runtime/src/services/dataSourceSrv', () => ({ })); describe('GroupByVariableEditor', () => { - it('renders AdHocVariableForm with correct props', async () => { + it('renders GroupByVariableForm with correct props', async () => { const { renderer } = await setup(); const dataSourcePicker = renderer.getByTestId( selectors.pages.Dashboard.Settings.Variables.Edit.GroupByVariable.dataSourceSelect ); const infoText = renderer.getByTestId(selectors.pages.Dashboard.Settings.Variables.Edit.GroupByVariable.infoText); + const allowCustomValueCheckbox = renderer.getByTestId( + selectors.pages.Dashboard.Settings.Variables.Edit.General.selectionOptionsAllowCustomValueSwitch + ); + expect(allowCustomValueCheckbox).toBeInTheDocument(); + expect(allowCustomValueCheckbox).toBeChecked(); expect(dataSourcePicker).toBeInTheDocument(); expect(dataSourcePicker.getAttribute('placeholder')).toBe('Default Test Data Source'); expect(infoText).toBeInTheDocument(); diff --git a/public/app/features/dashboard-scene/settings/variables/editors/GroupByVariableEditor.tsx b/public/app/features/dashboard-scene/settings/variables/editors/GroupByVariableEditor.tsx index 2663ab7c47c..759a886059b 100644 --- a/public/app/features/dashboard-scene/settings/variables/editors/GroupByVariableEditor.tsx +++ b/public/app/features/dashboard-scene/settings/variables/editors/GroupByVariableEditor.tsx @@ -1,3 +1,4 @@ +import { FormEvent } from 'react'; import { useAsync } from 'react-use'; import { DataSourceInstanceSettings, MetricFindValue, getDataSourceRef } from '@grafana/data'; @@ -13,7 +14,7 @@ interface GroupByVariableEditorProps { export function GroupByVariableEditor(props: GroupByVariableEditorProps) { const { variable, onRunQuery } = props; - const { datasource: datasourceRef, defaultOptions } = variable.useState(); + const { datasource: datasourceRef, defaultOptions, allowCustomValue = true } = variable.useState(); const { value: datasource } = useAsync(async () => { return await getDataSourceSrv().get(datasourceRef); @@ -35,6 +36,10 @@ export function GroupByVariableEditor(props: GroupByVariableEditorProps) { onRunQuery(); }; + const onAllowCustomValueChange = (event: FormEvent) => { + variable.setState({ allowCustomValue: event.currentTarget.checked }); + }; + return ( ); } diff --git a/public/app/features/dashboard-scene/utils/variables.test.ts b/public/app/features/dashboard-scene/utils/variables.test.ts index 9bdd9c6c5be..5cc249ff9fe 100644 --- a/public/app/features/dashboard-scene/utils/variables.test.ts +++ b/public/app/features/dashboard-scene/utils/variables.test.ts @@ -557,6 +557,7 @@ describe('when creating variables objects', () => { type: 'prometheus', }, multi: true, + allowCustomValue: true, options: [ { selected: false, @@ -608,6 +609,7 @@ describe('when creating variables objects', () => { value: [], datasource: { uid: 'gdev-prometheus', type: 'prometheus' }, applyMode: 'auto', + allowCustomValue: true, }); }); }); diff --git a/public/app/features/dashboard-scene/utils/variables.ts b/public/app/features/dashboard-scene/utils/variables.ts index 4b6c1bdcb6d..983a217b5c4 100644 --- a/public/app/features/dashboard-scene/utils/variables.ts +++ b/public/app/features/dashboard-scene/utils/variables.ts @@ -246,6 +246,7 @@ export function createSceneVariableFromVariableModel(variable: TypedVariableMode hide: variable.hide, // @ts-expect-error defaultOptions: variable.options, + allowCustomValue: variable.allowCustomValue ?? true, }); } else { throw new Error(`Scenes: Unsupported variable type ${variable.type}`);