DashboardScene: Get rid of panel edit route (#80605)
* Wip: get rod of panel edit route * Cleanup unused code * Test update * Simplify url sync for inspect and vie/edit panel * Update navigating back to dashboard from edit panel * DashboardScene: Panel inspect improvements (#80655) Improve inspect, andle view pane end edit mode inspection * Url sync fixes * Test update
This commit is contained in:
@@ -15,6 +15,9 @@ import { Alert, Drawer, Tab, TabsBar } from '@grafana/ui';
|
|||||||
import { getDataSourceWithInspector } from 'app/features/dashboard/components/Inspector/hooks';
|
import { getDataSourceWithInspector } from 'app/features/dashboard/components/Inspector/hooks';
|
||||||
import { supportsDataQuery } from 'app/features/dashboard/components/PanelEditor/utils';
|
import { supportsDataQuery } from 'app/features/dashboard/components/PanelEditor/utils';
|
||||||
|
|
||||||
|
import { getDashboardUrl } from '../utils/urlBuilders';
|
||||||
|
import { getDashboardSceneFor } from '../utils/utils';
|
||||||
|
|
||||||
import { InspectDataTab } from './InspectDataTab';
|
import { InspectDataTab } from './InspectDataTab';
|
||||||
import { InspectJsonTab } from './InspectJsonTab';
|
import { InspectJsonTab } from './InspectJsonTab';
|
||||||
import { InspectMetaDataTab } from './InspectMetaDataTab';
|
import { InspectMetaDataTab } from './InspectMetaDataTab';
|
||||||
@@ -24,7 +27,7 @@ import { SceneInspectTab } from './types';
|
|||||||
|
|
||||||
interface PanelInspectDrawerState extends SceneObjectState {
|
interface PanelInspectDrawerState extends SceneObjectState {
|
||||||
tabs?: SceneInspectTab[];
|
tabs?: SceneInspectTab[];
|
||||||
panelRef: SceneObjectRef<VizPanel>;
|
panelRef?: SceneObjectRef<VizPanel>;
|
||||||
pluginNotLoaded?: boolean;
|
pluginNotLoaded?: boolean;
|
||||||
canEdit?: boolean;
|
canEdit?: boolean;
|
||||||
}
|
}
|
||||||
@@ -47,8 +50,7 @@ export class PanelInspectDrawer extends SceneObjectBase<PanelInspectDrawerState>
|
|||||||
*/
|
*/
|
||||||
async buildTabs(retry: number) {
|
async buildTabs(retry: number) {
|
||||||
const panelRef = this.state.panelRef;
|
const panelRef = this.state.panelRef;
|
||||||
const panel = panelRef.resolve();
|
const plugin = panelRef?.resolve()?.getPlugin();
|
||||||
const plugin = panel.getPlugin();
|
|
||||||
const tabs: SceneInspectTab[] = [];
|
const tabs: SceneInspectTab[] = [];
|
||||||
|
|
||||||
if (!plugin) {
|
if (!plugin) {
|
||||||
@@ -59,31 +61,46 @@ export class PanelInspectDrawer extends SceneObjectBase<PanelInspectDrawerState>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (supportsDataQuery(plugin)) {
|
if (panelRef) {
|
||||||
const data = sceneGraph.getData(panel);
|
if (supportsDataQuery(plugin)) {
|
||||||
|
const data = sceneGraph.getData(panelRef.resolve());
|
||||||
|
|
||||||
tabs.push(new InspectDataTab({ panelRef }));
|
tabs.push(new InspectDataTab({ panelRef }));
|
||||||
tabs.push(new InspectStatsTab({ panelRef }));
|
tabs.push(new InspectStatsTab({ panelRef }));
|
||||||
tabs.push(new InspectQueryTab({ panelRef }));
|
tabs.push(new InspectQueryTab({ panelRef }));
|
||||||
|
|
||||||
const dsWithInspector = await getDataSourceWithInspector(data.state.data);
|
const dsWithInspector = await getDataSourceWithInspector(data.state.data);
|
||||||
if (dsWithInspector) {
|
if (dsWithInspector) {
|
||||||
tabs.push(new InspectMetaDataTab({ panelRef, dataSource: dsWithInspector }));
|
tabs.push(new InspectMetaDataTab({ panelRef, dataSource: dsWithInspector }));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
tabs.push(new InspectJsonTab({ panelRef, onClose: this.onClose }));
|
tabs.push(new InspectJsonTab({ panelRef, onClose: this.onClose }));
|
||||||
|
}
|
||||||
|
|
||||||
this.setState({ tabs });
|
this.setState({ tabs });
|
||||||
}
|
}
|
||||||
|
|
||||||
getDrawerTitle() {
|
getDrawerTitle() {
|
||||||
const panel = this.state.panelRef.resolve();
|
const panel = this.state.panelRef?.resolve();
|
||||||
return sceneGraph.interpolate(panel, `Inspect: ${panel.state.title}`);
|
if (panel) {
|
||||||
|
return sceneGraph.interpolate(panel, `Inspect: ${panel.state.title}`);
|
||||||
|
}
|
||||||
|
return `Inspect panel`;
|
||||||
}
|
}
|
||||||
|
|
||||||
onClose = () => {
|
onClose = () => {
|
||||||
locationService.partial({ inspect: null, inspectTab: null });
|
const dashboard = getDashboardSceneFor(this);
|
||||||
|
locationService.push(
|
||||||
|
getDashboardUrl({
|
||||||
|
uid: dashboard.state.uid,
|
||||||
|
currentQueryParams: locationService.getLocation().search,
|
||||||
|
updateQuery: {
|
||||||
|
inspect: null,
|
||||||
|
inspectTab: null,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,10 +9,9 @@ import { buildNavModel } from 'app/features/folders/state/navModel';
|
|||||||
import { store } from 'app/store/store';
|
import { store } from 'app/store/store';
|
||||||
import { DashboardDTO, DashboardMeta, DashboardRoutes } from 'app/types';
|
import { DashboardDTO, DashboardMeta, DashboardRoutes } from 'app/types';
|
||||||
|
|
||||||
import { buildPanelEditScene, PanelEditor } from '../panel-edit/PanelEditor';
|
import { PanelEditor } from '../panel-edit/PanelEditor';
|
||||||
import { DashboardScene } from '../scene/DashboardScene';
|
import { DashboardScene } from '../scene/DashboardScene';
|
||||||
import { transformSaveModelToScene } from '../serialization/transformSaveModelToScene';
|
import { transformSaveModelToScene } from '../serialization/transformSaveModelToScene';
|
||||||
import { getVizPanelKeyForPanelId, findVizPanelByKey } from '../utils/utils';
|
|
||||||
|
|
||||||
export interface DashboardScenePageState {
|
export interface DashboardScenePageState {
|
||||||
dashboard?: DashboardScene;
|
dashboard?: DashboardScene;
|
||||||
@@ -96,25 +95,6 @@ export class DashboardScenePageStateManager extends StateManagerBase<DashboardSc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async loadPanelEdit(uid: string, panelId: string) {
|
|
||||||
try {
|
|
||||||
const dashboard = await this.loadScene(uid);
|
|
||||||
const panel = findVizPanelByKey(dashboard, getVizPanelKeyForPanelId(parseInt(panelId, 10)));
|
|
||||||
|
|
||||||
if (!panel) {
|
|
||||||
this.setState({ isLoading: false, loadError: 'Panel not found' });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const panelEditor = buildPanelEditScene(dashboard, panel);
|
|
||||||
panelEditor.startUrlSync();
|
|
||||||
|
|
||||||
this.setState({ isLoading: false, panelEditor });
|
|
||||||
} catch (err) {
|
|
||||||
this.setState({ isLoading: false, loadError: String(err) });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async loadScene(uid: string): Promise<DashboardScene> {
|
private async loadScene(uid: string): Promise<DashboardScene> {
|
||||||
const fromCache = this.cache[uid];
|
const fromCache = this.cache[uid];
|
||||||
if (fromCache) {
|
if (fromCache) {
|
||||||
|
|||||||
@@ -1,36 +0,0 @@
|
|||||||
// Libraries
|
|
||||||
import React, { useEffect } from 'react';
|
|
||||||
|
|
||||||
import { PageLayoutType } from '@grafana/data';
|
|
||||||
import { Page } from 'app/core/components/Page/Page';
|
|
||||||
import PageLoader from 'app/core/components/PageLoader/PageLoader';
|
|
||||||
import { GrafanaRouteComponentProps } from 'app/core/navigation/types';
|
|
||||||
|
|
||||||
import { getDashboardScenePageStateManager } from './DashboardScenePageStateManager';
|
|
||||||
|
|
||||||
export interface Props extends GrafanaRouteComponentProps<{ uid: string; panelId: string }> {}
|
|
||||||
|
|
||||||
export function PanelEditPage({ match }: Props) {
|
|
||||||
const stateManager = getDashboardScenePageStateManager();
|
|
||||||
const { panelEditor, isLoading, loadError } = stateManager.useState();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
stateManager.loadPanelEdit(match.params.uid, match.params.panelId);
|
|
||||||
return () => {
|
|
||||||
stateManager.clearState();
|
|
||||||
};
|
|
||||||
}, [stateManager, match.params.uid, match.params.panelId]);
|
|
||||||
|
|
||||||
if (!panelEditor) {
|
|
||||||
return (
|
|
||||||
<Page layout={PageLayoutType.Canvas}>
|
|
||||||
{isLoading && <PageLoader />}
|
|
||||||
{loadError && <h2>{loadError}</h2>}
|
|
||||||
</Page>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return <panelEditor.Component model={panelEditor} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default PanelEditPage;
|
|
||||||
@@ -3,7 +3,6 @@ import * as H from 'history';
|
|||||||
import { NavIndex } from '@grafana/data';
|
import { NavIndex } from '@grafana/data';
|
||||||
import { locationService } from '@grafana/runtime';
|
import { locationService } from '@grafana/runtime';
|
||||||
import {
|
import {
|
||||||
getUrlSyncManager,
|
|
||||||
SceneFlexItem,
|
SceneFlexItem,
|
||||||
SceneFlexLayout,
|
SceneFlexLayout,
|
||||||
SceneGridItem,
|
SceneGridItem,
|
||||||
@@ -11,19 +10,20 @@ import {
|
|||||||
SceneObjectBase,
|
SceneObjectBase,
|
||||||
SceneObjectRef,
|
SceneObjectRef,
|
||||||
SceneObjectState,
|
SceneObjectState,
|
||||||
sceneUtils,
|
|
||||||
SplitLayout,
|
SplitLayout,
|
||||||
VizPanel,
|
VizPanel,
|
||||||
} from '@grafana/scenes';
|
} from '@grafana/scenes';
|
||||||
import { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv';
|
|
||||||
|
|
||||||
import { DashboardScene } from '../scene/DashboardScene';
|
|
||||||
import { DashboardModelCompatibilityWrapper } from '../utils/DashboardModelCompatibilityWrapper';
|
|
||||||
import { getDashboardUrl } from '../utils/urlBuilders';
|
import { getDashboardUrl } from '../utils/urlBuilders';
|
||||||
|
import {
|
||||||
|
findVizPanelByKey,
|
||||||
|
getDashboardSceneFor,
|
||||||
|
getPanelIdForVizPanel,
|
||||||
|
getVizPanelKeyForPanelId,
|
||||||
|
} from '../utils/utils';
|
||||||
|
|
||||||
import { PanelDataPane } from './PanelDataPane/PanelDataPane';
|
import { PanelDataPane } from './PanelDataPane/PanelDataPane';
|
||||||
import { PanelEditorRenderer } from './PanelEditorRenderer';
|
import { PanelEditorRenderer } from './PanelEditorRenderer';
|
||||||
import { PanelEditorUrlSync } from './PanelEditorUrlSync';
|
|
||||||
import { PanelOptionsPane } from './PanelOptionsPane';
|
import { PanelOptionsPane } from './PanelOptionsPane';
|
||||||
import { VizPanelManager } from './VizPanelManager';
|
import { VizPanelManager } from './VizPanelManager';
|
||||||
|
|
||||||
@@ -31,49 +31,26 @@ export interface PanelEditorState extends SceneObjectState {
|
|||||||
body: SceneObject;
|
body: SceneObject;
|
||||||
controls?: SceneObject[];
|
controls?: SceneObject[];
|
||||||
isDirty?: boolean;
|
isDirty?: boolean;
|
||||||
/** Panel to inspect */
|
panelId: number;
|
||||||
inspectPanelKey?: string;
|
|
||||||
/** Scene object that handles the current drawer */
|
|
||||||
overlay?: SceneObject;
|
|
||||||
|
|
||||||
dashboardRef: SceneObjectRef<DashboardScene>;
|
|
||||||
sourcePanelRef: SceneObjectRef<VizPanel>;
|
|
||||||
panelRef: SceneObjectRef<VizPanelManager>;
|
panelRef: SceneObjectRef<VizPanelManager>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PanelEditor extends SceneObjectBase<PanelEditorState> {
|
export class PanelEditor extends SceneObjectBase<PanelEditorState> {
|
||||||
static Component = PanelEditorRenderer;
|
static Component = PanelEditorRenderer;
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles url sync
|
|
||||||
*/
|
|
||||||
protected _urlSync = new PanelEditorUrlSync(this);
|
|
||||||
|
|
||||||
public constructor(state: PanelEditorState) {
|
public constructor(state: PanelEditorState) {
|
||||||
super(state);
|
super(state);
|
||||||
|
|
||||||
this.addActivationHandler(() => this._activationHandler());
|
|
||||||
}
|
}
|
||||||
|
public getUrlKey() {
|
||||||
private _activationHandler() {
|
return this.state.panelId.toString();
|
||||||
const oldDashboardWrapper = new DashboardModelCompatibilityWrapper(this.state.dashboardRef.resolve());
|
|
||||||
// @ts-expect-error
|
|
||||||
getDashboardSrv().setCurrent(oldDashboardWrapper);
|
|
||||||
|
|
||||||
// Deactivation logic
|
|
||||||
return () => {
|
|
||||||
getUrlSyncManager().cleanUp(this);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public startUrlSync() {
|
|
||||||
getUrlSyncManager().initSync(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public getPageNav(location: H.Location, navIndex: NavIndex) {
|
public getPageNav(location: H.Location, navIndex: NavIndex) {
|
||||||
|
const dashboard = getDashboardSceneFor(this);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
text: 'Edit panel',
|
text: 'Edit panel',
|
||||||
parentItem: this.state.dashboardRef.resolve().getPageNav(location, navIndex),
|
parentItem: dashboard.getPageNav(location, navIndex),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,8 +72,8 @@ export class PanelEditor extends SceneObjectBase<PanelEditorState> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
private _commitChanges() {
|
private _commitChanges() {
|
||||||
const dashboard = this.state.dashboardRef.resolve();
|
const dashboard = getDashboardSceneFor(this);
|
||||||
const sourcePanel = this.state.sourcePanelRef.resolve();
|
const sourcePanel = findVizPanelByKey(dashboard.state.body, getVizPanelKeyForPanelId(this.state.panelId));
|
||||||
|
|
||||||
if (!dashboard.state.isEditing) {
|
if (!dashboard.state.isEditing) {
|
||||||
dashboard.onEnterEditMode();
|
dashboard.onEnterEditMode();
|
||||||
@@ -104,41 +81,38 @@ export class PanelEditor extends SceneObjectBase<PanelEditorState> {
|
|||||||
|
|
||||||
const panelMngr = this.state.panelRef.resolve();
|
const panelMngr = this.state.panelRef.resolve();
|
||||||
|
|
||||||
if (sourcePanel.parent instanceof SceneGridItem) {
|
if (sourcePanel!.parent instanceof SceneGridItem) {
|
||||||
sourcePanel.parent.setState({ body: panelMngr.state.panel.clone() });
|
sourcePanel!.parent.setState({ body: panelMngr.state.panel.clone() });
|
||||||
}
|
}
|
||||||
|
|
||||||
// preserve time range and variables state
|
|
||||||
dashboard.setState({
|
dashboard.setState({
|
||||||
$timeRange: this.state.$timeRange?.clone(),
|
|
||||||
$variables: this.state.$variables?.clone(),
|
|
||||||
isDirty: true,
|
isDirty: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _navigateBackToDashboard() {
|
private _navigateBackToDashboard() {
|
||||||
|
const dashboard = getDashboardSceneFor(this);
|
||||||
locationService.push(
|
locationService.push(
|
||||||
getDashboardUrl({
|
getDashboardUrl({
|
||||||
uid: this.state.dashboardRef.resolve().state.uid,
|
uid: dashboard.state.uid,
|
||||||
currentQueryParams: locationService.getLocation().search,
|
currentQueryParams: locationService.getLocation().search,
|
||||||
|
updateQuery: {
|
||||||
|
editPanel: null,
|
||||||
|
// Clean the PanelEditor data pane tab query param
|
||||||
|
tab: null,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function buildPanelEditScene(dashboard: DashboardScene, panel: VizPanel): PanelEditor {
|
export function buildPanelEditScene(panel: VizPanel): PanelEditor {
|
||||||
const panelClone = panel.clone();
|
const panelClone = panel.clone();
|
||||||
|
|
||||||
const vizPanelMgr = new VizPanelManager(panelClone);
|
const vizPanelMgr = new VizPanelManager(panelClone);
|
||||||
const dashboardStateCloned = sceneUtils.cloneSceneObjectState(dashboard.state);
|
|
||||||
|
|
||||||
return new PanelEditor({
|
return new PanelEditor({
|
||||||
dashboardRef: dashboard.getRef(),
|
panelId: getPanelIdForVizPanel(panel),
|
||||||
sourcePanelRef: panel.getRef(),
|
|
||||||
panelRef: vizPanelMgr.getRef(),
|
panelRef: vizPanelMgr.getRef(),
|
||||||
controls: dashboardStateCloned.controls,
|
|
||||||
$variables: dashboardStateCloned.$variables,
|
|
||||||
$timeRange: dashboardStateCloned.$timeRange,
|
|
||||||
body: new SplitLayout({
|
body: new SplitLayout({
|
||||||
direction: 'row',
|
direction: 'row',
|
||||||
primary: new SplitLayout({
|
primary: new SplitLayout({
|
||||||
|
|||||||
@@ -1,26 +1,24 @@
|
|||||||
import { css } from '@emotion/css';
|
import { css } from '@emotion/css';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useLocation } from 'react-router-dom';
|
|
||||||
|
|
||||||
import { GrafanaTheme2, PageLayoutType } from '@grafana/data';
|
import { GrafanaTheme2 } from '@grafana/data';
|
||||||
import { SceneComponentProps } from '@grafana/scenes';
|
import { SceneComponentProps } from '@grafana/scenes';
|
||||||
import { Button, useStyles2 } from '@grafana/ui';
|
import { Button, useStyles2 } from '@grafana/ui';
|
||||||
import { AppChromeUpdate } from 'app/core/components/AppChrome/AppChromeUpdate';
|
import { AppChromeUpdate } from 'app/core/components/AppChrome/AppChromeUpdate';
|
||||||
import { NavToolbarSeparator } from 'app/core/components/AppChrome/NavToolbar/NavToolbarSeparator';
|
import { NavToolbarSeparator } from 'app/core/components/AppChrome/NavToolbar/NavToolbarSeparator';
|
||||||
import { Page } from 'app/core/components/Page/Page';
|
|
||||||
import { useSelector } from 'app/types/store';
|
import { getDashboardSceneFor } from '../utils/utils';
|
||||||
|
|
||||||
import { PanelEditor } from './PanelEditor';
|
import { PanelEditor } from './PanelEditor';
|
||||||
|
|
||||||
export function PanelEditorRenderer({ model }: SceneComponentProps<PanelEditor>) {
|
export function PanelEditorRenderer({ model }: SceneComponentProps<PanelEditor>) {
|
||||||
const { body, controls, overlay } = model.useState();
|
const dashboard = getDashboardSceneFor(model);
|
||||||
|
const { body } = model.useState();
|
||||||
|
const { controls } = dashboard.useState();
|
||||||
const styles = useStyles2(getStyles);
|
const styles = useStyles2(getStyles);
|
||||||
const location = useLocation();
|
|
||||||
const navIndex = useSelector((state) => state.navIndex);
|
|
||||||
const pageNav = model.getPageNav(location, navIndex);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Page navId="scenes" pageNav={pageNav} layout={PageLayoutType.Custom}>
|
<>
|
||||||
<AppChromeUpdate actions={getToolbarActions(model)} />
|
<AppChromeUpdate actions={getToolbarActions(model)} />
|
||||||
<div className={styles.canvasContent}>
|
<div className={styles.canvasContent}>
|
||||||
{controls && (
|
{controls && (
|
||||||
@@ -34,8 +32,7 @@ export function PanelEditorRenderer({ model }: SceneComponentProps<PanelEditor>)
|
|||||||
<body.Component model={body} />
|
<body.Component model={body} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{overlay && <overlay.Component model={overlay} />}
|
</>
|
||||||
</Page>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,49 +0,0 @@
|
|||||||
import { AppEvents } from '@grafana/data';
|
|
||||||
import { locationService } from '@grafana/runtime';
|
|
||||||
import { SceneObjectUrlSyncHandler, SceneObjectUrlValues } from '@grafana/scenes';
|
|
||||||
import appEvents from 'app/core/app_events';
|
|
||||||
|
|
||||||
import { PanelInspectDrawer } from '../inspect/PanelInspectDrawer';
|
|
||||||
import { findVizPanelByKey } from '../utils/utils';
|
|
||||||
|
|
||||||
import { PanelEditor, PanelEditorState } from './PanelEditor';
|
|
||||||
|
|
||||||
export class PanelEditorUrlSync implements SceneObjectUrlSyncHandler {
|
|
||||||
constructor(private _scene: PanelEditor) {}
|
|
||||||
|
|
||||||
getKeys(): string[] {
|
|
||||||
return ['inspect'];
|
|
||||||
}
|
|
||||||
|
|
||||||
getUrlState(): SceneObjectUrlValues {
|
|
||||||
const state = this._scene.state;
|
|
||||||
return {
|
|
||||||
inspect: state.inspectPanelKey,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
updateFromUrl(values: SceneObjectUrlValues): void {
|
|
||||||
const { inspectPanelKey } = this._scene.state;
|
|
||||||
const update: Partial<PanelEditorState> = {};
|
|
||||||
|
|
||||||
// Handle inspect object state
|
|
||||||
if (typeof values.inspect === 'string') {
|
|
||||||
const panel = findVizPanelByKey(this._scene, values.inspect);
|
|
||||||
if (!panel) {
|
|
||||||
appEvents.emit(AppEvents.alertError, ['Panel not found']);
|
|
||||||
locationService.partial({ inspect: null });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
update.inspectPanelKey = values.inspect;
|
|
||||||
update.overlay = new PanelInspectDrawer({ panelRef: panel.getRef() });
|
|
||||||
} else if (inspectPanelKey) {
|
|
||||||
update.inspectPanelKey = undefined;
|
|
||||||
update.overlay = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Object.keys(update).length > 0) {
|
|
||||||
this._scene.setState(update);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -540,10 +540,11 @@ describe('VizPanelManager', () => {
|
|||||||
describe('dashboard queries', () => {
|
describe('dashboard queries', () => {
|
||||||
it('should update queries', () => {
|
it('should update queries', () => {
|
||||||
const { scene, panel } = setupTest('panel-3');
|
const { scene, panel } = setupTest('panel-3');
|
||||||
|
scene.setState({
|
||||||
|
editPanel: buildPanelEditScene(panel),
|
||||||
|
});
|
||||||
|
|
||||||
const panelEditScene = buildPanelEditScene(scene, panel);
|
const vizPanelManager = scene.state.editPanel!.state.panelRef.resolve();
|
||||||
|
|
||||||
const vizPanelManager = panelEditScene.state.panelRef.resolve();
|
|
||||||
vizPanelManager.activate();
|
vizPanelManager.activate();
|
||||||
vizPanelManager.state.panel.state.$data?.activate();
|
vizPanelManager.state.panel.state.$data?.activate();
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import * as H from 'history';
|
import * as H from 'history';
|
||||||
import { Unsubscribable } from 'rxjs';
|
import { Unsubscribable } from 'rxjs';
|
||||||
|
|
||||||
import { CoreApp, DataQueryRequest, NavIndex, NavModelItem } from '@grafana/data';
|
import { CoreApp, DataQueryRequest, NavIndex, NavModelItem, locationUtil } from '@grafana/data';
|
||||||
import { locationService } from '@grafana/runtime';
|
import { locationService } from '@grafana/runtime';
|
||||||
import {
|
import {
|
||||||
getUrlSyncManager,
|
getUrlSyncManager,
|
||||||
@@ -25,6 +25,7 @@ import { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv';
|
|||||||
import { VariablesChanged } from 'app/features/variables/types';
|
import { VariablesChanged } from 'app/features/variables/types';
|
||||||
import { DashboardMeta } from 'app/types';
|
import { DashboardMeta } from 'app/types';
|
||||||
|
|
||||||
|
import { PanelEditor } from '../panel-edit/PanelEditor';
|
||||||
import { DashboardSceneRenderer } from '../scene/DashboardSceneRenderer';
|
import { DashboardSceneRenderer } from '../scene/DashboardSceneRenderer';
|
||||||
import { SaveDashboardDrawer } from '../serialization/SaveDashboardDrawer';
|
import { SaveDashboardDrawer } from '../serialization/SaveDashboardDrawer';
|
||||||
import { DashboardEditView } from '../settings/utils';
|
import { DashboardEditView } from '../settings/utils';
|
||||||
@@ -73,6 +74,9 @@ export interface DashboardSceneState extends SceneObjectState {
|
|||||||
viewPanelScene?: ViewPanelScene;
|
viewPanelScene?: ViewPanelScene;
|
||||||
/** Edit view */
|
/** Edit view */
|
||||||
editview?: DashboardEditView;
|
editview?: DashboardEditView;
|
||||||
|
/** Edit panel */
|
||||||
|
editPanel?: PanelEditor;
|
||||||
|
|
||||||
/** Scene object that handles the current drawer or modal */
|
/** Scene object that handles the current drawer or modal */
|
||||||
overlay?: SceneObject;
|
overlay?: SceneObject;
|
||||||
}
|
}
|
||||||
@@ -169,8 +173,21 @@ export class DashboardScene extends SceneObjectBase<DashboardSceneState> {
|
|||||||
this.stopTrackingChanges();
|
this.stopTrackingChanges();
|
||||||
// Stop url sync before updating url
|
// Stop url sync before updating url
|
||||||
this.stopUrlSync();
|
this.stopUrlSync();
|
||||||
// Now we can update url
|
|
||||||
locationService.replace({ pathname: this._initialUrlState?.pathname, search: this._initialUrlState?.search });
|
// Now we can update urls
|
||||||
|
// We are updating url and removing editview and editPanel.
|
||||||
|
// The initial url may be including edit view, edit panel or inspect query params if the user pasted the url,
|
||||||
|
// hence we need to cleanup those query params to get back to the dashboard view. Otherwise url sync can trigger overlays.
|
||||||
|
locationService.replace(
|
||||||
|
locationUtil.getUrlForPartial(this._initialUrlState!, {
|
||||||
|
editPanel: null,
|
||||||
|
editview: null,
|
||||||
|
inspect: null,
|
||||||
|
inspectTab: null,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// locationService.replace({ pathname: this._initialUrlState?.pathname, search: this._initialUrlState?.search });
|
||||||
// Update state and disable editing
|
// Update state and disable editing
|
||||||
this.setState({ ...this._initialState, isEditing: false });
|
this.setState({ ...this._initialState, isEditing: false });
|
||||||
// and start url sync again
|
// and start url sync again
|
||||||
@@ -188,14 +205,14 @@ export class DashboardScene extends SceneObjectBase<DashboardSceneState> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
public getPageNav(location: H.Location, navIndex: NavIndex) {
|
public getPageNav(location: H.Location, navIndex: NavIndex) {
|
||||||
const { meta, viewPanelScene } = this.state;
|
const { meta, viewPanelScene, editPanel } = this.state;
|
||||||
|
|
||||||
let pageNav: NavModelItem = {
|
let pageNav: NavModelItem = {
|
||||||
text: this.state.title,
|
text: this.state.title,
|
||||||
url: getDashboardUrl({
|
url: getDashboardUrl({
|
||||||
uid: this.state.uid,
|
uid: this.state.uid,
|
||||||
currentQueryParams: location.search,
|
currentQueryParams: location.search,
|
||||||
updateQuery: { viewPanel: null, inspect: null, editview: null },
|
updateQuery: { viewPanel: null, inspect: null, editview: null, editPanel: null, tab: null },
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -220,6 +237,13 @@ export class DashboardScene extends SceneObjectBase<DashboardSceneState> {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (editPanel) {
|
||||||
|
pageNav = {
|
||||||
|
text: 'Edit panel',
|
||||||
|
parentItem: pageNav,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return pageNav;
|
return pageNav;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import { DashboardScene } from './DashboardScene';
|
|||||||
import { NavToolbarActions } from './NavToolbarActions';
|
import { NavToolbarActions } from './NavToolbarActions';
|
||||||
|
|
||||||
export function DashboardSceneRenderer({ model }: SceneComponentProps<DashboardScene>) {
|
export function DashboardSceneRenderer({ model }: SceneComponentProps<DashboardScene>) {
|
||||||
const { controls, overlay, editview } = model.useState();
|
const { controls, overlay, editview, editPanel } = model.useState();
|
||||||
const styles = useStyles2(getStyles);
|
const styles = useStyles2(getStyles);
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const navIndex = useSelector((state) => state.navIndex);
|
const navIndex = useSelector((state) => state.navIndex);
|
||||||
@@ -27,23 +27,26 @@ export function DashboardSceneRenderer({ model }: SceneComponentProps<DashboardS
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Page navModel={navModel} pageNav={pageNav} layout={PageLayoutType.Custom}>
|
<Page navModel={navModel} pageNav={pageNav} layout={PageLayoutType.Custom}>
|
||||||
<CustomScrollbar autoHeightMin={'100%'}>
|
{editPanel && <editPanel.Component model={editPanel} />}
|
||||||
<div className={styles.canvasContent}>
|
{!editPanel && (
|
||||||
<NavToolbarActions dashboard={model} />
|
<CustomScrollbar autoHeightMin={'100%'}>
|
||||||
|
<div className={styles.canvasContent}>
|
||||||
|
<NavToolbarActions dashboard={model} />
|
||||||
|
|
||||||
{controls && (
|
{controls && (
|
||||||
<div className={styles.controls}>
|
<div className={styles.controls}>
|
||||||
{controls.map((control) => (
|
{controls.map((control) => (
|
||||||
<control.Component key={control.state.key} model={control} />
|
<control.Component key={control.state.key} model={control} />
|
||||||
))}
|
))}
|
||||||
<SceneDebugger scene={model} key={'scene-debugger'} />
|
<SceneDebugger scene={model} key={'scene-debugger'} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className={cx(styles.body)}>
|
||||||
|
<bodyToRender.Component model={bodyToRender} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
<div className={cx(styles.body)}>
|
|
||||||
<bodyToRender.Component model={bodyToRender} />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</CustomScrollbar>
|
||||||
</CustomScrollbar>
|
)}
|
||||||
{overlay && <overlay.Component model={overlay} />}
|
{overlay && <overlay.Component model={overlay} />}
|
||||||
</Page>
|
</Page>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -2,12 +2,13 @@ import { Unsubscribable } from 'rxjs';
|
|||||||
|
|
||||||
import { AppEvents } from '@grafana/data';
|
import { AppEvents } from '@grafana/data';
|
||||||
import { locationService } from '@grafana/runtime';
|
import { locationService } from '@grafana/runtime';
|
||||||
import { SceneObjectUrlSyncHandler, SceneObjectUrlValues } from '@grafana/scenes';
|
import { SceneObjectBase, SceneObjectState, SceneObjectUrlSyncHandler, SceneObjectUrlValues } from '@grafana/scenes';
|
||||||
import appEvents from 'app/core/app_events';
|
import appEvents from 'app/core/app_events';
|
||||||
|
|
||||||
import { PanelInspectDrawer } from '../inspect/PanelInspectDrawer';
|
import { PanelInspectDrawer } from '../inspect/PanelInspectDrawer';
|
||||||
|
import { buildPanelEditScene } from '../panel-edit/PanelEditor';
|
||||||
import { createDashboardEditViewFor } from '../settings/utils';
|
import { createDashboardEditViewFor } from '../settings/utils';
|
||||||
import { findVizPanelByKey, isPanelClone } from '../utils/utils';
|
import { findVizPanelByKey, getDashboardSceneFor, isPanelClone } from '../utils/utils';
|
||||||
|
|
||||||
import { DashboardScene, DashboardSceneState } from './DashboardScene';
|
import { DashboardScene, DashboardSceneState } from './DashboardScene';
|
||||||
import { ViewPanelScene } from './ViewPanelScene';
|
import { ViewPanelScene } from './ViewPanelScene';
|
||||||
@@ -19,7 +20,7 @@ export class DashboardSceneUrlSync implements SceneObjectUrlSyncHandler {
|
|||||||
constructor(private _scene: DashboardScene) {}
|
constructor(private _scene: DashboardScene) {}
|
||||||
|
|
||||||
getKeys(): string[] {
|
getKeys(): string[] {
|
||||||
return ['inspect', 'viewPanel', 'editview'];
|
return ['inspect', 'viewPanel', 'editPanel', 'editview'];
|
||||||
}
|
}
|
||||||
|
|
||||||
getUrlState(): SceneObjectUrlValues {
|
getUrlState(): SceneObjectUrlValues {
|
||||||
@@ -28,11 +29,12 @@ export class DashboardSceneUrlSync implements SceneObjectUrlSyncHandler {
|
|||||||
inspect: state.inspectPanelKey,
|
inspect: state.inspectPanelKey,
|
||||||
viewPanel: state.viewPanelScene?.getUrlKey(),
|
viewPanel: state.viewPanelScene?.getUrlKey(),
|
||||||
editview: state.editview?.getUrlKey(),
|
editview: state.editview?.getUrlKey(),
|
||||||
|
editPanel: state.editPanel?.getUrlKey() || undefined,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
updateFromUrl(values: SceneObjectUrlValues): void {
|
updateFromUrl(values: SceneObjectUrlValues): void {
|
||||||
const { inspectPanelKey, viewPanelScene, meta, isEditing } = this._scene.state;
|
const { inspectPanelKey, viewPanelScene, meta, isEditing, editPanel } = this._scene.state;
|
||||||
const update: Partial<DashboardSceneState> = {};
|
const update: Partial<DashboardSceneState> = {};
|
||||||
|
|
||||||
if (typeof values.editview === 'string' && meta.canEdit) {
|
if (typeof values.editview === 'string' && meta.canEdit) {
|
||||||
@@ -50,7 +52,7 @@ export class DashboardSceneUrlSync implements SceneObjectUrlSyncHandler {
|
|||||||
|
|
||||||
// Handle inspect object state
|
// Handle inspect object state
|
||||||
if (typeof values.inspect === 'string') {
|
if (typeof values.inspect === 'string') {
|
||||||
const panel = findVizPanelByKey(this._scene, values.inspect);
|
let panel = findVizPanelByKey(this._scene, values.inspect);
|
||||||
if (!panel) {
|
if (!panel) {
|
||||||
appEvents.emit(AppEvents.alertError, ['Panel not found']);
|
appEvents.emit(AppEvents.alertError, ['Panel not found']);
|
||||||
locationService.partial({ inspect: null });
|
locationService.partial({ inspect: null });
|
||||||
@@ -58,7 +60,9 @@ export class DashboardSceneUrlSync implements SceneObjectUrlSyncHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
update.inspectPanelKey = values.inspect;
|
update.inspectPanelKey = values.inspect;
|
||||||
update.overlay = new PanelInspectDrawer({ panelRef: panel.getRef() });
|
update.overlay = new PanelInspectDrawer({
|
||||||
|
$behaviors: [new ResolveInspectPanelByKey({ panelKey: values.inspect })],
|
||||||
|
});
|
||||||
} else if (inspectPanelKey) {
|
} else if (inspectPanelKey) {
|
||||||
update.inspectPanelKey = undefined;
|
update.inspectPanelKey = undefined;
|
||||||
update.overlay = undefined;
|
update.overlay = undefined;
|
||||||
@@ -80,10 +84,26 @@ export class DashboardSceneUrlSync implements SceneObjectUrlSyncHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
update.viewPanelScene = new ViewPanelScene({ panelRef: panel.getRef() });
|
update.viewPanelScene = new ViewPanelScene({ panelRef: panel.getRef() });
|
||||||
} else if (viewPanelScene) {
|
} else if (viewPanelScene && values.viewPanel === null) {
|
||||||
update.viewPanelScene = undefined;
|
update.viewPanelScene = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle edit panel state
|
||||||
|
if (typeof values.editPanel === 'string') {
|
||||||
|
const panel = findVizPanelByKey(this._scene, values.editPanel);
|
||||||
|
if (!panel) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we are not in editing (for example after full page reload)
|
||||||
|
if (!isEditing) {
|
||||||
|
this._scene.onEnterEditMode();
|
||||||
|
}
|
||||||
|
update.editPanel = buildPanelEditScene(panel);
|
||||||
|
} else if (editPanel && values.editPanel === null) {
|
||||||
|
update.editPanel = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
if (Object.keys(update).length > 0) {
|
if (Object.keys(update).length > 0) {
|
||||||
this._scene.setState(update);
|
this._scene.setState(update);
|
||||||
}
|
}
|
||||||
@@ -101,3 +121,41 @@ export class DashboardSceneUrlSync implements SceneObjectUrlSyncHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface ResolveInspectPanelByKeyState extends SceneObjectState {
|
||||||
|
panelKey: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ResolveInspectPanelByKey extends SceneObjectBase<ResolveInspectPanelByKeyState> {
|
||||||
|
constructor(state: ResolveInspectPanelByKeyState) {
|
||||||
|
super(state);
|
||||||
|
this.addActivationHandler(this._onActivate);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _onActivate = () => {
|
||||||
|
const parent = this.parent;
|
||||||
|
|
||||||
|
if (!parent || !(parent instanceof PanelInspectDrawer)) {
|
||||||
|
throw new Error('ResolveInspectPanelByKey must be attached to a PanelInspectDrawer');
|
||||||
|
}
|
||||||
|
|
||||||
|
const dashboard = getDashboardSceneFor(parent);
|
||||||
|
if (!dashboard) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const panelId = this.state.panelKey;
|
||||||
|
let panel = findVizPanelByKey(dashboard, panelId);
|
||||||
|
|
||||||
|
if (dashboard.state.editPanel) {
|
||||||
|
panel = dashboard.state.editPanel.state.panelRef.resolve().state.panel;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dashboard.state.viewPanelScene && dashboard.state.viewPanelScene.state.body) {
|
||||||
|
panel = dashboard.state.viewPanelScene.state.body;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (panel) {
|
||||||
|
parent.setState({ panelRef: panel.getRef() });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ describe('panelMenuBehavior', () => {
|
|||||||
// verify view panel url keeps url params and adds viewPanel=<panel-key>
|
// verify view panel url keeps url params and adds viewPanel=<panel-key>
|
||||||
expect(menu.state.items?.[0].href).toBe('/d/dash-1?from=now-5m&to=now&viewPanel=panel-12');
|
expect(menu.state.items?.[0].href).toBe('/d/dash-1?from=now-5m&to=now&viewPanel=panel-12');
|
||||||
// verify edit url keeps url time range
|
// verify edit url keeps url time range
|
||||||
expect(menu.state.items?.[1].href).toBe('/d/dash-1/panel-edit/12?from=now-5m&to=now');
|
expect(menu.state.items?.[1].href).toBe('/d/dash-1?from=now-5m&to=now&editPanel=12');
|
||||||
// verify share
|
// verify share
|
||||||
expect(menu.state.items?.[2].text).toBe('Share');
|
expect(menu.state.items?.[2].text).toBe('Share');
|
||||||
// verify explore url
|
// verify explore url
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import { addDataTrailPanelAction } from 'app/features/trails/dashboardIntegratio
|
|||||||
|
|
||||||
import { ShareModal } from '../sharing/ShareModal';
|
import { ShareModal } from '../sharing/ShareModal';
|
||||||
import { DashboardInteractions } from '../utils/interactions';
|
import { DashboardInteractions } from '../utils/interactions';
|
||||||
import { getDashboardUrl, getInspectUrl, getViewPanelUrl, tryGetExploreUrlForPanel } from '../utils/urlBuilders';
|
import { getEditPanelUrl, getInspectUrl, getViewPanelUrl, tryGetExploreUrlForPanel } from '../utils/urlBuilders';
|
||||||
import { getPanelIdForVizPanel, getQueryRunnerFor } from '../utils/utils';
|
import { getPanelIdForVizPanel, getQueryRunnerFor } from '../utils/utils';
|
||||||
|
|
||||||
import { DashboardScene } from './DashboardScene';
|
import { DashboardScene } from './DashboardScene';
|
||||||
@@ -34,7 +34,6 @@ export function panelMenuBehavior(menu: VizPanelMenu) {
|
|||||||
const panel = menu.parent as VizPanel;
|
const panel = menu.parent as VizPanel;
|
||||||
const plugin = panel.getPlugin();
|
const plugin = panel.getPlugin();
|
||||||
|
|
||||||
const location = locationService.getLocation();
|
|
||||||
const items: PanelMenuItem[] = [];
|
const items: PanelMenuItem[] = [];
|
||||||
const moreSubMenu: PanelMenuItem[] = [];
|
const moreSubMenu: PanelMenuItem[] = [];
|
||||||
const inspectSubMenu: PanelMenuItem[] = [];
|
const inspectSubMenu: PanelMenuItem[] = [];
|
||||||
@@ -57,12 +56,8 @@ export function panelMenuBehavior(menu: VizPanelMenu) {
|
|||||||
text: t('panel.header-menu.edit', `Edit`),
|
text: t('panel.header-menu.edit', `Edit`),
|
||||||
iconClassName: 'eye',
|
iconClassName: 'eye',
|
||||||
shortcut: 'e',
|
shortcut: 'e',
|
||||||
onClick: () => () => DashboardInteractions.panelMenuItemClicked('edit'),
|
onClick: () => DashboardInteractions.panelMenuItemClicked('edit'),
|
||||||
href: getDashboardUrl({
|
href: getEditPanelUrl(panelId),
|
||||||
uid: dashboard.state.uid,
|
|
||||||
subPath: `/panel-edit/${panelId}`,
|
|
||||||
currentQueryParams: location.search,
|
|
||||||
}),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { KeybindingSet } from 'app/core/services/KeybindingSet';
|
|||||||
|
|
||||||
import { ShareModal } from '../sharing/ShareModal';
|
import { ShareModal } from '../sharing/ShareModal';
|
||||||
import { dashboardSceneGraph } from '../utils/dashboardSceneGraph';
|
import { dashboardSceneGraph } from '../utils/dashboardSceneGraph';
|
||||||
import { getDashboardUrl, getInspectUrl, getViewPanelUrl, tryGetExploreUrlForPanel } from '../utils/urlBuilders';
|
import { getEditPanelUrl, getInspectUrl, getViewPanelUrl, tryGetExploreUrlForPanel } from '../utils/urlBuilders';
|
||||||
import { getPanelIdForVizPanel } from '../utils/utils';
|
import { getPanelIdForVizPanel } from '../utils/utils';
|
||||||
|
|
||||||
import { DashboardScene } from './DashboardScene';
|
import { DashboardScene } from './DashboardScene';
|
||||||
@@ -30,13 +30,9 @@ export function setupKeyboardShortcuts(scene: DashboardScene) {
|
|||||||
const sceneRoot = vizPanel.getRoot();
|
const sceneRoot = vizPanel.getRoot();
|
||||||
if (sceneRoot instanceof DashboardScene) {
|
if (sceneRoot instanceof DashboardScene) {
|
||||||
const panelId = getPanelIdForVizPanel(vizPanel);
|
const panelId = getPanelIdForVizPanel(vizPanel);
|
||||||
locationService.push(
|
if (!scene.state.editPanel) {
|
||||||
getDashboardUrl({
|
locationService.push(getEditPanelUrl(panelId));
|
||||||
uid: sceneRoot.state.uid,
|
}
|
||||||
subPath: `/panel-edit/${panelId}`,
|
|
||||||
currentQueryParams: location.search,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -67,8 +67,14 @@ export function getViewPanelUrl(vizPanel: VizPanel) {
|
|||||||
return locationUtil.getUrlForPartial(locationService.getLocation(), { viewPanel: vizPanel.state.key });
|
return locationUtil.getUrlForPartial(locationService.getLocation(), { viewPanel: vizPanel.state.key });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getEditPanelUrl(panelId: number) {
|
||||||
|
return locationUtil.getUrlForPartial(locationService.getLocation(), { editPanel: panelId });
|
||||||
|
}
|
||||||
|
|
||||||
export function getInspectUrl(vizPanel: VizPanel, inspectTab?: InspectTab) {
|
export function getInspectUrl(vizPanel: VizPanel, inspectTab?: InspectTab) {
|
||||||
return locationUtil.getUrlForPartial(locationService.getLocation(), { inspect: vizPanel.state.key, inspectTab });
|
const inspect = vizPanel.state.key?.replace('-view', '');
|
||||||
|
|
||||||
|
return locationUtil.getUrlForPartial(locationService.getLocation(), { inspect, inspectTab });
|
||||||
}
|
}
|
||||||
|
|
||||||
export function tryGetExploreUrlForPanel(vizPanel: VizPanel): Promise<string | undefined> {
|
export function tryGetExploreUrlForPanel(vizPanel: VizPanel): Promise<string | undefined> {
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import {
|
|||||||
} from '@grafana/scenes';
|
} from '@grafana/scenes';
|
||||||
import { initialIntervalVariableModelState } from 'app/features/variables/interval/reducer';
|
import { initialIntervalVariableModelState } from 'app/features/variables/interval/reducer';
|
||||||
|
|
||||||
import { PanelEditor } from '../panel-edit/PanelEditor';
|
|
||||||
import { DashboardScene } from '../scene/DashboardScene';
|
import { DashboardScene } from '../scene/DashboardScene';
|
||||||
|
|
||||||
export function getVizPanelKeyForPanelId(panelId: number) {
|
export function getVizPanelKeyForPanelId(panelId: number) {
|
||||||
@@ -166,10 +165,6 @@ export function getQueryRunnerFor(sceneObject: SceneObject | undefined): SceneQu
|
|||||||
export function getDashboardSceneFor(sceneObject: SceneObject): DashboardScene {
|
export function getDashboardSceneFor(sceneObject: SceneObject): DashboardScene {
|
||||||
const root = sceneObject.getRoot();
|
const root = sceneObject.getRoot();
|
||||||
|
|
||||||
if (root instanceof PanelEditor) {
|
|
||||||
return root.state.dashboardRef.resolve();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (root instanceof DashboardScene) {
|
if (root instanceof DashboardScene) {
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import {
|
|||||||
TestDataSourceResponse,
|
TestDataSourceResponse,
|
||||||
} from '@grafana/data';
|
} from '@grafana/data';
|
||||||
import { SceneDataProvider, SceneDataTransformer, SceneObject } from '@grafana/scenes';
|
import { SceneDataProvider, SceneDataTransformer, SceneObject } from '@grafana/scenes';
|
||||||
import { PanelEditor } from 'app/features/dashboard-scene/panel-edit/PanelEditor';
|
|
||||||
import {
|
import {
|
||||||
findVizPanelByKey,
|
findVizPanelByKey,
|
||||||
getQueryRunnerFor,
|
getQueryRunnerFor,
|
||||||
@@ -84,13 +83,7 @@ export class DashboardDatasource extends DataSourceApi<DashboardQuery> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private findSourcePanel(scene: SceneObject, panelId: number) {
|
private findSourcePanel(scene: SceneObject, panelId: number) {
|
||||||
let sceneToSearch = scene.getRoot();
|
return findVizPanelByKey(scene, getVizPanelKeyForPanelId(panelId));
|
||||||
|
|
||||||
if (sceneToSearch instanceof PanelEditor) {
|
|
||||||
sceneToSearch = sceneToSearch.state.dashboardRef.resolve();
|
|
||||||
}
|
|
||||||
|
|
||||||
return findVizPanelByKey(sceneToSearch, getVizPanelKeyForPanelId(panelId));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
testDatasource(): Promise<TestDataSourceResponse> {
|
testDatasource(): Promise<TestDataSourceResponse> {
|
||||||
|
|||||||
@@ -38,12 +38,6 @@ export function getAppRoutes(): RouteDescriptor[] {
|
|||||||
() => import(/* webpackChunkName: "DashboardPageProxy" */ '../features/dashboard/containers/DashboardPageProxy')
|
() => import(/* webpackChunkName: "DashboardPageProxy" */ '../features/dashboard/containers/DashboardPageProxy')
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: '/d/:uid/panel-edit/:panelId',
|
|
||||||
component: SafeDynamicImport(
|
|
||||||
() => import(/* webpackChunkName: "scenes"*/ 'app/features/dashboard-scene/pages/PanelEditPage')
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: '/d/:uid/:slug?',
|
path: '/d/:uid/:slug?',
|
||||||
pageClass: 'page-dashboard',
|
pageClass: 'page-dashboard',
|
||||||
|
|||||||
Reference in New Issue
Block a user