Dynamic Dashboards: Show hidden variables greyed out (#115723)

This commit is contained in:
Ida Štambuk
2026-01-07 09:48:50 +01:00
committed by GitHub
parent 844a7332b9
commit 05681efee3
7 changed files with 154 additions and 20 deletions
@@ -1,6 +1,6 @@
import { test, expect } from '@grafana/plugin-e2e';
import { flows, type Variable } from './utils';
import { flows, saveDashboard, type Variable } from './utils';
test.use({
featureToggles: {
@@ -64,20 +64,7 @@ test.describe(
label: 'VariableUnderTest',
};
// common steps to add a new variable
await flows.newEditPaneVariableClick(dashboardPage, selectors);
await flows.newEditPanelCommonVariableInputs(dashboardPage, selectors, variable);
// set the textbox variable value
const type = 'variable-type Value';
const fieldLabel = dashboardPage.getByGrafanaSelector(
selectors.components.PanelEditor.OptionsPane.fieldLabel(type)
);
await expect(fieldLabel).toBeVisible();
const inputField = fieldLabel.locator('input');
await expect(inputField).toBeVisible();
await inputField.fill(variable.value);
await inputField.blur();
await flows.addNewTextBoxVariable(dashboardPage, variable);
// select the variable in the dashboard and confirm the variable value is set
await dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItem).click();
@@ -140,5 +127,94 @@ test.describe(
await expect(panelContent).toBeVisible();
await expect(markdownContent).toContainText('VariableUnderTest: 10m');
});
test('can hide a variable', async ({ dashboardPage, selectors, page }) => {
const variable: Variable = {
type: 'textbox',
name: 'VariableUnderTest',
value: 'foo',
label: 'VariableUnderTest',
};
await saveDashboard(dashboardPage, page, selectors, 'can hide a variable');
await flows.addNewTextBoxVariable(dashboardPage, variable);
// check the variable is visible in the dashboard
const variableLabel = dashboardPage.getByGrafanaSelector(
selectors.pages.Dashboard.SubMenu.submenuItemLabels(variable.label)
);
await expect(variableLabel).toBeVisible();
// hide the variable
await dashboardPage
.getByGrafanaSelector(selectors.pages.Dashboard.Settings.Variables.Edit.General.generalDisplaySelect)
.click();
await page.getByText('Hidden', { exact: true }).click();
// check that the variable is still visible
await expect(
dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemLabels(variable.label!))
).toBeVisible();
// save dashboard and exit edit mode and check variable is not visible
await saveDashboard(dashboardPage, page, selectors);
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click();
await expect(
dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemLabels(variable.label!))
).toBeHidden();
// refresh and check that variable isn't visible
await page.reload();
await expect(
dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemLabels(variable.label!))
).toBeHidden();
// check that the variable is visible in edit mode
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click();
await expect(
dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemLabels(variable.label!))
).toBeVisible();
});
test('can hide variable under the controls menu', async ({ dashboardPage, selectors, page }) => {
const variable: Variable = {
type: 'textbox',
name: 'VariableUnderTest',
value: 'foo',
label: 'VariableUnderTest',
};
await saveDashboard(dashboardPage, page, selectors, 'can hide a variable in controls menu');
await flows.addNewTextBoxVariable(dashboardPage, variable);
// check the variable is visible in the dashboard
const variableLabel = dashboardPage.getByGrafanaSelector(
selectors.pages.Dashboard.SubMenu.submenuItemLabels(variable.label)
);
await expect(variableLabel).toBeVisible();
// hide the variable
await dashboardPage
.getByGrafanaSelector(selectors.pages.Dashboard.Settings.Variables.Edit.General.generalDisplaySelect)
.click();
await page.getByText('Controls menu', { exact: true }).click();
// check that the variable is hidden under the controls menu
await expect(
dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemLabels(variable.label!))
).toBeHidden();
await dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.ControlsButton).click();
await expect(
dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemLabels(variable.label!))
).toBeVisible();
// save dashboard and refresh
await saveDashboard(dashboardPage, page, selectors);
await page.reload();
//check that the variable is hidden under the controls menu
await expect(
dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemLabels(variable.label!))
).toBeHidden();
await dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.ControlsButton).click();
await expect(
dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemLabels(variable.label!))
).toBeVisible();
});
}
);
+23 -1
View File
@@ -79,6 +79,20 @@ export const flows = {
await variableLabelInput.blur();
}
},
async addNewTextBoxVariable(dashboardPage: DashboardPage, variable: Variable) {
await flows.newEditPaneVariableClick(dashboardPage, selectors);
await flows.newEditPanelCommonVariableInputs(dashboardPage, selectors, variable);
// set the textbox variable value
const type = 'variable-type Value';
const fieldLabel = dashboardPage.getByGrafanaSelector(
selectors.components.PanelEditor.OptionsPane.fieldLabel(type)
);
await expect(fieldLabel).toBeVisible();
const inputField = fieldLabel.locator('input');
await expect(inputField).toBeVisible();
await inputField.fill(variable.value);
await inputField.blur();
},
};
export type Variable = {
@@ -89,8 +103,16 @@ export type Variable = {
value: string;
};
export async function saveDashboard(dashboardPage: DashboardPage, page: Page, selectors: E2ESelectorGroups) {
export async function saveDashboard(
dashboardPage: DashboardPage,
page: Page,
selectors: E2ESelectorGroups,
title?: string
) {
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.saveButton).click();
if (title) {
await page.getByTestId(selectors.components.Drawer.DashboardSaveDrawer.saveAsTitleInput).fill(title);
}
await dashboardPage.getByGrafanaSelector(selectors.components.Drawer.DashboardSaveDrawer.saveButton).click();
await expect(page.getByText('Dashboard saved')).toBeVisible();
}
@@ -266,6 +266,9 @@ export const versionedPages = {
Controls: {
'11.1.0': 'data-testid dashboard controls',
},
ControlsButton: {
'12.3.0': 'data-testid dashboard controls button',
},
SubMenu: {
submenu: {
[MIN_GRAFANA_VERSION]: 'Dashboard submenu',
@@ -19,6 +19,8 @@ import { AddVariableButton } from './VariableControlsAddButton';
export function VariableControls({ dashboard }: { dashboard: DashboardScene }) {
const { variables } = sceneGraph.getVariables(dashboard)!.useState();
const { isEditing } = dashboard.useState();
const isEditingNewLayouts = isEditing && config.featureToggles.dashboardNewLayouts;
// Get visible variables for drilldown layout
const visibleVariables = variables.filter((v) => v.state.hide !== VariableHide.inControlsMenu);
@@ -35,13 +37,22 @@ export function VariableControls({ dashboard }: { dashboard: DashboardScene }) {
// Variables to render (exclude adhoc/groupby when drilldown controls are shown in top row)
const variablesToRender = hasDrilldownControls
? restVariables.filter((v) => v.state.hide !== VariableHide.inControlsMenu)
: variables.filter((v) => v.state.hide !== VariableHide.inControlsMenu);
: variables.filter(
(v) =>
// if we're editing in dynamic dashboards, still shows hidden variable but greyed out
(isEditingNewLayouts && v.state.hide === VariableHide.hideVariable) ||
v.state.hide !== VariableHide.inControlsMenu
);
return (
<>
{variablesToRender.length > 0 &&
variablesToRender.map((variable) => (
<VariableValueSelectWrapper key={variable.state.key} variable={variable} />
<VariableValueSelectWrapper
key={variable.state.key}
variable={variable}
isEditingNewLayouts={isEditingNewLayouts}
/>
))}
{config.featureToggles.dashboardNewLayouts ? <AddVariableButton dashboard={dashboard} /> : null}
@@ -52,14 +63,17 @@ export function VariableControls({ dashboard }: { dashboard: DashboardScene }) {
interface VariableSelectProps {
variable: SceneVariable;
inMenu?: boolean;
isEditingNewLayouts?: boolean;
}
export function VariableValueSelectWrapper({ variable, inMenu }: VariableSelectProps) {
export function VariableValueSelectWrapper({ variable, inMenu, isEditingNewLayouts }: VariableSelectProps) {
const state = useSceneObjectState<SceneVariableState>(variable, { shouldActivateOrKeepAlive: true });
const { isSelected, onSelect, isSelectable } = useElementSelection(variable.state.key);
const isHidden = state.hide === VariableHide.hideVariable;
const shouldShowHiddenVariables = isEditingNewLayouts && isHidden;
const styles = useStyles2(getStyles);
if (state.hide === VariableHide.hideVariable) {
if (isHidden && !isEditingNewLayouts) {
if (variable.UNSAFE_renderAsHidden) {
return <variable.Component model={variable} />;
}
@@ -97,6 +111,7 @@ export function VariableValueSelectWrapper({ variable, inMenu }: VariableSelectP
<div
className={cx(
styles.switchMenuContainer,
shouldShowHiddenVariables && styles.hidden,
isSelected && 'dashboard-selected-element',
isSelectable && !isSelected && 'dashboard-selectable-element'
)}
@@ -120,6 +135,7 @@ export function VariableValueSelectWrapper({ variable, inMenu }: VariableSelectP
<div
className={cx(
styles.verticalContainer,
shouldShowHiddenVariables && styles.hidden,
isSelected && 'dashboard-selected-element',
isSelectable && !isSelected && 'dashboard-selectable-element'
)}
@@ -136,6 +152,7 @@ export function VariableValueSelectWrapper({ variable, inMenu }: VariableSelectP
<div
className={cx(
styles.container,
shouldShowHiddenVariables && styles.hidden,
isSelected && 'dashboard-selected-element',
isSelectable && !isSelected && 'dashboard-selectable-element'
)}
@@ -223,4 +240,13 @@ const getStyles = (theme: GrafanaTheme2) => ({
display: 'flex',
alignItems: 'center',
}),
hidden: css({
opacity: 0.6,
'&:hover': css({
opacity: 1,
}),
label: css({
textDecoration: 'line-through',
}),
}),
});
@@ -1,6 +1,7 @@
import { css } from '@emotion/css';
import { GrafanaTheme2 } from '@grafana/data';
import { selectors } from '@grafana/e2e-selectors';
import { t } from '@grafana/i18n';
import { Dropdown, ToolbarButton, useStyles2 } from '@grafana/ui';
@@ -33,6 +34,7 @@ export function DashboardControlsButton({ dashboard }: { dashboard: DashboardSce
<ToolbarButton
aria-label={t('dashboard.controls.menu.aria-label', DASHBOARD_CONTROLS_MENU_ARIA_LABEL)}
title={t('dashboard.controls.menu.title', DASHBOARD_CONTROLS_MENU_TITLE)}
data-testid={selectors.pages.Dashboard.ControlsButton}
icon="sliders-v-alt"
iconSize="md"
variant="canvas"
@@ -38,6 +38,10 @@ export function VariableDisplaySelect({ onChange, display, type, minWidth = 52 }
{
value: VariableHide.hideVariable,
label: t('dashboard-scene.variable-display-select.options.hidden.label', 'Hidden'),
description: t(
'dashboard-scene.variable-display-select.options.hidden.description',
'Only visible in edit mode'
),
},
],
[]
+1
View File
@@ -6578,6 +6578,7 @@
"label": "Controls menu"
},
"hidden": {
"description": "Only visible in edit mode",
"label": "Hidden"
},
"hidden-label": {