diff --git a/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataPane.tsx b/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataPane.tsx index 9d44e50e689..66a5c9816f5 100644 --- a/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataPane.tsx +++ b/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataPane.tsx @@ -144,7 +144,7 @@ function PanelDataPaneRendered({ model }: SceneComponentProps) { const currentTab = tabs.find((t) => t.tabId === tab); return ( - <> +
{tabs.map((t, index) => { return ( @@ -161,12 +161,19 @@ function PanelDataPaneRendered({ model }: SceneComponentProps) { {currentTab && } - +
); } function getStyles(theme: GrafanaTheme2) { return { + dataPane: css({ + display: 'flex', + flexDirection: 'column', + flexGrow: 1, + minHeight: 0, + height: '100%', + }), tabContent: css({ padding: theme.spacing(2), border: `1px solid ${theme.colors.border.weak}`, diff --git a/public/app/features/dashboard-scene/panel-edit/PanelEditor.test.ts b/public/app/features/dashboard-scene/panel-edit/PanelEditor.test.ts index 874d24c2257..d2f1261a4e6 100644 --- a/public/app/features/dashboard-scene/panel-edit/PanelEditor.test.ts +++ b/public/app/features/dashboard-scene/panel-edit/PanelEditor.test.ts @@ -1,10 +1,9 @@ import { PanelPlugin, PanelPluginMeta, PluginType } from '@grafana/data'; -import { SceneFlexItem, SceneGridItem, SceneGridLayout, SplitLayout, VizPanel } from '@grafana/scenes'; +import { SceneGridItem, SceneGridLayout, VizPanel } from '@grafana/scenes'; import { DashboardScene } from '../scene/DashboardScene'; import { activateFullSceneTree } from '../utils/test-utils'; -import { PanelDataPane } from './PanelDataPane/PanelDataPane'; import { buildPanelEditScene } from './PanelEditor'; let pluginToLoad: PanelPlugin | undefined; @@ -47,8 +46,7 @@ describe('PanelEditor', () => { const deactivate = activateFullSceneTree(scene); - const vizManager = editScene.state.panelRef.resolve(); - vizManager.state.panel.setState({ title: 'changed title' }); + editScene.state.vizManager.state.panel.setState({ title: 'changed title' }); deactivate(); @@ -72,7 +70,7 @@ describe('PanelEditor', () => { activateFullSceneTree(scene); - expect(((editScene.state.body as SplitLayout).state.primary as SplitLayout).state.secondary).toBeUndefined(); + expect(editScene.state.dataPane).toBeUndefined(); }); it('should exist if panel is supporting querying', () => { @@ -88,10 +86,7 @@ describe('PanelEditor', () => { }); activateFullSceneTree(scene); - const secondaryPane = ((editScene.state.body as SplitLayout).state.primary as SplitLayout).state.secondary; - - expect(secondaryPane).toBeInstanceOf(SceneFlexItem); - expect((secondaryPane as SceneFlexItem).state.body).toBeInstanceOf(PanelDataPane); + expect(editScene.state.dataPane).toBeDefined(); }); }); }); diff --git a/public/app/features/dashboard-scene/panel-edit/PanelEditor.tsx b/public/app/features/dashboard-scene/panel-edit/PanelEditor.tsx index e7e22365769..e88c5c3f28f 100644 --- a/public/app/features/dashboard-scene/panel-edit/PanelEditor.tsx +++ b/public/app/features/dashboard-scene/panel-edit/PanelEditor.tsx @@ -2,17 +2,7 @@ import * as H from 'history'; import { NavIndex } from '@grafana/data'; import { config, locationService } from '@grafana/runtime'; -import { - SceneFlexItem, - SceneFlexLayout, - SceneGridItem, - SceneObject, - SceneObjectBase, - SceneObjectRef, - SceneObjectState, - SplitLayout, - VizPanel, -} from '@grafana/scenes'; +import { SceneGridItem, SceneObject, SceneObjectBase, SceneObjectState, VizPanel } from '@grafana/scenes'; import { findVizPanelByKey, @@ -27,11 +17,12 @@ import { PanelOptionsPane } from './PanelOptionsPane'; import { VizPanelManager } from './VizPanelManager'; export interface PanelEditorState extends SceneObjectState { - body: SceneObject; controls?: SceneObject[]; isDirty?: boolean; panelId: number; - panelRef: SceneObjectRef; + optionsPane?: PanelOptionsPane; + dataPane?: PanelDataPane; + vizManager: VizPanelManager; } export class PanelEditor extends SceneObjectBase { @@ -42,14 +33,41 @@ export class PanelEditor extends SceneObjectBase { public constructor(state: PanelEditorState) { super(state); - this.addActivationHandler(() => { - return () => { - if (!this._discardChanges) { - this.commitChanges(); - } - }; - }); + this.addActivationHandler(this._activationHandler.bind(this)); } + + private _activationHandler() { + const panelManager = this.state.vizManager; + const panel = panelManager.state.panel; + + this._subs.add( + panelManager.subscribeToState((n, p) => { + if (n.panel.state.pluginId !== p.panel.state.pluginId) { + this._initDataPane(n.panel.state.pluginId); + } + }) + ); + + this._initDataPane(panel.state.pluginId); + + return () => { + if (!this._discardChanges) { + this.commitChanges(); + } + }; + } + + private _initDataPane(pluginId: string) { + const skipDataQuery = config.panels[pluginId].skipDataQuery; + + if (!skipDataQuery && !this.state.dataPane) { + this.setState({ dataPane: new PanelDataPane(this.state.vizManager) }); + } else if (this.state.dataPane) { + locationService.partial({ tab: null }, true); + this.setState({ dataPane: undefined }); + } + } + public getUrlKey() { return this.state.panelId.toString(); } @@ -76,10 +94,20 @@ export class PanelEditor extends SceneObjectBase { dashboard.onEnterEditMode(); } - const panelMngr = this.state.panelRef.resolve(); - if (sourcePanel!.parent instanceof SceneGridItem) { - sourcePanel!.parent.setState({ body: panelMngr.state.panel.clone() }); + sourcePanel!.parent.setState({ body: this.state.vizManager.state.panel.clone() }); + } + } + + public toggleOptionsPane(withOpenVizPicker?: boolean) { + if (this.state.optionsPane) { + this.setState({ optionsPane: undefined }); + } else { + this.setState({ + optionsPane: new PanelOptionsPane({ + isVizPickerOpen: withOpenVizPicker, + }), + }); } } } @@ -90,78 +118,7 @@ export function buildPanelEditScene(panel: VizPanel): PanelEditor { return new PanelEditor({ panelId: getPanelIdForVizPanel(panel), - panelRef: vizPanelMgr.getRef(), - body: new SplitLayout({ - direction: 'row', - initialSize: 0.75, - primary: new SplitLayout({ - direction: 'column', - $behaviors: [conditionalDataPaneBehavior], - primary: new SceneFlexLayout({ - direction: 'column', - minHeight: 200, - children: [vizPanelMgr], - }), - primaryPaneStyles: { - minHeight: 0, - overflow: 'hidden', - }, - secondaryPaneStyles: { - minHeight: 0, - }, - }), - secondary: new SceneFlexItem({ - body: new PanelOptionsPane(vizPanelMgr), - width: '100%', - }), - primaryPaneStyles: { - minWidth: '0', - }, - secondaryPaneStyles: { - minWidth: '0', - }, - }), + optionsPane: new PanelOptionsPane({}), + vizManager: vizPanelMgr, }); } - -// This function is used to conditionally add the data pane to the panel editor, -// depending on the type of a panel being edited. -function conditionalDataPaneBehavior(scene: SplitLayout) { - const dashboard = getDashboardSceneFor(scene); - - const editor = dashboard.state.editPanel; - - if (!editor) { - return; - } - - const panelManager = editor.state.panelRef.resolve(); - const panel = panelManager.state.panel; - - const getDataPane = () => - new SceneFlexItem({ - body: new PanelDataPane(panelManager), - }); - - if (!config.panels[panel.state.pluginId].skipDataQuery) { - scene.setState({ - secondary: getDataPane(), - }); - } - - const sub = panelManager.subscribeToState((n, p) => { - const hadDataSupport = !config.panels[p.panel.state.pluginId].skipDataQuery; - const willHaveDataSupport = !config.panels[n.panel.state.pluginId].skipDataQuery; - - if (hadDataSupport && !willHaveDataSupport) { - locationService.partial({ tab: null }, true); - scene.setState({ secondary: undefined }); - } else if (!hadDataSupport && willHaveDataSupport) { - scene.setState({ secondary: getDataPane() }); - } - }); - - return () => { - sub.unsubscribe(); - }; -} diff --git a/public/app/features/dashboard-scene/panel-edit/PanelEditorRenderer.tsx b/public/app/features/dashboard-scene/panel-edit/PanelEditorRenderer.tsx index f3ab6279ef2..8316b8b2ac9 100644 --- a/public/app/features/dashboard-scene/panel-edit/PanelEditorRenderer.tsx +++ b/public/app/features/dashboard-scene/panel-edit/PanelEditorRenderer.tsx @@ -3,16 +3,17 @@ import React from 'react'; import { GrafanaTheme2 } from '@grafana/data'; import { SceneComponentProps } from '@grafana/scenes'; -import { useStyles2 } from '@grafana/ui'; +import { Splitter, useStyles2 } from '@grafana/ui'; import { NavToolbarActions } from '../scene/NavToolbarActions'; import { getDashboardSceneFor } from '../utils/utils'; import { PanelEditor } from './PanelEditor'; +import { VisualizationButton } from './PanelOptionsPane'; export function PanelEditorRenderer({ model }: SceneComponentProps) { const dashboard = getDashboardSceneFor(model); - const { body } = model.useState(); + const { optionsPane, vizManager, dataPane } = model.useState(); const { controls } = dashboard.useState(); const styles = useStyles2(getStyles); @@ -25,10 +26,34 @@ export function PanelEditorRenderer({ model }: SceneComponentProps) {controls.map((control) => ( ))} + {!optionsPane && ( + model.toggleOptionsPane(true)} + isOpen={false} + onTogglePane={() => model.toggleOptionsPane()} + /> + )} )}
- + + + + {dataPane && } + + {optionsPane && } +
diff --git a/public/app/features/dashboard-scene/panel-edit/PanelOptionsPane.tsx b/public/app/features/dashboard-scene/panel-edit/PanelOptionsPane.tsx index b73fd69f04b..a1da35bb3c4 100644 --- a/public/app/features/dashboard-scene/panel-edit/PanelOptionsPane.tsx +++ b/public/app/features/dashboard-scene/panel-edit/PanelOptionsPane.tsx @@ -11,8 +11,8 @@ import { getPanelFrameCategory2 } from 'app/features/dashboard/components/PanelE import { getVisualizationOptions2 } from 'app/features/dashboard/components/PanelEditor/getVisualizationOptions'; import { getAllPanelPluginMeta } from 'app/features/panel/state/util'; +import { PanelEditor } from './PanelEditor'; import { PanelVizTypePicker } from './PanelVizTypePicker'; -import { VizPanelManager } from './VizPanelManager'; export interface PanelOptionsPaneState extends SceneObjectState { isVizPickerOpen?: boolean; @@ -21,15 +21,16 @@ export interface PanelOptionsPaneState extends SceneObjectState { } export class PanelOptionsPane extends SceneObjectBase { - public panelManager: VizPanelManager; - - public constructor(panelMgr: VizPanelManager) { + public constructor(state: Partial) { super({ searchQuery: '', listMode: OptionFilter.All, + ...state, }); + } - this.panelManager = panelMgr; + public getVizManager() { + return sceneGraph.getAncestor(this, PanelEditor).state.vizManager; } onToggleVizPicker = () => { @@ -44,11 +45,14 @@ export class PanelOptionsPane extends SceneObjectBase { this.setState({ listMode }); }; - onCollapsePane = () => {}; + onCollapsePane = () => { + const editor = sceneGraph.getAncestor(this, PanelEditor); + editor.toggleOptionsPane(); + }; static Component = ({ model }: SceneComponentProps) => { const { isVizPickerOpen, searchQuery, listMode } = model.useState(); - const { panelManager } = model; + const panelManager = model.getVizManager(); const { panel } = panelManager.state; const dataObject = sceneGraph.getData(panel); const { data } = dataObject.useState(); diff --git a/public/app/features/dashboard-scene/panel-edit/VizPanelManager.test.tsx b/public/app/features/dashboard-scene/panel-edit/VizPanelManager.test.tsx index c9eab250ced..1048d4a0d0e 100644 --- a/public/app/features/dashboard-scene/panel-edit/VizPanelManager.test.tsx +++ b/public/app/features/dashboard-scene/panel-edit/VizPanelManager.test.tsx @@ -506,11 +506,9 @@ describe('VizPanelManager', () => { describe('change transformations', () => { it('should update and reprocess transformations', () => { const { scene, panel } = setupTest('panel-3'); - scene.setState({ - editPanel: buildPanelEditScene(panel), - }); + scene.setState({ editPanel: buildPanelEditScene(panel) }); - const vizPanelManager = scene.state.editPanel!.state.panelRef.resolve(); + const vizPanelManager = scene.state.editPanel!.state.vizManager; vizPanelManager.activate(); vizPanelManager.state.panel.state.$data?.activate(); @@ -560,11 +558,9 @@ describe('VizPanelManager', () => { describe('dashboard queries', () => { it('should update queries', () => { const { scene, panel } = setupTest('panel-3'); - scene.setState({ - editPanel: buildPanelEditScene(panel), - }); + scene.setState({ editPanel: buildPanelEditScene(panel) }); - const vizPanelManager = scene.state.editPanel!.state.panelRef.resolve(); + const vizPanelManager = scene.state.editPanel!.state.vizManager; vizPanelManager.activate(); vizPanelManager.state.panel.state.$data?.activate(); @@ -578,6 +574,7 @@ describe('VizPanelManager', () => { panelId: panelWithTransformations.id, }, ]); + expect(vizPanelManager.panelData).toBeInstanceOf(SceneDataTransformer); expect(vizPanelManager.queryRunner.state.queries[0].panelId).toEqual(panelWithTransformations.id); diff --git a/public/app/features/dashboard-scene/saving/getSaveDashboardChange.test.ts b/public/app/features/dashboard-scene/saving/getSaveDashboardChange.test.ts index 3c4b5577ed4..553d2bc0683 100644 --- a/public/app/features/dashboard-scene/saving/getSaveDashboardChange.test.ts +++ b/public/app/features/dashboard-scene/saving/getSaveDashboardChange.test.ts @@ -71,8 +71,7 @@ describe('getSaveDashboardChange', () => { dashboard.onEnterEditMode(); dashboard.setState({ editPanel: editScene }); - const vizManager = editScene.state.panelRef.resolve(); - vizManager.state.panel.setState({ title: 'changed title' }); + editScene.state.vizManager.state.panel.setState({ title: 'changed title' }); const result = getSaveDashboardChange(dashboard, false, true); const panelSaveModel = result.changedSaveModel.panels![0]; diff --git a/public/app/features/dashboard-scene/scene/DashboardSceneUrlSync.ts b/public/app/features/dashboard-scene/scene/DashboardSceneUrlSync.ts index 406a844abe5..5b922de6bc3 100644 --- a/public/app/features/dashboard-scene/scene/DashboardSceneUrlSync.ts +++ b/public/app/features/dashboard-scene/scene/DashboardSceneUrlSync.ts @@ -147,7 +147,7 @@ class ResolveInspectPanelByKey extends SceneObjectBase