mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Dashboard: Various fixes to new layouts (#100107)
* Dashboard: Various fixes to new layouts * review fixes * Fix * Update * Fix test
This commit is contained in:
parent
d5f1f4eb5c
commit
0916994d0a
@ -58,7 +58,13 @@ import { dashboardSceneGraph } from '../utils/dashboardSceneGraph';
|
||||
import { djb2Hash } from '../utils/djb2Hash';
|
||||
import { getDashboardUrl } from '../utils/getDashboardUrl';
|
||||
import { getViewPanelUrl } from '../utils/urlBuilders';
|
||||
import { getClosestVizPanel, getDashboardSceneFor, getDefaultVizPanel, getPanelIdForVizPanel } from '../utils/utils';
|
||||
import {
|
||||
getClosestVizPanel,
|
||||
getDashboardSceneFor,
|
||||
getDefaultVizPanel,
|
||||
getLayoutManagerFor,
|
||||
getPanelIdForVizPanel,
|
||||
} from '../utils/utils';
|
||||
import { SchemaV2EditorDrawer } from '../v2schema/SchemaV2EditorDrawer';
|
||||
|
||||
import { AddLibraryPanelDrawer } from './AddLibraryPanelDrawer';
|
||||
@ -475,10 +481,6 @@ export class DashboardScene extends SceneObjectBase<DashboardSceneState> {
|
||||
return this._initialState;
|
||||
}
|
||||
|
||||
public getNextPanelId(): number {
|
||||
return this.state.body.getMaxPanelId() + 1;
|
||||
}
|
||||
|
||||
public addPanel(vizPanel: VizPanel): void {
|
||||
if (!this.state.isEditing) {
|
||||
this.onEnterEditMode();
|
||||
@ -502,7 +504,7 @@ export class DashboardScene extends SceneObjectBase<DashboardSceneState> {
|
||||
}
|
||||
|
||||
public duplicatePanel(vizPanel: VizPanel) {
|
||||
this.state.body.duplicatePanel(vizPanel);
|
||||
getLayoutManagerFor(vizPanel).duplicatePanel(vizPanel);
|
||||
}
|
||||
|
||||
public copyPanel(vizPanel: VizPanel) {
|
||||
@ -536,7 +538,7 @@ export class DashboardScene extends SceneObjectBase<DashboardSceneState> {
|
||||
}
|
||||
|
||||
public removePanel(panel: VizPanel) {
|
||||
this.state.body.removePanel(panel);
|
||||
getLayoutManagerFor(panel).removePanel(panel);
|
||||
}
|
||||
|
||||
public unlinkLibraryPanel(panel: VizPanel) {
|
||||
|
@ -26,22 +26,6 @@ describe('DefaultGridLayoutManager', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('getMaxPanelId', () => {
|
||||
it('should get max panel id in a simple 3 panel layout', () => {
|
||||
const { manager } = setup();
|
||||
const id = manager.getMaxPanelId();
|
||||
|
||||
expect(id).toBe(3);
|
||||
});
|
||||
|
||||
it('should return 0 if no panels are found', () => {
|
||||
const { manager } = setup({ gridItems: [] });
|
||||
const id = manager.getMaxPanelId();
|
||||
|
||||
expect(id).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('addPanel', () => {
|
||||
it('Should add a new panel', () => {
|
||||
const { manager } = setup();
|
||||
|
@ -14,6 +14,7 @@ import { GRID_COLUMN_COUNT } from 'app/core/constants';
|
||||
import { t } from 'app/core/internationalization';
|
||||
|
||||
import { isClonedKey, joinCloneKeys } from '../../utils/clone';
|
||||
import { dashboardSceneGraph } from '../../utils/dashboardSceneGraph';
|
||||
import {
|
||||
forceRenderChildren,
|
||||
getPanelIdForVizPanel,
|
||||
@ -21,7 +22,6 @@ import {
|
||||
NEW_PANEL_WIDTH,
|
||||
getVizPanelKeyForPanelId,
|
||||
getGridItemKeyForPanelId,
|
||||
getDashboardSceneFor,
|
||||
} from '../../utils/utils';
|
||||
import { DashboardLayoutManager } from '../types/DashboardLayoutManager';
|
||||
|
||||
@ -72,7 +72,7 @@ export class DefaultGridLayoutManager
|
||||
}
|
||||
|
||||
public addPanel(vizPanel: VizPanel): void {
|
||||
const panelId = this.getNextPanelId();
|
||||
const panelId = dashboardSceneGraph.getNextPanelId(this);
|
||||
|
||||
vizPanel.setState({ key: getVizPanelKeyForPanelId(panelId) });
|
||||
vizPanel.clearParent();
|
||||
@ -95,7 +95,8 @@ export class DefaultGridLayoutManager
|
||||
* Adds a new empty row
|
||||
*/
|
||||
public addNewRow(): SceneGridRow {
|
||||
const id = this.getNextPanelId();
|
||||
const id = dashboardSceneGraph.getNextPanelId(this);
|
||||
|
||||
const row = new SceneGridRow({
|
||||
key: getVizPanelKeyForPanelId(id),
|
||||
title: 'Row title',
|
||||
@ -183,7 +184,7 @@ export class DefaultGridLayoutManager
|
||||
let panelData;
|
||||
let newGridItem;
|
||||
|
||||
const newPanelId = this.getNextPanelId();
|
||||
const newPanelId = dashboardSceneGraph.getNextPanelId(this);
|
||||
const grid = this.state.grid;
|
||||
|
||||
if (gridItem instanceof DashboardGridItem) {
|
||||
@ -248,53 +249,6 @@ export class DefaultGridLayoutManager
|
||||
return panels;
|
||||
}
|
||||
|
||||
public getMaxPanelId(): number {
|
||||
let max = 0;
|
||||
|
||||
for (const child of this.state.grid.state.children) {
|
||||
if (child instanceof DashboardGridItem) {
|
||||
const vizPanel = child.state.body;
|
||||
|
||||
if (vizPanel) {
|
||||
const panelId = getPanelIdForVizPanel(vizPanel);
|
||||
|
||||
if (panelId > max) {
|
||||
max = panelId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (child instanceof SceneGridRow) {
|
||||
//rows follow the same key pattern --- e.g.: `panel-6`
|
||||
const panelId = getPanelIdForVizPanel(child);
|
||||
|
||||
if (panelId > max) {
|
||||
max = panelId;
|
||||
}
|
||||
|
||||
for (const rowChild of child.state.children) {
|
||||
if (rowChild instanceof DashboardGridItem) {
|
||||
const vizPanel = rowChild.state.body;
|
||||
|
||||
if (vizPanel) {
|
||||
const panelId = getPanelIdForVizPanel(vizPanel);
|
||||
|
||||
if (panelId > max) {
|
||||
max = panelId;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return max;
|
||||
}
|
||||
|
||||
public getNextPanelId(): number {
|
||||
return getDashboardSceneFor(this).getNextPanelId();
|
||||
}
|
||||
|
||||
public collapseAllRows(): void {
|
||||
this.state.grid.state.children.forEach((child) => {
|
||||
if (!(child instanceof SceneGridRow)) {
|
||||
|
@ -4,7 +4,8 @@ import { Select } from '@grafana/ui';
|
||||
import { t } from 'app/core/internationalization';
|
||||
import { OptionsPaneItemDescriptor } from 'app/features/dashboard/components/PanelEditor/OptionsPaneItemDescriptor';
|
||||
|
||||
import { getDashboardSceneFor, getPanelIdForVizPanel, getVizPanelKeyForPanelId } from '../../utils/utils';
|
||||
import { dashboardSceneGraph } from '../../utils/dashboardSceneGraph';
|
||||
import { getDashboardSceneFor, getGridItemKeyForPanelId, getVizPanelKeyForPanelId } from '../../utils/utils';
|
||||
import { RowsLayoutManager } from '../layout-rows/RowsLayoutManager';
|
||||
import { DashboardLayoutManager } from '../types/DashboardLayoutManager';
|
||||
|
||||
@ -33,10 +34,17 @@ export class ResponsiveGridLayoutManager
|
||||
|
||||
public readonly descriptor = ResponsiveGridLayoutManager.descriptor;
|
||||
|
||||
public constructor(state: ResponsiveGridLayoutManagerState) {
|
||||
super(state);
|
||||
|
||||
//@ts-ignore
|
||||
this.state.layout.getDragClassCancel = () => 'drag-cancel';
|
||||
}
|
||||
|
||||
public editModeChanged(isEditing: boolean): void {}
|
||||
|
||||
public addPanel(vizPanel: VizPanel): void {
|
||||
const panelId = this.getNextPanelId();
|
||||
const panelId = dashboardSceneGraph.getNextPanelId(this);
|
||||
|
||||
vizPanel.setState({ key: getVizPanelKeyForPanelId(panelId) });
|
||||
vizPanel.clearParent();
|
||||
@ -52,33 +60,35 @@ export class ResponsiveGridLayoutManager
|
||||
getDashboardSceneFor(this).switchLayout(rowsLayout);
|
||||
}
|
||||
|
||||
public getMaxPanelId(): number {
|
||||
let max = 0;
|
||||
|
||||
for (const child of this.state.layout.state.children) {
|
||||
if (child instanceof VizPanel) {
|
||||
let panelId = getPanelIdForVizPanel(child);
|
||||
|
||||
if (panelId > max) {
|
||||
max = panelId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return max;
|
||||
}
|
||||
|
||||
public getNextPanelId(): number {
|
||||
return getDashboardSceneFor(this).getNextPanelId();
|
||||
}
|
||||
|
||||
public removePanel(panel: VizPanel) {
|
||||
const element = panel.parent;
|
||||
this.state.layout.setState({ children: this.state.layout.state.children.filter((child) => child !== element) });
|
||||
}
|
||||
|
||||
public duplicatePanel(panel: VizPanel): void {
|
||||
throw new Error('Method not implemented.');
|
||||
const gridItem = panel.parent;
|
||||
if (!(gridItem instanceof ResponsiveGridItem)) {
|
||||
console.error('Trying to duplicate a panel that is not inside a DashboardGridItem');
|
||||
return;
|
||||
}
|
||||
|
||||
const newPanelId = dashboardSceneGraph.getNextPanelId(this);
|
||||
const grid = this.state.layout;
|
||||
|
||||
const newGridItem = gridItem.clone({
|
||||
key: getGridItemKeyForPanelId(newPanelId),
|
||||
body: panel.clone({
|
||||
key: getVizPanelKeyForPanelId(newPanelId),
|
||||
}),
|
||||
});
|
||||
|
||||
const sourceIndex = grid.state.children.indexOf(gridItem);
|
||||
const newChildren = [...grid.state.children];
|
||||
|
||||
// insert after
|
||||
newChildren.splice(sourceIndex + 1, 0, newGridItem);
|
||||
|
||||
grid.setState({ children: newChildren });
|
||||
}
|
||||
|
||||
public getVizPanels(): VizPanel[] {
|
||||
@ -124,9 +134,10 @@ export class ResponsiveGridLayoutManager
|
||||
});
|
||||
}
|
||||
|
||||
activateRepeaters?(): void {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
/**
|
||||
* Might as well implement this as a no-top function as vs-code very eagerly adds optional functions that throw Method not implemented
|
||||
*/
|
||||
public activateRepeaters(): void {}
|
||||
|
||||
public static Component = ({ model }: SceneComponentProps<ResponsiveGridLayoutManager>) => {
|
||||
return <model.state.layout.Component model={model.state.layout} />;
|
||||
|
@ -75,14 +75,6 @@ export class RowsLayoutManager extends SceneObjectBase<RowsLayoutManagerState> i
|
||||
});
|
||||
}
|
||||
|
||||
public getMaxPanelId(): number {
|
||||
return Math.max(...this.state.rows.map((row) => row.getLayout().getMaxPanelId()));
|
||||
}
|
||||
|
||||
public getNextPanelId(): number {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public removePanel(panel: VizPanel) {}
|
||||
|
||||
public removeRow(row: RowItem) {
|
||||
|
@ -38,11 +38,6 @@ export interface DashboardLayoutManager<S = {}> extends SceneObject {
|
||||
*/
|
||||
getVizPanels(): VizPanel[];
|
||||
|
||||
/**
|
||||
* Returns the highest panel id in the layout
|
||||
*/
|
||||
getMaxPanelId(): number;
|
||||
|
||||
/**
|
||||
* Add row
|
||||
*/
|
||||
|
@ -9,7 +9,7 @@ import { VizPanelLinks, VizPanelLinksMenu } from '../scene/PanelLinks';
|
||||
import { DashboardGridItem } from '../scene/layout-default/DashboardGridItem';
|
||||
import { DefaultGridLayoutManager } from '../scene/layout-default/DefaultGridLayoutManager';
|
||||
|
||||
import { dashboardSceneGraph } from './dashboardSceneGraph';
|
||||
import { dashboardSceneGraph, getNextPanelId } from './dashboardSceneGraph';
|
||||
import { findVizPanelByKey } from './utils';
|
||||
|
||||
describe('dashboardSceneGraph', () => {
|
||||
@ -22,7 +22,7 @@ describe('dashboardSceneGraph', () => {
|
||||
|
||||
it('should resolve VizPanelLinks object', () => {
|
||||
const scene = buildTestScene();
|
||||
const panelWithNoLinks = findVizPanelByKey(scene, 'panel-with-links')!;
|
||||
const panelWithNoLinks = findVizPanelByKey(scene, 'panel-2')!;
|
||||
expect(dashboardSceneGraph.getPanelLinks(panelWithNoLinks)).toBeInstanceOf(VizPanelLinks);
|
||||
});
|
||||
});
|
||||
@ -66,6 +66,24 @@ describe('dashboardSceneGraph', () => {
|
||||
expect(cursorSync).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('getNextPanelId', () => {
|
||||
it('should get next panel id in a simple 3 panel layout', () => {
|
||||
const scene = buildTestScene();
|
||||
const id = getNextPanelId(scene);
|
||||
|
||||
expect(id).toBe(3);
|
||||
});
|
||||
|
||||
it('should return 1 if no panels are found', () => {
|
||||
const scene = buildTestScene();
|
||||
|
||||
const grid = scene.state.body as DefaultGridLayoutManager;
|
||||
grid.state.grid.setState({ children: [] });
|
||||
const id = getNextPanelId(scene);
|
||||
expect(id).toBe(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function buildTestScene(overrides?: Partial<DashboardSceneState>) {
|
||||
@ -111,7 +129,7 @@ function buildTestScene(overrides?: Partial<DashboardSceneState>) {
|
||||
new DashboardGridItem({
|
||||
body: new VizPanel({
|
||||
title: 'Panel D',
|
||||
key: 'panel-with-links',
|
||||
key: 'panel-2',
|
||||
pluginId: 'table',
|
||||
$data: new SceneQueryRunner({ key: 'data-query-runner3', queries: [{ refId: 'A' }] }),
|
||||
titleItems: [new VizPanelLinks({ menu: new VizPanelLinksMenu({}) })],
|
||||
|
@ -1,10 +1,11 @@
|
||||
import { VizPanel, sceneGraph, behaviors } from '@grafana/scenes';
|
||||
import { VizPanel, sceneGraph, behaviors, SceneObject, SceneGridRow } from '@grafana/scenes';
|
||||
|
||||
import { DashboardDataLayerSet } from '../scene/DashboardDataLayerSet';
|
||||
import { DashboardScene } from '../scene/DashboardScene';
|
||||
import { VizPanelLinks } from '../scene/PanelLinks';
|
||||
|
||||
import { getLayoutManagerFor } from './utils';
|
||||
import { isClonedKey } from './clone';
|
||||
import { getLayoutManagerFor, getPanelIdForVizPanel } from './utils';
|
||||
|
||||
function getTimePicker(scene: DashboardScene) {
|
||||
return scene.state.controls?.state.timePicker;
|
||||
@ -28,6 +29,29 @@ function getVizPanels(scene: DashboardScene): VizPanel[] {
|
||||
return scene.state.body.getVizPanels();
|
||||
}
|
||||
|
||||
/**
|
||||
* Will look for all panels in the entire scene starting from root
|
||||
* and find the next free panel id
|
||||
*/
|
||||
export function getNextPanelId(scene: SceneObject): number {
|
||||
let max = 0;
|
||||
|
||||
sceneGraph
|
||||
.findAllObjects(scene.getRoot(), (obj) => obj instanceof VizPanel || obj instanceof SceneGridRow)
|
||||
.forEach((panel) => {
|
||||
if (isClonedKey(panel.state.key!)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const panelId = getPanelIdForVizPanel(panel);
|
||||
if (panelId > max) {
|
||||
max = panelId;
|
||||
}
|
||||
});
|
||||
|
||||
return max + 1;
|
||||
}
|
||||
|
||||
function getDataLayers(scene: DashboardScene): DashboardDataLayerSet {
|
||||
const data = sceneGraph.getData(scene);
|
||||
|
||||
@ -56,4 +80,5 @@ export const dashboardSceneGraph = {
|
||||
getDataLayers,
|
||||
getCursorSync,
|
||||
getLayoutManagerFor,
|
||||
getNextPanelId,
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user