mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
ViewPanel: Refactoring to simplify logic by rendering the source panel instead of a clone (#93000)
* ViewPanel: Refactoring to simplify logic by rendering the source panel instead of a clone * Moved util function to utils * Update
This commit is contained in:
parent
427af2e7ec
commit
3111f23bf1
@ -58,7 +58,6 @@ export class DashboardGridItem extends SceneObjectBase<DashboardGridItemState> i
|
|||||||
this._prevRepeatValues = undefined;
|
this._prevRepeatValues = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._oldBody = this.state.body;
|
|
||||||
this.performRepeat();
|
this.performRepeat();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -117,7 +116,9 @@ export class DashboardGridItem extends SceneObjectBase<DashboardGridItemState> i
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._oldBody = this.state.body;
|
||||||
this._prevRepeatValues = values;
|
this._prevRepeatValues = values;
|
||||||
|
|
||||||
const panelToRepeat = this.state.body;
|
const panelToRepeat = this.state.body;
|
||||||
const repeatedPanels: VizPanel[] = [];
|
const repeatedPanels: VizPanel[] = [];
|
||||||
|
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
import { AppEvents } from '@grafana/data';
|
import { AppEvents } from '@grafana/data';
|
||||||
import { SceneGridLayout, SceneGridRow, SceneQueryRunner, VizPanel } from '@grafana/scenes';
|
import { SceneGridLayout, SceneQueryRunner, VizPanel } from '@grafana/scenes';
|
||||||
import appEvents from 'app/core/app_events';
|
import appEvents from 'app/core/app_events';
|
||||||
import { KioskMode } from 'app/types';
|
import { KioskMode } from 'app/types';
|
||||||
|
|
||||||
import { DashboardGridItem } from './DashboardGridItem';
|
import { DashboardGridItem } from './DashboardGridItem';
|
||||||
import { DashboardScene } from './DashboardScene';
|
import { DashboardScene } from './DashboardScene';
|
||||||
import { RowRepeaterBehavior } from './RowRepeaterBehavior';
|
|
||||||
import { DashboardRepeatsProcessedEvent } from './types';
|
import { DashboardRepeatsProcessedEvent } from './types';
|
||||||
|
|
||||||
describe('DashboardSceneUrlSync', () => {
|
describe('DashboardSceneUrlSync', () => {
|
||||||
@ -109,35 +108,6 @@ describe('DashboardSceneUrlSync', () => {
|
|||||||
scene.publishEvent(new DashboardRepeatsProcessedEvent({ source: scene }));
|
scene.publishEvent(new DashboardRepeatsProcessedEvent({ source: scene }));
|
||||||
expect(scene.state.viewPanelScene?.getUrlKey()).toBe('panel-1-clone-1');
|
expect(scene.state.viewPanelScene?.getUrlKey()).toBe('panel-1-clone-1');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should subscribe and update view panel if panel is in a repeated row', () => {
|
|
||||||
const scene = buildTestScene();
|
|
||||||
|
|
||||||
// fake adding row panel
|
|
||||||
const layout = scene.state.body as SceneGridLayout;
|
|
||||||
layout.setState({
|
|
||||||
children: [
|
|
||||||
new SceneGridRow({
|
|
||||||
$behaviors: [new RowRepeaterBehavior({ variableName: 'test' })],
|
|
||||||
children: [
|
|
||||||
new VizPanel({
|
|
||||||
title: 'Panel A',
|
|
||||||
key: 'panel-1',
|
|
||||||
pluginId: 'table',
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
scene.urlSync?.updateFromUrl({ viewPanel: 'panel-1' });
|
|
||||||
|
|
||||||
expect(scene.state.viewPanelScene?.getUrlKey()).toBeUndefined();
|
|
||||||
|
|
||||||
// Verify it subscribes to DashboardRepeatsProcessedEvent
|
|
||||||
scene.publishEvent(new DashboardRepeatsProcessedEvent({ source: scene }));
|
|
||||||
expect(scene.state.viewPanelScene?.getUrlKey()).toBe('panel-1');
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function buildTestScene() {
|
function buildTestScene() {
|
||||||
|
@ -18,13 +18,7 @@ import { buildPanelEditScene } from '../panel-edit/PanelEditor';
|
|||||||
import { createDashboardEditViewFor } from '../settings/utils';
|
import { createDashboardEditViewFor } from '../settings/utils';
|
||||||
import { ShareDrawer } from '../sharing/ShareDrawer/ShareDrawer';
|
import { ShareDrawer } from '../sharing/ShareDrawer/ShareDrawer';
|
||||||
import { ShareModal } from '../sharing/ShareModal';
|
import { ShareModal } from '../sharing/ShareModal';
|
||||||
import {
|
import { findVizPanelByKey, getDashboardSceneFor, getLibraryPanelBehavior, isPanelClone } from '../utils/utils';
|
||||||
findVizPanelByKey,
|
|
||||||
getDashboardSceneFor,
|
|
||||||
getLibraryPanelBehavior,
|
|
||||||
isPanelClone,
|
|
||||||
isWithinUnactivatedRepeatRow,
|
|
||||||
} from '../utils/utils';
|
|
||||||
|
|
||||||
import { DashboardScene, DashboardSceneState } from './DashboardScene';
|
import { DashboardScene, DashboardSceneState } from './DashboardScene';
|
||||||
import { LibraryPanelBehavior } from './LibraryPanelBehavior';
|
import { LibraryPanelBehavior } from './LibraryPanelBehavior';
|
||||||
@ -108,11 +102,6 @@ export class DashboardSceneUrlSync implements SceneObjectUrlSyncHandler {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isWithinUnactivatedRepeatRow(panel)) {
|
|
||||||
this._handleViewRepeatClone(values.viewPanel);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
update.viewPanelScene = new ViewPanelScene({ panelRef: panel.getRef() });
|
update.viewPanelScene = new ViewPanelScene({ panelRef: panel.getRef() });
|
||||||
} else if (viewPanelScene && values.viewPanel === null) {
|
} else if (viewPanelScene && values.viewPanel === null) {
|
||||||
update.viewPanelScene = undefined;
|
update.viewPanelScene = undefined;
|
||||||
@ -236,10 +225,6 @@ class ResolveInspectPanelByKey extends SceneObjectBase<ResolveInspectPanelByKeyS
|
|||||||
panel = dashboard.state.editPanel.state.vizManager.state.panel;
|
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) {
|
if (panel) {
|
||||||
parent.setState({ panelRef: panel.getRef() });
|
parent.setState({ panelRef: panel.getRef() });
|
||||||
}
|
}
|
||||||
|
@ -4,19 +4,19 @@ import { DashboardGridItem } from './DashboardGridItem';
|
|||||||
import { DashboardScene } from './DashboardScene';
|
import { DashboardScene } from './DashboardScene';
|
||||||
import { ViewPanelScene } from './ViewPanelScene';
|
import { ViewPanelScene } from './ViewPanelScene';
|
||||||
|
|
||||||
|
jest.mock('@grafana/runtime', () => ({
|
||||||
|
...jest.requireActual('@grafana/runtime'),
|
||||||
|
getPluginImportUtils: () => ({
|
||||||
|
getPanelPluginFromCache: jest.fn(() => {}),
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
describe('ViewPanelScene', () => {
|
describe('ViewPanelScene', () => {
|
||||||
it('Should build scene on activate', () => {
|
it('Should activate panel parents', () => {
|
||||||
const { viewPanelScene } = buildScene();
|
const { viewPanelScene, dashboard } = buildScene();
|
||||||
viewPanelScene.activate();
|
viewPanelScene.activate();
|
||||||
expect(viewPanelScene.state.body).toBeDefined();
|
expect(viewPanelScene.state.panelRef.resolve().isActive).toBe(true);
|
||||||
});
|
expect(dashboard.state.body.isActive).toBe(true);
|
||||||
|
|
||||||
it('Should look copy row variable scope', () => {
|
|
||||||
const { viewPanelScene } = buildScene({ rowVariables: true, panelVariables: true });
|
|
||||||
viewPanelScene.activate();
|
|
||||||
|
|
||||||
const variables = viewPanelScene.state.body?.state.$variables;
|
|
||||||
expect(variables?.state.variables.length).toBe(2);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -29,11 +29,6 @@ function buildScene(options?: SceneOptions) {
|
|||||||
// builds a scene how it looks like after row and panel repeats are processed
|
// builds a scene how it looks like after row and panel repeats are processed
|
||||||
const panel = new VizPanel({
|
const panel = new VizPanel({
|
||||||
key: 'panel-22',
|
key: 'panel-22',
|
||||||
$variables: options?.panelVariables
|
|
||||||
? new SceneVariableSet({
|
|
||||||
variables: [new LocalValueVariable({ value: 'panel-var-value' })],
|
|
||||||
})
|
|
||||||
: undefined,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const dashboard = new DashboardScene({
|
const dashboard = new DashboardScene({
|
||||||
@ -43,11 +38,9 @@ function buildScene(options?: SceneOptions) {
|
|||||||
x: 0,
|
x: 0,
|
||||||
y: 10,
|
y: 10,
|
||||||
width: 24,
|
width: 24,
|
||||||
$variables: options?.rowVariables
|
$variables: new SceneVariableSet({
|
||||||
? new SceneVariableSet({
|
variables: [new LocalValueVariable({ value: 'row-var-value' })],
|
||||||
variables: [new LocalValueVariable({ value: 'row-var-value' })],
|
}),
|
||||||
})
|
|
||||||
: undefined,
|
|
||||||
height: 1,
|
height: 1,
|
||||||
children: [
|
children: [
|
||||||
new DashboardGridItem({
|
new DashboardGridItem({
|
||||||
|
@ -1,20 +1,9 @@
|
|||||||
import {
|
import { SceneComponentProps, SceneObjectBase, SceneObjectRef, SceneObjectState, VizPanel } from '@grafana/scenes';
|
||||||
SceneComponentProps,
|
|
||||||
SceneObjectBase,
|
import { activateInActiveParents } from '../utils/utils';
|
||||||
SceneObjectRef,
|
|
||||||
SceneObjectState,
|
|
||||||
VizPanel,
|
|
||||||
sceneUtils,
|
|
||||||
SceneVariables,
|
|
||||||
SceneGridRow,
|
|
||||||
sceneGraph,
|
|
||||||
SceneVariableSet,
|
|
||||||
SceneVariable,
|
|
||||||
} from '@grafana/scenes';
|
|
||||||
|
|
||||||
interface ViewPanelSceneState extends SceneObjectState {
|
interface ViewPanelSceneState extends SceneObjectState {
|
||||||
panelRef: SceneObjectRef<VizPanel>;
|
panelRef: SceneObjectRef<VizPanel>;
|
||||||
body?: VizPanel;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ViewPanelScene extends SceneObjectBase<ViewPanelSceneState> {
|
export class ViewPanelScene extends SceneObjectBase<ViewPanelSceneState> {
|
||||||
@ -26,47 +15,7 @@ export class ViewPanelScene extends SceneObjectBase<ViewPanelSceneState> {
|
|||||||
|
|
||||||
public _activationHandler() {
|
public _activationHandler() {
|
||||||
const panel = this.state.panelRef.resolve();
|
const panel = this.state.panelRef.resolve();
|
||||||
const panelState = sceneUtils.cloneSceneObjectState(panel.state, {
|
return activateInActiveParents(panel);
|
||||||
key: panel.state.key + '-view',
|
|
||||||
$variables: this.getScopedVariables(panel),
|
|
||||||
});
|
|
||||||
|
|
||||||
const body = new VizPanel(panelState);
|
|
||||||
|
|
||||||
this.setState({ body });
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
// Make sure we preserve data state
|
|
||||||
if (body.state.$data) {
|
|
||||||
panel.setState({ $data: body.state.$data.clone() });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// In case the panel is inside a repeated row
|
|
||||||
private getScopedVariables(panel: VizPanel): SceneVariables | undefined {
|
|
||||||
const row = tryGetParentRow(panel);
|
|
||||||
const variables: SceneVariable[] = [];
|
|
||||||
|
|
||||||
// Because we are rendering the panel outside it's potential row context we need to copy the row (scoped) varables
|
|
||||||
if (row && row.state.$variables) {
|
|
||||||
for (const variable of row.state.$variables.state.variables) {
|
|
||||||
variables.push(variable.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we have local scoped panel variables we need to add the row variables to it
|
|
||||||
if (panel.state.$variables) {
|
|
||||||
for (const variable of panel.state.$variables.state.variables) {
|
|
||||||
variables.push(variable.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (variables.length > 0) {
|
|
||||||
return new SceneVariableSet({ variables });
|
|
||||||
}
|
|
||||||
|
|
||||||
return undefined;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public getUrlKey() {
|
public getUrlKey() {
|
||||||
@ -74,20 +23,9 @@ export class ViewPanelScene extends SceneObjectBase<ViewPanelSceneState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static Component = ({ model }: SceneComponentProps<ViewPanelScene>) => {
|
public static Component = ({ model }: SceneComponentProps<ViewPanelScene>) => {
|
||||||
const { body } = model.useState();
|
const { panelRef } = model.useState();
|
||||||
|
const panel = panelRef.resolve();
|
||||||
|
|
||||||
if (!body) {
|
return <panel.Component model={panel} />;
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return <body.Component model={body} />;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function tryGetParentRow(panel: VizPanel): SceneGridRow | undefined {
|
|
||||||
try {
|
|
||||||
return sceneGraph.getAncestor(panel, SceneGridRow);
|
|
||||||
} catch {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { getDataSourceRef, IntervalVariableModel } from '@grafana/data';
|
import { getDataSourceRef, IntervalVariableModel } from '@grafana/data';
|
||||||
import { getDataSourceSrv } from '@grafana/runtime';
|
import { getDataSourceSrv } from '@grafana/runtime';
|
||||||
import {
|
import {
|
||||||
|
CancelActivationHandler,
|
||||||
CustomVariable,
|
CustomVariable,
|
||||||
MultiValueVariable,
|
MultiValueVariable,
|
||||||
SceneDataTransformer,
|
SceneDataTransformer,
|
||||||
@ -18,7 +19,6 @@ import { DashboardScene } from '../scene/DashboardScene';
|
|||||||
import { LibraryPanelBehavior } from '../scene/LibraryPanelBehavior';
|
import { LibraryPanelBehavior } from '../scene/LibraryPanelBehavior';
|
||||||
import { VizPanelLinks, VizPanelLinksMenu } from '../scene/PanelLinks';
|
import { VizPanelLinks, VizPanelLinksMenu } from '../scene/PanelLinks';
|
||||||
import { panelMenuBehavior } from '../scene/PanelMenuBehavior';
|
import { panelMenuBehavior } from '../scene/PanelMenuBehavior';
|
||||||
import { RowRepeaterBehavior } from '../scene/RowRepeaterBehavior';
|
|
||||||
import { RowActions } from '../scene/row-actions/RowActions';
|
import { RowActions } from '../scene/row-actions/RowActions';
|
||||||
|
|
||||||
import { dashboardSceneGraph } from './dashboardSceneGraph';
|
import { dashboardSceneGraph } from './dashboardSceneGraph';
|
||||||
@ -267,20 +267,26 @@ export function getLibraryPanelBehavior(vizPanel: VizPanel): LibraryPanelBehavio
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If the panel is within a repeated row, it must wait until the row resolves the variables.
|
* Activates any inactive parents of the scene object.
|
||||||
* This ensures that the scoped variable for the row is assigned and the panel is initialized with them.
|
* Useful when rendering a scene object out of context of it's parent
|
||||||
|
* @returns
|
||||||
*/
|
*/
|
||||||
export function isWithinUnactivatedRepeatRow(panel: VizPanel): boolean {
|
export function activateInActiveParents(so: SceneObject): CancelActivationHandler | undefined {
|
||||||
let row;
|
let cancel: CancelActivationHandler | undefined;
|
||||||
|
let parentCancel: CancelActivationHandler | undefined;
|
||||||
|
|
||||||
try {
|
if (so.isActive) {
|
||||||
row = sceneGraph.getAncestor(panel, SceneGridRow);
|
return cancel;
|
||||||
} catch (err) {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const hasBehavior = !!(row.state.$behaviors && row.state.$behaviors.find((b) => b instanceof RowRepeaterBehavior));
|
if (so.parent) {
|
||||||
const hasVariables = !!(row.state.$variables && row.state.$variables.state.variables.length > 0);
|
parentCancel = activateInActiveParents(so.parent);
|
||||||
|
}
|
||||||
|
|
||||||
return hasBehavior && !hasVariables;
|
cancel = so.activate();
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
parentCancel?.();
|
||||||
|
cancel();
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user