diff --git a/public/app/features/dashboard-scene/edit-pane/DashboardEditableElement.tsx b/public/app/features/dashboard-scene/edit-pane/DashboardEditableElement.tsx index ae3085bc055..e324b09075c 100644 --- a/public/app/features/dashboard-scene/edit-pane/DashboardEditableElement.tsx +++ b/public/app/features/dashboard-scene/edit-pane/DashboardEditableElement.tsx @@ -25,7 +25,7 @@ export class DashboardEditableElement implements EditableDashboardElement { const { body } = dashboard.useState(); const dashboardOptions = useMemo(() => { - return new OptionsPaneCategoryDescriptor({ + const editPaneHeaderOptions = new OptionsPaneCategoryDescriptor({ title: t('dashboard.options.title', 'Dashboard options'), id: 'dashboard-options', isOpenable: false, @@ -48,6 +48,14 @@ export class DashboardEditableElement implements EditableDashboardElement { render: () => , }) ); + + if (body.getOptions) { + for (const option of body.getOptions()) { + editPaneHeaderOptions.addItem(option); + } + } + + return editPaneHeaderOptions; }, [body, dashboard]); return [dashboardOptions]; diff --git a/public/app/features/dashboard-scene/edit-pane/MultiSelectedVizPanelsEditableElement.tsx b/public/app/features/dashboard-scene/edit-pane/MultiSelectedVizPanelsEditableElement.tsx index 3e6a7fa98ae..a14b10f485b 100644 --- a/public/app/features/dashboard-scene/edit-pane/MultiSelectedVizPanelsEditableElement.tsx +++ b/public/app/features/dashboard-scene/edit-pane/MultiSelectedVizPanelsEditableElement.tsx @@ -29,7 +29,7 @@ export class MultiSelectedVizPanelsEditableElement implements MultiSelectedEdita isOpenable: false, renderTitle: () => ( this.onDelete()} diff --git a/public/app/features/dashboard-scene/scene/layout-rows/RowItem.tsx b/public/app/features/dashboard-scene/scene/layout-rows/RowItem.tsx index e92ed0f5466..71099c6fa02 100644 --- a/public/app/features/dashboard-scene/scene/layout-rows/RowItem.tsx +++ b/public/app/features/dashboard-scene/scene/layout-rows/RowItem.tsx @@ -1,16 +1,15 @@ -import { ReactNode } from 'react'; - import { SceneObjectState, SceneObjectBase, sceneGraph, VariableDependencyConfig, SceneObject } from '@grafana/scenes'; import { t } from 'app/core/internationalization'; import { OptionsPaneCategoryDescriptor } from 'app/features/dashboard/components/PanelEditor/OptionsPaneCategoryDescriptor'; +import { getDefaultVizPanel } from '../../utils/utils'; import { ResponsiveGridLayoutManager } from '../layout-responsive-grid/ResponsiveGridLayoutManager'; import { BulkActionElement } from '../types/BulkActionElement'; import { DashboardLayoutManager } from '../types/DashboardLayoutManager'; import { EditableDashboardElement, EditableDashboardElementInfo } from '../types/EditableDashboardElement'; import { LayoutParent } from '../types/LayoutParent'; -import { getEditOptions, renderActions } from './RowItemEditor'; +import { getEditOptions } from './RowItemEditor'; import { RowItemRenderer } from './RowItemRenderer'; import { RowItemRepeaterBehavior } from './RowItemRepeaterBehavior'; import { RowItems } from './RowItems'; @@ -60,19 +59,42 @@ export class RowItem return getEditOptions(this); } - public renderActions(): ReactNode { - return renderActions(this); - } - public onDelete() { - const layout = sceneGraph.getAncestor(this, RowsLayoutManager); - layout.removeRow(this); + this._getParentLayout().removeRow(this); } public createMultiSelectedElement(items: SceneObject[]): RowItems { return new RowItems(items.filter((item) => item instanceof RowItem)); } + public onAddPanel(panel = getDefaultVizPanel()) { + this.getLayout().addPanel(panel); + } + + public onAddRowAbove() { + this._getParentLayout().addRowAbove(this); + } + + public onAddRowBelow() { + this._getParentLayout().addRowBelow(this); + } + + public onMoveUp() { + this._getParentLayout().moveRowUp(this); + } + + public onMoveDown() { + this._getParentLayout().moveRowDown(this); + } + + public isFirstRow(): boolean { + return this._getParentLayout().isFirstRow(this); + } + + public isLastRow(): boolean { + return this._getParentLayout().isLastRow(this); + } + public getRepeatVariable(): string | undefined { return this._getRepeatBehavior()?.state.variableName; } @@ -110,6 +132,10 @@ export class RowItem this.setState({ isCollapsed: !this.state.isCollapsed }); } + private _getParentLayout(): RowsLayoutManager { + return sceneGraph.getAncestor(this, RowsLayoutManager); + } + private _getRepeatBehavior(): RowItemRepeaterBehavior | undefined { return this.state.$behaviors?.find((b) => b instanceof RowItemRepeaterBehavior); } 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 e45f9fa99ba..3ff3181129d 100644 --- a/public/app/features/dashboard-scene/scene/layout-rows/RowItemEditor.tsx +++ b/public/app/features/dashboard-scene/scene/layout-rows/RowItemEditor.tsx @@ -1,8 +1,7 @@ import { useMemo } from 'react'; -import { SelectableValue } from '@grafana/data'; import { selectors } from '@grafana/e2e-selectors'; -import { Alert, Button, Input, RadioButtonGroup, Switch, TextLink } from '@grafana/ui'; +import { Alert, Input, Switch, TextLink } 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'; @@ -10,18 +9,25 @@ import { RepeatRowSelect2 } from 'app/features/dashboard/components/RepeatRowSel import { SHARED_DASHBOARD_QUERY } from 'app/plugins/datasource/dashboard/constants'; import { MIXED_DATASOURCE_NAME } from 'app/plugins/datasource/mixed/MixedDataSource'; +import { EditPaneHeader } from '../../edit-pane/EditPaneHeader'; import { getDashboardSceneFor, getQueryRunnerFor } from '../../utils/utils'; import { DashboardScene } from '../DashboardScene'; -import { useLayoutCategory } from '../layouts-shared/DashboardLayoutSelector'; +import { DashboardLayoutSelector } from '../layouts-shared/DashboardLayoutSelector'; import { RowItem } from './RowItem'; export function getEditOptions(model: RowItem): OptionsPaneCategoryDescriptor[] { + const { layout } = model.useState(); const rowOptions = useMemo(() => { - return new OptionsPaneCategoryDescriptor({ - title: t('dashboard.rows-layout.row-options.title', 'Row options'), + const dashboard = getDashboardSceneFor(model); + + const editPaneHeaderOptions = new OptionsPaneCategoryDescriptor({ + title: t('dashboard.rows-layout.row-options.title', 'Row'), id: 'row-options', - isOpenDefault: true, + isOpenable: false, + renderTitle: () => ( + model.onDelete()} /> + ), }) .addItem( new OptionsPaneItemDescriptor({ @@ -31,8 +37,22 @@ export function getEditOptions(model: RowItem): OptionsPaneCategoryDescriptor[] ) .addItem( new OptionsPaneItemDescriptor({ - title: t('dashboard.rows-layout.row-options.height.title', 'Height'), - render: () => , + title: t('dashboard.layout.common.layout', 'Layout'), + render: () => , + }) + ); + + if (layout.getOptions) { + for (const option of layout.getOptions()) { + editPaneHeaderOptions.addItem(option); + } + } + + editPaneHeaderOptions + .addItem( + new OptionsPaneItemDescriptor({ + title: t('dashboard.rows-layout.row-options.repeat.variable.title', 'Repeat for'), + render: () => , }) ) .addItem( @@ -41,42 +61,23 @@ export function getEditOptions(model: RowItem): OptionsPaneCategoryDescriptor[] render: () => , }) ); - }, [model]); - const rowRepeatOptions = useMemo(() => { - const dashboard = getDashboardSceneFor(model); + return editPaneHeaderOptions; + }, [layout, model]); - return new OptionsPaneCategoryDescriptor({ - title: t('dashboard.rows-layout.row-options.repeat.title', 'Repeat options'), - id: 'row-repeat-options', - isOpenDefault: true, - }).addItem( - new OptionsPaneItemDescriptor({ - title: t('dashboard.rows-layout.row-options.repeat.variable.title', 'Variable'), - render: () => , - }) - ); - }, [model]); - - const { layout } = model.useState(); - const layoutOptions = useLayoutCategory(layout); - - return [rowOptions, rowRepeatOptions, layoutOptions]; -} - -export function renderActions(model: RowItem) { - return ( - <> - - {!isClone && isEditing && ( -