mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Dashboard: Fix repeated row panel placement with larger number of rows (#72011)
Closes #69833
This commit is contained in:
@@ -2333,17 +2333,16 @@ exports[`better eslint`] = {
|
|||||||
[0, 0, 0, "Unexpected any. Specify a different type.", "23"],
|
[0, 0, 0, "Unexpected any. Specify a different type.", "23"],
|
||||||
[0, 0, 0, "Unexpected any. Specify a different type.", "24"],
|
[0, 0, 0, "Unexpected any. Specify a different type.", "24"],
|
||||||
[0, 0, 0, "Unexpected any. Specify a different type.", "25"],
|
[0, 0, 0, "Unexpected any. Specify a different type.", "25"],
|
||||||
[0, 0, 0, "Unexpected any. Specify a different type.", "26"],
|
[0, 0, 0, "Do not use any type assertions.", "26"],
|
||||||
[0, 0, 0, "Unexpected any. Specify a different type.", "27"],
|
[0, 0, 0, "Unexpected any. Specify a different type.", "27"],
|
||||||
[0, 0, 0, "Do not use any type assertions.", "28"],
|
[0, 0, 0, "Unexpected any. Specify a different type.", "28"],
|
||||||
[0, 0, 0, "Unexpected any. Specify a different type.", "29"],
|
[0, 0, 0, "Do not use any type assertions.", "29"],
|
||||||
[0, 0, 0, "Unexpected any. Specify a different type.", "30"],
|
[0, 0, 0, "Unexpected any. Specify a different type.", "30"],
|
||||||
[0, 0, 0, "Do not use any type assertions.", "31"],
|
[0, 0, 0, "Unexpected any. Specify a different type.", "31"],
|
||||||
[0, 0, 0, "Unexpected any. Specify a different type.", "32"],
|
[0, 0, 0, "Unexpected any. Specify a different type.", "32"],
|
||||||
[0, 0, 0, "Unexpected any. Specify a different type.", "33"],
|
[0, 0, 0, "Unexpected any. Specify a different type.", "33"],
|
||||||
[0, 0, 0, "Unexpected any. Specify a different type.", "34"],
|
[0, 0, 0, "Unexpected any. Specify a different type.", "34"],
|
||||||
[0, 0, 0, "Unexpected any. Specify a different type.", "35"],
|
[0, 0, 0, "Unexpected any. Specify a different type.", "35"]
|
||||||
[0, 0, 0, "Unexpected any. Specify a different type.", "36"]
|
|
||||||
],
|
],
|
||||||
"public/app/features/dashboard/state/PanelModel.test.ts:5381": [
|
"public/app/features/dashboard/state/PanelModel.test.ts:5381": [
|
||||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import {
|
|||||||
PanelModel as IPanelModel,
|
PanelModel as IPanelModel,
|
||||||
TimeRange,
|
TimeRange,
|
||||||
TimeZone,
|
TimeZone,
|
||||||
|
TypedVariableModel,
|
||||||
UrlQueryValue,
|
UrlQueryValue,
|
||||||
} from '@grafana/data';
|
} from '@grafana/data';
|
||||||
import { RefreshEvent, TimeRangeUpdatedEvent, config } from '@grafana/runtime';
|
import { RefreshEvent, TimeRangeUpdatedEvent, config } from '@grafana/runtime';
|
||||||
@@ -42,7 +43,7 @@ import { getTimeSrv } from '../services/TimeSrv';
|
|||||||
import { mergePanels, PanelMergeInfo } from '../utils/panelMerge';
|
import { mergePanels, PanelMergeInfo } from '../utils/panelMerge';
|
||||||
|
|
||||||
import { DashboardMigrator } from './DashboardMigrator';
|
import { DashboardMigrator } from './DashboardMigrator';
|
||||||
import { GridPos, PanelModel, autoMigrateAngular } from './PanelModel';
|
import { PanelModel, autoMigrateAngular } from './PanelModel';
|
||||||
import { TimeModel } from './TimeModel';
|
import { TimeModel } from './TimeModel';
|
||||||
import { deleteScopeVars, isOnTheSameGridRow } from './utils';
|
import { deleteScopeVars, isOnTheSameGridRow } from './utils';
|
||||||
|
|
||||||
@@ -667,12 +668,13 @@ export class DashboardModel implements TimeModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getRowRepeatClone(sourceRowPanel: PanelModel, valueIndex: number, sourcePanelIndex: number) {
|
getRowRepeatClone(sourceRowPanel: PanelModel, valueIndex: number, sourcePanelIndex: number) {
|
||||||
// if first clone return source
|
// if first clone, return source
|
||||||
if (valueIndex === 0) {
|
if (valueIndex === 0) {
|
||||||
if (!sourceRowPanel.collapsed) {
|
if (!sourceRowPanel.collapsed) {
|
||||||
const rowPanels = this.getRowPanels(sourcePanelIndex);
|
const rowPanels = this.getRowPanels(sourcePanelIndex);
|
||||||
sourceRowPanel.panels = rowPanels;
|
sourceRowPanel.panels = rowPanels;
|
||||||
}
|
}
|
||||||
|
|
||||||
return sourceRowPanel;
|
return sourceRowPanel;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -682,11 +684,13 @@ export class DashboardModel implements TimeModel {
|
|||||||
if (sourceRowPanel.collapsed) {
|
if (sourceRowPanel.collapsed) {
|
||||||
rowPanels = cloneDeep(sourceRowPanel.panels) ?? [];
|
rowPanels = cloneDeep(sourceRowPanel.panels) ?? [];
|
||||||
clone.panels = rowPanels;
|
clone.panels = rowPanels;
|
||||||
|
|
||||||
// insert copied row after preceding row
|
// insert copied row after preceding row
|
||||||
insertPos = sourcePanelIndex + valueIndex;
|
insertPos = sourcePanelIndex + valueIndex;
|
||||||
} else {
|
} else {
|
||||||
rowPanels = this.getRowPanels(sourcePanelIndex);
|
rowPanels = this.getRowPanels(sourcePanelIndex);
|
||||||
clone.panels = rowPanels.map((panel) => panel.getSaveModel());
|
clone.panels = rowPanels.map((panel) => panel.getSaveModel());
|
||||||
|
|
||||||
// insert copied row after preceding row's panels
|
// insert copied row after preceding row's panels
|
||||||
insertPos = sourcePanelIndex + (rowPanels.length + 1) * valueIndex;
|
insertPos = sourcePanelIndex + (rowPanels.length + 1) * valueIndex;
|
||||||
}
|
}
|
||||||
@@ -757,59 +761,56 @@ export class DashboardModel implements TimeModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
repeatRow(panel: PanelModel, panelIndex: number, variable: any) {
|
repeatRow(panel: PanelModel, panelIndex: number, variable: TypedVariableModel) {
|
||||||
const selectedOptions = this.getSelectedVariableOptions(variable);
|
const selectedOptions = this.getSelectedVariableOptions(variable);
|
||||||
let yPos = panel.gridPos.y;
|
|
||||||
|
|
||||||
function setScopedVars(panel: PanelModel, variableOption: any) {
|
|
||||||
panel.scopedVars ??= {};
|
|
||||||
panel.scopedVars[variable.name] = variableOption;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let optionIndex = 0; optionIndex < selectedOptions.length; optionIndex++) {
|
for (let optionIndex = 0; optionIndex < selectedOptions.length; optionIndex++) {
|
||||||
const option = selectedOptions[optionIndex];
|
const curOption = selectedOptions[optionIndex];
|
||||||
const rowCopy = this.getRowRepeatClone(panel, optionIndex, panelIndex);
|
const rowClone = this.getRowRepeatClone(panel, optionIndex, panelIndex);
|
||||||
|
|
||||||
setScopedVars(rowCopy, option);
|
setScopedVars(rowClone, variable, curOption);
|
||||||
|
|
||||||
const rowHeight = this.getRowHeight(rowCopy);
|
const rowHeight = this.getRowHeight(rowClone);
|
||||||
const rowPanels = rowCopy.panels || [];
|
const panelsInRow = rowClone.panels || [];
|
||||||
let panelBelowIndex;
|
let panelBelowIndex;
|
||||||
|
|
||||||
if (panel.collapsed) {
|
if (panel.collapsed) {
|
||||||
// For collapsed row just copy its panels and set scoped vars and proper IDs
|
// For a collapsed row, just copy its panels, set scoped vars and proper IDs
|
||||||
for (const rowPanel of rowPanels) {
|
for (const panelInRow of panelsInRow) {
|
||||||
setScopedVars(rowPanel, option);
|
setScopedVars(panelInRow, variable, curOption);
|
||||||
if (optionIndex > 0) {
|
if (optionIndex > 0) {
|
||||||
this.updateRepeatedPanelIds(rowPanel, true);
|
this.updateRepeatedPanelIds(panelInRow, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rowCopy.gridPos.y += optionIndex;
|
|
||||||
yPos += optionIndex;
|
// push nth row clone's y-pos down by n
|
||||||
|
rowClone.gridPos.y += optionIndex;
|
||||||
panelBelowIndex = panelIndex + optionIndex + 1;
|
panelBelowIndex = panelIndex + optionIndex + 1;
|
||||||
} else {
|
} else {
|
||||||
// insert after 'row' panel
|
// insert after row panel
|
||||||
const insertPos = panelIndex + (rowPanels.length + 1) * optionIndex + 1;
|
const insertPos = panelIndex + (panelsInRow.length + 1) * optionIndex + 1;
|
||||||
rowPanels.forEach((rowPanel: PanelModel, i: number) => {
|
panelsInRow.forEach((panelInRow, i) => {
|
||||||
setScopedVars(rowPanel, option);
|
setScopedVars(panelInRow, variable, curOption);
|
||||||
|
|
||||||
if (optionIndex > 0) {
|
if (optionIndex > 0) {
|
||||||
const cloneRowPanel = new PanelModel(rowPanel);
|
const panelInRowClone = new PanelModel(panelInRow);
|
||||||
this.updateRepeatedPanelIds(cloneRowPanel, true);
|
this.updateRepeatedPanelIds(panelInRowClone, true);
|
||||||
// For exposed row additionally set proper Y grid position and add it to dashboard panels
|
|
||||||
cloneRowPanel.gridPos.y += rowHeight * optionIndex;
|
// For exposed row, set correct grid y-position and add it to dashboard panels
|
||||||
this.panels.splice(insertPos + i, 0, cloneRowPanel);
|
panelInRowClone.gridPos.y += rowHeight * optionIndex;
|
||||||
|
this.panels.splice(insertPos + i, 0, panelInRowClone);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
rowCopy.panels = [];
|
|
||||||
rowCopy.gridPos.y += rowHeight * optionIndex;
|
rowClone.panels = [];
|
||||||
yPos += rowHeight;
|
rowClone.gridPos.y += rowHeight * optionIndex;
|
||||||
panelBelowIndex = insertPos + rowPanels.length;
|
panelBelowIndex = insertPos + panelsInRow.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update gridPos for panels below if we inserted more than 1 repeated row panel
|
// Update gridPos for panels below if we inserted more than 1 repeated row panel
|
||||||
if (selectedOptions.length > 1) {
|
if (selectedOptions.length > 1) {
|
||||||
for (const panel of this.panels.slice(panelBelowIndex)) {
|
for (const panel of this.panels.slice(panelBelowIndex)) {
|
||||||
panel.gridPos.y += yPos;
|
panel.gridPos.y += rowHeight;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -841,12 +842,13 @@ export class DashboardModel implements TimeModel {
|
|||||||
getRowHeight(rowPanel: PanelModel): number {
|
getRowHeight(rowPanel: PanelModel): number {
|
||||||
if (!rowPanel.panels || rowPanel.panels.length === 0) {
|
if (!rowPanel.panels || rowPanel.panels.length === 0) {
|
||||||
return 0;
|
return 0;
|
||||||
|
} else if (rowPanel.collapsed) {
|
||||||
|
// A collapsed row will always have height 1
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const rowYPos = rowPanel.gridPos.y;
|
const maxYPos = maxBy(rowPanel.panels, ({ gridPos }) => gridPos.y + gridPos.h)!.gridPos;
|
||||||
const positions = map(rowPanel.panels, 'gridPos');
|
return maxYPos.y + maxYPos.h - rowPanel.gridPos.y;
|
||||||
const maxPos = maxBy(positions, (pos: GridPos) => pos.y + pos.h);
|
|
||||||
return maxPos!.y + maxPos!.h - rowYPos;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
removePanel(panel: PanelModel) {
|
removePanel(panel: PanelModel) {
|
||||||
@@ -1300,3 +1302,8 @@ export class DashboardModel implements TimeModel {
|
|||||||
function isPanelWithLegend(panel: PanelModel): panel is PanelModel & Pick<Required<PanelModel>, 'legend'> {
|
function isPanelWithLegend(panel: PanelModel): panel is PanelModel & Pick<Required<PanelModel>, 'legend'> {
|
||||||
return Boolean(panel.legend);
|
return Boolean(panel.legend);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setScopedVars(panel: PanelModel, variable: TypedVariableModel, variableOption: any) {
|
||||||
|
panel.scopedVars ??= {};
|
||||||
|
panel.scopedVars[variable.name] = variableOption;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user