DashboardModel: Tidy up some of the older code (#48355)

This commit is contained in:
kay delaney 2022-05-04 09:39:41 +01:00 committed by GitHub
parent c41397a6e7
commit 713e624790
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 184 additions and 307 deletions

View File

@ -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' });
}); });
}); });

View File

@ -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));
} }

View File

@ -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', () => {

View File

@ -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);
}

View File

@ -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;

View File

@ -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();
}); });
}); });
}); });