Dashboard: Add variable button in dashboard controls (#112387)
This commit is contained in:
@@ -17,6 +17,7 @@ import { SceneGridRowEditableElement } from '../scene/layout-default/SceneGridRo
|
||||
import { redoButtonId, undoButtonID } from '../scene/new-toolbar/RightActions';
|
||||
import { EditableDashboardElement, isEditableDashboardElement } from '../scene/types/EditableDashboardElement';
|
||||
import { LocalVariableEditableElement } from '../settings/variables/LocalVariableEditableElement';
|
||||
import { VariableAdd, VariableAddEditableElement } from '../settings/variables/VariableAddEditableElement';
|
||||
import { VariableEditableElement } from '../settings/variables/VariableEditableElement';
|
||||
import { VariableSetEditableElement } from '../settings/variables/VariableSetEditableElement';
|
||||
import { isSceneVariable } from '../settings/variables/utils';
|
||||
@@ -61,6 +62,10 @@ export function getEditableElementFor(sceneObj: SceneObject | undefined): Editab
|
||||
return new VariableEditableElement(sceneObj);
|
||||
}
|
||||
|
||||
if (sceneObj instanceof VariableAdd) {
|
||||
return new VariableAddEditableElement(sceneObj);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -202,7 +207,7 @@ export const dashboardEditActions = {
|
||||
}),
|
||||
|
||||
addVariable({ source, addedObject }: AddVariableActionHelperProps) {
|
||||
const varsBeforeAddition = [...source.state.variables];
|
||||
const varsBeforeAddition = [...(source.state.variables ?? [])];
|
||||
|
||||
dashboardEditActions.addElement({
|
||||
source,
|
||||
|
||||
@@ -2,6 +2,7 @@ import { css, cx } from '@emotion/css';
|
||||
|
||||
import { VariableHide, GrafanaTheme2 } from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { config } from '@grafana/runtime';
|
||||
import {
|
||||
sceneGraph,
|
||||
useSceneObjectState,
|
||||
@@ -14,6 +15,7 @@ import {
|
||||
import { useElementSelection, useStyles2 } from '@grafana/ui';
|
||||
|
||||
import { DashboardScene } from './DashboardScene';
|
||||
import { AddVariableButton } from './VariableControlsAddButton';
|
||||
|
||||
export function VariableControls({ dashboard }: { dashboard: DashboardScene }) {
|
||||
const { variables } = sceneGraph.getVariables(dashboard)!.useState();
|
||||
@@ -25,6 +27,7 @@ export function VariableControls({ dashboard }: { dashboard: DashboardScene }) {
|
||||
.map((variable) => (
|
||||
<VariableValueSelectWrapper key={variable.state.key} variable={variable} />
|
||||
))}
|
||||
{config.featureToggles.dashboardNewLayouts ? <AddVariableButton dashboard={dashboard} /> : null}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
import { PointerEventHandler, useCallback } from 'react';
|
||||
|
||||
import { Trans } from '@grafana/i18n';
|
||||
import { Button } from '@grafana/ui';
|
||||
|
||||
import { openAddVariablePane } from '../settings/variables/VariableAddEditableElement';
|
||||
import { DashboardInteractions } from '../utils/interactions';
|
||||
|
||||
import { DashboardScene } from './DashboardScene';
|
||||
|
||||
export function AddVariableButton({ dashboard }: { dashboard: DashboardScene }) {
|
||||
const { isEditing } = dashboard.useState();
|
||||
|
||||
const handlePointerDown: PointerEventHandler = useCallback(
|
||||
(evt) => {
|
||||
evt.stopPropagation();
|
||||
openAddVariablePane(dashboard);
|
||||
DashboardInteractions.addVariableButtonClicked({ source: 'variable_controls' });
|
||||
},
|
||||
[dashboard]
|
||||
);
|
||||
|
||||
if (!isEditing) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Button icon="plus" variant="primary" fill="text" onPointerDown={handlePointerDown}>
|
||||
<Trans i18nKey="dashboard-scene.variable-controls.add-variable">Add</Trans>
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
import { css } from '@emotion/css';
|
||||
import { useCallback, useId, useMemo } from 'react';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { Trans, t } from '@grafana/i18n';
|
||||
import { sceneGraph, SceneObjectBase, SceneObjectRef, SceneObjectState, SceneVariableSet } from '@grafana/scenes';
|
||||
import { Box, Card, Stack, useStyles2 } from '@grafana/ui';
|
||||
import { OptionsPaneCategoryDescriptor } from 'app/features/dashboard/components/PanelEditor/OptionsPaneCategoryDescriptor';
|
||||
import { OptionsPaneItemDescriptor } from 'app/features/dashboard/components/PanelEditor/OptionsPaneItemDescriptor';
|
||||
|
||||
import { dashboardEditActions } from '../../edit-pane/shared';
|
||||
import { DashboardScene } from '../../scene/DashboardScene';
|
||||
import { EditableDashboardElement, EditableDashboardElementInfo } from '../../scene/types/EditableDashboardElement';
|
||||
|
||||
import { EditableVariableType, getNextAvailableId, getVariableScene, getVariableTypeSelectOptions } from './utils';
|
||||
|
||||
export function openAddVariablePane(dashboard: DashboardScene) {
|
||||
const element = new VariableAdd({ dashboardRef: dashboard.getRef() });
|
||||
dashboard.state.editPane.selectObject(element, element.state.key!, { force: true, multi: false });
|
||||
}
|
||||
|
||||
export interface VariableAddState extends SceneObjectState {
|
||||
dashboardRef: SceneObjectRef<DashboardScene>;
|
||||
}
|
||||
|
||||
export class VariableAdd extends SceneObjectBase<VariableAddState> {}
|
||||
|
||||
function useEditPaneOptions(
|
||||
this: VariableAddEditableElement,
|
||||
variableAdd: VariableAdd
|
||||
): OptionsPaneCategoryDescriptor[] {
|
||||
const id = useId();
|
||||
const options = useMemo(() => {
|
||||
return new OptionsPaneCategoryDescriptor({ title: '', id: 'variables' }).addItem(
|
||||
new OptionsPaneItemDescriptor({
|
||||
title: '',
|
||||
id,
|
||||
skipField: true,
|
||||
render: () => <VariableTypeSelection variableAdd={variableAdd} />,
|
||||
})
|
||||
);
|
||||
}, [variableAdd, id]);
|
||||
|
||||
return [options];
|
||||
}
|
||||
|
||||
export class VariableAddEditableElement implements EditableDashboardElement {
|
||||
public readonly isEditableDashboardElement = true;
|
||||
public readonly typeName = 'Variable';
|
||||
|
||||
public constructor(private variableAdd: VariableAdd) {}
|
||||
|
||||
public getEditableElementInfo(): EditableDashboardElementInfo {
|
||||
return {
|
||||
typeName: t('dashboard.edit-pane.elements.variable-set', 'Variables'),
|
||||
icon: 'x',
|
||||
instanceName: t('dashboard.edit-pane.elements.variable-set', 'Variables'),
|
||||
};
|
||||
}
|
||||
|
||||
public useEditPaneOptions = useEditPaneOptions.bind(this, this.variableAdd);
|
||||
}
|
||||
|
||||
function VariableTypeSelection({ variableAdd }: { variableAdd: VariableAdd }) {
|
||||
const options = useMemo(() => getVariableTypeSelectOptions(), []);
|
||||
const styles = useStyles2(getStyles);
|
||||
|
||||
const onAddVariable = useCallback(
|
||||
(type: EditableVariableType) => {
|
||||
const dashboard = variableAdd.state.dashboardRef.resolve();
|
||||
const variablesSet = sceneGraph.getVariables(dashboard);
|
||||
|
||||
if (!(variablesSet instanceof SceneVariableSet)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const newVar = getVariableScene(type, { name: getNextAvailableId(type, variablesSet.state.variables ?? []) });
|
||||
dashboardEditActions.addVariable({ source: variablesSet, addedObject: newVar });
|
||||
dashboard.state.editPane.selectObject(newVar, newVar.state.key!, { force: true, multi: false });
|
||||
},
|
||||
[variableAdd]
|
||||
);
|
||||
|
||||
return (
|
||||
<Stack direction="column" gap={0}>
|
||||
<Box paddingBottom={1} display={'flex'}>
|
||||
<Trans i18nKey="dashboard.edit-pane.variables.select-type">Choose variable type</Trans>
|
||||
</Box>
|
||||
<Stack direction="column" gap={1}>
|
||||
{options.map((option) => (
|
||||
<Card
|
||||
noMargin
|
||||
isCompact
|
||||
onClick={() => onAddVariable(option.value!)}
|
||||
key={option.value}
|
||||
title={t('dashboard.edit-pane.variables.select-type-card-tooltip', 'Click to select type')}
|
||||
data-testid={selectors.components.PanelEditor.ElementEditPane.variableType(option.value!)}
|
||||
>
|
||||
<Card.Heading>{option.label}</Card.Heading>
|
||||
<Card.Description className={styles.cardDescription}>{option.description}</Card.Description>
|
||||
</Card>
|
||||
))}
|
||||
</Stack>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
function getStyles(theme: GrafanaTheme2) {
|
||||
return {
|
||||
cardDescription: css({
|
||||
fontSize: theme.typography.bodySmall.fontSize,
|
||||
marginTop: theme.spacing(0),
|
||||
}),
|
||||
};
|
||||
}
|
||||
+16
-66
@@ -1,22 +1,20 @@
|
||||
import { css } from '@emotion/css';
|
||||
import { useId, useMemo } from 'react';
|
||||
import { useToggle } from 'react-use';
|
||||
import { useCallback, useId, useMemo } from 'react';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { Trans, t } from '@grafana/i18n';
|
||||
import { SceneVariable, SceneVariableSet } from '@grafana/scenes';
|
||||
import { Box, Button, Card, Stack, Text, useStyles2 } from '@grafana/ui';
|
||||
import { Box, Button, Stack, Text, useStyles2 } from '@grafana/ui';
|
||||
import { OptionsPaneCategoryDescriptor } from 'app/features/dashboard/components/PanelEditor/OptionsPaneCategoryDescriptor';
|
||||
import { OptionsPaneItemDescriptor } from 'app/features/dashboard/components/PanelEditor/OptionsPaneItemDescriptor';
|
||||
|
||||
import { dashboardEditActions } from '../../edit-pane/shared';
|
||||
import { DashboardScene } from '../../scene/DashboardScene';
|
||||
import { EditableDashboardElement, EditableDashboardElementInfo } from '../../scene/types/EditableDashboardElement';
|
||||
import { DashboardInteractions } from '../../utils/interactions';
|
||||
import { getDashboardSceneFor } from '../../utils/utils';
|
||||
|
||||
import { EditableVariableType, getNextAvailableId, getVariableScene, getVariableTypeSelectOptions } from './utils';
|
||||
import { openAddVariablePane } from './VariableAddEditableElement';
|
||||
|
||||
function useEditPaneOptions(this: VariableSetEditableElement, set: SceneVariableSet): OptionsPaneCategoryDescriptor[] {
|
||||
const variableListId = useId();
|
||||
@@ -33,6 +31,7 @@ function useEditPaneOptions(this: VariableSetEditableElement, set: SceneVariable
|
||||
|
||||
return [options];
|
||||
}
|
||||
|
||||
export class VariableSetEditableElement implements EditableDashboardElement {
|
||||
public readonly isEditableDashboardElement = true;
|
||||
public readonly typeName = 'Variable';
|
||||
@@ -57,30 +56,20 @@ export class VariableSetEditableElement implements EditableDashboardElement {
|
||||
export function VariableList({ set }: { set: SceneVariableSet }) {
|
||||
const { variables } = set.useState();
|
||||
const styles = useStyles2(getStyles);
|
||||
const [isAdding, setIsAdding] = useToggle(false);
|
||||
const canAdd = set.parent instanceof DashboardScene;
|
||||
|
||||
const onEditVariable = (variable: SceneVariable) => {
|
||||
const { editPane } = getDashboardSceneFor(set).state;
|
||||
editPane.selectObject(variable, variable.state.key!);
|
||||
};
|
||||
const onEditVariable = useCallback(
|
||||
(variable: SceneVariable) => {
|
||||
const { editPane } = getDashboardSceneFor(set).state;
|
||||
editPane.selectObject(variable, variable.state.key!);
|
||||
},
|
||||
[set]
|
||||
);
|
||||
|
||||
const onAddVariable = (type: EditableVariableType) => {
|
||||
const { variables } = set.state;
|
||||
const nextName = getNextAvailableId(type, variables);
|
||||
const newVar = getVariableScene(type, { name: nextName });
|
||||
|
||||
dashboardEditActions.addVariable({
|
||||
source: set,
|
||||
addedObject: newVar,
|
||||
});
|
||||
|
||||
setIsAdding(false);
|
||||
};
|
||||
|
||||
if (isAdding) {
|
||||
return <VariableTypeSelection onAddVariable={onAddVariable} />;
|
||||
}
|
||||
const onAddVariable = useCallback(() => {
|
||||
openAddVariablePane(getDashboardSceneFor(set));
|
||||
DashboardInteractions.addVariableButtonClicked({ source: 'edit_pane' });
|
||||
}, [set]);
|
||||
|
||||
return (
|
||||
<Stack direction="column" gap={0}>
|
||||
@@ -103,10 +92,7 @@ export function VariableList({ set }: { set: SceneVariableSet }) {
|
||||
icon="plus"
|
||||
size="sm"
|
||||
variant="secondary"
|
||||
onClick={() => {
|
||||
DashboardInteractions.addVariableButtonClicked({ source: 'edit_pane' });
|
||||
setIsAdding();
|
||||
}}
|
||||
onClick={onAddVariable}
|
||||
data-testid={selectors.components.PanelEditor.ElementEditPane.addVariableButton}
|
||||
>
|
||||
<Trans i18nKey="dashboard.edit-pane.variables.add-variable">Add variable</Trans>
|
||||
@@ -117,38 +103,6 @@ export function VariableList({ set }: { set: SceneVariableSet }) {
|
||||
);
|
||||
}
|
||||
|
||||
interface VariableTypeSelectionProps {
|
||||
onAddVariable: (type: EditableVariableType) => void;
|
||||
}
|
||||
|
||||
function VariableTypeSelection({ onAddVariable }: VariableTypeSelectionProps) {
|
||||
const options = getVariableTypeSelectOptions();
|
||||
const styles = useStyles2(getStyles);
|
||||
|
||||
return (
|
||||
<Stack direction={'column'} gap={0}>
|
||||
<Box paddingBottom={1} display={'flex'}>
|
||||
<Trans i18nKey="dashboard.edit-pane.variables.select-type">Choose variable type</Trans>
|
||||
</Box>
|
||||
<Stack direction="column">
|
||||
{options.map((option) => (
|
||||
<Card
|
||||
isCompact
|
||||
noMargin
|
||||
onClick={() => onAddVariable(option.value!)}
|
||||
key={option.value}
|
||||
title={t('dashboard.edit-pane.variables.select-type-card-tooltip', 'Click to select type')}
|
||||
data-testid={selectors.components.PanelEditor.ElementEditPane.variableType(option.value!)}
|
||||
>
|
||||
<Card.Heading>{option.label}</Card.Heading>
|
||||
<Card.Description className={styles.cardDescription}>{option.description}</Card.Description>
|
||||
</Card>
|
||||
))}
|
||||
</Stack>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
function getStyles(theme: GrafanaTheme2) {
|
||||
return {
|
||||
variableItem: css({
|
||||
@@ -177,9 +131,5 @@ function getStyles(theme: GrafanaTheme2) {
|
||||
},
|
||||
},
|
||||
}),
|
||||
cardDescription: css({
|
||||
fontSize: theme.typography.bodySmall.fontSize,
|
||||
marginTop: theme.spacing(0),
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ export const DashboardInteractions = {
|
||||
|
||||
// dashboards_add_variable_button_clicked
|
||||
// when a user clicks on ‘Add Variable’ or ‘New Variable’
|
||||
addVariableButtonClicked: (properties: { source: 'edit_pane' | 'settings_pane' }) => {
|
||||
addVariableButtonClicked: (properties: { source: 'edit_pane' | 'settings_pane' | 'variable_controls' }) => {
|
||||
reportDashboardInteraction('add_variable_button_clicked', properties);
|
||||
},
|
||||
|
||||
|
||||
@@ -6294,6 +6294,9 @@
|
||||
"content-variable-not-referenced-other-variables-dashboard": "This variable is not referenced by other variables or dashboard.",
|
||||
"content-variable-referenced-other-variables-dashboard": "This variable is referenced by other variables or dashboard."
|
||||
},
|
||||
"variable-controls": {
|
||||
"add-variable": "Add"
|
||||
},
|
||||
"variable-editor-form": {
|
||||
"aria-label-variable-editor-form": "Variable editor form",
|
||||
"back-to-list": "Back to list",
|
||||
|
||||
Reference in New Issue
Block a user