From 2f104739d748f90086af7ad663c361f7e6ca585f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Mon, 10 Mar 2025 08:03:55 +0100 Subject: [PATCH] Dashboard: Auto focus title fields in edit pane (#101668) * Dashboard: Auto focus title fields in edit pane * Fix scroll issue * Update * No auto focus in panel edit * Update to implementation * alt skip fix * fixed playwright test * Update * Fix --- .../edit-pane/DashboardEditPane.tsx | 23 +++++++--- .../dashboard-scene/edit-pane/shared.ts | 5 +++ .../panel-edit/getPanelFrameOptions.tsx | 4 ++ .../scene/DashboardScene.test.tsx | 6 +++ .../dashboard-scene/scene/DashboardScene.tsx | 10 +---- .../DefaultGridLayoutManager.tsx | 10 ++++- .../ResponsiveGridLayoutManager.tsx | 13 ++++-- .../scene/layout-rows/RowItemEditor.tsx | 3 ++ .../scene/layout-rows/RowsLayoutManager.tsx | 22 +++++++--- .../scene/layout-tabs/TabItemEditor.tsx | 4 +- .../scene/layouts-shared/addNew.ts | 44 ++++++++++++------- .../scene/layouts-shared/utils.ts | 19 ++++++++ .../scene/setDashboardPanelContext.ts | 4 +- .../features/dashboard-scene/utils/utils.ts | 2 + 14 files changed, 127 insertions(+), 42 deletions(-) diff --git a/public/app/features/dashboard-scene/edit-pane/DashboardEditPane.tsx b/public/app/features/dashboard-scene/edit-pane/DashboardEditPane.tsx index 5a397991f74..e9c5c31e85b 100644 --- a/public/app/features/dashboard-scene/edit-pane/DashboardEditPane.tsx +++ b/public/app/features/dashboard-scene/edit-pane/DashboardEditPane.tsx @@ -15,11 +15,13 @@ import { import { t } from 'app/core/internationalization'; import { isInCloneChain } from '../utils/clone'; +import { getDashboardSceneFor } from '../utils/utils'; import { DashboardAddPane } from './DashboardAddPane'; import { DashboardOutline } from './DashboardOutline'; import { ElementEditPane } from './ElementEditPane'; import { ElementSelection } from './ElementSelection'; +import { NewObjectAddedToCanvasEvent } from './shared'; import { useEditableElement } from './useEditableElement'; export interface DashboardEditPaneState extends SceneObjectState { @@ -39,6 +41,18 @@ export class DashboardEditPane extends SceneObjectBase { onSelect: (item, multi) => this.selectElement(item, multi), }, }); + + this.addActivationHandler(this.onActivate.bind(this)); + } + + private onActivate() { + const dashboard = getDashboardSceneFor(this); + + this._subs.add( + dashboard.subscribeToEvent(NewObjectAddedToCanvasEvent, ({ payload }) => { + this.newObjectAddedToCanvas(payload); + }) + ); } public enableSelection() { @@ -134,7 +148,7 @@ export class DashboardEditPane extends SceneObjectBase { this.setState({ tab }); }; - public newObjectAddedToCanvas(obj: SceneObject) { + private newObjectAddedToCanvas(obj: SceneObject) { this.selectObject(obj, obj.state.key!, false); if (this.state.tab !== 'configure') { @@ -173,13 +187,12 @@ export function DashboardEditPaneRenderer({ editPane, isCollapsed, onToggleColla const styles = useStyles2(getStyles); const paneRef = useRef(null); const editableElement = useEditableElement(selection, editPane); + const selectedObject = selection?.getFirstObject(); if (!editableElement) { return null; } - const { typeId } = editableElement.getEditableElementInfo(); - if (isCollapsed) { return ( <> @@ -196,7 +209,7 @@ export function DashboardEditPaneRenderer({ editPane, isCollapsed, onToggleColla {openOverlay && ( - + )} @@ -224,7 +237,7 @@ export function DashboardEditPaneRenderer({ editPane, isCollapsed, onToggleColla
{tab === 'add' && } - {tab === 'configure' && } + {tab === 'configure' && } {tab === 'outline' && }
diff --git a/public/app/features/dashboard-scene/edit-pane/shared.ts b/public/app/features/dashboard-scene/edit-pane/shared.ts index 72c44d5dc2b..7f284ef945c 100644 --- a/public/app/features/dashboard-scene/edit-pane/shared.ts +++ b/public/app/features/dashboard-scene/edit-pane/shared.ts @@ -1,5 +1,6 @@ import { useSessionStorage } from 'react-use'; +import { BusEventWithPayload } from '@grafana/data'; import { SceneGridRow, SceneObject, VizPanel } from '@grafana/scenes'; import { DashboardScene } from '../scene/DashboardScene'; @@ -53,3 +54,7 @@ export function hasEditableElement(sceneObj: SceneObject | undefined): boolean { return false; } + +export class NewObjectAddedToCanvasEvent extends BusEventWithPayload { + static type = 'new-object-added-to-canvas'; +} diff --git a/public/app/features/dashboard-scene/panel-edit/getPanelFrameOptions.tsx b/public/app/features/dashboard-scene/panel-edit/getPanelFrameOptions.tsx index 8e0ac6a5e4b..d0c57d18d2b 100644 --- a/public/app/features/dashboard-scene/panel-edit/getPanelFrameOptions.tsx +++ b/public/app/features/dashboard-scene/panel-edit/getPanelFrameOptions.tsx @@ -1,3 +1,4 @@ +import { CoreApp } from '@grafana/data'; import { selectors } from '@grafana/e2e-selectors'; import { config } from '@grafana/runtime'; import { SceneTimeRangeLike, VizPanel } from '@grafana/scenes'; @@ -10,6 +11,7 @@ import { getPanelLinksVariableSuggestions } from 'app/features/panel/panellinks/ import { VizPanelLinks } from '../scene/PanelLinks'; import { PanelTimeRange } from '../scene/PanelTimeRange'; +import { useEditPaneInputAutoFocus } from '../scene/layouts-shared/utils'; import { isDashboardLayoutItem } from '../scene/types/DashboardLayoutItem'; import { vizPanelToPanel, transformSceneToSaveModel } from '../serialization/transformSceneToSaveModel'; import { dashboardSceneGraph } from '../utils/dashboardSceneGraph'; @@ -108,9 +110,11 @@ function ScenePanelLinksEditor({ panelLinks }: ScenePanelLinksEditorProps) { export function PanelFrameTitleInput({ panel }: { panel: VizPanel }) { const { title } = panel.useState(); + let ref = useEditPaneInputAutoFocus({ noAutoFocus: panel.getPanelContext().app === CoreApp.PanelEditor }); return ( setPanelTitle(panel, e.currentTarget.value)} diff --git a/public/app/features/dashboard-scene/scene/DashboardScene.test.tsx b/public/app/features/dashboard-scene/scene/DashboardScene.test.tsx index 4a02c6d8d26..3a9ecd5cee2 100644 --- a/public/app/features/dashboard-scene/scene/DashboardScene.test.tsx +++ b/public/app/features/dashboard-scene/scene/DashboardScene.test.tsx @@ -453,16 +453,22 @@ describe('DashboardScene', () => { }); it('Should select new panel', () => { + scene.state.editPane.activate(); + const panel = scene.onCreateNewPanel(); expect(scene.state.editPane.state.selection?.getFirstObject()).toBe(panel); }); it('Should select new row', () => { + scene.state.editPane.activate(); + const row = scene.onCreateNewRow(); expect(scene.state.editPane.state.selection?.getFirstObject()).toBe(row); }); it('Should select new tab', () => { + scene.state.editPane.activate(); + const tab = scene.onCreateNewTab(); expect(scene.state.editPane.state.selection?.getFirstObject()).toBe(tab); }); diff --git a/public/app/features/dashboard-scene/scene/DashboardScene.tsx b/public/app/features/dashboard-scene/scene/DashboardScene.tsx index 8e30df9dde0..454eb0a25e2 100644 --- a/public/app/features/dashboard-scene/scene/DashboardScene.tsx +++ b/public/app/features/dashboard-scene/scene/DashboardScene.tsx @@ -490,8 +490,6 @@ export class DashboardScene extends SceneObjectBase impleme // Add panel to layout this.state.body.addPanel(vizPanel); - // Select panel - this.state.editPane.newObjectAddedToCanvas(vizPanel); } public createLibraryPanel(panelToReplace: VizPanel, libPanel: LibraryPanel) { @@ -599,15 +597,11 @@ export class DashboardScene extends SceneObjectBase impleme } public onCreateNewRow() { - const newRow = addNewRowTo(this.state.body); - this.state.editPane.newObjectAddedToCanvas(newRow); - return newRow; + return addNewRowTo(this.state.body); } public onCreateNewTab() { - const tab = addNewTabTo(this.state.body); - this.state.editPane.newObjectAddedToCanvas(tab); - return tab; + return addNewTabTo(this.state.body); } public onCreateNewPanel(): VizPanel { diff --git a/public/app/features/dashboard-scene/scene/layout-default/DefaultGridLayoutManager.tsx b/public/app/features/dashboard-scene/scene/layout-default/DefaultGridLayoutManager.tsx index 0688e0d0c92..992a6f9102d 100644 --- a/public/app/features/dashboard-scene/scene/layout-default/DefaultGridLayoutManager.tsx +++ b/public/app/features/dashboard-scene/scene/layout-default/DefaultGridLayoutManager.tsx @@ -15,6 +15,7 @@ import { GRID_COLUMN_COUNT } from 'app/core/constants'; import { t } from 'app/core/internationalization'; import DashboardEmpty from 'app/features/dashboard/dashgrid/DashboardEmpty'; +import { NewObjectAddedToCanvasEvent } from '../../edit-pane/shared'; import { isClonedKey, joinCloneKeys } from '../../utils/clone'; import { dashboardSceneGraph } from '../../utils/dashboardSceneGraph'; import { @@ -77,6 +78,8 @@ export class DefaultGridLayoutManager this.state.grid.setState({ children: [newGridItem, ...this.state.grid.state.children], }); + + this.publishEvent(new NewObjectAddedToCanvasEvent(vizPanel), true); } public removePanel(panel: VizPanel) { @@ -132,6 +135,8 @@ export class DefaultGridLayoutManager // when we duplicate a panel we don't want to clone the alert state delete panelData.state.data?.alertState; + const newPanel = new VizPanel({ ...panelState, $data: panelData, key: getVizPanelKeyForPanelId(newPanelId) }); + newGridItem = new DashboardGridItem({ x: gridItem.state.x, y: gridItem.state.y, @@ -142,19 +147,20 @@ export class DefaultGridLayoutManager repeatDirection: gridItem.state.repeatDirection, maxPerRow: gridItem.state.maxPerRow, key: getGridItemKeyForPanelId(newPanelId), - body: new VizPanel({ ...panelState, $data: panelData, key: getVizPanelKeyForPanelId(newPanelId) }), + body: newPanel, }); if (gridItem.parent instanceof SceneGridRow) { const row = gridItem.parent; row.setState({ children: [...row.state.children, newGridItem] }); - grid.forceRender(); return; } grid.setState({ children: [...grid.state.children, newGridItem] }); + + this.publishEvent(new NewObjectAddedToCanvasEvent(newPanel), true); } public getVizPanels(): VizPanel[] { diff --git a/public/app/features/dashboard-scene/scene/layout-responsive-grid/ResponsiveGridLayoutManager.tsx b/public/app/features/dashboard-scene/scene/layout-responsive-grid/ResponsiveGridLayoutManager.tsx index c5f2d0dd09a..f610ceac675 100644 --- a/public/app/features/dashboard-scene/scene/layout-responsive-grid/ResponsiveGridLayoutManager.tsx +++ b/public/app/features/dashboard-scene/scene/layout-responsive-grid/ResponsiveGridLayoutManager.tsx @@ -2,6 +2,7 @@ import { SceneComponentProps, SceneCSSGridLayout, SceneObjectBase, SceneObjectSt import { t } from 'app/core/internationalization'; import { OptionsPaneItemDescriptor } from 'app/features/dashboard/components/PanelEditor/OptionsPaneItemDescriptor'; +import { NewObjectAddedToCanvasEvent } from '../../edit-pane/shared'; import { joinCloneKeys } from '../../utils/clone'; import { dashboardSceneGraph } from '../../utils/dashboardSceneGraph'; import { getGridItemKeyForPanelId, getPanelIdForVizPanel, getVizPanelKeyForPanelId } from '../../utils/utils'; @@ -60,6 +61,8 @@ export class ResponsiveGridLayoutManager this.state.layout.setState({ children: [new ResponsiveGridItem({ body: vizPanel }), ...this.state.layout.state.children], }); + + this.publishEvent(new NewObjectAddedToCanvasEvent(vizPanel), true); } public removePanel(panel: VizPanel) { @@ -77,11 +80,13 @@ export class ResponsiveGridLayoutManager const newPanelId = dashboardSceneGraph.getNextPanelId(this); const grid = this.state.layout; + const newPanel = panel.clone({ + key: getVizPanelKeyForPanelId(newPanelId), + }); + const newGridItem = gridItem.clone({ key: getGridItemKeyForPanelId(newPanelId), - body: panel.clone({ - key: getVizPanelKeyForPanelId(newPanelId), - }), + body: newPanel, }); const sourceIndex = grid.state.children.indexOf(gridItem); @@ -91,6 +96,8 @@ export class ResponsiveGridLayoutManager newChildren.splice(sourceIndex + 1, 0, newGridItem); grid.setState({ children: newChildren }); + + this.publishEvent(new NewObjectAddedToCanvasEvent(newPanel), true); } public getVizPanels(): VizPanel[] { diff --git a/public/app/features/dashboard-scene/scene/layout-rows/RowItemEditor.tsx b/public/app/features/dashboard-scene/scene/layout-rows/RowItemEditor.tsx index 62e1a7c4949..498d16685f0 100644 --- a/public/app/features/dashboard-scene/scene/layout-rows/RowItemEditor.tsx +++ b/public/app/features/dashboard-scene/scene/layout-rows/RowItemEditor.tsx @@ -14,6 +14,7 @@ import { EditPaneHeader } from '../../edit-pane/EditPaneHeader'; import { getDashboardSceneFor, getQueryRunnerFor } from '../../utils/utils'; import { DashboardScene } from '../DashboardScene'; import { DashboardLayoutSelector } from '../layouts-shared/DashboardLayoutSelector'; +import { useEditPaneInputAutoFocus } from '../layouts-shared/utils'; import { RowItem } from './RowItem'; @@ -77,9 +78,11 @@ export function getEditOptions(model: RowItem): OptionsPaneCategoryDescriptor[] function RowTitleInput({ row }: { row: RowItem }) { const { title } = row.useState(); + const ref = useEditPaneInputAutoFocus(); return ( row.onChangeTitle(e.currentTarget.value)} diff --git a/public/app/features/dashboard-scene/scene/layout-rows/RowsLayoutManager.tsx b/public/app/features/dashboard-scene/scene/layout-rows/RowsLayoutManager.tsx index 3ef34cbbc0f..ceccc165557 100644 --- a/public/app/features/dashboard-scene/scene/layout-rows/RowsLayoutManager.tsx +++ b/public/app/features/dashboard-scene/scene/layout-rows/RowsLayoutManager.tsx @@ -1,6 +1,7 @@ import { SceneGridItemLike, SceneGridRow, SceneObjectBase, SceneObjectState, VizPanel } from '@grafana/scenes'; import { t } from 'app/core/internationalization'; +import { NewObjectAddedToCanvasEvent } from '../../edit-pane/shared'; import { isClonedKey } from '../../utils/clone'; import { dashboardSceneGraph } from '../../utils/dashboardSceneGraph'; import { DashboardGridItem } from '../layout-default/DashboardGridItem'; @@ -96,10 +97,14 @@ export class RowsLayoutManager extends SceneObjectBase i } public addRowAbove(row: RowItem) { - const rows = this.state.rows; - const index = rows.indexOf(row); - rows.splice(index, 0, new RowItem()); - this.setState({ rows }); + const index = this.state.rows.indexOf(row); + const newRow = new RowItem(); + const newRows = [...this.state.rows]; + + newRows.splice(index, 0, newRow); + + this.setState({ rows: newRows }); + this.publishEvent(new NewObjectAddedToCanvasEvent(newRow), true); } public addRowBelow(row: RowItem) { @@ -111,8 +116,13 @@ export class RowsLayoutManager extends SceneObjectBase i index = index + 1; } - rows.splice(index + 1, 0, new RowItem()); - this.setState({ rows }); + const newRow = new RowItem(); + const newRows = [...this.state.rows]; + + newRows.splice(index + 1, 0, newRow); + + this.setState({ rows: newRows }); + this.publishEvent(new NewObjectAddedToCanvasEvent(newRow), true); } public removeRow(row: RowItem) { diff --git a/public/app/features/dashboard-scene/scene/layout-tabs/TabItemEditor.tsx b/public/app/features/dashboard-scene/scene/layout-tabs/TabItemEditor.tsx index 2769620060e..da057db4710 100644 --- a/public/app/features/dashboard-scene/scene/layout-tabs/TabItemEditor.tsx +++ b/public/app/features/dashboard-scene/scene/layout-tabs/TabItemEditor.tsx @@ -7,6 +7,7 @@ import { OptionsPaneItemDescriptor } from 'app/features/dashboard/components/Pan import { EditPaneHeader } from '../../edit-pane/EditPaneHeader'; import { useLayoutCategory } from '../layouts-shared/DashboardLayoutSelector'; +import { useEditPaneInputAutoFocus } from '../layouts-shared/utils'; import { TabItem } from './TabItem'; @@ -35,6 +36,7 @@ export function getEditOptions(model: TabItem): OptionsPaneCategoryDescriptor[] function TabTitleInput({ tab }: { tab: TabItem }) { const { title } = tab.useState(); + const ref = useEditPaneInputAutoFocus(); - return tab.onChangeTitle(e.currentTarget.value)} />; + return tab.onChangeTitle(e.currentTarget.value)} />; } diff --git a/public/app/features/dashboard-scene/scene/layouts-shared/addNew.ts b/public/app/features/dashboard-scene/scene/layouts-shared/addNew.ts index 68827d0ab11..379e2f83f55 100644 --- a/public/app/features/dashboard-scene/scene/layouts-shared/addNew.ts +++ b/public/app/features/dashboard-scene/scene/layouts-shared/addNew.ts @@ -1,18 +1,22 @@ -import { SceneGridRow, SceneObject } from '@grafana/scenes'; +import { SceneGridRow } from '@grafana/scenes'; +import { NewObjectAddedToCanvasEvent } from '../../edit-pane/shared'; import { DefaultGridLayoutManager } from '../layout-default/DefaultGridLayoutManager'; import { RowItem } from '../layout-rows/RowItem'; import { RowsLayoutManager } from '../layout-rows/RowsLayoutManager'; import { TabItem } from '../layout-tabs/TabItem'; import { TabsLayoutManager } from '../layout-tabs/TabsLayoutManager'; +import { DashboardLayoutManager } from '../types/DashboardLayoutManager'; import { isLayoutParent } from '../types/LayoutParent'; -export function addNewTabTo(sceneObject: SceneObject): TabItem { - if (sceneObject instanceof TabsLayoutManager) { - return sceneObject.addNewTab(); +export function addNewTabTo(layout: DashboardLayoutManager): TabItem { + if (layout instanceof TabsLayoutManager) { + const tab = layout.addNewTab(); + layout.publishEvent(new NewObjectAddedToCanvasEvent(tab), true); + return tab; } - const layoutParent = sceneObject.parent!; + const layoutParent = layout.parent!; if (!isLayoutParent(layoutParent)) { throw new Error('Parent layout is not a LayoutParent'); } @@ -20,24 +24,31 @@ export function addNewTabTo(sceneObject: SceneObject): TabItem { const tabsLayout = TabsLayoutManager.createFromLayout(layoutParent.getLayout()); layoutParent.switchLayout(tabsLayout); - return tabsLayout.state.tabs[0]; + const tab = tabsLayout.state.tabs[0]; + layout.publishEvent(new NewObjectAddedToCanvasEvent(tab), true); + + return tab; } -export function addNewRowTo(sceneObject: SceneObject): RowItem | SceneGridRow { - if (sceneObject instanceof RowsLayoutManager) { - return sceneObject.addNewRow(); +export function addNewRowTo(layout: DashboardLayoutManager): RowItem | SceneGridRow { + if (layout instanceof RowsLayoutManager) { + const row = layout.addNewRow(); + layout.publishEvent(new NewObjectAddedToCanvasEvent(row), true); + return row; } - if (sceneObject instanceof DefaultGridLayoutManager) { - return sceneObject.addNewRow(); + if (layout instanceof DefaultGridLayoutManager) { + const row = layout.addNewRow(); + layout.publishEvent(new NewObjectAddedToCanvasEvent(row), true); + return row; } - if (sceneObject instanceof TabsLayoutManager) { - const currentTab = sceneObject.getCurrentTab(); + if (layout instanceof TabsLayoutManager) { + const currentTab = layout.getCurrentTab(); return addNewRowTo(currentTab.state.layout); } - const layoutParent = sceneObject.parent!; + const layoutParent = layout.parent!; if (!isLayoutParent(layoutParent)) { throw new Error('Parent layout is not a LayoutParent'); } @@ -45,5 +56,8 @@ export function addNewRowTo(sceneObject: SceneObject): RowItem | SceneGridRow { const rowsLayout = RowsLayoutManager.createFromLayout(layoutParent.getLayout()); layoutParent.switchLayout(rowsLayout); - return rowsLayout.state.rows[0]; + const row = rowsLayout.state.rows[0]; + layout.publishEvent(new NewObjectAddedToCanvasEvent(row), true); + + return row; } diff --git a/public/app/features/dashboard-scene/scene/layouts-shared/utils.ts b/public/app/features/dashboard-scene/scene/layouts-shared/utils.ts index 61a6d574877..9d4dbc3b2ce 100644 --- a/public/app/features/dashboard-scene/scene/layouts-shared/utils.ts +++ b/public/app/features/dashboard-scene/scene/layouts-shared/utils.ts @@ -1,3 +1,5 @@ +import { useEffect, useRef } from 'react'; + import { SceneObject } from '@grafana/scenes'; import { DashboardLayoutManager, isDashboardLayoutManager } from '../types/DashboardLayoutManager'; @@ -15,3 +17,20 @@ export function findParentLayout(sceneObject: SceneObject): DashboardLayoutManag return null; } + +export interface EditPaneInputAutoFocusProps { + noAutoFocus?: boolean; +} + +export function useEditPaneInputAutoFocus({ noAutoFocus }: EditPaneInputAutoFocusProps = {}) { + const ref = useRef(null); + + useEffect(() => { + if (ref.current && !noAutoFocus) { + // Need the setTimeout here for some reason + setTimeout(() => ref.current?.focus(), 200); + } + }, [noAutoFocus]); + + return ref; +} diff --git a/public/app/features/dashboard-scene/scene/setDashboardPanelContext.ts b/public/app/features/dashboard-scene/scene/setDashboardPanelContext.ts index 81b3cc012c6..119b5d0ba8d 100644 --- a/public/app/features/dashboard-scene/scene/setDashboardPanelContext.ts +++ b/public/app/features/dashboard-scene/scene/setDashboardPanelContext.ts @@ -12,10 +12,10 @@ import { DashboardScene } from './DashboardScene'; export function setDashboardPanelContext(vizPanel: VizPanel, context: PanelContext) { const dashboard = getDashboardSceneFor(vizPanel); - context.app = dashboard.state.isEditing ? CoreApp.PanelEditor : CoreApp.Dashboard; + context.app = dashboard.state.editPanel ? CoreApp.PanelEditor : CoreApp.Dashboard; dashboard.subscribeToState((state) => { - if (state.isEditing) { + if (state.editPanel) { context.app = CoreApp.PanelEditor; } else { context.app = CoreApp.Dashboard; diff --git a/public/app/features/dashboard-scene/utils/utils.ts b/public/app/features/dashboard-scene/utils/utils.ts index 05358cfd0d2..f22210dd3d2 100644 --- a/public/app/features/dashboard-scene/utils/utils.ts +++ b/public/app/features/dashboard-scene/utils/utils.ts @@ -19,6 +19,7 @@ import { LibraryPanelBehavior } from '../scene/LibraryPanelBehavior'; import { VizPanelLinks, VizPanelLinksMenu } from '../scene/PanelLinks'; import { panelMenuBehavior } from '../scene/PanelMenuBehavior'; import { DashboardGridItem } from '../scene/layout-default/DashboardGridItem'; +import { setDashboardPanelContext } from '../scene/setDashboardPanelContext'; import { DashboardLayoutManager, isDashboardLayoutManager } from '../scene/types/DashboardLayoutManager'; import { containsCloneKey, getLastKeyFromClone, getOriginalKey, isInCloneChain } from './clone'; @@ -323,6 +324,7 @@ export function getDefaultVizPanel(): VizPanel { titleItems: [new VizPanelLinks({ menu: new VizPanelLinksMenu({}) })], hoverHeaderOffset: 0, $behaviors: [], + extendPanelContext: setDashboardPanelContext, menu: new VizPanelMenu({ $behaviors: [panelMenuBehavior], }),