mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
DashboardModel: Tidy up some of the older code (#48355)
This commit is contained in:
parent
c41397a6e7
commit
713e624790
@ -1449,7 +1449,7 @@ describe('DashboardModel', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should ignore fieldConfig.defaults', () => {
|
it('should ignore fieldConfig.defaults', () => {
|
||||||
expect(model.panels[0].panels[0].fieldConfig.defaults).toEqual(undefined);
|
expect(model.panels[0].panels?.[0].fieldConfig.defaults).toEqual(undefined);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1749,8 +1749,8 @@ describe('DashboardModel', () => {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
panel1Targets = nestedModel.panels[0].panels[0].targets;
|
panel1Targets = nestedModel.panels[0].panels?.[0].targets;
|
||||||
panel2Targets = nestedModel.panels[0].panels[1].targets;
|
panel2Targets = nestedModel.panels[0].panels?.[1].targets;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('multiple stats query should have been split into one query per stat', () => {
|
it('multiple stats query should have been split into one query per stat', () => {
|
||||||
@ -1851,7 +1851,7 @@ describe('DashboardModel', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should update datasources in panels collapsed rows', () => {
|
it('should update datasources in panels collapsed rows', () => {
|
||||||
expect(model.panels[3].panels[0].datasource).toEqual({ type: 'prometheus', uid: 'prom-uid' });
|
expect(model.panels[3].panels?.[0].datasource).toEqual({ type: 'prometheus', uid: 'prom-uid' });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -784,9 +784,10 @@ export class DashboardMigrator {
|
|||||||
for (j = 0; j < this.dashboard.panels.length; j++) {
|
for (j = 0; j < this.dashboard.panels.length; j++) {
|
||||||
for (k = 0; k < panelUpgrades.length; k++) {
|
for (k = 0; k < panelUpgrades.length; k++) {
|
||||||
this.dashboard.panels[j] = panelUpgrades[k].call(this, this.dashboard.panels[j]);
|
this.dashboard.panels[j] = panelUpgrades[k].call(this, this.dashboard.panels[j]);
|
||||||
if (this.dashboard.panels[j].panels) {
|
const rowPanels = this.dashboard.panels[j].panels;
|
||||||
for (n = 0; n < this.dashboard.panels[j].panels.length; n++) {
|
if (rowPanels) {
|
||||||
this.dashboard.panels[j].panels[n] = panelUpgrades[k].call(this, this.dashboard.panels[j].panels[n]);
|
for (n = 0; n < rowPanels.length; n++) {
|
||||||
|
rowPanels[n] = panelUpgrades[k].call(this, rowPanels[n]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -895,7 +896,7 @@ export class DashboardMigrator {
|
|||||||
delete panel.span;
|
delete panel.span;
|
||||||
|
|
||||||
if (rowPanelModel && rowPanel.collapsed) {
|
if (rowPanelModel && rowPanel.collapsed) {
|
||||||
rowPanelModel.panels.push(panel);
|
rowPanelModel.panels?.push(panel);
|
||||||
} else {
|
} else {
|
||||||
this.dashboard.panels.push(new PanelModel(panel));
|
this.dashboard.panels.push(new PanelModel(panel));
|
||||||
}
|
}
|
||||||
|
@ -370,7 +370,7 @@ describe('DashboardModel', () => {
|
|||||||
|
|
||||||
it('should remove panels and put them inside collapsed row', () => {
|
it('should remove panels and put them inside collapsed row', () => {
|
||||||
expect(dashboard.panels.length).toBe(3);
|
expect(dashboard.panels.length).toBe(3);
|
||||||
expect(dashboard.panels[1].panels.length).toBe(2);
|
expect(dashboard.panels[1].panels?.length).toBe(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('and when removing row and its panels', () => {
|
describe('and when removing row and its panels', () => {
|
||||||
|
@ -1,17 +1,4 @@
|
|||||||
import {
|
import { cloneDeep, defaults as _defaults, filter, indexOf, isEqual, map, maxBy, pull } from 'lodash';
|
||||||
cloneDeep,
|
|
||||||
defaults as _defaults,
|
|
||||||
each,
|
|
||||||
filter,
|
|
||||||
find,
|
|
||||||
findIndex,
|
|
||||||
indexOf,
|
|
||||||
isEqual,
|
|
||||||
map,
|
|
||||||
maxBy,
|
|
||||||
pull,
|
|
||||||
some,
|
|
||||||
} from 'lodash';
|
|
||||||
import { Subscription } from 'rxjs';
|
import { Subscription } from 'rxjs';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -194,14 +181,7 @@ export class DashboardModel implements TimeModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
addBuiltInAnnotationQuery() {
|
addBuiltInAnnotationQuery() {
|
||||||
let found = false;
|
const found = this.annotations.list.some((item) => item.builtIn === 1);
|
||||||
for (const item of this.annotations.list) {
|
|
||||||
if (item.builtIn === 1) {
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (found) {
|
if (found) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -267,9 +247,7 @@ export class DashboardModel implements TimeModel {
|
|||||||
|
|
||||||
// sort by keys
|
// sort by keys
|
||||||
copy = sortedDeepCloneWithoutNulls(copy);
|
copy = sortedDeepCloneWithoutNulls(copy);
|
||||||
copy.getVariables = () => {
|
copy.getVariables = () => copy.templating.list;
|
||||||
return copy.templating.list;
|
|
||||||
};
|
|
||||||
|
|
||||||
return copy;
|
return copy;
|
||||||
}
|
}
|
||||||
@ -296,24 +274,11 @@ export class DashboardModel implements TimeModel {
|
|||||||
|
|
||||||
private getPanelSaveModels() {
|
private getPanelSaveModels() {
|
||||||
return this.panels
|
return this.panels
|
||||||
.filter((panel: PanelModel) => {
|
.filter(
|
||||||
if (this.isSnapshotTruthy()) {
|
(panel) =>
|
||||||
return true;
|
this.isSnapshotTruthy() || !(panel.type === 'add-panel' || panel.repeatPanelId || panel.repeatedByRow)
|
||||||
}
|
)
|
||||||
if (panel.type === 'add-panel') {
|
.map((panel) => {
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// skip repeated panels in the saved model
|
|
||||||
if (panel.repeatPanelId) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// skip repeated rows in the saved model
|
|
||||||
if (panel.repeatedByRow) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
})
|
|
||||||
.map((panel: PanelModel) => {
|
|
||||||
// If we save while editing we should include the panel in edit mode instead of the
|
// If we save while editing we should include the panel in edit mode instead of the
|
||||||
// unmodified source panel
|
// unmodified source panel
|
||||||
if (this.panelInEdit && this.panelInEdit.id === panel.id) {
|
if (this.panelInEdit && this.panelInEdit.id === panel.id) {
|
||||||
@ -358,18 +323,19 @@ export class DashboardModel implements TimeModel {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (!defaults.saveVariables) {
|
if (!defaults.saveVariables) {
|
||||||
for (let i = 0; i < copy.templating.list.length; i++) {
|
for (const current of copy.templating.list) {
|
||||||
const current = copy.templating.list[i];
|
const original = originalVariables.find(
|
||||||
const original: any = find(originalVariables, { name: current.name, type: current.type });
|
({ name, type }: any) => name === current.name && type === current.type
|
||||||
|
);
|
||||||
|
|
||||||
if (!original) {
|
if (!original) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (current.type === 'adhoc') {
|
if (current.type === 'adhoc') {
|
||||||
copy.templating.list[i].filters = original.filters;
|
current.filters = original.filters;
|
||||||
} else {
|
} else {
|
||||||
copy.templating.list[i].current = original.current;
|
current.current = original.current;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -384,18 +350,14 @@ export class DashboardModel implements TimeModel {
|
|||||||
this.events.publish(new RefreshEvent());
|
this.events.publish(new RefreshEvent());
|
||||||
this.lastRefresh = Date.now();
|
this.lastRefresh = Date.now();
|
||||||
|
|
||||||
if (this.panelInEdit) {
|
if (this.panelInEdit && (event.refreshAll || event.panelIds.includes(this.panelInEdit.id))) {
|
||||||
if (event.refreshAll || event.panelIds.includes(this.panelInEdit.id)) {
|
this.panelInEdit.refresh();
|
||||||
this.panelInEdit.refresh();
|
return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const panel of this.panels) {
|
for (const panel of this.panels) {
|
||||||
if (!this.otherPanelInFullscreen(panel)) {
|
if (!this.otherPanelInFullscreen(panel) && (event.refreshAll || event.panelIds.includes(panel.id))) {
|
||||||
if (event.refreshAll || event.panelIds.includes(panel.id)) {
|
panel.refresh();
|
||||||
panel.refresh();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -453,51 +415,40 @@ export class DashboardModel implements TimeModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private ensurePanelsHaveIds() {
|
private ensurePanelsHaveIds() {
|
||||||
for (const panel of this.panels) {
|
let nextPanelId = this.getNextPanelId();
|
||||||
if (!panel.id) {
|
for (const panel of this.panelIterator()) {
|
||||||
panel.id = this.getNextPanelId();
|
panel.id ??= nextPanelId++;
|
||||||
}
|
|
||||||
|
|
||||||
if (panel.panels) {
|
|
||||||
for (const rowPanel of panel.panels) {
|
|
||||||
if (!rowPanel.id) {
|
|
||||||
rowPanel.id = this.getNextPanelId();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ensureListExist(data: any) {
|
private ensureListExist(data: any = {}) {
|
||||||
if (!data) {
|
data.list ??= [];
|
||||||
data = {};
|
|
||||||
}
|
|
||||||
if (!data.list) {
|
|
||||||
data.list = [];
|
|
||||||
}
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
getNextPanelId() {
|
getNextPanelId() {
|
||||||
let max = 0;
|
let max = 0;
|
||||||
|
|
||||||
for (const panel of this.panels) {
|
for (const panel of this.panelIterator()) {
|
||||||
if (panel.id > max) {
|
if (panel.id > max) {
|
||||||
max = panel.id;
|
max = panel.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (panel.collapsed) {
|
|
||||||
for (const rowPanel of panel.panels) {
|
|
||||||
if (rowPanel.id > max) {
|
|
||||||
max = rowPanel.id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return max + 1;
|
return max + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*panelIterator() {
|
||||||
|
for (const panel of this.panels) {
|
||||||
|
yield panel;
|
||||||
|
|
||||||
|
const rowPanels = panel.panels ?? [];
|
||||||
|
for (const rowPanel of rowPanels) {
|
||||||
|
yield rowPanel as PanelModel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
forEachPanel(callback: (panel: PanelModel, index: number) => void) {
|
forEachPanel(callback: (panel: PanelModel, index: number) => void) {
|
||||||
for (let i = 0; i < this.panels.length; i++) {
|
for (let i = 0; i < this.panels.length; i++) {
|
||||||
callback(this.panels[i], i);
|
callback(this.panels[i], i);
|
||||||
@ -509,13 +460,7 @@ export class DashboardModel implements TimeModel {
|
|||||||
return this.panelInEdit;
|
return this.panelInEdit;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const panel of this.panels) {
|
return this.panels.find((p) => p.id === id) ?? null;
|
||||||
if (panel.id === id) {
|
|
||||||
return panel;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
canEditPanel(panel?: PanelModel | null): boolean | undefined | null {
|
canEditPanel(panel?: PanelModel | null): boolean | undefined | null {
|
||||||
@ -553,13 +498,12 @@ export class DashboardModel implements TimeModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
hasUnsavedChanges() {
|
hasUnsavedChanges() {
|
||||||
for (const panel of this.panels) {
|
const changedPanel = this.panels.find((p) => p.hasChanged);
|
||||||
if (panel.hasChanged) {
|
if (changedPanel) {
|
||||||
console.log('Panel has changed', panel);
|
console.log('Panel has changed', changedPanel);
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
|
return Boolean(changedPanel);
|
||||||
}
|
}
|
||||||
|
|
||||||
cleanUpRepeats() {
|
cleanUpRepeats() {
|
||||||
@ -568,17 +512,12 @@ export class DashboardModel implements TimeModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.iteration = (this.iteration || new Date().getTime()) + 1;
|
this.iteration = (this.iteration || new Date().getTime()) + 1;
|
||||||
const panelsToRemove = [];
|
|
||||||
|
|
||||||
// cleanup scopedVars
|
// cleanup scopedVars
|
||||||
deleteScopeVars(this.panels);
|
deleteScopeVars(this.panels);
|
||||||
|
|
||||||
for (let i = 0; i < this.panels.length; i++) {
|
const panelsToRemove = this.panels.filter(
|
||||||
const panel = this.panels[i];
|
(p) => (!p.repeat || p.repeatedByRow) && p.repeatPanelId && p.repeatIteration !== this.iteration
|
||||||
if ((!panel.repeat || panel.repeatedByRow) && panel.repeatPanelId && panel.repeatIteration !== this.iteration) {
|
);
|
||||||
panelsToRemove.push(panel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove panels
|
// remove panels
|
||||||
pull(this.panels, ...panelsToRemove);
|
pull(this.panels, ...panelsToRemove);
|
||||||
@ -607,13 +546,8 @@ export class DashboardModel implements TimeModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cleanUpRowRepeats(rowPanels: PanelModel[]) {
|
cleanUpRowRepeats(rowPanels: PanelModel[]) {
|
||||||
const panelsToRemove = [];
|
const panelsToRemove = rowPanels.filter((p) => !p.repeat && p.repeatPanelId);
|
||||||
for (let i = 0; i < rowPanels.length; i++) {
|
|
||||||
const panel = rowPanels[i];
|
|
||||||
if (!panel.repeat && panel.repeatPanelId) {
|
|
||||||
panelsToRemove.push(panel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pull(rowPanels, ...panelsToRemove);
|
pull(rowPanels, ...panelsToRemove);
|
||||||
pull(this.panels, ...panelsToRemove);
|
pull(this.panels, ...panelsToRemove);
|
||||||
}
|
}
|
||||||
@ -623,18 +557,17 @@ export class DashboardModel implements TimeModel {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let rowPanels = row.panels;
|
let rowPanels = row.panels ?? [];
|
||||||
if (!row.collapsed) {
|
if (!row.collapsed) {
|
||||||
const rowPanelIndex = findIndex(this.panels, (p: PanelModel) => p.id === row.id);
|
const rowPanelIndex = this.panels.findIndex((p) => p.id === row.id);
|
||||||
rowPanels = this.getRowPanels(rowPanelIndex);
|
rowPanels = this.getRowPanels(rowPanelIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.cleanUpRowRepeats(rowPanels);
|
this.cleanUpRowRepeats(rowPanels);
|
||||||
|
|
||||||
for (let i = 0; i < rowPanels.length; i++) {
|
for (const panel of rowPanels) {
|
||||||
const panel = rowPanels[i];
|
|
||||||
if (panel.repeat) {
|
if (panel.repeat) {
|
||||||
const panelIndex = findIndex(this.panels, (p: PanelModel) => p.id === panel.id);
|
const panelIndex = this.panels.findIndex((p) => p.id === panel.id);
|
||||||
this.repeatPanel(panel, panelIndex);
|
this.repeatPanel(panel, panelIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -679,13 +612,13 @@ export class DashboardModel implements TimeModel {
|
|||||||
// for row clones we need to figure out panels under row to clone and where to insert clone
|
// for row clones we need to figure out panels under row to clone and where to insert clone
|
||||||
let rowPanels: PanelModel[], insertPos: number;
|
let rowPanels: PanelModel[], insertPos: number;
|
||||||
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 = map(rowPanels, (panel: PanelModel) => 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;
|
||||||
}
|
}
|
||||||
@ -696,7 +629,7 @@ export class DashboardModel implements TimeModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
repeatPanel(panel: PanelModel, panelIndex: number) {
|
repeatPanel(panel: PanelModel, panelIndex: number) {
|
||||||
const variable: any = this.getPanelRepeatVariable(panel);
|
const variable = this.getPanelRepeatVariable(panel);
|
||||||
if (!variable) {
|
if (!variable) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -717,7 +650,7 @@ export class DashboardModel implements TimeModel {
|
|||||||
let copy;
|
let copy;
|
||||||
|
|
||||||
copy = this.getPanelRepeatClone(panel, index, panelIndex);
|
copy = this.getPanelRepeatClone(panel, index, panelIndex);
|
||||||
copy.scopedVars = copy.scopedVars || {};
|
copy.scopedVars ??= {};
|
||||||
copy.scopedVars[variable.name] = option;
|
copy.scopedVars[variable.name] = option;
|
||||||
|
|
||||||
if (panel.repeatDirection === REPEAT_DIR_VERTICAL) {
|
if (panel.repeatDirection === REPEAT_DIR_VERTICAL) {
|
||||||
@ -746,12 +679,12 @@ export class DashboardModel implements TimeModel {
|
|||||||
const yOffset = yPos - panel.gridPos.y;
|
const yOffset = yPos - panel.gridPos.y;
|
||||||
if (yOffset > 0) {
|
if (yOffset > 0) {
|
||||||
const panelBelowIndex = panelIndex + selectedOptions.length;
|
const panelBelowIndex = panelIndex + selectedOptions.length;
|
||||||
for (let i = panelBelowIndex; i < this.panels.length; i++) {
|
for (const curPanel of this.panels.slice(panelBelowIndex)) {
|
||||||
if (isOnTheSameGridRow(panel, this.panels[i])) {
|
if (isOnTheSameGridRow(panel, curPanel)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.panels[i].gridPos.y += yOffset;
|
curPanel.gridPos.y += yOffset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -761,7 +694,7 @@ export class DashboardModel implements TimeModel {
|
|||||||
let yPos = panel.gridPos.y;
|
let yPos = panel.gridPos.y;
|
||||||
|
|
||||||
function setScopedVars(panel: PanelModel, variableOption: any) {
|
function setScopedVars(panel: PanelModel, variableOption: any) {
|
||||||
panel.scopedVars = panel.scopedVars || {};
|
panel.scopedVars ??= {};
|
||||||
panel.scopedVars[variable.name] = variableOption;
|
panel.scopedVars[variable.name] = variableOption;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -776,19 +709,19 @@ export class DashboardModel implements TimeModel {
|
|||||||
|
|
||||||
if (panel.collapsed) {
|
if (panel.collapsed) {
|
||||||
// For collapsed row just copy its panels and set scoped vars and proper IDs
|
// For collapsed row just copy its panels and set scoped vars and proper IDs
|
||||||
each(rowPanels, (rowPanel: PanelModel, i: number) => {
|
for (const rowPanel of rowPanels) {
|
||||||
setScopedVars(rowPanel, option);
|
setScopedVars(rowPanel, option);
|
||||||
if (optionIndex > 0) {
|
if (optionIndex > 0) {
|
||||||
this.updateRepeatedPanelIds(rowPanel, true);
|
this.updateRepeatedPanelIds(rowPanel, true);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
rowCopy.gridPos.y += optionIndex;
|
rowCopy.gridPos.y += optionIndex;
|
||||||
yPos += optionIndex;
|
yPos += 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 + (rowPanels.length + 1) * optionIndex + 1;
|
||||||
each(rowPanels, (rowPanel: PanelModel, i: number) => {
|
rowPanels.forEach((rowPanel: PanelModel, i: number) => {
|
||||||
setScopedVars(rowPanel, option);
|
setScopedVars(rowPanel, option);
|
||||||
if (optionIndex > 0) {
|
if (optionIndex > 0) {
|
||||||
const cloneRowPanel = new PanelModel(rowPanel);
|
const cloneRowPanel = new PanelModel(rowPanel);
|
||||||
@ -806,8 +739,8 @@ export class DashboardModel implements TimeModel {
|
|||||||
|
|
||||||
// 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 (let i = panelBelowIndex; i < this.panels.length; i++) {
|
for (const panel of this.panels.slice(panelBelowIndex)) {
|
||||||
this.panels[i].gridPos.y += yPos;
|
panel.gridPos.y += yPos;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -839,11 +772,10 @@ export class DashboardModel implements TimeModel {
|
|||||||
if (!rowPanel.panels || rowPanel.panels.length === 0) {
|
if (!rowPanel.panels || rowPanel.panels.length === 0) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const rowYPos = rowPanel.gridPos.y;
|
const rowYPos = rowPanel.gridPos.y;
|
||||||
const positions = map(rowPanel.panels, 'gridPos');
|
const positions = map(rowPanel.panels, 'gridPos');
|
||||||
const maxPos = maxBy(positions, (pos: GridPos) => {
|
const maxPos = maxBy(positions, (pos: GridPos) => pos.y + pos.h);
|
||||||
return pos.y + pos.h;
|
|
||||||
});
|
|
||||||
return maxPos!.y + maxPos!.h - rowYPos;
|
return maxPos!.y + maxPos!.h - rowYPos;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -853,9 +785,9 @@ export class DashboardModel implements TimeModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
removeRow(row: PanelModel, removePanels: boolean) {
|
removeRow(row: PanelModel, removePanels: boolean) {
|
||||||
const needToogle = (!removePanels && row.collapsed) || (removePanels && !row.collapsed);
|
const needToggle = (!removePanels && row.collapsed) || (removePanels && !row.collapsed);
|
||||||
|
|
||||||
if (needToogle) {
|
if (needToggle) {
|
||||||
this.toggleRow(row);
|
this.toggleRow(row);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -863,60 +795,30 @@ export class DashboardModel implements TimeModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
expandRows() {
|
expandRows() {
|
||||||
for (let i = 0; i < this.panels.length; i++) {
|
const collapsedRows = this.panels.filter((p) => p.type === 'row' && p.collapsed);
|
||||||
const panel = this.panels[i];
|
for (const row of collapsedRows) {
|
||||||
|
this.toggleRow(row);
|
||||||
if (panel.type !== 'row') {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (panel.collapsed) {
|
|
||||||
this.toggleRow(panel);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
collapseRows() {
|
collapseRows() {
|
||||||
for (let i = 0; i < this.panels.length; i++) {
|
const collapsedRows = this.panels.filter((p) => p.type === 'row' && !p.collapsed);
|
||||||
const panel = this.panels[i];
|
for (const row of collapsedRows) {
|
||||||
|
this.toggleRow(row);
|
||||||
if (panel.type !== 'row') {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!panel.collapsed) {
|
|
||||||
this.toggleRow(panel);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
isSubMenuVisible() {
|
isSubMenuVisible() {
|
||||||
if (this.links.length > 0) {
|
return (
|
||||||
return true;
|
this.links.length > 0 ||
|
||||||
}
|
this.getVariables().some((variable) => variable.hide !== 2) ||
|
||||||
|
this.annotations.list.some((annotation) => !annotation.hide)
|
||||||
if (this.getVariables().find((variable) => variable.hide !== 2)) {
|
);
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.annotations.list.find((annotation) => annotation.hide !== true)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getPanelInfoById(panelId: number) {
|
getPanelInfoById(panelId: number) {
|
||||||
for (let i = 0; i < this.panels.length; i++) {
|
const panelIndex = this.panels.findIndex((p) => p.id === panelId);
|
||||||
if (this.panels[i].id === panelId) {
|
return panelIndex >= 0 ? { panel: this.panels[panelIndex], index: panelIndex } : null;
|
||||||
return {
|
|
||||||
panel: this.panels[i],
|
|
||||||
index: i,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
duplicatePanel(panel: PanelModel) {
|
duplicatePanel(panel: PanelModel) {
|
||||||
@ -962,64 +864,64 @@ export class DashboardModel implements TimeModel {
|
|||||||
toggleRow(row: PanelModel) {
|
toggleRow(row: PanelModel) {
|
||||||
const rowIndex = indexOf(this.panels, row);
|
const rowIndex = indexOf(this.panels, row);
|
||||||
|
|
||||||
if (row.collapsed) {
|
if (!row.collapsed) {
|
||||||
row.collapsed = false;
|
const rowPanels = this.getRowPanels(rowIndex);
|
||||||
const hasRepeat = some(row.panels as PanelModel[], (p: PanelModel) => p.repeat);
|
|
||||||
|
|
||||||
if (row.panels.length > 0) {
|
// remove panels
|
||||||
// Use first panel to figure out if it was moved or pushed
|
pull(this.panels, ...rowPanels);
|
||||||
// If the panel doesn't have gridPos.y, use the row gridPos.y instead.
|
// save panel models inside row panel
|
||||||
// This can happen for some generated dashboards.
|
row.panels = rowPanels.map((panel: PanelModel) => panel.getSaveModel());
|
||||||
const firstPanelYPos = row.panels[0].gridPos.y ?? row.gridPos.y;
|
row.collapsed = true;
|
||||||
const yDiff = firstPanelYPos - (row.gridPos.y + row.gridPos.h);
|
|
||||||
|
|
||||||
// start inserting after row
|
|
||||||
let insertPos = rowIndex + 1;
|
|
||||||
// y max will represent the bottom y pos after all panels have been added
|
|
||||||
// needed to know home much panels below should be pushed down
|
|
||||||
let yMax = row.gridPos.y;
|
|
||||||
|
|
||||||
for (const panel of row.panels) {
|
|
||||||
// set the y gridPos if it wasn't already set
|
|
||||||
panel.gridPos.y ?? (panel.gridPos.y = row.gridPos.y); // (Safari 13.1 lacks ??= support)
|
|
||||||
// make sure y is adjusted (in case row moved while collapsed)
|
|
||||||
panel.gridPos.y -= yDiff;
|
|
||||||
// insert after row
|
|
||||||
this.panels.splice(insertPos, 0, new PanelModel(panel));
|
|
||||||
// update insert post and y max
|
|
||||||
insertPos += 1;
|
|
||||||
yMax = Math.max(yMax, panel.gridPos.y + panel.gridPos.h);
|
|
||||||
}
|
|
||||||
|
|
||||||
const pushDownAmount = yMax - row.gridPos.y - 1;
|
|
||||||
|
|
||||||
// push panels below down
|
|
||||||
for (let panelIndex = insertPos; panelIndex < this.panels.length; panelIndex++) {
|
|
||||||
this.panels[panelIndex].gridPos.y += pushDownAmount;
|
|
||||||
}
|
|
||||||
|
|
||||||
row.panels = [];
|
|
||||||
|
|
||||||
if (hasRepeat) {
|
|
||||||
this.processRowRepeats(row);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// sort panels
|
|
||||||
this.sortPanelsByGridPos();
|
|
||||||
|
|
||||||
// emit change event
|
// emit change event
|
||||||
this.events.publish(new DashboardPanelsChangedEvent());
|
this.events.publish(new DashboardPanelsChangedEvent());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const rowPanels = this.getRowPanels(rowIndex);
|
row.collapsed = false;
|
||||||
|
const rowPanels = row.panels ?? [];
|
||||||
|
const hasRepeat = rowPanels.some((p: PanelModel) => p.repeat);
|
||||||
|
if (rowPanels.length > 0) {
|
||||||
|
// Use first panel to figure out if it was moved or pushed
|
||||||
|
// If the panel doesn't have gridPos.y, use the row gridPos.y instead.
|
||||||
|
// This can happen for some generated dashboards.
|
||||||
|
const firstPanelYPos = rowPanels[0].gridPos.y ?? row.gridPos.y;
|
||||||
|
const yDiff = firstPanelYPos - (row.gridPos.y + row.gridPos.h);
|
||||||
|
|
||||||
// remove panels
|
// start inserting after row
|
||||||
pull(this.panels, ...rowPanels);
|
let insertPos = rowIndex + 1;
|
||||||
// save panel models inside row panel
|
// y max will represent the bottom y pos after all panels have been added
|
||||||
row.panels = map(rowPanels, (panel: PanelModel) => panel.getSaveModel());
|
// needed to know home much panels below should be pushed down
|
||||||
row.collapsed = true;
|
let yMax = row.gridPos.y;
|
||||||
|
|
||||||
|
for (const panel of rowPanels) {
|
||||||
|
// set the y gridPos if it wasn't already set
|
||||||
|
panel.gridPos.y ?? (panel.gridPos.y = row.gridPos.y); // (Safari 13.1 lacks ??= support)
|
||||||
|
// make sure y is adjusted (in case row moved while collapsed)
|
||||||
|
panel.gridPos.y -= yDiff;
|
||||||
|
// insert after row
|
||||||
|
this.panels.splice(insertPos, 0, new PanelModel(panel));
|
||||||
|
// update insert post and y max
|
||||||
|
insertPos += 1;
|
||||||
|
yMax = Math.max(yMax, panel.gridPos.y + panel.gridPos.h);
|
||||||
|
}
|
||||||
|
|
||||||
|
const pushDownAmount = yMax - row.gridPos.y - 1;
|
||||||
|
|
||||||
|
// push panels below down
|
||||||
|
for (const panel of this.panels.slice(insertPos)) {
|
||||||
|
panel.gridPos.y += pushDownAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
row.panels = [];
|
||||||
|
|
||||||
|
if (hasRepeat) {
|
||||||
|
this.processRowRepeats(row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// sort panels
|
||||||
|
this.sortPanelsByGridPos();
|
||||||
|
|
||||||
// emit change event
|
// emit change event
|
||||||
this.events.publish(new DashboardPanelsChangedEvent());
|
this.events.publish(new DashboardPanelsChangedEvent());
|
||||||
@ -1029,19 +931,11 @@ export class DashboardModel implements TimeModel {
|
|||||||
* Will return all panels after rowIndex until it encounters another row
|
* Will return all panels after rowIndex until it encounters another row
|
||||||
*/
|
*/
|
||||||
getRowPanels(rowIndex: number): PanelModel[] {
|
getRowPanels(rowIndex: number): PanelModel[] {
|
||||||
const rowPanels = [];
|
const panelsBelowRow = this.panels.slice(rowIndex + 1);
|
||||||
|
const nextRowIndex = panelsBelowRow.findIndex((p) => p.type === 'row');
|
||||||
|
|
||||||
for (let index = rowIndex + 1; index < this.panels.length; index++) {
|
// Take all panels up to next row, or all panels if there are no other rows
|
||||||
const panel = this.panels[index];
|
const rowPanels = panelsBelowRow.slice(0, nextRowIndex >= 0 ? nextRowIndex : this.panels.length);
|
||||||
|
|
||||||
// break when encountering another row
|
|
||||||
if (panel.type === 'row') {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// this panel must belong to row
|
|
||||||
rowPanels.push(panel);
|
|
||||||
}
|
|
||||||
|
|
||||||
return rowPanels;
|
return rowPanels;
|
||||||
}
|
}
|
||||||
@ -1095,14 +989,12 @@ export class DashboardModel implements TimeModel {
|
|||||||
|
|
||||||
hasTimeChanged() {
|
hasTimeChanged() {
|
||||||
const { time, originalTime } = this;
|
const { time, originalTime } = this;
|
||||||
if (isEqual(time, originalTime)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compare momemt values vs strings values
|
// Compare moment values vs strings values
|
||||||
return !(
|
return !(
|
||||||
isEqual(dateTime(time?.from), dateTime(originalTime?.from)) &&
|
isEqual(time, originalTime) ||
|
||||||
isEqual(dateTime(time?.to), dateTime(originalTime?.to))
|
(isEqual(dateTime(time?.from), dateTime(originalTime?.from)) &&
|
||||||
|
isEqual(dateTime(time?.to), dateTime(originalTime?.to)))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1120,11 +1012,7 @@ export class DashboardModel implements TimeModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
autoFitPanels(viewHeight: number, kioskMode?: UrlQueryValue) {
|
autoFitPanels(viewHeight: number, kioskMode?: UrlQueryValue) {
|
||||||
const currentGridHeight = Math.max(
|
const currentGridHeight = Math.max(...this.panels.map((panel) => panel.gridPos.h + panel.gridPos.y));
|
||||||
...this.panels.map((panel) => {
|
|
||||||
return panel.gridPos.h + panel.gridPos.y;
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
const navbarHeight = 55;
|
const navbarHeight = 55;
|
||||||
const margin = 20;
|
const margin = 20;
|
||||||
@ -1145,10 +1033,10 @@ export class DashboardModel implements TimeModel {
|
|||||||
const visibleGridHeight = Math.floor(visibleHeight / (GRID_CELL_HEIGHT + GRID_CELL_VMARGIN));
|
const visibleGridHeight = Math.floor(visibleHeight / (GRID_CELL_HEIGHT + GRID_CELL_VMARGIN));
|
||||||
const scaleFactor = currentGridHeight / visibleGridHeight;
|
const scaleFactor = currentGridHeight / visibleGridHeight;
|
||||||
|
|
||||||
this.panels.forEach((panel, i) => {
|
for (const panel of this.panels) {
|
||||||
panel.gridPos.y = Math.round(panel.gridPos.y / scaleFactor) || 1;
|
panel.gridPos.y = Math.round(panel.gridPos.y / scaleFactor) || 1;
|
||||||
panel.gridPos.h = Math.round(panel.gridPos.h / scaleFactor) || 1;
|
panel.gridPos.h = Math.round(panel.gridPos.h / scaleFactor) || 1;
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
templateVariableValueUpdated() {
|
templateVariableValueUpdated() {
|
||||||
@ -1160,39 +1048,32 @@ export class DashboardModel implements TimeModel {
|
|||||||
const panelId = parseInt(panelUrlId ?? '0', 10);
|
const panelId = parseInt(panelUrlId ?? '0', 10);
|
||||||
|
|
||||||
// First try to find it in a collapsed row and exand it
|
// First try to find it in a collapsed row and exand it
|
||||||
for (const panel of this.panels) {
|
const collapsedPanels = this.panels.filter((p) => p.collapsed);
|
||||||
if (panel.collapsed) {
|
for (const panel of collapsedPanels) {
|
||||||
for (const rowPanel of panel.panels) {
|
const hasPanel = panel.panels?.some((rp: any) => rp.id === panelId);
|
||||||
if (rowPanel.id === panelId) {
|
hasPanel && this.toggleRow(panel);
|
||||||
this.toggleRow(panel);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.getPanelById(panelId);
|
return this.getPanelById(panelId);
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleLegendsForAll() {
|
toggleLegendsForAll() {
|
||||||
const panelsWithLegends = this.panels.filter((panel) => {
|
const panelsWithLegends = this.panels.filter(isPanelWithLegend);
|
||||||
return panel.legend !== undefined && panel.legend !== null;
|
|
||||||
});
|
|
||||||
|
|
||||||
// determine if more panels are displaying legends or not
|
// determine if more panels are displaying legends or not
|
||||||
const onCount = panelsWithLegends.filter((panel) => panel.legend!.show).length;
|
const onCount = panelsWithLegends.filter((panel) => panel.legend.show).length;
|
||||||
const offCount = panelsWithLegends.length - onCount;
|
const offCount = panelsWithLegends.length - onCount;
|
||||||
const panelLegendsOn = onCount >= offCount;
|
const panelLegendsOn = onCount >= offCount;
|
||||||
|
|
||||||
for (const panel of panelsWithLegends) {
|
for (const panel of panelsWithLegends) {
|
||||||
panel.legend!.show = !panelLegendsOn;
|
panel.legend.show = !panelLegendsOn;
|
||||||
panel.render();
|
panel.render();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getVariables = () => {
|
getVariables() {
|
||||||
return this.getVariablesFromState(this.uid);
|
return this.getVariablesFromState(this.uid);
|
||||||
};
|
}
|
||||||
|
|
||||||
canEditAnnotations(dashboardId: number) {
|
canEditAnnotations(dashboardId: number) {
|
||||||
let canEdit = true;
|
let canEdit = true;
|
||||||
@ -1209,13 +1090,8 @@ export class DashboardModel implements TimeModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
canAddAnnotations() {
|
canAddAnnotations() {
|
||||||
let canAdd = true;
|
// If RBAC is enabled there are additional conditions to check
|
||||||
|
const canAdd = !contextSrv.accessControlEnabled() || this.meta.annotationsPermissions?.dashboard.canAdd;
|
||||||
// if RBAC is enabled there are additional conditions to check
|
|
||||||
if (contextSrv.accessControlEnabled()) {
|
|
||||||
canAdd = !!this.meta.annotationsPermissions?.dashboard.canAdd;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.canEditDashboard() && canAdd;
|
return this.canEditDashboard() && canAdd;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1247,27 +1123,23 @@ export class DashboardModel implements TimeModel {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const updated = map(currentVariables, (variable: any) => {
|
const updated = currentVariables.map((variable: any) => ({
|
||||||
return {
|
name: variable.name,
|
||||||
name: variable.name,
|
type: variable.type,
|
||||||
type: variable.type,
|
current: cloneDeep(variable.current),
|
||||||
current: cloneDeep(variable.current),
|
filters: cloneDeep(variable.filters),
|
||||||
filters: cloneDeep(variable.filters),
|
}));
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
return !isEqual(updated, originalVariables);
|
return !isEqual(updated, originalVariables);
|
||||||
}
|
}
|
||||||
|
|
||||||
private cloneVariablesFrom(variables: any[]): any[] {
|
private cloneVariablesFrom(variables: any[]): any[] {
|
||||||
return variables.map((variable) => {
|
return variables.map((variable) => ({
|
||||||
return {
|
name: variable.name,
|
||||||
name: variable.name,
|
type: variable.type,
|
||||||
type: variable.type,
|
current: cloneDeep(variable.current),
|
||||||
current: cloneDeep(variable.current),
|
filters: cloneDeep(variable.filters),
|
||||||
filters: cloneDeep(variable.filters),
|
}));
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private variablesTimeRangeProcessDoneHandler(event: VariablesTimeRangeProcessDone) {
|
private variablesTimeRangeProcessDoneHandler(event: VariablesTimeRangeProcessDone) {
|
||||||
@ -1299,3 +1171,7 @@ export class DashboardModel implements TimeModel {
|
|||||||
this.startRefresh(event.payload);
|
this.startRefresh(event.payload);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isPanelWithLegend(panel: PanelModel): panel is PanelModel & Pick<Required<PanelModel>, 'legend'> {
|
||||||
|
return Boolean(panel.legend);
|
||||||
|
}
|
||||||
|
@ -139,7 +139,7 @@ export class PanelModel implements DataConfigSource, IPanelModel {
|
|||||||
maxPerRow?: number;
|
maxPerRow?: number;
|
||||||
collapsed?: boolean;
|
collapsed?: boolean;
|
||||||
|
|
||||||
panels?: any;
|
panels?: PanelModel[];
|
||||||
declare targets: DataQuery[];
|
declare targets: DataQuery[];
|
||||||
transformations?: DataTransformerConfig[];
|
transformations?: DataTransformerConfig[];
|
||||||
datasource: DataSourceRef | null = null;
|
datasource: DataSourceRef | null = null;
|
||||||
|
@ -50,14 +50,14 @@ describe('deleteScopeVars', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
expect(panel1.scopedVars).toBeDefined();
|
expect(panel1.scopedVars).toBeDefined();
|
||||||
expect(panel1.panels[0].scopedVars).toBeDefined();
|
expect(panel1.panels?.[0].scopedVars).toBeDefined();
|
||||||
expect(panel1.panels[1].scopedVars).toBeDefined();
|
expect(panel1.panels?.[1].scopedVars).toBeDefined();
|
||||||
|
|
||||||
deleteScopeVars([panel1]);
|
deleteScopeVars([panel1]);
|
||||||
|
|
||||||
expect(panel1.scopedVars).toBeUndefined();
|
expect(panel1.scopedVars).toBeUndefined();
|
||||||
expect(panel1.panels[0].scopedVars).toBeUndefined();
|
expect(panel1.panels?.[0].scopedVars).toBeUndefined();
|
||||||
expect(panel1.panels[1].scopedVars).toBeUndefined();
|
expect(panel1.panels?.[1].scopedVars).toBeUndefined();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user