Dashboards: Make it possible to render variables under a drop-down (#109225)

* feat: extend the variable models

* test(DropDownVariableControls): add tests

* refactor(VariableControls): filter in the render method
This commit is contained in:
Levente Balogh
2025-08-29 14:56:26 +02:00
committed by GitHub
parent 72eeefabd7
commit a746f6e121
15 changed files with 395 additions and 74 deletions

View File

@ -719,6 +719,7 @@ QueryVariableSpec: {
refresh: VariableRefresh
skipUrlSync: bool | *false
description?: string
showInControlsMenu?: bool
query: DataQueryKind
regex: string | *""
sort: VariableSort
@ -731,6 +732,7 @@ QueryVariableSpec: {
allowCustomValue: bool | *true
staticOptions?: [...VariableOption]
staticOptionsOrder?: "before" | "after" | "sorted"
showInControlsMenu?: bool
}
// Query variable kind
@ -751,6 +753,7 @@ TextVariableSpec: {
hide: VariableHide
skipUrlSync: bool | *false
description?: string
showInControlsMenu?: bool
}
// Text variable kind
@ -771,6 +774,7 @@ ConstantVariableSpec: {
hide: VariableHide
skipUrlSync: bool | *false
description?: string
showInControlsMenu?: bool
}
// Constant variable kind
@ -798,6 +802,7 @@ DatasourceVariableSpec: {
skipUrlSync: bool | *false
description?: string
allowCustomValue: bool | *true
showInControlsMenu?: bool
}
// Datasource variable kind
@ -823,6 +828,7 @@ IntervalVariableSpec: {
hide: VariableHide
skipUrlSync: bool | *false
description?: string
showInControlsMenu?: bool
}
// Interval variable kind
@ -845,6 +851,7 @@ CustomVariableSpec: {
skipUrlSync: bool | *false
description?: string
allowCustomValue: bool | *true
showInControlsMenu?: bool
}
// Custom variable kind
@ -867,6 +874,7 @@ GroupByVariableSpec: {
hide: VariableHide
skipUrlSync: bool | *false
description?: string
showInControlsMenu?: bool
}
// Group variable kind
@ -890,6 +898,7 @@ AdhocVariableSpec: {
skipUrlSync: bool | *false
description?: string
allowCustomValue: bool | *true
showInControlsMenu?: bool
}
// Define the MetricFindValue type

View File

@ -723,6 +723,7 @@ QueryVariableSpec: {
refresh: VariableRefresh
skipUrlSync: bool | *false
description?: string
showInControlsMenu?: bool
query: DataQueryKind
regex: string | *""
sort: VariableSort
@ -735,6 +736,7 @@ QueryVariableSpec: {
allowCustomValue: bool | *true
staticOptions?: [...VariableOption]
staticOptionsOrder?: "before" | "after" | "sorted"
showInControlsMenu?: bool
}
// Query variable kind
@ -755,6 +757,7 @@ TextVariableSpec: {
hide: VariableHide
skipUrlSync: bool | *false
description?: string
showInControlsMenu?: bool
}
// Text variable kind
@ -775,6 +778,7 @@ ConstantVariableSpec: {
hide: VariableHide
skipUrlSync: bool | *false
description?: string
showInControlsMenu?: bool
}
// Constant variable kind
@ -802,6 +806,7 @@ DatasourceVariableSpec: {
skipUrlSync: bool | *false
description?: string
allowCustomValue: bool | *true
showInControlsMenu?: bool
}
// Datasource variable kind
@ -827,6 +832,7 @@ IntervalVariableSpec: {
hide: VariableHide
skipUrlSync: bool | *false
description?: string
showInControlsMenu?: bool
}
// Interval variable kind
@ -849,6 +855,7 @@ CustomVariableSpec: {
skipUrlSync: bool | *false
description?: string
allowCustomValue: bool | *true
showInControlsMenu?: bool
}
// Custom variable kind
@ -871,6 +878,7 @@ GroupByVariableSpec: {
hide: VariableHide
skipUrlSync: bool | *false
description?: string
showInControlsMenu?: bool
}
// Group variable kind
@ -894,6 +902,7 @@ AdhocVariableSpec: {
skipUrlSync: bool | *false
description?: string
allowCustomValue: bool | *true
showInControlsMenu?: bool
}
// Define the MetricFindValue type

View File

@ -1223,6 +1223,7 @@ type DashboardQueryVariableSpec struct {
Refresh DashboardVariableRefresh `json:"refresh"`
SkipUrlSync bool `json:"skipUrlSync"`
Description *string `json:"description,omitempty"`
ShowInControlsMenu *bool `json:"showInControlsMenu,omitempty"`
Query DashboardDataQueryKind `json:"query"`
Regex string `json:"regex"`
Sort DashboardVariableSort `json:"sort"`
@ -1356,6 +1357,7 @@ type DashboardTextVariableSpec struct {
Hide DashboardVariableHide `json:"hide"`
SkipUrlSync bool `json:"skipUrlSync"`
Description *string `json:"description,omitempty"`
ShowInControlsMenu *bool `json:"showInControlsMenu,omitempty"`
}
// NewDashboardTextVariableSpec creates a new DashboardTextVariableSpec object.
@ -1401,6 +1403,7 @@ type DashboardConstantVariableSpec struct {
Hide DashboardVariableHide `json:"hide"`
SkipUrlSync bool `json:"skipUrlSync"`
Description *string `json:"description,omitempty"`
ShowInControlsMenu *bool `json:"showInControlsMenu,omitempty"`
}
// NewDashboardConstantVariableSpec creates a new DashboardConstantVariableSpec object.
@ -1453,6 +1456,7 @@ type DashboardDatasourceVariableSpec struct {
SkipUrlSync bool `json:"skipUrlSync"`
Description *string `json:"description,omitempty"`
AllowCustomValue bool `json:"allowCustomValue"`
ShowInControlsMenu *bool `json:"showInControlsMenu,omitempty"`
}
// NewDashboardDatasourceVariableSpec creates a new DashboardDatasourceVariableSpec object.
@ -1509,6 +1513,7 @@ type DashboardIntervalVariableSpec struct {
Hide DashboardVariableHide `json:"hide"`
SkipUrlSync bool `json:"skipUrlSync"`
Description *string `json:"description,omitempty"`
ShowInControlsMenu *bool `json:"showInControlsMenu,omitempty"`
}
// NewDashboardIntervalVariableSpec creates a new DashboardIntervalVariableSpec object.
@ -1564,6 +1569,7 @@ type DashboardCustomVariableSpec struct {
SkipUrlSync bool `json:"skipUrlSync"`
Description *string `json:"description,omitempty"`
AllowCustomValue bool `json:"allowCustomValue"`
ShowInControlsMenu *bool `json:"showInControlsMenu,omitempty"`
}
// NewDashboardCustomVariableSpec creates a new DashboardCustomVariableSpec object.
@ -1610,6 +1616,7 @@ type DashboardGroupByVariableSpec struct {
Hide DashboardVariableHide `json:"hide"`
SkipUrlSync bool `json:"skipUrlSync"`
Description *string `json:"description,omitempty"`
ShowInControlsMenu *bool `json:"showInControlsMenu,omitempty"`
}
// NewDashboardGroupByVariableSpec creates a new DashboardGroupByVariableSpec object.
@ -1660,6 +1667,7 @@ type DashboardAdhocVariableSpec struct {
SkipUrlSync bool `json:"skipUrlSync"`
Description *string `json:"description,omitempty"`
AllowCustomValue bool `json:"allowCustomValue"`
ShowInControlsMenu *bool `json:"showInControlsMenu,omitempty"`
}
// NewDashboardAdhocVariableSpec creates a new DashboardAdhocVariableSpec object.

View File

@ -526,6 +526,12 @@ func schema_pkg_apis_dashboard_v2beta1_DashboardAdhocVariableSpec(ref common.Ref
Format: "",
},
},
"showInControlsMenu": {
SchemaProps: spec.SchemaProps{
Type: []string{"boolean"},
Format: "",
},
},
},
Required: []string{"name", "baseFilters", "filters", "defaultKeys", "hide", "skipUrlSync", "allowCustomValue"},
},
@ -1191,6 +1197,12 @@ func schema_pkg_apis_dashboard_v2beta1_DashboardConstantVariableSpec(ref common.
Format: "",
},
},
"showInControlsMenu": {
SchemaProps: spec.SchemaProps{
Type: []string{"boolean"},
Format: "",
},
},
},
Required: []string{"name", "query", "current", "hide", "skipUrlSync"},
},
@ -1358,6 +1370,12 @@ func schema_pkg_apis_dashboard_v2beta1_DashboardCustomVariableSpec(ref common.Re
Format: "",
},
},
"showInControlsMenu": {
SchemaProps: spec.SchemaProps{
Type: []string{"boolean"},
Format: "",
},
},
},
Required: []string{"name", "query", "current", "options", "multi", "includeAll", "hide", "skipUrlSync", "allowCustomValue"},
},
@ -1743,6 +1761,12 @@ func schema_pkg_apis_dashboard_v2beta1_DashboardDatasourceVariableSpec(ref commo
Format: "",
},
},
"showInControlsMenu": {
SchemaProps: spec.SchemaProps{
Type: []string{"boolean"},
Format: "",
},
},
},
Required: []string{"name", "pluginId", "refresh", "regex", "current", "options", "multi", "includeAll", "hide", "skipUrlSync", "allowCustomValue"},
},
@ -2343,6 +2367,12 @@ func schema_pkg_apis_dashboard_v2beta1_DashboardGroupByVariableSpec(ref common.R
Format: "",
},
},
"showInControlsMenu": {
SchemaProps: spec.SchemaProps{
Type: []string{"boolean"},
Format: "",
},
},
},
Required: []string{"name", "current", "options", "multi", "hide", "skipUrlSync"},
},
@ -2475,6 +2505,12 @@ func schema_pkg_apis_dashboard_v2beta1_DashboardIntervalVariableSpec(ref common.
Format: "",
},
},
"showInControlsMenu": {
SchemaProps: spec.SchemaProps{
Type: []string{"boolean"},
Format: "",
},
},
},
Required: []string{"name", "query", "current", "options", "auto", "auto_min", "auto_count", "refresh", "hide", "skipUrlSync"},
},
@ -3250,6 +3286,12 @@ func schema_pkg_apis_dashboard_v2beta1_DashboardQueryVariableSpec(ref common.Ref
Format: "",
},
},
"showInControlsMenu": {
SchemaProps: spec.SchemaProps{
Type: []string{"boolean"},
Format: "",
},
},
"query": {
SchemaProps: spec.SchemaProps{
Default: map[string]interface{}{},
@ -4094,6 +4136,12 @@ func schema_pkg_apis_dashboard_v2beta1_DashboardTextVariableSpec(ref common.Refe
Format: "",
},
},
"showInControlsMenu": {
SchemaProps: spec.SchemaProps{
Type: []string{"boolean"},
Format: "",
},
},
},
Required: []string{"name", "current", "query", "hide", "skipUrlSync"},
},

View File

@ -186,6 +186,7 @@ export interface BaseVariableModel {
error: any | null;
description: string | null;
usedInRepeat?: boolean;
showInControlsMenu?: boolean;
}
export interface SnapshotVariableModel extends VariableWithOptions {

View File

@ -993,6 +993,7 @@ export interface QueryVariableSpec {
refresh: VariableRefresh;
skipUrlSync: boolean;
description?: string;
showInControlsMenu?: boolean;
query: DataQueryKind;
regex: string;
sort: VariableSort;
@ -1087,6 +1088,7 @@ export interface TextVariableSpec {
hide: VariableHide;
skipUrlSync: boolean;
description?: string;
showInControlsMenu?: boolean;
}
export const defaultTextVariableSpec = (): TextVariableSpec => ({
@ -1117,6 +1119,7 @@ export interface ConstantVariableSpec {
hide: VariableHide;
skipUrlSync: boolean;
description?: string;
showInControlsMenu?: boolean;
}
export const defaultConstantVariableSpec = (): ConstantVariableSpec => ({
@ -1154,6 +1157,7 @@ export interface DatasourceVariableSpec {
skipUrlSync: boolean;
description?: string;
allowCustomValue: boolean;
showInControlsMenu?: boolean;
}
export const defaultDatasourceVariableSpec = (): DatasourceVariableSpec => ({
@ -1195,6 +1199,7 @@ export interface IntervalVariableSpec {
hide: VariableHide;
skipUrlSync: boolean;
description?: string;
showInControlsMenu?: boolean;
}
export const defaultIntervalVariableSpec = (): IntervalVariableSpec => ({
@ -1235,6 +1240,7 @@ export interface CustomVariableSpec {
skipUrlSync: boolean;
description?: string;
allowCustomValue: boolean;
showInControlsMenu?: boolean;
}
export const defaultCustomVariableSpec = (): CustomVariableSpec => ({
@ -1276,6 +1282,7 @@ export interface GroupByVariableSpec {
hide: VariableHide;
skipUrlSync: boolean;
description?: string;
showInControlsMenu?: boolean;
}
export const defaultGroupByVariableSpec = (): GroupByVariableSpec => ({
@ -1314,6 +1321,7 @@ export interface AdhocVariableSpec {
skipUrlSync: boolean;
description?: string;
allowCustomValue: boolean;
showInControlsMenu?: boolean;
}
export const defaultAdhocVariableSpec = (): AdhocVariableSpec => ({

View File

@ -22,6 +22,7 @@ import { getDashboardSceneFor } from '../utils/utils';
import { DashboardLinksControls } from './DashboardLinksControls';
import { DashboardScene } from './DashboardScene';
import { DropdownVariableControls } from './DropdownVariableControls';
import { VariableControls } from './VariableControls';
export interface DashboardControlsState extends SceneObjectState {
@ -151,6 +152,9 @@ function DashboardControlsRenderer({ model }: SceneComponentProps<DashboardContr
<refreshPicker.Component model={refreshPicker} />
</Stack>
)}
<Stack>
<DropdownVariableControls dashboard={dashboard} />
</Stack>
{showDebugger && <SceneDebugger scene={model} key={'scene-debugger'} />}
</div>
);

View File

@ -0,0 +1,120 @@
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { SceneVariableSet, TextBoxVariable, QueryVariable, CustomVariable, SceneVariable } from '@grafana/scenes';
import { DashboardScene } from './DashboardScene';
import {
DROPDOWN_CONTROLS_ARIA_LABEL,
DROPDOWN_CONTROLS_TITLE,
DropdownVariableControls,
} from './DropdownVariableControls';
describe('DropdownVariableControls', () => {
it('should return null and not render anything when there are no variables', () => {
const { container } = render(<DropdownVariableControls dashboard={getDashboard([])} />);
expect(container.firstChild).toBeNull();
});
it('should return null when variables exist but none of them is meant to be shown in the controls menu', () => {
const variables = [
new TextBoxVariable({
name: 'textVar',
value: 'test',
showInControlsMenu: false,
}),
];
const { container } = render(<DropdownVariableControls dashboard={getDashboard(variables)} />);
expect(container.firstChild).toBeNull();
});
it('should render a dropdown with a toolbar-button when there are any variables that are set to be shown under the controls menu', () => {
const variables = [
new TextBoxVariable({
name: 'textVar',
value: 'test',
showInControlsMenu: true,
}),
];
render(<DropdownVariableControls dashboard={getDashboard(variables)} />);
// Should render the toolbar button
const button = screen.getByRole('button');
expect(button).toBeInTheDocument();
expect(button).toHaveAttribute('aria-label', DROPDOWN_CONTROLS_ARIA_LABEL);
expect(button).toHaveAttribute('title', DROPDOWN_CONTROLS_TITLE);
});
it('should render multiple variables in dropdown menu', async () => {
const variables = [
new TextBoxVariable({
name: 'textVar1',
value: 'test1',
showInControlsMenu: true,
}),
new TextBoxVariable({
name: 'textVar2',
value: 'test2',
showInControlsMenu: true,
}),
new QueryVariable({
name: 'queryVar',
query: 'test query',
showInControlsMenu: true,
}),
];
render(<DropdownVariableControls dashboard={getDashboard(variables)} />);
// Should have rendered a dropdown
expect(screen.getByRole('button')).toBeInTheDocument();
// Open the dropdown
userEvent.click(screen.getByRole('button'));
expect(await screen.findByText('textVar1')).toBeInTheDocument();
expect(await screen.findByText('textVar2')).toBeInTheDocument();
expect(await screen.findByText('queryVar')).toBeInTheDocument();
});
it('should filter out variables with showInControlsMenu=false', async () => {
const variables = [
new TextBoxVariable({
name: 'textVar1',
value: 'test1',
showInControlsMenu: true,
}),
new TextBoxVariable({
name: 'textVar2',
value: 'test2',
showInControlsMenu: false, // This should be filtered out
}),
new CustomVariable({
name: 'customVar',
query: 'option1,option2',
showInControlsMenu: true,
}),
];
render(<DropdownVariableControls dashboard={getDashboard(variables)} />);
// Should still render dropdown since we have variables with showInControlsMenu=true
expect(screen.getByRole('button')).toBeInTheDocument();
// Open the dropdown
userEvent.click(screen.getByRole('button'));
expect(await screen.findByText('textVar1')).toBeInTheDocument();
expect(await screen.findByText('customVar')).toBeInTheDocument();
expect(screen.queryByText('textVar2')).not.toBeInTheDocument();
});
});
function getDashboard(variables: SceneVariable[]): DashboardScene {
return new DashboardScene({
uid: 'test-dashboard',
title: 'Test Dashboard',
$variables: new SceneVariableSet({
variables,
}),
});
}

View File

@ -0,0 +1,56 @@
import { css } from '@emotion/css';
import { GrafanaTheme2 } from '@grafana/data';
import { t } from '@grafana/i18n';
import { sceneGraph } from '@grafana/scenes';
import { Dropdown, Menu, ToolbarButton, useStyles2 } from '@grafana/ui';
import { DashboardScene } from './DashboardScene';
import { VariableValueSelectWrapper } from './VariableControls';
export const DROPDOWN_CONTROLS_ARIA_LABEL = 'Dashboard controls menu';
export const DROPDOWN_CONTROLS_TITLE = 'Dashboard controls';
export function DropdownVariableControls({ dashboard }: { dashboard: DashboardScene }) {
const styles = useStyles2(getStyles);
const variables = sceneGraph
.getVariables(dashboard)!
.useState()
.variables.filter((v) => v.state.showInControlsMenu === true);
if (variables.length === 0) {
return null;
}
return (
<Dropdown
overlay={
<Menu
onClick={(e) => {
e.stopPropagation();
}}
>
{variables.map((variable) => (
<div className={styles.menuItem} key={variable.state.key}>
<VariableValueSelectWrapper variable={variable} />
</div>
))}
</Menu>
}
>
<ToolbarButton
aria-label={t('dashboard.controls.menu.aria-label', DROPDOWN_CONTROLS_ARIA_LABEL)}
title={t('dashboard.controls.menu.title', DROPDOWN_CONTROLS_TITLE)}
icon="ellipsis-v"
iconSize="md"
narrow
/>
</Dropdown>
);
}
const getStyles = (theme: GrafanaTheme2) => ({
menuItem: css({
padding: theme.spacing(0.5),
}),
});

View File

@ -8,11 +8,13 @@ import { useElementSelection, useStyles2 } from '@grafana/ui';
import { DashboardScene } from './DashboardScene';
export function VariableControls({ dashboard }: { dashboard: DashboardScene }) {
const variables = sceneGraph.getVariables(dashboard)!.useState();
const { variables } = sceneGraph.getVariables(dashboard)!.useState();
return (
<>
{variables.variables.map((variable) => (
{variables
.filter((v) => !v.state.showInControlsMenu)
.map((variable) => (
<VariableValueSelectWrapper key={variable.state.key} variable={variable} />
))}
</>

View File

@ -93,6 +93,7 @@ describe('sceneVariablesSetToVariables', () => {
name: 'test',
label: 'test-label',
description: 'test-desc',
showInControlsMenu: true,
value: ['selected-value'],
text: ['selected-value-text'],
datasource: { uid: 'fake-uid', type: 'fake-type' },
@ -138,6 +139,7 @@ describe('sceneVariablesSetToVariables', () => {
"query": "query",
"refresh": 1,
"regex": "",
"showInControlsMenu": true,
"staticOptions": [
{
"text": "test",
@ -155,6 +157,7 @@ describe('sceneVariablesSetToVariables', () => {
name: 'test',
label: 'test-label',
description: 'test-desc',
showInControlsMenu: true,
value: ['selected-value'],
text: ['selected-value-text'],
datasource: { uid: 'fake-uid', type: 'fake-type' },
@ -200,6 +203,7 @@ describe('sceneVariablesSetToVariables', () => {
"query": "query",
"refresh": 1,
"regex": "",
"showInControlsMenu": true,
"staticOptions": [
{
"text": "test",
@ -217,6 +221,7 @@ describe('sceneVariablesSetToVariables', () => {
name: 'test',
label: 'test-label',
description: 'test-desc',
showInControlsMenu: true,
value: ['selected-value'],
text: ['selected-value-text'],
datasource: { uid: 'fake-uid', type: 'fake-type' },
@ -244,6 +249,7 @@ describe('sceneVariablesSetToVariables', () => {
name: 'test',
label: 'test-label',
description: 'test-desc',
showInControlsMenu: true,
value: ['test'],
text: ['test'],
datasource: { uid: 'fake-uid', type: 'fake-type' },
@ -273,6 +279,7 @@ describe('sceneVariablesSetToVariables', () => {
name: 'test',
label: 'test-label',
description: 'test-desc',
showInControlsMenu: true,
value: ['selected-ds-1', 'selected-ds-2'],
text: ['selected-ds-1-text', 'selected-ds-2-text'],
pluginId: 'fake-std',
@ -311,6 +318,7 @@ describe('sceneVariablesSetToVariables', () => {
"query": "fake-std",
"refresh": 1,
"regex": "",
"showInControlsMenu": true,
"type": "datasource",
}
`);
@ -321,6 +329,7 @@ describe('sceneVariablesSetToVariables', () => {
name: 'test',
label: 'test-label',
description: 'test-desc',
showInControlsMenu: true,
value: ['test', 'test2'],
text: ['test', 'test2'],
query: 'test,test1,test2',
@ -378,6 +387,7 @@ describe('sceneVariablesSetToVariables', () => {
},
],
"query": "test,test1,test2",
"showInControlsMenu": true,
"type": "custom",
}
`);
@ -388,6 +398,7 @@ describe('sceneVariablesSetToVariables', () => {
name: 'test',
label: 'test-label',
description: 'test-desc',
showInControlsMenu: true,
value: 'constant value',
skipUrlSync: true,
});
@ -409,6 +420,7 @@ describe('sceneVariablesSetToVariables', () => {
"label": "test-label",
"name": "test",
"query": "constant value",
"showInControlsMenu": true,
"skipUrlSync": true,
"type": "constant",
}
@ -420,6 +432,7 @@ describe('sceneVariablesSetToVariables', () => {
name: 'test',
label: 'test-label',
description: 'test-desc',
showInControlsMenu: true,
value: 'text value',
skipUrlSync: true,
});
@ -447,6 +460,7 @@ describe('sceneVariablesSetToVariables', () => {
},
],
"query": "text value",
"showInControlsMenu": true,
"skipUrlSync": true,
"type": "textbox",
}
@ -458,6 +472,7 @@ describe('sceneVariablesSetToVariables', () => {
intervals: ['1m', '2m', '3m', '1h', '1d'],
value: '1m',
refresh: VariableRefresh.onDashboardLoad,
showInControlsMenu: true,
});
const set = new SceneVariableSet({
variables: [variable],
@ -506,6 +521,7 @@ describe('sceneVariablesSetToVariables', () => {
],
"query": "1m,2m,3m,1h,1d",
"refresh": 1,
"showInControlsMenu": true,
"type": "interval",
}
`);
@ -517,6 +533,7 @@ describe('sceneVariablesSetToVariables', () => {
allowCustomValue: true,
label: 'test-label',
description: 'test-desc',
showInControlsMenu: true,
datasource: { uid: 'fake-uid', type: 'fake-type' },
filters: [
{
@ -565,6 +582,7 @@ describe('sceneVariablesSetToVariables', () => {
],
"label": "test-label",
"name": "test",
"showInControlsMenu": true,
"type": "adhoc",
}
`);
@ -577,6 +595,7 @@ describe('sceneVariablesSetToVariables', () => {
allowCustomValue: true,
label: 'test-label',
description: 'test-desc',
showInControlsMenu: true,
datasource: { uid: 'fake-std', type: 'fake-std' },
originFilters: [
{
@ -608,6 +627,7 @@ describe('sceneVariablesSetToVariables', () => {
"filters": [],
"label": "test-label",
"name": "test",
"showInControlsMenu": true,
"type": "adhoc",
}
`);
@ -619,6 +639,7 @@ describe('sceneVariablesSetToVariables', () => {
allowCustomValue: true,
label: 'test-label',
description: 'test-desc',
showInControlsMenu: true,
datasource: { uid: 'fake-std', type: 'fake-std' },
originFilters: [
{
@ -668,6 +689,7 @@ describe('sceneVariablesSetToVariables', () => {
],
"label": "test-label",
"name": "test",
"showInControlsMenu": true,
"type": "adhoc",
}
`);
@ -680,6 +702,7 @@ describe('sceneVariablesSetToVariables', () => {
allowCustomValue: true,
label: 'test-label',
description: 'test-desc',
showInControlsMenu: true,
datasource: { uid: 'fake-uid', type: 'fake-type' },
defaultKeys: [
{
@ -755,6 +778,7 @@ describe('sceneVariablesSetToVariables', () => {
],
"label": "test-label",
"name": "test",
"showInControlsMenu": true,
"type": "adhoc",
}
`);
@ -775,6 +799,7 @@ describe('sceneVariablesSetToVariables', () => {
label: 'test-label',
description: 'test-desc',
allowCustomValue: true,
showInControlsMenu: true,
datasource: { uid: 'fake-uid', type: 'fake-type' },
defaultOptions: [
{
@ -819,6 +844,7 @@ describe('sceneVariablesSetToVariables', () => {
"value": "bar",
},
],
"showInControlsMenu": true,
"type": "groupby",
}
`);
@ -831,6 +857,7 @@ describe('sceneVariablesSetToVariables', () => {
name: 'test',
label: 'test-label',
description: 'test-desc',
showInControlsMenu: true,
datasource: { uid: 'fake-uid', type: 'fake-type' },
defaultOptions: [
{
@ -866,6 +893,7 @@ describe('sceneVariablesSetToVariables', () => {
isMulti: true,
staticOptions: [{ label: 'test', value: 'test' }],
staticOptionsOrder: 'after',
showInControlsMenu: true,
});
const set = new SceneVariableSet({
@ -910,6 +938,7 @@ describe('sceneVariablesSetToVariables', () => {
},
"refresh": "onDashboardLoad",
"regex": "",
"showInControlsMenu": true,
"skipUrlSync": false,
"sort": "disabled",
"staticOptions": [
@ -932,6 +961,7 @@ describe('sceneVariablesSetToVariables', () => {
value: ['test', 'test2'],
text: ['test', 'test2'],
query: 'test,test1,test2',
showInControlsMenu: true,
options: [
{ label: 'test', value: 'test' },
{ label: 'test1', value: 'test1' },
@ -988,6 +1018,7 @@ describe('sceneVariablesSetToVariables', () => {
},
],
"query": "test,test1,test2",
"showInControlsMenu": true,
"skipUrlSync": false,
},
}
@ -1005,6 +1036,7 @@ describe('sceneVariablesSetToVariables', () => {
includeAll: true,
allValue: 'test-all',
isMulti: true,
showInControlsMenu: true,
});
const set = new SceneVariableSet({
variables: [variable],
@ -1039,6 +1071,7 @@ describe('sceneVariablesSetToVariables', () => {
"pluginId": "fake-std",
"refresh": "onDashboardLoad",
"regex": "",
"showInControlsMenu": true,
"skipUrlSync": false,
},
}
@ -1051,6 +1084,7 @@ describe('sceneVariablesSetToVariables', () => {
label: 'test-label',
description: 'test-desc',
value: 'constant value',
showInControlsMenu: true,
skipUrlSync: true,
});
const set = new SceneVariableSet({
@ -1073,6 +1107,7 @@ describe('sceneVariablesSetToVariables', () => {
"label": "test-label",
"name": "test",
"query": "constant value",
"showInControlsMenu": true,
"skipUrlSync": true,
},
}
@ -1085,6 +1120,7 @@ describe('sceneVariablesSetToVariables', () => {
label: 'test-label',
description: 'test-desc',
value: 'text value',
showInControlsMenu: true,
skipUrlSync: true,
});
const set = new SceneVariableSet({
@ -1107,6 +1143,7 @@ describe('sceneVariablesSetToVariables', () => {
"label": "test-label",
"name": "test",
"query": "text value",
"showInControlsMenu": true,
"skipUrlSync": true,
},
}
@ -1117,6 +1154,7 @@ describe('sceneVariablesSetToVariables', () => {
const variable = new IntervalVariable({
intervals: ['1m', '2m', '3m', '1h', '1d'],
value: '1m',
showInControlsMenu: true,
refresh: VariableRefresh.onDashboardLoad,
});
const set = new SceneVariableSet({
@ -1169,6 +1207,7 @@ describe('sceneVariablesSetToVariables', () => {
],
"query": "1m,2m,3m,1h,1d",
"refresh": "onTimeRangeChanged",
"showInControlsMenu": true,
"skipUrlSync": false,
},
}
@ -1180,6 +1219,7 @@ describe('sceneVariablesSetToVariables', () => {
name: 'test',
label: 'test-label',
description: 'test-desc',
showInControlsMenu: true,
datasource: { uid: 'fake-uid', type: 'fake-type' },
filters: [
{
@ -1231,6 +1271,7 @@ describe('sceneVariablesSetToVariables', () => {
"hide": "dontHide",
"label": "test-label",
"name": "test",
"showInControlsMenu": true,
"skipUrlSync": false,
},
}
@ -1242,6 +1283,7 @@ describe('sceneVariablesSetToVariables', () => {
name: 'test',
label: 'test-label',
description: 'test-desc',
showInControlsMenu: true,
datasource: { uid: 'fake-uid', type: 'fake-type' },
defaultKeys: [
{
@ -1320,6 +1362,7 @@ describe('sceneVariablesSetToVariables', () => {
"hide": "dontHide",
"label": "test-label",
"name": "test",
"showInControlsMenu": true,
"skipUrlSync": false,
},
}
@ -1340,6 +1383,7 @@ describe('sceneVariablesSetToVariables', () => {
name: 'test',
label: 'test-label',
description: 'test-desc',
showInControlsMenu: true,
datasource: { uid: 'fake-uid', type: 'fake-type' },
defaultOptions: [
{
@ -1387,6 +1431,7 @@ describe('sceneVariablesSetToVariables', () => {
"value": "bar",
},
],
"showInControlsMenu": true,
"skipUrlSync": false,
},
}
@ -1400,6 +1445,7 @@ describe('sceneVariablesSetToVariables', () => {
name: 'test',
label: 'test-label',
description: 'test-desc',
showInControlsMenu: true,
datasource: { uid: 'fake-uid', type: 'fake-type' },
defaultOptions: [
{

View File

@ -60,6 +60,7 @@ export function sceneVariablesSetToVariables(set: SceneVariables, keepQueryOptio
skipUrlSync: Boolean(variable.state.skipUrlSync),
hide: variable.state.hide || OldVariableHide.dontHide,
type: variable.state.type,
showInControlsMenu: variable.state.showInControlsMenu,
};
if (sceneUtils.isQueryVariable(variable)) {
@ -283,6 +284,7 @@ export function sceneVariablesSetToSchemaV2Variables(
description: variable.state.description ?? undefined,
skipUrlSync: Boolean(variable.state.skipUrlSync),
hide: transformVariableHideToEnum(variable.state.hide) || defaultVariableHide(),
showInControlsMenu: variable.state.showInControlsMenu,
};
// current: VariableOption;

View File

@ -273,6 +273,7 @@ function createSceneVariableFromVariableModel(variable: TypedVariableModelV2): S
name: variable.spec.name,
label: variable.spec.label,
description: variable.spec.description,
showInControlsMenu: variable.spec.showInControlsMenu,
};
if (variable.kind === defaultAdhocVariableKind().kind) {
const ds = getDataSourceForQuery(

View File

@ -131,6 +131,7 @@ export function createSceneVariableFromVariableModel(variable: TypedVariableMode
name: variable.name,
label: variable.label,
description: variable.description,
showInControlsMenu: variable.showInControlsMenu,
};
if (variable.type === 'adhoc') {
const originFilters: AdHocVariableFilter[] = [];

View File

@ -4562,6 +4562,12 @@
"overwrite": "Overwrite",
"title-plugin-dashboard": "Plugin dashboard"
},
"controls": {
"menu": {
"aria-label": "",
"title": ""
}
},
"dash-nav": {
"on-open-snapshot-original": {
"confirmText": {