mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Dashboard SchemaV2: Panel repeater (#98654)
* Wip: working layout for repeaters * Update schema * only persist orig panel * Keep only supported mode and rename repeater function * refactor dimension calcs * v1 transformer uses calculateGridItemDimensions
This commit is contained in:
parent
7499611129
commit
f3d2313f09
@ -648,6 +648,21 @@ export const defaultTimeSettingsSpec = (): TimeSettingsSpec => ({
|
|||||||
fiscalYearStartMonth: 0,
|
fiscalYearStartMonth: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// other repeat modes will be added in the future: label, frame
|
||||||
|
export const RepeatMode = "variable";
|
||||||
|
|
||||||
|
export interface RepeatOptions {
|
||||||
|
mode: "variable";
|
||||||
|
value: string;
|
||||||
|
direction?: "h" | "v";
|
||||||
|
maxPerRow?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const defaultRepeatOptions = (): RepeatOptions => ({
|
||||||
|
mode: RepeatMode,
|
||||||
|
value: "",
|
||||||
|
});
|
||||||
|
|
||||||
export interface GridLayoutItemSpec {
|
export interface GridLayoutItemSpec {
|
||||||
x: number;
|
x: number;
|
||||||
y: number;
|
y: number;
|
||||||
@ -655,6 +670,7 @@ export interface GridLayoutItemSpec {
|
|||||||
height: number;
|
height: number;
|
||||||
// reference to a PanelKind from dashboard.spec.elements Expressed as JSON Schema reference
|
// reference to a PanelKind from dashboard.spec.elements Expressed as JSON Schema reference
|
||||||
element: ElementReference;
|
element: ElementReference;
|
||||||
|
repeat?: RepeatOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const defaultGridLayoutItemSpec = (): GridLayoutItemSpec => ({
|
export const defaultGridLayoutItemSpec = (): GridLayoutItemSpec => ({
|
||||||
|
@ -455,12 +455,22 @@ TimeSettingsSpec: {
|
|||||||
nowDelay?: string // v1: timepicker.nowDelay
|
nowDelay?: string // v1: timepicker.nowDelay
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RepeatMode: "variable" // other repeat modes will be added in the future: label, frame
|
||||||
|
|
||||||
|
RepeatOptions: {
|
||||||
|
mode: RepeatMode
|
||||||
|
value: string
|
||||||
|
direction?: "h" | "v"
|
||||||
|
maxPerRow?: int
|
||||||
|
}
|
||||||
|
|
||||||
GridLayoutItemSpec: {
|
GridLayoutItemSpec: {
|
||||||
x: int
|
x: int
|
||||||
y: int
|
y: int
|
||||||
width: int
|
width: int
|
||||||
height: int
|
height: int
|
||||||
element: ElementReference // reference to a PanelKind from dashboard.spec.elements Expressed as JSON Schema reference
|
element: ElementReference // reference to a PanelKind from dashboard.spec.elements Expressed as JSON Schema reference
|
||||||
|
repeat?: RepeatOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
GridLayoutItemKind: {
|
GridLayoutItemKind: {
|
||||||
|
@ -198,6 +198,11 @@ export const handyTestingSchema: DashboardV2Spec = {
|
|||||||
width: 200,
|
width: 200,
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 0,
|
y: 0,
|
||||||
|
repeat: {
|
||||||
|
mode: 'variable',
|
||||||
|
value: 'customVar',
|
||||||
|
maxPerRow: 3,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -245,10 +245,13 @@ function createSceneGridLayoutForItems(dashboard: DashboardV2Spec): SceneGridIte
|
|||||||
key: `grid-item-${panel.spec.id}`,
|
key: `grid-item-${panel.spec.id}`,
|
||||||
x: element.spec.x,
|
x: element.spec.x,
|
||||||
y: element.spec.y,
|
y: element.spec.y,
|
||||||
width: element.spec.width,
|
width: element.spec.repeat?.direction === 'h' ? 24 : element.spec.width,
|
||||||
height: element.spec.height,
|
height: element.spec.height,
|
||||||
itemHeight: element.spec.height,
|
itemHeight: element.spec.height,
|
||||||
body: vizPanel,
|
body: vizPanel,
|
||||||
|
variableName: element.spec.repeat?.value,
|
||||||
|
repeatDirection: element.spec.repeat?.direction,
|
||||||
|
maxPerRow: element.spec.repeat?.maxPerRow,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
throw new Error(`Unknown element kind: ${element.kind}`);
|
throw new Error(`Unknown element kind: ${element.kind}`);
|
||||||
|
@ -37,7 +37,13 @@ import { RowRepeaterBehavior } from '../scene/RowRepeaterBehavior';
|
|||||||
import { DashboardGridItem } from '../scene/layout-default/DashboardGridItem';
|
import { DashboardGridItem } from '../scene/layout-default/DashboardGridItem';
|
||||||
import { DefaultGridLayoutManager } from '../scene/layout-default/DefaultGridLayoutManager';
|
import { DefaultGridLayoutManager } from '../scene/layout-default/DefaultGridLayoutManager';
|
||||||
import { dashboardSceneGraph } from '../utils/dashboardSceneGraph';
|
import { dashboardSceneGraph } from '../utils/dashboardSceneGraph';
|
||||||
import { getLibraryPanelBehavior, getPanelIdForVizPanel, getQueryRunnerFor, isLibraryPanel } from '../utils/utils';
|
import {
|
||||||
|
calculateGridItemDimensions,
|
||||||
|
getLibraryPanelBehavior,
|
||||||
|
getPanelIdForVizPanel,
|
||||||
|
getQueryRunnerFor,
|
||||||
|
isLibraryPanel,
|
||||||
|
} from '../utils/utils';
|
||||||
|
|
||||||
import { GRAFANA_DATASOURCE_REF } from './const';
|
import { GRAFANA_DATASOURCE_REF } from './const';
|
||||||
import { dataLayersToAnnotations } from './dataLayersToAnnotations';
|
import { dataLayersToAnnotations } from './dataLayersToAnnotations';
|
||||||
@ -319,11 +325,7 @@ export function panelRepeaterToPanels(repeater: DashboardGridItem, isSnapshot =
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (repeater.state.repeatedPanels) {
|
if (repeater.state.repeatedPanels) {
|
||||||
const itemHeight = repeater.state.itemHeight ?? 10;
|
const { h, w, columnCount } = calculateGridItemDimensions(repeater);
|
||||||
const rowCount = Math.ceil(repeater.state.repeatedPanels!.length / repeater.getMaxPerRow());
|
|
||||||
const columnCount = Math.ceil(repeater.state.repeatedPanels!.length / rowCount);
|
|
||||||
const w = 24 / columnCount;
|
|
||||||
const h = itemHeight;
|
|
||||||
const panels = repeater.state.repeatedPanels!.map((panel, index) => {
|
const panels = repeater.state.repeatedPanels!.map((panel, index) => {
|
||||||
let x = 0,
|
let x = 0,
|
||||||
y = 0;
|
y = 0;
|
||||||
|
@ -36,6 +36,7 @@ import {
|
|||||||
AdhocVariableKind,
|
AdhocVariableKind,
|
||||||
AnnotationQueryKind,
|
AnnotationQueryKind,
|
||||||
DataLink,
|
DataLink,
|
||||||
|
RepeatOptions,
|
||||||
} from '../../../../../packages/grafana-schema/src/schema/dashboard/v2alpha0/dashboard.gen';
|
} from '../../../../../packages/grafana-schema/src/schema/dashboard/v2alpha0/dashboard.gen';
|
||||||
import { DashboardDataLayerSet } from '../scene/DashboardDataLayerSet';
|
import { DashboardDataLayerSet } from '../scene/DashboardDataLayerSet';
|
||||||
import { DashboardScene, DashboardSceneState } from '../scene/DashboardScene';
|
import { DashboardScene, DashboardSceneState } from '../scene/DashboardScene';
|
||||||
@ -43,7 +44,13 @@ import { PanelTimeRange } from '../scene/PanelTimeRange';
|
|||||||
import { DashboardGridItem } from '../scene/layout-default/DashboardGridItem';
|
import { DashboardGridItem } from '../scene/layout-default/DashboardGridItem';
|
||||||
import { DefaultGridLayoutManager } from '../scene/layout-default/DefaultGridLayoutManager';
|
import { DefaultGridLayoutManager } from '../scene/layout-default/DefaultGridLayoutManager';
|
||||||
import { dashboardSceneGraph } from '../utils/dashboardSceneGraph';
|
import { dashboardSceneGraph } from '../utils/dashboardSceneGraph';
|
||||||
import { getPanelIdForVizPanel, getQueryRunnerFor, getVizPanelKeyForPanelId } from '../utils/utils';
|
import {
|
||||||
|
getPanelIdForVizPanel,
|
||||||
|
getQueryRunnerFor,
|
||||||
|
getVizPanelKeyForPanelId,
|
||||||
|
isLibraryPanel,
|
||||||
|
calculateGridItemDimensions,
|
||||||
|
} from '../utils/utils';
|
||||||
|
|
||||||
import { sceneVariablesSetToSchemaV2Variables } from './sceneVariablesSetToVariables';
|
import { sceneVariablesSetToSchemaV2Variables } from './sceneVariablesSetToVariables';
|
||||||
import { transformCursorSynctoEnum } from './transformToV2TypesUtils';
|
import { transformCursorSynctoEnum } from './transformToV2TypesUtils';
|
||||||
@ -107,7 +114,7 @@ export function transformSceneToSaveModelSchemaV2(scene: DashboardScene, isSnaps
|
|||||||
layout: {
|
layout: {
|
||||||
kind: 'GridLayout',
|
kind: 'GridLayout',
|
||||||
spec: {
|
spec: {
|
||||||
items: getGridLayoutItems(oldDash),
|
items: getGridLayoutItems(oldDash, isSnapshot),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// EOF layout
|
// EOF layout
|
||||||
@ -142,16 +149,16 @@ function getLiveNow(state: DashboardSceneState) {
|
|||||||
|
|
||||||
function getGridLayoutItems(state: DashboardSceneState, isSnapshot?: boolean): GridLayoutItemKind[] {
|
function getGridLayoutItems(state: DashboardSceneState, isSnapshot?: boolean): GridLayoutItemKind[] {
|
||||||
const body = state.body;
|
const body = state.body;
|
||||||
const elements: GridLayoutItemKind[] = [];
|
let elements: GridLayoutItemKind[] = [];
|
||||||
if (body instanceof DefaultGridLayoutManager) {
|
if (body instanceof DefaultGridLayoutManager) {
|
||||||
for (const child of body.state.grid.state.children) {
|
for (const child of body.state.grid.state.children) {
|
||||||
if (child instanceof DashboardGridItem) {
|
if (child instanceof DashboardGridItem) {
|
||||||
// TODO: handle panel repeater scenario
|
// TODO: handle panel repeater scenario
|
||||||
// if (child.state.variableName) {
|
if (child.state.variableName) {
|
||||||
// panels = panels.concat(panelRepeaterToPanels(child, isSnapshot));
|
elements = elements.concat(repeaterToLayoutItems(child, isSnapshot));
|
||||||
// } else {
|
} else {
|
||||||
elements.push(gridItemToGridLayoutItemKind(child, isSnapshot));
|
elements.push(gridItemToGridLayoutItemKind(child, isSnapshot));
|
||||||
// }
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: OLD transformer code
|
// TODO: OLD transformer code
|
||||||
@ -164,6 +171,7 @@ function getGridLayoutItems(state: DashboardSceneState, isSnapshot?: boolean): G
|
|||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return elements;
|
return elements;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -185,6 +193,7 @@ export function gridItemToGridLayoutItemKind(gridItem: DashboardGridItem, isSnap
|
|||||||
x = gridItem_.state.x ?? 0;
|
x = gridItem_.state.x ?? 0;
|
||||||
y = gridItem_.state.y ?? 0;
|
y = gridItem_.state.y ?? 0;
|
||||||
width = gridItem_.state.width ?? 0;
|
width = gridItem_.state.width ?? 0;
|
||||||
|
const repeatVar = gridItem_.state.variableName;
|
||||||
|
|
||||||
// FIXME: which name should we use for the element reference, key or something else ?
|
// FIXME: which name should we use for the element reference, key or something else ?
|
||||||
const elementName = gridItem_.state.body.state.key ?? 'DefaultName';
|
const elementName = gridItem_.state.body.state.key ?? 'DefaultName';
|
||||||
@ -202,6 +211,23 @@ export function gridItemToGridLayoutItemKind(gridItem: DashboardGridItem, isSnap
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (repeatVar) {
|
||||||
|
const repeat: RepeatOptions = {
|
||||||
|
mode: 'variable',
|
||||||
|
value: repeatVar,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (gridItem_.state.maxPerRow) {
|
||||||
|
repeat.maxPerRow = gridItem_.getMaxPerRow();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gridItem_.state.repeatDirection) {
|
||||||
|
repeat.direction = gridItem_.getRepeatDirection();
|
||||||
|
}
|
||||||
|
|
||||||
|
elementGridItem.spec.repeat = repeat;
|
||||||
|
}
|
||||||
|
|
||||||
if (!elementGridItem) {
|
if (!elementGridItem) {
|
||||||
throw new Error('Unsupported grid item type');
|
throw new Error('Unsupported grid item type');
|
||||||
}
|
}
|
||||||
@ -367,6 +393,60 @@ function createElements(panels: PanelKind[]): Record<string, PanelKind> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function repeaterToLayoutItems(repeater: DashboardGridItem, isSnapshot = false): GridLayoutItemKind[] {
|
||||||
|
if (!isSnapshot) {
|
||||||
|
return [gridItemToGridLayoutItemKind(repeater)];
|
||||||
|
} else {
|
||||||
|
if (repeater.state.body instanceof VizPanel && isLibraryPanel(repeater.state.body)) {
|
||||||
|
// TODO: implement
|
||||||
|
// const { x = 0, y = 0, width: w = 0, height: h = 0 } = repeater.state;
|
||||||
|
// return [vizPanelToPanel(repeater.state.body, { x, y, w, h }, isSnapshot)];
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (repeater.state.repeatedPanels) {
|
||||||
|
const { h, w, columnCount } = calculateGridItemDimensions(repeater);
|
||||||
|
const panels = repeater.state.repeatedPanels!.map((panel, index) => {
|
||||||
|
let x = 0,
|
||||||
|
y = 0;
|
||||||
|
if (repeater.state.repeatDirection === 'v') {
|
||||||
|
x = repeater.state.x!;
|
||||||
|
y = index * h;
|
||||||
|
} else {
|
||||||
|
x = (index % columnCount) * w;
|
||||||
|
y = repeater.state.y! + Math.floor(index / columnCount) * h;
|
||||||
|
}
|
||||||
|
|
||||||
|
const gridPos = { x, y, w, h };
|
||||||
|
|
||||||
|
const result: GridLayoutItemKind = {
|
||||||
|
kind: 'GridLayoutItem',
|
||||||
|
spec: {
|
||||||
|
x: gridPos.x,
|
||||||
|
y: gridPos.y,
|
||||||
|
width: gridPos.w,
|
||||||
|
height: gridPos.h,
|
||||||
|
repeat: {
|
||||||
|
mode: 'variable',
|
||||||
|
value: repeater.state.variableName!,
|
||||||
|
maxPerRow: repeater.getMaxPerRow(),
|
||||||
|
direction: repeater.state.repeatDirection,
|
||||||
|
},
|
||||||
|
element: {
|
||||||
|
kind: 'ElementReference',
|
||||||
|
name: panel.state.key!,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
|
||||||
|
return panels;
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function getVariables(oldDash: DashboardSceneState) {
|
function getVariables(oldDash: DashboardSceneState) {
|
||||||
const variablesSet = oldDash.$variables;
|
const variablesSet = oldDash.$variables;
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@ 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 { DashboardGridItem } from '../scene/layout-default/DashboardGridItem';
|
||||||
import { DashboardLayoutManager, isDashboardLayoutManager } from '../scene/types';
|
import { DashboardLayoutManager, isDashboardLayoutManager } from '../scene/types';
|
||||||
|
|
||||||
export const NEW_PANEL_HEIGHT = 8;
|
export const NEW_PANEL_HEIGHT = 8;
|
||||||
@ -250,6 +251,14 @@ export function getLibraryPanelBehavior(vizPanel: VizPanel): LibraryPanelBehavio
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function calculateGridItemDimensions(repeater: DashboardGridItem) {
|
||||||
|
const rowCount = Math.ceil(repeater.state.repeatedPanels!.length / repeater.getMaxPerRow());
|
||||||
|
const columnCount = Math.ceil(repeater.state.repeatedPanels!.length / rowCount);
|
||||||
|
const w = 24 / columnCount;
|
||||||
|
const h = repeater.state.itemHeight ?? 10;
|
||||||
|
return { h, w, columnCount };
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Activates any inactive ancestors of the scene object.
|
* Activates any inactive ancestors of the scene object.
|
||||||
* Useful when rendering a scene object out of context of it's parent
|
* Useful when rendering a scene object out of context of it's parent
|
||||||
|
Loading…
Reference in New Issue
Block a user