diff --git a/public/app/features/dashboard-scene/edit-pane/shared.ts b/public/app/features/dashboard-scene/edit-pane/shared.ts
index 1171d9ef776..6d9a2279563 100644
--- a/public/app/features/dashboard-scene/edit-pane/shared.ts
+++ b/public/app/features/dashboard-scene/edit-pane/shared.ts
@@ -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,
diff --git a/public/app/features/dashboard-scene/scene/VariableControls.tsx b/public/app/features/dashboard-scene/scene/VariableControls.tsx
index e285b94e5e1..4052efc6bd4 100644
--- a/public/app/features/dashboard-scene/scene/VariableControls.tsx
+++ b/public/app/features/dashboard-scene/scene/VariableControls.tsx
@@ -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) => (
))}
+ {config.featureToggles.dashboardNewLayouts ? : null}
>
);
}
diff --git a/public/app/features/dashboard-scene/scene/VariableControlsAddButton.tsx b/public/app/features/dashboard-scene/scene/VariableControlsAddButton.tsx
new file mode 100644
index 00000000000..84e4f456578
--- /dev/null
+++ b/public/app/features/dashboard-scene/scene/VariableControlsAddButton.tsx
@@ -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 (
+
+ );
+}
diff --git a/public/app/features/dashboard-scene/settings/variables/VariableAddEditableElement.tsx b/public/app/features/dashboard-scene/settings/variables/VariableAddEditableElement.tsx
new file mode 100644
index 00000000000..f32c63a083a
--- /dev/null
+++ b/public/app/features/dashboard-scene/settings/variables/VariableAddEditableElement.tsx
@@ -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;
+}
+
+export class VariableAdd extends SceneObjectBase {}
+
+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: () => ,
+ })
+ );
+ }, [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 (
+
+
+ Choose variable type
+
+
+ {options.map((option) => (
+ 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!)}
+ >
+ {option.label}
+ {option.description}
+
+ ))}
+
+
+ );
+}
+
+function getStyles(theme: GrafanaTheme2) {
+ return {
+ cardDescription: css({
+ fontSize: theme.typography.bodySmall.fontSize,
+ marginTop: theme.spacing(0),
+ }),
+ };
+}
diff --git a/public/app/features/dashboard-scene/settings/variables/VariableSetEditableElement.tsx b/public/app/features/dashboard-scene/settings/variables/VariableSetEditableElement.tsx
index 0cefe5cec8a..462b7e7bf03 100644
--- a/public/app/features/dashboard-scene/settings/variables/VariableSetEditableElement.tsx
+++ b/public/app/features/dashboard-scene/settings/variables/VariableSetEditableElement.tsx
@@ -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 ;
- }
+ const onAddVariable = useCallback(() => {
+ openAddVariablePane(getDashboardSceneFor(set));
+ DashboardInteractions.addVariableButtonClicked({ source: 'edit_pane' });
+ }, [set]);
return (
@@ -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}
>
Add variable
@@ -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 (
-
-
- Choose variable type
-
-
- {options.map((option) => (
- 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!)}
- >
- {option.label}
- {option.description}
-
- ))}
-
-
- );
-}
-
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),
- }),
};
}
diff --git a/public/app/features/dashboard-scene/utils/interactions.ts b/public/app/features/dashboard-scene/utils/interactions.ts
index e95040ae2c8..701d9f37a99 100644
--- a/public/app/features/dashboard-scene/utils/interactions.ts
+++ b/public/app/features/dashboard-scene/utils/interactions.ts
@@ -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);
},
diff --git a/public/locales/en-US/grafana.json b/public/locales/en-US/grafana.json
index aa1e0d71471..d85d332d402 100644
--- a/public/locales/en-US/grafana.json
+++ b/public/locales/en-US/grafana.json
@@ -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",