diff --git a/.betterer.results b/.betterer.results index 976a2137c61..7b7375fa9b9 100644 --- a/.betterer.results +++ b/.betterer.results @@ -1606,7 +1606,8 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], "public/app/features/dashboard-scene/settings/variables/utils.ts:5381": [ - [0, 0, 0, "Unexpected any. Specify a different type.", "0"] + [0, 0, 0, "Do not use any type assertions.", "0"], + [0, 0, 0, "Unexpected any. Specify a different type.", "1"] ], "public/app/features/dashboard-scene/settings/version-history/RevertDashboardModal.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] diff --git a/package.json b/package.json index e5bd0bfb49a..9f91adb3250 100644 --- a/package.json +++ b/package.json @@ -275,8 +275,8 @@ "@grafana/prometheus": "workspace:*", "@grafana/runtime": "workspace:*", "@grafana/saga-icons": "workspace:*", - "@grafana/scenes": "^6.7.0", - "@grafana/scenes-react": "^6.7.0", + "@grafana/scenes": "^6.8.1", + "@grafana/scenes-react": "^6.8.1", "@grafana/schema": "workspace:*", "@grafana/sql": "workspace:*", "@grafana/ui": "workspace:*", diff --git a/public/app/features/dashboard-scene/edit-pane/DashboardEditPane.tsx b/public/app/features/dashboard-scene/edit-pane/DashboardEditPane.tsx index c03102bf873..a667b877357 100644 --- a/public/app/features/dashboard-scene/edit-pane/DashboardEditPane.tsx +++ b/public/app/features/dashboard-scene/edit-pane/DashboardEditPane.tsx @@ -132,13 +132,7 @@ export class DashboardEditPane extends SceneObjectBase { const { selection, contextItems: selected } = elementSelection.getStateWithValue(id, obj, !!multi); - this.setState({ - selection: new ElementSelection(selection), - selectionContext: { - ...this.state.selectionContext, - selected, - }, - }); + this.updateSelection(new ElementSelection(selection), selected); } private removeMultiSelectedObject(id: string) { @@ -153,13 +147,17 @@ export class DashboardEditPane extends SceneObjectBase { return; } - this.setState({ - selection: new ElementSelection([...entries]), - selectionContext: { - ...this.state.selectionContext, - selected, - }, - }); + this.updateSelection(new ElementSelection([...entries]), selected); + } + + private updateSelection(selection: ElementSelection | undefined, selected: ElementSelectionContextItem[]) { + // onBlur events are not fired on unmount and some edit pane inputs have important onBlur events + // This make sure they fire before unmounting + if (document.activeElement instanceof HTMLElement) { + document.activeElement.blur(); + } + + this.setState({ selection, selectionContext: { ...this.state.selectionContext, selected } }); } public clearSelection() { @@ -167,13 +165,7 @@ export class DashboardEditPane extends SceneObjectBase { return; } - this.setState({ - selection: undefined, - selectionContext: { - ...this.state.selectionContext, - selected: [], - }, - }); + this.updateSelection(undefined, []); } private newObjectAddedToCanvas(obj: SceneObject) { diff --git a/public/app/features/dashboard-scene/scene/VariableControls.tsx b/public/app/features/dashboard-scene/scene/VariableControls.tsx index ee425f28519..2f32bc8ee67 100644 --- a/public/app/features/dashboard-scene/scene/VariableControls.tsx +++ b/public/app/features/dashboard-scene/scene/VariableControls.tsx @@ -33,6 +33,17 @@ export function VariableValueSelectWrapper({ variable }: VariableSelectProps) { } const onPointerDown = (evt: React.PointerEvent) => { + if (!isSelectable) { + return; + } + + // Ignore click if it's inside the value control + if (evt.target instanceof Element && !evt.target.closest(`label`)) { + // Prevent clearing selection when clicking inside value + evt.stopPropagation(); + return; + } + if (isSelectable && onSelect) { evt.stopPropagation(); onSelect(evt); diff --git a/public/app/features/dashboard-scene/serialization/custom-variables/SnapshotVariable.tsx b/public/app/features/dashboard-scene/serialization/custom-variables/SnapshotVariable.tsx index 3a84ade2759..5c198abec0d 100644 --- a/public/app/features/dashboard-scene/serialization/custom-variables/SnapshotVariable.tsx +++ b/public/app/features/dashboard-scene/serialization/custom-variables/SnapshotVariable.tsx @@ -7,7 +7,7 @@ import { ValidateAndUpdateResult, VariableDependencyConfig, VariableValueOption, - renderSelectForVariable, + MultiOrSingleValueSelect, sceneGraph, VariableGetOptionsArgs, } from '@grafana/scenes'; @@ -64,7 +64,7 @@ export class SnapshotVariable extends MultiValueVariable } public static Component = ({ model }: SceneComponentProps>) => { - return renderSelectForVariable(model); + return ; }; // we will always preserve the current value and text for snapshots private _updateValueGivenNewOptions(options: VariableValueOption[]) { diff --git a/public/app/features/dashboard-scene/settings/variables/VariableEditableElement.tsx b/public/app/features/dashboard-scene/settings/variables/VariableEditableElement.tsx index 7c0da2add37..b8d2ee6f281 100644 --- a/public/app/features/dashboard-scene/settings/variables/VariableEditableElement.tsx +++ b/public/app/features/dashboard-scene/settings/variables/VariableEditableElement.tsx @@ -2,8 +2,8 @@ import { FormEvent, useMemo, useState } from 'react'; import { VariableHide } from '@grafana/data'; import { locationService } from '@grafana/runtime'; -import { SceneVariable, SceneVariableSet } from '@grafana/scenes'; -import { Combobox, Input, TextArea, Stack, Button, Field } from '@grafana/ui'; +import { MultiValueVariable, SceneVariable, SceneVariableSet } from '@grafana/scenes'; +import { Input, TextArea, Button, Field, Box } from '@grafana/ui'; import { t, Trans } from 'app/core/internationalization'; import { OptionsPaneCategoryDescriptor } from 'app/features/dashboard/components/PanelEditor/OptionsPaneCategoryDescriptor'; import { OptionsPaneItemDescriptor } from 'app/features/dashboard/components/PanelEditor/OptionsPaneItemDescriptor'; @@ -13,7 +13,9 @@ import { useEditPaneInputAutoFocus } from '../../scene/layouts-shared/utils'; import { BulkActionElement } from '../../scene/types/BulkActionElement'; import { EditableDashboardElement, EditableDashboardElementInfo } from '../../scene/types/EditableDashboardElement'; import { VariableHideSelect } from '../../settings/variables/components/VariableHideSelect'; -import { getVariableTypeSelectOptions, validateVariableName } from '../../settings/variables/utils'; +import { getEditableVariableDefinition, validateVariableName } from '../../settings/variables/utils'; + +import { useVariableSelectionOptionsCategory } from './useVariableSelectionOptionsCategory'; export class VariableEditableElement implements EditableDashboardElement, BulkActionElement { public readonly isEditableDashboardElement = true; @@ -22,8 +24,10 @@ export class VariableEditableElement implements EditableDashboardElement, BulkAc public constructor(public variable: SceneVariable) {} public getEditableElementInfo(): EditableDashboardElementInfo { + const variableEditorDef = getEditableVariableDefinition(this.variable.state.type); + return { - typeName: t('dashboard.edit-pane.elements.variable', 'Variable'), + typeName: t('dashboard.edit-pane.elements.variable', '{{type}} variable', { type: variableEditorDef.name }), icon: 'dollar-alt', instanceName: this.variable.state.name, isHidden: this.variable.state.hide === VariableHide.hideVariable, @@ -33,8 +37,8 @@ export class VariableEditableElement implements EditableDashboardElement, BulkAc public useEditPaneOptions(isNewElement: boolean): OptionsPaneCategoryDescriptor[] { const variable = this.variable; - const options = useMemo(() => { - return new OptionsPaneCategoryDescriptor({ title: '', id: 'panel-options' }) + const basicOptions = useMemo(() => { + return new OptionsPaneCategoryDescriptor({ title: '', id: 'variable-options' }) .addItem( new OptionsPaneItemDescriptor({ title: '', @@ -44,17 +48,14 @@ export class VariableEditableElement implements EditableDashboardElement, BulkAc ) .addItem( new OptionsPaneItemDescriptor({ - title: t('dashboard-scene.variable-editor-form.label', 'Label'), - description: t( - 'dashboard-scene.variable-editor-form.description-optional-display-name', - 'Optional display name' - ), + title: t('dashboard.edit-pane.variable.label', 'Label'), + description: t('dashboard.edit-pane.variable.label-description', 'Optional display name'), render: () => , }) ) .addItem( new OptionsPaneItemDescriptor({ - title: t('dashboard-scene.variable-editor-form.description', 'Description'), + title: t('dashboard.edit-pane.variable.description', 'Description'), render: () => , }) ) @@ -64,16 +65,18 @@ export class VariableEditableElement implements EditableDashboardElement, BulkAc skipField: true, render: () => , }) - ) - .addItem( - new OptionsPaneItemDescriptor({ - title: t('dashboard-scene.variable-editor-form.type', 'Type'), - render: () => , - }) ); }, [variable, isNewElement]); - return [options]; + const categories = [basicOptions]; + const typeCategory = useVariableTypeCategory(variable); + categories.push(typeCategory); + + if (variable instanceof MultiValueVariable) { + categories.push(useVariableSelectionOptionsCategory(variable)); + } + + return categories; } public onDelete() { @@ -126,7 +129,7 @@ function VariableNameInput({ variable, isNewElement }: { variable: SceneVariable }; return ( - + ); @@ -144,7 +147,7 @@ function VariableDescriptionTextArea({ variable }: VariableInputProps) {