mirror of
https://github.com/grafana/grafana.git
synced 2025-09-17 22:35:29 +08:00
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:
@ -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
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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"},
|
||||
},
|
||||
|
@ -186,6 +186,7 @@ export interface BaseVariableModel {
|
||||
error: any | null;
|
||||
description: string | null;
|
||||
usedInRepeat?: boolean;
|
||||
showInControlsMenu?: boolean;
|
||||
}
|
||||
|
||||
export interface SnapshotVariableModel extends VariableWithOptions {
|
||||
|
@ -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 => ({
|
||||
|
@ -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>
|
||||
);
|
||||
|
@ -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,
|
||||
}),
|
||||
});
|
||||
}
|
@ -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),
|
||||
}),
|
||||
});
|
@ -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} />
|
||||
))}
|
||||
</>
|
||||
|
@ -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: [
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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(
|
||||
|
@ -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[] = [];
|
||||
|
@ -4562,6 +4562,12 @@
|
||||
"overwrite": "Overwrite",
|
||||
"title-plugin-dashboard": "Plugin dashboard"
|
||||
},
|
||||
"controls": {
|
||||
"menu": {
|
||||
"aria-label": "",
|
||||
"title": ""
|
||||
}
|
||||
},
|
||||
"dash-nav": {
|
||||
"on-open-snapshot-original": {
|
||||
"confirmText": {
|
||||
|
Reference in New Issue
Block a user