Dashboard: Only auto focus new objects (#102931)

* Dashboard: Only auto focus new objects

* fix outline issue

* Also set isNew when adding row below/above and tab before/after

---------

Co-authored-by: oscarkilhed <oscar.kilhed@grafana.com>
This commit is contained in:
Torkel Ödegaard 2025-03-27 16:16:30 +01:00 committed by GitHub
parent d3220fc44f
commit d7be68ab3c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 30 additions and 19 deletions

View File

@ -210,7 +210,7 @@ export function DashboardEditPaneRenderer({ editPane, isCollapsed, onToggleColla
const selectedObject = selection?.getFirstObject();
const [outlineCollapsed, setOutlineCollapsed] = useLocalStorage(
'grafana.dashboard.edit-pane.outline.collapsed',
false
true
);
const [outlinePaneSize = 0.4, setOutlinePaneSize] = useLocalStorage('grafana.dashboard.edit-pane.outline.size', 0.4);

View File

@ -88,7 +88,7 @@ function getStyles(theme: GrafanaTheme2) {
boxShadow: 'none',
border: 'none',
background: 'transparent',
padding: theme.spacing(0.25, 1),
padding: theme.spacing(0.25, 1, 0.25, 0),
borderRadius: theme.shape.radius.default,
color: theme.colors.text.secondary,
display: 'flex',

View File

@ -3,6 +3,7 @@ import { selectors } from '@grafana/e2e-selectors';
import { config } from '@grafana/runtime';
import { SceneTimeRangeLike, VizPanel } from '@grafana/scenes';
import { DataLinksInlineEditor, Input, TextArea, Switch } from '@grafana/ui';
import { t } from 'app/core/internationalization';
import { GenAIPanelDescriptionButton } from 'app/features/dashboard/components/GenAI/GenAIPanelDescriptionButton';
import { GenAIPanelTitleButton } from 'app/features/dashboard/components/GenAI/GenAIPanelTitleButton';
import { OptionsPaneCategoryDescriptor } from 'app/features/dashboard/components/PanelEditor/OptionsPaneCategoryDescriptor';
@ -110,7 +111,12 @@ function ScenePanelLinksEditor({ panelLinks }: ScenePanelLinksEditorProps) {
export function PanelFrameTitleInput({ panel }: { panel: VizPanel }) {
const { title } = panel.useState();
let ref = useEditPaneInputAutoFocus({ noAutoFocus: panel.getPanelContext().app === CoreApp.PanelEditor });
const notInPanelEdit = panel.getPanelContext().app !== CoreApp.PanelEditor;
const newPanelTitle = t('dashboard.new-panel-title', 'New panel');
let ref = useEditPaneInputAutoFocus({
autoFocus: notInPanelEdit && title === newPanelTitle,
});
return (
<Input

View File

@ -34,6 +34,7 @@ export interface RowItemState extends SceneObjectState {
fillScreen?: boolean;
isDropTarget?: boolean;
conditionalRendering?: ConditionalRendering;
isNew?: boolean;
}
export class RowItem
@ -161,7 +162,7 @@ export class RowItem
}
public onChangeTitle(title: string) {
this.setState({ title });
this.setState({ title, isNew: false });
}
public onHeaderHiddenToggle(hideHeader = !this.state.hideHeader) {

View File

@ -79,8 +79,8 @@ export function getEditOptions(model: RowItem): OptionsPaneCategoryDescriptor[]
}
function RowTitleInput({ row }: { row: RowItem }) {
const { title } = row.useState();
const ref = useEditPaneInputAutoFocus();
const { title, isNew } = row.useState();
const ref = useEditPaneInputAutoFocus({ autoFocus: isNew });
return (
<Input

View File

@ -87,7 +87,7 @@ export class RowsLayoutManager extends SceneObjectBase<RowsLayoutManagerState> i
}
public addNewRow(): RowItem {
const row = new RowItem();
const row = new RowItem({ isNew: true });
this.setState({ rows: [...this.state.rows, row] });
this.publishEvent(new NewObjectAddedToCanvasEvent(row), true);
return row;
@ -115,7 +115,7 @@ export class RowsLayoutManager extends SceneObjectBase<RowsLayoutManagerState> i
public addRowAbove(row: RowItem): RowItem {
const index = this.state.rows.indexOf(row);
const newRow = new RowItem();
const newRow = new RowItem({ isNew: true });
const newRows = [...this.state.rows];
newRows.splice(index, 0, newRow);
@ -135,7 +135,7 @@ export class RowsLayoutManager extends SceneObjectBase<RowsLayoutManagerState> i
index = index + 1;
}
const newRow = new RowItem();
const newRow = new RowItem({ isNew: true });
const newRows = [...this.state.rows];
newRows.splice(index + 1, 0, newRow);

View File

@ -27,6 +27,10 @@ import { TabsLayoutManager } from './TabsLayoutManager';
export interface TabItemState extends SceneObjectState {
layout: DashboardLayoutManager;
title?: string;
/**
* Used to auto focus the title input
*/
isNew?: boolean;
isDropTarget?: boolean;
}
@ -123,7 +127,7 @@ export class TabItem
}
public onChangeTitle(title: string) {
this.setState({ title });
this.setState({ title, isNew: false });
}
public setIsDropTarget(isDropTarget: boolean) {

View File

@ -30,8 +30,8 @@ export function getEditOptions(model: TabItem): OptionsPaneCategoryDescriptor[]
}
function TabTitleInput({ tab }: { tab: TabItem }) {
const { title } = tab.useState();
const ref = useEditPaneInputAutoFocus();
const { title, isNew } = tab.useState();
const ref = useEditPaneInputAutoFocus({ autoFocus: isNew });
return <Input ref={ref} value={title} onChange={(e) => tab.onChangeTitle(e.currentTarget.value)} />;
}

View File

@ -113,7 +113,7 @@ export class TabsLayoutManager extends SceneObjectBase<TabsLayoutManagerState> i
}
public addNewTab() {
const newTab = new TabItem();
const newTab = new TabItem({ isNew: true });
this.setState({ tabs: [...this.state.tabs, newTab], currentTabIndex: this.state.tabs.length });
this.publishEvent(new NewObjectAddedToCanvasEvent(newTab), true);
return newTab;
@ -150,7 +150,7 @@ export class TabsLayoutManager extends SceneObjectBase<TabsLayoutManagerState> i
}
public addTabBefore(tab: TabItem): TabItem {
const newTab = new TabItem();
const newTab = new TabItem({ isNew: true });
const tabs = this.state.tabs.slice();
tabs.splice(tabs.indexOf(tab), 0, newTab);
this.setState({ tabs, currentTabIndex: this.state.currentTabIndex });
@ -160,7 +160,7 @@ export class TabsLayoutManager extends SceneObjectBase<TabsLayoutManagerState> i
}
public addTabAfter(tab: TabItem): TabItem {
const newTab = new TabItem();
const newTab = new TabItem({ isNew: true });
const tabs = this.state.tabs.slice();
tabs.splice(tabs.indexOf(tab) + 1, 0, newTab);
this.setState({ tabs, currentTabIndex: this.state.currentTabIndex + 1 });

View File

@ -19,18 +19,18 @@ export function findParentLayout(sceneObject: SceneObject): DashboardLayoutManag
}
export interface EditPaneInputAutoFocusProps {
noAutoFocus?: boolean;
autoFocus?: boolean;
}
export function useEditPaneInputAutoFocus({ noAutoFocus }: EditPaneInputAutoFocusProps = {}) {
export function useEditPaneInputAutoFocus({ autoFocus }: EditPaneInputAutoFocusProps = {}) {
const ref = useRef<HTMLInputElement>(null);
useEffect(() => {
if (ref.current && !noAutoFocus) {
if (ref.current && autoFocus) {
// Need the setTimeout here for some reason
setTimeout(() => ref.current?.focus(), 200);
}
}, [noAutoFocus]);
}, [autoFocus]);
return ref;
}