mirror of
https://github.com/grafana/grafana.git
synced 2025-02-13 17:15:40 -06:00
Scenes: Add support for repeated library panels (#83579)
This commit is contained in:
parent
dbe621eeb4
commit
10a55560df
@ -2573,7 +2573,8 @@ exports[`better eslint`] = {
|
||||
[0, 0, 0, "Do not use any type assertions.", "3"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "4"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "5"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "6"]
|
||||
[0, 0, 0, "Do not use any type assertions.", "6"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "7"]
|
||||
],
|
||||
"public/app/features/dashboard-scene/settings/variables/components/VariableSelectField.tsx:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
|
||||
|
@ -0,0 +1,136 @@
|
||||
import 'whatwg-fetch';
|
||||
import { waitFor } from '@testing-library/dom';
|
||||
import { merge } from 'lodash';
|
||||
import { http, HttpResponse } from 'msw';
|
||||
import { setupServer, SetupServerApi } from 'msw/node';
|
||||
|
||||
import { setBackendSrv } from '@grafana/runtime';
|
||||
import { SceneGridItem, SceneGridLayout, VizPanel } from '@grafana/scenes';
|
||||
import { LibraryPanel } from '@grafana/schema';
|
||||
import { backendSrv } from 'app/core/services/backend_srv';
|
||||
|
||||
import { LibraryVizPanel } from './LibraryVizPanel';
|
||||
import { PanelRepeaterGridItem } from './PanelRepeaterGridItem';
|
||||
|
||||
describe('LibraryVizPanel', () => {
|
||||
const server = setupServer();
|
||||
|
||||
beforeAll(() => {
|
||||
setBackendSrv(backendSrv);
|
||||
server.listen();
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
server.close();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
server.resetHandlers();
|
||||
});
|
||||
|
||||
it('should fetch and init', async () => {
|
||||
setUpApiMock(server);
|
||||
const libVizPanel = new LibraryVizPanel({
|
||||
name: 'My Library Panel',
|
||||
title: 'Panel title',
|
||||
uid: 'fdcvggvfy2qdca',
|
||||
panelKey: 'lib-panel',
|
||||
});
|
||||
libVizPanel.activate();
|
||||
await waitFor(() => {
|
||||
expect(libVizPanel.state.panel).toBeInstanceOf(VizPanel);
|
||||
});
|
||||
});
|
||||
|
||||
it('should change parent from SceneGridItem to PanelRepeaterGridItem if repeat is set', async () => {
|
||||
setUpApiMock(server, { model: { repeat: 'query0', repeatDirection: 'h' } });
|
||||
const libVizPanel = new LibraryVizPanel({
|
||||
name: 'My Library Panel',
|
||||
title: 'Panel title',
|
||||
uid: 'fdcvggvfy2qdca',
|
||||
panelKey: 'lib-panel',
|
||||
});
|
||||
|
||||
const layout = new SceneGridLayout({
|
||||
children: [new SceneGridItem({ body: libVizPanel })],
|
||||
});
|
||||
layout.activate();
|
||||
libVizPanel.activate();
|
||||
await waitFor(() => {
|
||||
expect(layout.state.children[0]).toBeInstanceOf(PanelRepeaterGridItem);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function setUpApiMock(
|
||||
server: SetupServerApi,
|
||||
overrides: Omit<Partial<LibraryPanel>, 'model'> & { model?: Partial<LibraryPanel['model']> } = {}
|
||||
) {
|
||||
const libPanel: LibraryPanel = merge(
|
||||
{
|
||||
folderUid: 'general',
|
||||
uid: 'fdcvggvfy2qdca',
|
||||
name: 'My Library Panel',
|
||||
type: 'timeseries',
|
||||
description: '',
|
||||
model: {
|
||||
datasource: {
|
||||
type: 'grafana-testdata-datasource',
|
||||
uid: 'PD8C576611E62080A',
|
||||
},
|
||||
description: '',
|
||||
|
||||
maxPerRow: 4,
|
||||
options: {
|
||||
legend: {
|
||||
calcs: [],
|
||||
displayMode: 'list',
|
||||
placement: 'bottom',
|
||||
showLegend: true,
|
||||
},
|
||||
tooltip: {
|
||||
maxHeight: 600,
|
||||
mode: 'single',
|
||||
sort: 'none',
|
||||
},
|
||||
},
|
||||
targets: [
|
||||
{
|
||||
datasource: {
|
||||
type: 'grafana-testdata-datasource',
|
||||
uid: 'PD8C576611E62080A',
|
||||
},
|
||||
refId: 'A',
|
||||
},
|
||||
],
|
||||
title: 'Panel Title',
|
||||
type: 'timeseries',
|
||||
},
|
||||
version: 6,
|
||||
meta: {
|
||||
folderName: 'General',
|
||||
folderUid: '',
|
||||
connectedDashboards: 1,
|
||||
created: '2024-02-15T15:26:46Z',
|
||||
updated: '2024-02-28T15:54:22Z',
|
||||
createdBy: {
|
||||
avatarUrl: '/avatar/46d229b033af06a191ff2267bca9ae56',
|
||||
id: 1,
|
||||
name: 'admin',
|
||||
},
|
||||
updatedBy: {
|
||||
avatarUrl: '/avatar/46d229b033af06a191ff2267bca9ae56',
|
||||
id: 1,
|
||||
name: 'admin',
|
||||
},
|
||||
},
|
||||
},
|
||||
overrides
|
||||
);
|
||||
|
||||
const libPanelMock: { result: LibraryPanel } = {
|
||||
result: libPanel,
|
||||
};
|
||||
|
||||
server.use(http.get('/api/library-elements/:uid', () => HttpResponse.json(libPanelMock)));
|
||||
}
|
@ -1,6 +1,15 @@
|
||||
import React from 'react';
|
||||
|
||||
import { SceneComponentProps, SceneObjectBase, SceneObjectState, VizPanel, VizPanelMenu } from '@grafana/scenes';
|
||||
import {
|
||||
SceneComponentProps,
|
||||
SceneGridItem,
|
||||
SceneGridLayout,
|
||||
SceneObjectBase,
|
||||
SceneObjectState,
|
||||
VizPanel,
|
||||
VizPanelMenu,
|
||||
VizPanelState,
|
||||
} from '@grafana/scenes';
|
||||
import { PanelModel } from 'app/features/dashboard/state';
|
||||
import { getLibraryPanel } from 'app/features/library-panels/state/api';
|
||||
|
||||
@ -9,6 +18,7 @@ import { createPanelDataProvider } from '../utils/createPanelDataProvider';
|
||||
import { VizPanelLinks, VizPanelLinksMenu } from './PanelLinks';
|
||||
import { panelLinksBehavior, panelMenuBehavior } from './PanelMenuBehavior';
|
||||
import { PanelNotices } from './PanelNotices';
|
||||
import { PanelRepeaterGridItem } from './PanelRepeaterGridItem';
|
||||
|
||||
interface LibraryVizPanelState extends SceneObjectState {
|
||||
// Library panels use title from dashboard JSON's panel model, not from library panel definition, hence we pass it.
|
||||
@ -52,7 +62,7 @@ export class LibraryVizPanel extends SceneObjectBase<LibraryVizPanelState> {
|
||||
|
||||
const libPanelModel = new PanelModel(libPanel.model);
|
||||
|
||||
const panel = new VizPanel({
|
||||
const vizPanelState: VizPanelState = {
|
||||
title: this.state.title,
|
||||
key: this.state.panelKey,
|
||||
options: libPanelModel.options ?? {},
|
||||
@ -70,12 +80,36 @@ export class LibraryVizPanel extends SceneObjectBase<LibraryVizPanelState> {
|
||||
}),
|
||||
new PanelNotices(),
|
||||
],
|
||||
});
|
||||
};
|
||||
|
||||
const panel = new VizPanel(vizPanelState);
|
||||
const gridItem = this.parent;
|
||||
if (libPanelModel.repeat && gridItem instanceof SceneGridItem && gridItem.parent instanceof SceneGridLayout) {
|
||||
this._parent = undefined;
|
||||
const repeater = new PanelRepeaterGridItem({
|
||||
key: gridItem.state.key,
|
||||
x: libPanelModel.gridPos.x,
|
||||
y: libPanelModel.gridPos.y,
|
||||
width: libPanelModel.repeatDirection === 'h' ? 24 : libPanelModel.gridPos.w,
|
||||
height: libPanelModel.gridPos.h,
|
||||
itemHeight: libPanelModel.gridPos.h,
|
||||
source: this,
|
||||
variableName: libPanelModel.repeat,
|
||||
repeatedPanels: [],
|
||||
repeatDirection: libPanelModel.repeatDirection === 'h' ? 'h' : 'v',
|
||||
maxPerRow: libPanelModel.maxPerRow,
|
||||
});
|
||||
gridItem.parent.setState({
|
||||
children: gridItem.parent.state.children.map((child) =>
|
||||
child.state.key === gridItem.state.key ? repeater : child
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
this.setState({ panel, _loadedVersion: libPanel.version, isLoaded: true });
|
||||
} catch (err) {
|
||||
vizPanel.setState({
|
||||
_pluginLoadError: 'Unable to load library panel: ' + this.state.uid,
|
||||
_pluginLoadError: `Unable to load library panel: ${this.state.uid}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -19,10 +19,11 @@ import { GRID_CELL_HEIGHT, GRID_CELL_VMARGIN } from 'app/core/constants';
|
||||
|
||||
import { getMultiVariableValues } from '../utils/utils';
|
||||
|
||||
import { LibraryVizPanel } from './LibraryVizPanel';
|
||||
import { DashboardRepeatsProcessedEvent } from './types';
|
||||
|
||||
interface PanelRepeaterGridItemState extends SceneGridItemStateLike {
|
||||
source: VizPanel;
|
||||
source: VizPanel | LibraryVizPanel;
|
||||
repeatedPanels?: VizPanel[];
|
||||
variableName: string;
|
||||
itemHeight?: number;
|
||||
@ -94,11 +95,12 @@ export class PanelRepeaterGridItem extends SceneObjectBase<PanelRepeaterGridItem
|
||||
return;
|
||||
}
|
||||
|
||||
const panelToRepeat = this.state.source;
|
||||
let panelToRepeat =
|
||||
this.state.source instanceof LibraryVizPanel ? this.state.source.state.panel! : this.state.source;
|
||||
const { values, texts } = getMultiVariableValues(variable);
|
||||
const repeatedPanels: VizPanel[] = [];
|
||||
|
||||
// Loop through variable values and create repeates
|
||||
// Loop through variable values and create repeats
|
||||
for (let index = 0; index < values.length; index++) {
|
||||
const clone = panelToRepeat.clone({
|
||||
$variables: new SceneVariableSet({
|
||||
|
@ -194,7 +194,12 @@ export function gridItemToPanel(gridItem: SceneGridItemLike, isSnapshot = false)
|
||||
}
|
||||
|
||||
if (gridItem instanceof PanelRepeaterGridItem) {
|
||||
vizPanel = gridItem.state.source;
|
||||
if (gridItem.state.source instanceof LibraryVizPanel) {
|
||||
vizPanel = gridItem.state.source.state.panel;
|
||||
} else {
|
||||
vizPanel = gridItem.state.source;
|
||||
}
|
||||
|
||||
x = gridItem.state.x ?? 0;
|
||||
y = gridItem.state.y ?? 0;
|
||||
w = gridItem.state.width ?? 0;
|
||||
@ -316,6 +321,22 @@ export function panelRepeaterToPanels(repeater: PanelRepeaterGridItem, isSnapsho
|
||||
if (!isSnapshot) {
|
||||
return [gridItemToPanel(repeater)];
|
||||
} else {
|
||||
if (repeater.state.source instanceof LibraryVizPanel) {
|
||||
const { x = 0, y = 0, width: w = 0, height: h = 0 } = repeater.state;
|
||||
|
||||
return [
|
||||
{
|
||||
id: getPanelIdForVizPanel(repeater.state.source),
|
||||
title: repeater.state.source.state.title,
|
||||
gridPos: { x, y, w, h },
|
||||
libraryPanel: {
|
||||
name: repeater.state.source.state.name,
|
||||
uid: repeater.state.source.state.uid,
|
||||
},
|
||||
} as Panel,
|
||||
];
|
||||
}
|
||||
|
||||
if (repeater.state.repeatedPanels) {
|
||||
const itemHeight = repeater.state.itemHeight ?? 10;
|
||||
const rowCount = Math.ceil(repeater.state.repeatedPanels!.length / repeater.getMaxPerRow());
|
||||
|
@ -65,7 +65,7 @@ function rowTypes(gridRow: SceneGridRow) {
|
||||
}
|
||||
|
||||
function panelDatasourceTypes(gridItem: SceneGridItemLike) {
|
||||
let vizPanel: VizPanel | undefined;
|
||||
let vizPanel: VizPanel | LibraryVizPanel | undefined;
|
||||
if (gridItem instanceof SceneGridItem) {
|
||||
if (gridItem.state.body instanceof LibraryVizPanel) {
|
||||
vizPanel = gridItem.state.body.state.panel;
|
||||
|
Loading…
Reference in New Issue
Block a user