diff --git a/packages/grafana-schema/src/schema/dashboard/v2_examples.ts b/packages/grafana-schema/src/schema/dashboard/v2_examples.ts index 651d858e799..649546e17e1 100644 --- a/packages/grafana-schema/src/schema/dashboard/v2_examples.ts +++ b/packages/grafana-schema/src/schema/dashboard/v2_examples.ts @@ -490,5 +490,18 @@ export const handyTestingSchema: Spec = { allowCustomValue: true, }, }, + { + kind: 'SwitchVariable', + spec: { + name: 'switchVar', + label: 'Switch Variable', + description: 'A switch variable', + current: 'false', + enabledValue: 'true', + disabledValue: 'false', + hide: 'dontHide', + skipUrlSync: false, + }, + }, ], }; diff --git a/public/app/features/dashboard-scene/serialization/transformSaveModelSchemaV2ToScene.test.ts b/public/app/features/dashboard-scene/serialization/transformSaveModelSchemaV2ToScene.test.ts index 87391044813..f054a9f7cab 100644 --- a/public/app/features/dashboard-scene/serialization/transformSaveModelSchemaV2ToScene.test.ts +++ b/public/app/features/dashboard-scene/serialization/transformSaveModelSchemaV2ToScene.test.ts @@ -14,6 +14,7 @@ import { AdHocFiltersVariable, SceneDataTransformer, SceneGridItem, + SwitchVariable, } from '@grafana/scenes'; import { AdhocVariableKind, @@ -27,6 +28,7 @@ import { GroupByVariableKind, IntervalVariableKind, QueryVariableKind, + SwitchVariableKind, TextVariableKind, } from '@grafana/schema/dist/esm/schema/dashboard/v2'; import { handyTestingSchema } from '@grafana/schema/dist/esm/schema/dashboard/v2_examples'; @@ -204,6 +206,14 @@ describe('transformSaveModelSchemaV2ToScene', () => { sceneVariableClass: AdHocFiltersVariable, index: 7, }); + validateVariable({ + sceneVariable: variables?.state.variables[8], + variableKind: dash.variables[8] as SwitchVariableKind, + scene: scene, + dashSpec: dash, + sceneVariableClass: SwitchVariable, + index: 8, + }); // Annotations expect(scene.state.$data).toBeInstanceOf(DashboardDataLayerSet); @@ -371,7 +381,7 @@ describe('transformSaveModelSchemaV2ToScene', () => { const scene = transformSaveModelSchemaV2ToScene(snapshot); // check variables were converted to snapshot variables - expect(scene.state.$variables?.state.variables).toHaveLength(8); + expect(scene.state.$variables?.state.variables).toHaveLength(9); expect(scene.state.$variables?.getByName('customVar')).toBeInstanceOf(SnapshotVariable); expect(scene.state.$variables?.getByName('adhocVar')).toBeInstanceOf(AdHocFiltersVariable); expect(scene.state.$variables?.getByName('intervalVar')).toBeInstanceOf(SnapshotVariable); diff --git a/public/app/features/dashboard/api/ResponseTransformers.test.ts b/public/app/features/dashboard/api/ResponseTransformers.test.ts index 5ce3e2ffadd..642729d9ed5 100644 --- a/public/app/features/dashboard/api/ResponseTransformers.test.ts +++ b/public/app/features/dashboard/api/ResponseTransformers.test.ts @@ -311,6 +311,31 @@ describe('ResponseTransformers', () => { type: 'query', query: { refId: 'A', query: 'label_values(grafanacloud_org_info{org_slug="$org_slug"}, org_id)' }, }, + { + type: 'switch', + name: 'var9', + label: 'Switch variable', + description: 'Switch variable description', + skipUrlSync: false, + hide: 0, + current: { + value: 'true', + text: 'true', + }, + options: [ + { + selected: true, + text: 'true', + value: 'true', + }, + { + selected: false, + text: 'false', + value: 'false', + }, + ], + query: '', + }, ], }, panels: [ @@ -523,6 +548,7 @@ describe('ResponseTransformers', () => { validateVariablesV1ToV2(spec.variables[6], dashboardV1.templating?.list?.[6]); validateVariablesV1ToV2(spec.variables[7], dashboardV1.templating?.list?.[7]); validateVariablesV1ToV2(spec.variables[8], dashboardV1.templating?.list?.[8]); + validateVariablesV1ToV2(spec.variables[9], dashboardV1.templating?.list?.[9]); }); }); @@ -930,6 +956,7 @@ describe('ResponseTransformers', () => { validateVariablesV1ToV2(dashboardV2.spec.variables[5], dashboard.templating?.list?.[5]); validateVariablesV1ToV2(dashboardV2.spec.variables[6], dashboard.templating?.list?.[6]); validateVariablesV1ToV2(dashboardV2.spec.variables[7], dashboard.templating?.list?.[7]); + validateVariablesV1ToV2(dashboardV2.spec.variables[8], dashboard.templating?.list?.[8]); // annotations validateAnnotation(dashboard.annotations!.list![0], dashboardV2.spec.annotations[0]); validateAnnotation(dashboard.annotations!.list![1], dashboardV2.spec.annotations[1]); @@ -1172,5 +1199,23 @@ describe('ResponseTransformers', () => { expect(v2.group).toEqual(v1.datasource?.type); expect(v2.spec.options).toEqual(v1.options); } + + if (v2.kind === 'SwitchVariable') { + // V1 switch variables have options array with exactly 2 options + // First option is enabledValue, second is disabledValue + const options = v1.options ?? []; + const enabledValueRaw = options[0]?.value ?? 'true'; + const disabledValueRaw = options[1]?.value ?? 'false'; + const enabledValue = Array.isArray(enabledValueRaw) ? enabledValueRaw[0] : enabledValueRaw; + const disabledValue = Array.isArray(disabledValueRaw) ? disabledValueRaw[0] : disabledValueRaw; + + // Current value should be a string (not array) + const currentValueRaw = v1.current?.value ?? disabledValue; + const currentValue = Array.isArray(currentValueRaw) ? currentValueRaw[0] : currentValueRaw; + + expect(v2.spec.current).toBe(currentValue); + expect(v2.spec.enabledValue).toBe(enabledValue); + expect(v2.spec.disabledValue).toBe(disabledValue); + } } }); diff --git a/public/app/features/dashboard/api/ResponseTransformers.ts b/public/app/features/dashboard/api/ResponseTransformers.ts index ca8a48de32d..d4bce12f211 100644 --- a/public/app/features/dashboard/api/ResponseTransformers.ts +++ b/public/app/features/dashboard/api/ResponseTransformers.ts @@ -34,6 +34,7 @@ import { IntervalVariableKind, TextVariableKind, GroupByVariableKind, + SwitchVariableKind, LibraryPanelKind, PanelKind, GridLayoutItemKind, @@ -809,6 +810,29 @@ function getVariables(vars: TypedVariableModel[]): DashboardV2Spec['variables'] variables.push(gb); break; + case 'switch': + // V1 switch variables have options array with exactly 2 options + // First option is typically enabledValue, second is disabledValue + const options = v.options ?? []; + const enabledValueRaw = options[0]?.value ?? 'true'; + const disabledValueRaw = options[1]?.value ?? 'false'; + const enabledValue = Array.isArray(enabledValueRaw) ? enabledValueRaw[0] : enabledValueRaw; + const disabledValue = Array.isArray(disabledValueRaw) ? disabledValueRaw[0] : disabledValueRaw; + // Current value should be a string (not array) + const currentValueRaw = v.current?.value ?? disabledValue; + const currentValue = Array.isArray(currentValueRaw) ? currentValueRaw[0] : currentValueRaw; + + const sw: SwitchVariableKind = { + kind: 'SwitchVariable', + spec: { + ...commonProperties, + current: currentValue, + enabledValue, + disabledValue, + }, + }; + variables.push(sw); + break; default: // do not throw error, just log it console.error(`Variable transformation not implemented: ${v.type}`); @@ -997,6 +1021,29 @@ function getVariablesV1(vars: DashboardV2Spec['variables']): VariableModel[] { }; variables.push(av); break; + case 'SwitchVariable': + const sv: VariableModel = { + ...commonProperties, + current: { + text: v.spec.current, + value: v.spec.current, + }, + options: [ + { + text: v.spec.enabledValue, + value: v.spec.enabledValue, + selected: v.spec.current === v.spec.enabledValue, + }, + { + text: v.spec.disabledValue, + value: v.spec.disabledValue, + selected: v.spec.current === v.spec.disabledValue, + }, + ], + query: '', + }; + variables.push(sv); + break; default: // do not throw error, just log it console.error(`Variable transformation not implemented: ${v}`); @@ -1256,6 +1303,8 @@ function transformToV1VariableTypes(variable: TypedVariableModelV2): VariableTyp return 'groupby'; case 'AdhocVariable': return 'adhoc'; + case 'SwitchVariable': + return 'switch'; default: throw new Error(`Unknown variable type: ${variable}`); }