From 80e8a6e2118020274e2379ac37ef381903725089 Mon Sep 17 00:00:00 2001 From: Bogdan Matei Date: Tue, 12 Aug 2025 16:23:19 +0300 Subject: [PATCH] Dashboard: Improvements to conditional rendering (#108108) --- .../kinds/v2alpha1/dashboard_spec.cue | 2 +- .../kinds/v2beta1/dashboard_spec.cue | 2 +- .../dashboard/v2alpha1/dashboard_spec.cue | 2 +- .../dashboard/v2alpha1/dashboard_spec_gen.go | 6 +- .../apis/dashboard/v2beta1/dashboard_spec.cue | 2 +- .../dashboard/v2beta1/dashboard_spec_gen.go | 6 +- .../dashboard/v2alpha0/dashboard.schema.cue | 2 +- .../dashboard/v2alpha1/types.spec.gen.ts | 2 +- .../dashboard/v2beta1/types.spec.gen.ts | 2 +- .../ConditionalRenderingGroup.tsx | 18 +- .../ConditionalRenderingVariable.tsx | 161 ++++++++++++++---- .../conditional-rendering/types.ts | 2 +- .../layout-auto-grid/AutoGridItemRenderer.tsx | 14 +- public/locales/en-US/grafana.json | 7 +- 14 files changed, 164 insertions(+), 64 deletions(-) diff --git a/apps/dashboard/kinds/v2alpha1/dashboard_spec.cue b/apps/dashboard/kinds/v2alpha1/dashboard_spec.cue index 8ab1891e626..2aba16b9489 100644 --- a/apps/dashboard/kinds/v2alpha1/dashboard_spec.cue +++ b/apps/dashboard/kinds/v2alpha1/dashboard_spec.cue @@ -941,7 +941,7 @@ ConditionalRenderingVariableKind: { ConditionalRenderingVariableSpec: { variable: string - operator: "equals" | "notEquals" + operator: "equals" | "notEquals" | "matches" | "notMatches" value: string } diff --git a/apps/dashboard/kinds/v2beta1/dashboard_spec.cue b/apps/dashboard/kinds/v2beta1/dashboard_spec.cue index cf71e6b2dc4..1c7ca148a08 100644 --- a/apps/dashboard/kinds/v2beta1/dashboard_spec.cue +++ b/apps/dashboard/kinds/v2beta1/dashboard_spec.cue @@ -942,7 +942,7 @@ ConditionalRenderingVariableKind: { ConditionalRenderingVariableSpec: { variable: string - operator: "equals" | "notEquals" + operator: "equals" | "notEquals" | "matches" | "notMatches" value: string } diff --git a/apps/dashboard/pkg/apis/dashboard/v2alpha1/dashboard_spec.cue b/apps/dashboard/pkg/apis/dashboard/v2alpha1/dashboard_spec.cue index 160adf4e7bf..f566d3775b6 100644 --- a/apps/dashboard/pkg/apis/dashboard/v2alpha1/dashboard_spec.cue +++ b/apps/dashboard/pkg/apis/dashboard/v2alpha1/dashboard_spec.cue @@ -945,7 +945,7 @@ ConditionalRenderingVariableKind: { ConditionalRenderingVariableSpec: { variable: string - operator: "equals" | "notEquals" + operator: "equals" | "notEquals" | "matches" | "notMatches" value: string } diff --git a/apps/dashboard/pkg/apis/dashboard/v2alpha1/dashboard_spec_gen.go b/apps/dashboard/pkg/apis/dashboard/v2alpha1/dashboard_spec_gen.go index 9ddbe89502f..f1e36092724 100644 --- a/apps/dashboard/pkg/apis/dashboard/v2alpha1/dashboard_spec_gen.go +++ b/apps/dashboard/pkg/apis/dashboard/v2alpha1/dashboard_spec_gen.go @@ -1859,8 +1859,10 @@ const ( type DashboardConditionalRenderingVariableSpecOperator string const ( - DashboardConditionalRenderingVariableSpecOperatorEquals DashboardConditionalRenderingVariableSpecOperator = "equals" - DashboardConditionalRenderingVariableSpecOperatorNotEquals DashboardConditionalRenderingVariableSpecOperator = "notEquals" + DashboardConditionalRenderingVariableSpecOperatorEquals DashboardConditionalRenderingVariableSpecOperator = "equals" + DashboardConditionalRenderingVariableSpecOperatorNotEquals DashboardConditionalRenderingVariableSpecOperator = "notEquals" + DashboardConditionalRenderingVariableSpecOperatorMatches DashboardConditionalRenderingVariableSpecOperator = "matches" + DashboardConditionalRenderingVariableSpecOperatorNotMatches DashboardConditionalRenderingVariableSpecOperator = "notMatches" ) // +k8s:openapi-gen=true diff --git a/apps/dashboard/pkg/apis/dashboard/v2beta1/dashboard_spec.cue b/apps/dashboard/pkg/apis/dashboard/v2beta1/dashboard_spec.cue index a35eaa482f7..d110a33fa01 100644 --- a/apps/dashboard/pkg/apis/dashboard/v2beta1/dashboard_spec.cue +++ b/apps/dashboard/pkg/apis/dashboard/v2beta1/dashboard_spec.cue @@ -946,7 +946,7 @@ ConditionalRenderingVariableKind: { ConditionalRenderingVariableSpec: { variable: string - operator: "equals" | "notEquals" + operator: "equals" | "notEquals" | "matches" | "notMatches" value: string } diff --git a/apps/dashboard/pkg/apis/dashboard/v2beta1/dashboard_spec_gen.go b/apps/dashboard/pkg/apis/dashboard/v2beta1/dashboard_spec_gen.go index d66f582e42a..10e17f2c5e2 100644 --- a/apps/dashboard/pkg/apis/dashboard/v2beta1/dashboard_spec_gen.go +++ b/apps/dashboard/pkg/apis/dashboard/v2beta1/dashboard_spec_gen.go @@ -1884,8 +1884,10 @@ const ( type DashboardConditionalRenderingVariableSpecOperator string const ( - DashboardConditionalRenderingVariableSpecOperatorEquals DashboardConditionalRenderingVariableSpecOperator = "equals" - DashboardConditionalRenderingVariableSpecOperatorNotEquals DashboardConditionalRenderingVariableSpecOperator = "notEquals" + DashboardConditionalRenderingVariableSpecOperatorEquals DashboardConditionalRenderingVariableSpecOperator = "equals" + DashboardConditionalRenderingVariableSpecOperatorNotEquals DashboardConditionalRenderingVariableSpecOperator = "notEquals" + DashboardConditionalRenderingVariableSpecOperatorMatches DashboardConditionalRenderingVariableSpecOperator = "matches" + DashboardConditionalRenderingVariableSpecOperatorNotMatches DashboardConditionalRenderingVariableSpecOperator = "notMatches" ) // +k8s:openapi-gen=true diff --git a/packages/grafana-schema/src/schema/dashboard/v2alpha0/dashboard.schema.cue b/packages/grafana-schema/src/schema/dashboard/v2alpha0/dashboard.schema.cue index 3f1f40287cc..5369503d6f2 100644 --- a/packages/grafana-schema/src/schema/dashboard/v2alpha0/dashboard.schema.cue +++ b/packages/grafana-schema/src/schema/dashboard/v2alpha0/dashboard.schema.cue @@ -934,7 +934,7 @@ ConditionalRenderingVariableKind: { ConditionalRenderingVariableSpec: { variable: string - operator: "equals" | "notEquals" + operator: "equals" | "notEquals" | "matches" | "notMatches" value: string } diff --git a/packages/grafana-schema/src/schema/dashboard/v2alpha1/types.spec.gen.ts b/packages/grafana-schema/src/schema/dashboard/v2alpha1/types.spec.gen.ts index 04fc5092d8a..d821bff4532 100644 --- a/packages/grafana-schema/src/schema/dashboard/v2alpha1/types.spec.gen.ts +++ b/packages/grafana-schema/src/schema/dashboard/v2alpha1/types.spec.gen.ts @@ -700,7 +700,7 @@ export const defaultConditionalRenderingVariableKind = (): ConditionalRenderingV export interface ConditionalRenderingVariableSpec { variable: string; - operator: "equals" | "notEquals"; + operator: "equals" | "notEquals" | "matches" | "notMatches"; value: string; } diff --git a/packages/grafana-schema/src/schema/dashboard/v2beta1/types.spec.gen.ts b/packages/grafana-schema/src/schema/dashboard/v2beta1/types.spec.gen.ts index a6140372eb9..46b54c556fa 100644 --- a/packages/grafana-schema/src/schema/dashboard/v2beta1/types.spec.gen.ts +++ b/packages/grafana-schema/src/schema/dashboard/v2beta1/types.spec.gen.ts @@ -699,7 +699,7 @@ export const defaultConditionalRenderingVariableKind = (): ConditionalRenderingV export interface ConditionalRenderingVariableSpec { variable: string; - operator: "equals" | "notEquals"; + operator: "equals" | "notEquals" | "matches" | "notMatches"; value: string; } diff --git a/public/app/features/dashboard-scene/conditional-rendering/ConditionalRenderingGroup.tsx b/public/app/features/dashboard-scene/conditional-rendering/ConditionalRenderingGroup.tsx index 8ebba87cab3..59fdccbd190 100644 --- a/public/app/features/dashboard-scene/conditional-rendering/ConditionalRenderingGroup.tsx +++ b/public/app/features/dashboard-scene/conditional-rendering/ConditionalRenderingGroup.tsx @@ -70,15 +70,17 @@ export class ConditionalRenderingGroup extends ConditionalRenderingBase) { const { value } = model.useState(); + const [actualValue, setActualValue] = useState(value.value); + + useEffect(() => { + setActualValue(value.value); + }, [value.value]); + const variables = useMemo(() => sceneGraph.getVariables(model), [model]); - const variableNames: Array> = useMemo( + const variableNames: ComboboxOption[] = useMemo( () => variables.state.variables.map((v) => ({ value: v.state.name, label: v.state.label ?? v.state.name })), [variables.state.variables] ); @@ -109,10 +165,31 @@ function ConditionalRenderingVariableRenderer({ model }: SceneComponentProps { + if (value.operator === '=~' || value.operator === '!~') { + try { + new RegExp(actualValue); + return ''; + } catch (err) { + return t('dashboard.conditional-rendering.conditions.variable.error.invalid-regex', 'Invalid regex'); + } + } + + return ''; + }, [actualValue, value.operator]); + const undoText = t('dashboard.edit-actions.edit-template-variable-rule', 'Change template variable rule'); return ( @@ -124,12 +201,14 @@ function ConditionalRenderingVariableRenderer({ model }: SceneComponentProps { - dashboardEditActions.edit({ - description: undoText, - source: model, - perform: () => model.setStateAndNotify({ value: { ...value, name: option.value } }), - undo: () => model.setStateAndNotify({ value: { ...value, name: value.name } }), - }); + if (option.value !== value.name) { + dashboardEditActions.edit({ + description: undoText, + source: model, + perform: () => model.setStateAndNotify({ value: { ...value, name: option.value } }), + undo: () => model.setStateAndNotify({ value: { ...value, name: value.name } }), + }); + } }} /> @@ -140,28 +219,38 @@ function ConditionalRenderingVariableRenderer({ model }: SceneComponentProps { - dashboardEditActions.edit({ - description: undoText, - source: model, - perform: () => model.setStateAndNotify({ value: { ...value, operator: option.value } }), - undo: () => model.setStateAndNotify({ value: { ...value, operator: value.operator } }), - }); + if (option.value !== value.operator) { + dashboardEditActions.edit({ + description: undoText, + source: model, + perform: () => model.setStateAndNotify({ value: { ...value, operator: option.value } }), + undo: () => model.setStateAndNotify({ value: { ...value, operator: value.operator } }), + }); + } }} /> - { - const eventValue = e.currentTarget.value; - dashboardEditActions.edit({ - description: undoText, - source: model, - perform: () => model.setStateAndNotify({ value: { ...value, value: eventValue } }), - undo: () => model.setStateAndNotify({ value: { ...value, value: value.value } }), - }); - }} - /> + + { + if (evt.currentTarget.value !== value.value) { + setActualValue(evt.currentTarget.value); + } + }} + onBlur={() => { + if (actualValue !== value.value) { + dashboardEditActions.edit({ + description: undoText, + source: model, + perform: () => model.setStateAndNotify({ value: { ...value, value: actualValue } }), + undo: () => model.setStateAndNotify({ value: { ...value, value: value.value } }), + }); + } + }} + /> + ); } diff --git a/public/app/features/dashboard-scene/conditional-rendering/types.ts b/public/app/features/dashboard-scene/conditional-rendering/types.ts index 6a1f94876fd..f07330967de 100644 --- a/public/app/features/dashboard-scene/conditional-rendering/types.ts +++ b/public/app/features/dashboard-scene/conditional-rendering/types.ts @@ -22,7 +22,7 @@ export type GroupConditionValue = ConditionalRenderingConditions[]; export type TimeRangeSizeConditionValue = string; -export type VariableConditionValueOperator = '=' | '!='; +export type VariableConditionValueOperator = '=' | '!=' | '=~' | '!~'; export type VariableConditionValue = { name: string; diff --git a/public/app/features/dashboard-scene/scene/layout-auto-grid/AutoGridItemRenderer.tsx b/public/app/features/dashboard-scene/scene/layout-auto-grid/AutoGridItemRenderer.tsx index f3cee9f8b23..f7abc61955e 100644 --- a/public/app/features/dashboard-scene/scene/layout-auto-grid/AutoGridItemRenderer.tsx +++ b/public/app/features/dashboard-scene/scene/layout-auto-grid/AutoGridItemRenderer.tsx @@ -22,13 +22,6 @@ export function AutoGridItemRenderer({ model }: SceneComponentProps getIsLazy(preload), [preload]); - if (isConditionallyHidden && !isEditing) { - return null; - } - - const isDragging = !!draggingKey; - const isDragged = draggingKey === key; - const Wrapper = useMemo( () => memo( @@ -69,6 +62,13 @@ export function AutoGridItemRenderer({ model }: SceneComponentProps {repeatedPanels.map((item, index) => ( diff --git a/public/locales/en-US/grafana.json b/public/locales/en-US/grafana.json index 2c9adb3e3e4..d5bf489e26b 100644 --- a/public/locales/en-US/grafana.json +++ b/public/locales/en-US/grafana.json @@ -4497,12 +4497,17 @@ } }, "variable": { + "error": { + "invalid-regex": "Invalid regex" + }, "info": "Show or hide the {{type}} dynamically based on the variable value.", "label": "Template variable", "name": "Name", "operator": { "equals": "Equals", - "not-equals": "Not equals" + "matches": "Matches", + "not-equals": "Not equals", + "not-matches": "Not matches" }, "value": "Value" }