mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
* wip * Refactor find panel by key * clean up lint, make isLoading optional * change library panel so that the dashboard key is attached to the panel instead of the library panel * do not reload everything when the library panel is already loaded * Progress on library panel options in options pane * We can skip building the edit scene until we have the library panel loaded * undo changes to findLibraryPanelbyKey, changes not necessary when the panel has the findable id instead of the library panel * fix undo * make sure the save model gets the id from the panel and not the library panel * remove non necessary links and data providers from dummy loading panel * change library panel so that the dashboard key is attached to the panel instead of the library panel * make sure the save model gets the id from the panel and not the library panel * do not reload everything when the library panel is already loaded * Fix merge issue * Clean up * lint cleanup * wip saving * working save * use title from panel model * move library panel api functions * fix issue from merge * Add confirm save modal. Update library panel to response from save request. Add library panel information box to panel options * Better naming * Remove library panel from viz panel state, use sourcePanel.parent instead. Fix edited by time formatting * Add tests for editing library panels * implement changed from review feedback * minor refactor from feedback
215 lines
7.0 KiB
TypeScript
215 lines
7.0 KiB
TypeScript
import { Unsubscribable } from 'rxjs';
|
|
|
|
import { AppEvents } from '@grafana/data';
|
|
import { locationService } from '@grafana/runtime';
|
|
import {
|
|
SceneObjectBase,
|
|
SceneObjectState,
|
|
SceneObjectUrlSyncHandler,
|
|
SceneObjectUrlValues,
|
|
VizPanel,
|
|
} from '@grafana/scenes';
|
|
import appEvents from 'app/core/app_events';
|
|
|
|
import { PanelInspectDrawer } from '../inspect/PanelInspectDrawer';
|
|
import { buildPanelEditScene } from '../panel-edit/PanelEditor';
|
|
import { createDashboardEditViewFor } from '../settings/utils';
|
|
import { findVizPanelByKey, getDashboardSceneFor, getLibraryPanel, isPanelClone } from '../utils/utils';
|
|
|
|
import { DashboardScene, DashboardSceneState } from './DashboardScene';
|
|
import { LibraryVizPanel } from './LibraryVizPanel';
|
|
import { ViewPanelScene } from './ViewPanelScene';
|
|
import { DashboardRepeatsProcessedEvent } from './types';
|
|
|
|
export class DashboardSceneUrlSync implements SceneObjectUrlSyncHandler {
|
|
private _eventSub?: Unsubscribable;
|
|
|
|
constructor(private _scene: DashboardScene) {}
|
|
|
|
getKeys(): string[] {
|
|
return ['inspect', 'viewPanel', 'editPanel', 'editview'];
|
|
}
|
|
|
|
getUrlState(): SceneObjectUrlValues {
|
|
const state = this._scene.state;
|
|
return {
|
|
inspect: state.inspectPanelKey,
|
|
viewPanel: state.viewPanelScene?.getUrlKey(),
|
|
editview: state.editview?.getUrlKey(),
|
|
editPanel: state.editPanel?.getUrlKey() || undefined,
|
|
};
|
|
}
|
|
|
|
updateFromUrl(values: SceneObjectUrlValues): void {
|
|
const { inspectPanelKey, viewPanelScene, isEditing, editPanel } = this._scene.state;
|
|
const update: Partial<DashboardSceneState> = {};
|
|
|
|
if (typeof values.editview === 'string' && this._scene.canEditDashboard()) {
|
|
update.editview = createDashboardEditViewFor(values.editview);
|
|
|
|
// If we are not in editing (for example after full page reload)
|
|
if (!isEditing) {
|
|
// Not sure what is best to do here.
|
|
// The reason for the timeout is for this change to happen after the url sync has completed
|
|
setTimeout(() => this._scene.onEnterEditMode());
|
|
}
|
|
} else if (values.hasOwnProperty('editview')) {
|
|
update.editview = undefined;
|
|
}
|
|
|
|
// Handle inspect object state
|
|
if (typeof values.inspect === 'string') {
|
|
let panel = findVizPanelByKey(this._scene, values.inspect);
|
|
if (!panel) {
|
|
appEvents.emit(AppEvents.alertError, ['Panel not found']);
|
|
locationService.partial({ inspect: null });
|
|
return;
|
|
}
|
|
|
|
if (getLibraryPanel(panel)) {
|
|
this._handleLibraryPanel(panel, (p) => {
|
|
if (p.state.key === undefined) {
|
|
// Inspect drawer require a panel key to be set
|
|
throw new Error('library panel key is undefined');
|
|
}
|
|
const drawer = new PanelInspectDrawer({
|
|
$behaviors: [new ResolveInspectPanelByKey({ panelKey: p.state.key })],
|
|
});
|
|
this._scene.setState({ overlay: drawer, inspectPanelKey: p.state.key });
|
|
});
|
|
return;
|
|
}
|
|
|
|
update.inspectPanelKey = values.inspect;
|
|
update.overlay = new PanelInspectDrawer({
|
|
$behaviors: [new ResolveInspectPanelByKey({ panelKey: values.inspect })],
|
|
});
|
|
} else if (inspectPanelKey) {
|
|
update.inspectPanelKey = undefined;
|
|
update.overlay = undefined;
|
|
}
|
|
|
|
// Handle view panel state
|
|
if (typeof values.viewPanel === 'string') {
|
|
const panel = findVizPanelByKey(this._scene, values.viewPanel);
|
|
|
|
if (!panel) {
|
|
// // If we are trying to view a repeat clone that can't be found it might be that the repeats have not been processed yet
|
|
if (isPanelClone(values.viewPanel)) {
|
|
this._handleViewRepeatClone(values.viewPanel);
|
|
return;
|
|
}
|
|
|
|
appEvents.emit(AppEvents.alertError, ['Panel not found']);
|
|
locationService.partial({ viewPanel: null });
|
|
return;
|
|
}
|
|
|
|
if (getLibraryPanel(panel)) {
|
|
this._handleLibraryPanel(panel, (p) => this._buildLibraryPanelViewScene(p));
|
|
return;
|
|
}
|
|
|
|
update.viewPanelScene = new ViewPanelScene({ panelRef: panel.getRef() });
|
|
} else if (viewPanelScene && values.viewPanel === null) {
|
|
update.viewPanelScene = undefined;
|
|
}
|
|
|
|
// Handle edit panel state
|
|
if (typeof values.editPanel === 'string') {
|
|
const panel = findVizPanelByKey(this._scene, values.editPanel);
|
|
if (!panel) {
|
|
console.warn(`Panel ${values.editPanel} not found`);
|
|
return;
|
|
}
|
|
|
|
// If we are not in editing (for example after full page reload)
|
|
if (!isEditing) {
|
|
this._scene.onEnterEditMode();
|
|
}
|
|
if (getLibraryPanel(panel)) {
|
|
this._handleLibraryPanel(panel, (p) => {
|
|
this._scene.setState({ editPanel: buildPanelEditScene(p) });
|
|
});
|
|
return;
|
|
}
|
|
update.editPanel = buildPanelEditScene(panel);
|
|
} else if (editPanel && values.editPanel === null) {
|
|
update.editPanel = undefined;
|
|
}
|
|
|
|
if (Object.keys(update).length > 0) {
|
|
this._scene.setState(update);
|
|
}
|
|
}
|
|
|
|
private _buildLibraryPanelViewScene(vizPanel: VizPanel) {
|
|
this._scene.setState({ viewPanelScene: new ViewPanelScene({ panelRef: vizPanel.getRef() }) });
|
|
}
|
|
|
|
private _handleLibraryPanel(vizPanel: VizPanel, cb: (p: VizPanel) => void): void {
|
|
if (!(vizPanel.parent instanceof LibraryVizPanel)) {
|
|
throw new Error('Panel is not a child of a LibraryVizPanel');
|
|
}
|
|
const libraryPanel = vizPanel.parent;
|
|
if (libraryPanel.state.isLoaded) {
|
|
cb(vizPanel);
|
|
} else {
|
|
libraryPanel.subscribeToState((n) => {
|
|
cb(n.panel!);
|
|
});
|
|
libraryPanel.activate();
|
|
}
|
|
}
|
|
|
|
private _handleViewRepeatClone(viewPanel: string) {
|
|
if (!this._eventSub) {
|
|
this._eventSub = this._scene.subscribeToEvent(DashboardRepeatsProcessedEvent, () => {
|
|
const panel = findVizPanelByKey(this._scene, viewPanel);
|
|
if (panel) {
|
|
this._eventSub?.unsubscribe();
|
|
this._scene.setState({ viewPanelScene: new ViewPanelScene({ panelRef: panel.getRef() }) });
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
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.vizManager.state.panel;
|
|
}
|
|
|
|
if (dashboard.state.viewPanelScene && dashboard.state.viewPanelScene.state.body) {
|
|
panel = dashboard.state.viewPanelScene.state.body;
|
|
}
|
|
|
|
if (panel) {
|
|
parent.setState({ panelRef: panel.getRef() });
|
|
}
|
|
};
|
|
}
|