Repository: grafana/grafana
Pull request #101668: Dashboard: Auto focus title fields in edit pane
Author: login: torkelo
Labels: name: area/frontend
name: no-changelog
description: Skip including change in changelog/release notes
Created: 2025-03-06T06:15:15Z | Merged: 2025-03-10T07:03:55Z



* Auto selects title fields for Tabs, Rows and Panels 

Was a lot trickier to get this to work than I thought it would be, autoFocus did not work, and I needed a pretty large setTimeout 

The only downside with this approach is that if you switch tabs (move between add and back to configure) it will also auto focus but I think that is ok, the implementation would be a lot more complex in order to solve that,. 


Diff:
diff --git a/public/app/features/dashboard-scene/edit-pane/DashboardEditPane.tsx b/public/app/features/dashboard-scene/edit-pane/DashboardEditPane.tsx
index 5a397991f7430..e9c5c31e85b5a 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<DashboardEditPaneState> {
         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<DashboardEditPaneState> {
     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<HTMLDivElement>(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 && (
           <Resizable className={cx(styles.fixed, styles.container)} defaultSize={{ height: '100%', width: '20vw' }}>
-            <ElementEditPane element={editableElement} key={typeId} />
+            <ElementEditPane element={editableElement} key={selectedObject?.state.key} />
           </Resizable>
         )}
       </>
@@ -224,7 +237,7 @@ export function DashboardEditPaneRenderer({ editPane, isCollapsed, onToggleColla
       </TabsBar>
       <div className={styles.tabContent}>
         {tab === 'add' && <DashboardAddPane editPane={editPane} />}
-        {tab === 'configure' && <ElementEditPane element={editableElement} key={typeId} />}
+        {tab === 'configure' && <ElementEditPane element={editableElement} key={selectedObject?.state.key} />}
         {tab === 'outline' && <DashboardOutline editPane={editPane} />}
       </div>
     </div>
diff --git a/public/app/features/dashboard-scene/edit-pane/shared.ts b/public/app/features/dashboard-scene/edit-pane/shared.ts
index 72c44d5dc2b12..7f284ef945c1e 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<SceneObject> {
+  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 8e0ac6a5e4b0d..d0c57d18d2b70 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 (
     <Input
+      ref={ref}
       data-testid={selectors.components.PanelEditor.OptionsPane.fieldInput('Title')}
       value={title}
       onChange={(e) => 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 4a02c6d8d2679..3a9ecd5cee24b 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 8e30df9dde0e3..454eb0a25e2ee 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<DashboardSceneState> 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<DashboardSceneState> 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 0688e0d0c9254..992a6f9102da9 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 c5f2d0dd09a8c..f610ceac6755e 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 62e1a7c494981..498d16685f0cb 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 (
     <Input
+      ref={ref}
       title={t('dashboard.rows-layout.row-options.title-option', 'Title')}
       value={title}
       onChange={(e) => 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 3ef34cbbc0fa8..ceccc16555727 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<RowsLayoutManagerState> 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<RowsLayoutManagerState> 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 2769620060edd..da057db471082 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 <Input value={title} onChange={(e) => tab.onChangeTitle(e.currentTarget.value)} />;
+  return <Input ref={ref} value={title} onChange={(e) => 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 68827d0ab1134..379e2f83f5583 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 61a6d574877d6..9d4dbc3b2ce66 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<HTMLInputElement>(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 81b3cc012c630..119b5d0ba8d34 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 05358cfd0d23e..f22210dd3d294 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],
     }),


Reviews:
user: login: harisrozajac
body: Looks good. Are we not supporting the same behavior when adding a tab from the tab bar? 
![Screenshot 2025-03-06 at 1 09 14 PM](https://github.com/user-attachments/assets/0950ab9a-f74d-4d1c-ad45-57e2848077fb)

Maybe this is more of an ad-hoc adding without going to edit mode for each element?


I see some playwright e2e tests failing even after restarting the build. Maybe it needs the most recent main. 
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-06T20:11:05Z
user: login: Sergej-Vlasov
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-07T16:54:46Z
user: login: torkelo
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-08T06:48:50Z

Review comments:
diff_hunk: @@ -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 });
path: public/app/features/dashboard-scene/panel-edit/getPanelFrameOptions.tsx
user: login: Sergej-Vlasov
body: I don't get title focus when selecting panels right now. Might need additional prop to enable focus just for `VizPanelEditableElement.tsx`
created_at: 2025-03-07T16:54:46Z
author_association: CONTRIBUTOR
diff_hunk: @@ -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 });
path: public/app/features/dashboard-scene/panel-edit/getPanelFrameOptions.tsx
user: login: torkelo
body: @Sergej-Vlasov good catch ! the logic for setting PanelContext app was completely wrong, was recently changed here: https://github.com/grafana/grafana/pull/101567/files#diff-0c8546b2d9a6a8d3afdf3eae92daa803ca981a7a410f0ae0672c48ad71f0c761R15 
created_at: 2025-03-08T06:48:50Z
author_association: CONTRIBUTOR

PR comments:
user: login: torkelo
created_at: 2025-03-07T10:38:30Z
body: @harisrozajac good catch, pushed an update so that it works for addPanel everywhere (and addRow) 
author_association: CONTRIBUTOR
user: login: ghost
created_at: 2025-03-07T16:06:19Z
body: ## [Codecov](https://codecov.grafana-dev.net/gh/grafana/grafana/pull/101668?dropdown=coverage&src=pr&el=h1) Report
All modified and coverable lines are covered by tests :white_check_mark:

| [Flag](https://codecov.grafana-dev.net/gh/grafana/grafana/pull/101668/flags?src=pr&el=flags) | Coverage Δ | |
|---|---|---|
| [be-unit](https://codecov.grafana-dev.net/gh/grafana/grafana/pull/101668/flags?src=pr&el=flag) | `40.73% <ø> (?)` | |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=grafana#carryforward-flags-in-the-pull-request-comment) to find out more.

[see 2347 files with indirect coverage changes](https://codecov.grafana-dev.net/gh/grafana/grafana/pull/101668/indirect-changes?src=pr&el=tree-more)

author_association: NONE