Dashboard: Don't show unsaved changes modal for automatic schema changes (#50822)

* Dashboard: Don't show unsaved changes modal for automatic schema changes

* More refinements

* Fix logic in updateGridPos
This commit is contained in:
Torkel Ödegaard 2022-06-17 08:58:53 +02:00 committed by GitHub
parent eb25d8df89
commit 7e22b8e6fb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 58 additions and 24 deletions

View File

@ -1,3 +1,5 @@
import { getPanelPlugin } from 'app/features/plugins/__mocks__/pluginMocks';
import { setContextSrv } from '../../../../core/services/context_srv';
import { DashboardModel } from '../../state/DashboardModel';
import { PanelModel } from '../../state/PanelModel';
@ -138,6 +140,17 @@ describe('DashboardPrompt', () => {
});
});
it('Should ignore panel schema migrations', () => {
const { original, dash } = getTestContext();
const plugin = getPanelPlugin({}).setMigrationHandler((panel) => {
delete (panel as any).legend;
return { option1: 'Aasd' };
});
dash.panels[0].pluginLoaded(plugin);
expect(hasChanges(dash, original)).toBe(false);
});
describe('when called with fromFile', () => {
it('then it should return true', () => {
const { original, dash } = getTestContext();

View File

@ -1,5 +1,5 @@
import * as H from 'history';
import { each, filter, find } from 'lodash';
import { each, find } from 'lodash';
import React, { useContext, useEffect, useState } from 'react';
import { useDispatch } from 'react-redux';
import { Prompt } from 'react-router-dom';
@ -184,22 +184,7 @@ function cleanDashboardFromIgnoredChanges(dashData: any) {
// ignore iteration property
delete dash.iteration;
dash.panels = filter(dash.panels, (panel) => {
if (panel.repeatPanelId) {
return false;
}
// remove scopedVars
panel.scopedVars = undefined;
// ignore panel legend sort
if (panel.legend) {
delete panel.legend.sort;
delete panel.legend.sortDesc;
}
return true;
});
dash.panels = [];
// ignore template variable values
each(dash.getVariables(), (variable: any) => {
@ -212,6 +197,10 @@ function cleanDashboardFromIgnoredChanges(dashData: any) {
}
export function hasChanges(current: DashboardModel, original: any) {
if (current.hasUnsavedChanges()) {
return true;
}
const currentClean = cleanDashboardFromIgnoredChanges(current.getSaveModelClone());
const originalClean = cleanDashboardFromIgnoredChanges(original);

View File

@ -50,8 +50,8 @@ export class DashboardRow extends React.Component<DashboardRowProps, any> {
};
onUpdate = (title: string, repeat?: string | null) => {
this.props.panel['title'] = title;
this.props.panel['repeat'] = repeat ?? undefined;
this.props.panel.setProperty('title', title);
this.props.panel.setProperty('repeat', repeat ?? undefined);
this.props.panel.render();
this.props.dashboard.processRepeats();
this.forceUpdate();

View File

@ -122,7 +122,6 @@ export class DashboardGridUnconnected extends PureComponent<Props, State> {
onResize: ItemCallback = (layout, oldItem, newItem) => {
const panel = this.panelMap[newItem.i!];
panel.updateGridPos(newItem);
panel.configRev++; // trigger change handler
};
onResizeStop: ItemCallback = (layout, oldItem, newItem) => {

View File

@ -367,6 +367,15 @@ describe('DashboardModel', () => {
dashboard.toggleRow(dashboard.panels[1]);
});
it('should not impact hasUnsavedChanges', () => {
expect(dashboard.hasUnsavedChanges()).toBe(false);
});
it('should impact hasUnsavedChanges if panels have changes when row is collapsed', () => {
dashboard.panels[0].setProperty('title', 'new title');
expect(dashboard.hasUnsavedChanges()).toBe(true);
});
it('should remove panels and put them inside collapsed row', () => {
expect(dashboard.panels.length).toBe(3);
expect(dashboard.panels[1].panels?.length).toBe(2);

View File

@ -499,10 +499,6 @@ export class DashboardModel implements TimeModel {
hasUnsavedChanges() {
const changedPanel = this.panels.find((p) => p.hasChanged);
if (changedPanel) {
console.log('Panel has changed', changedPanel);
}
return Boolean(changedPanel);
}
@ -875,6 +871,10 @@ export class DashboardModel implements TimeModel {
row.panels = rowPanels.map((panel: PanelModel) => panel.getSaveModel());
row.collapsed = true;
if (rowPanels.some((panel) => panel.hasChanged)) {
row.configRev++;
}
// emit change event
this.events.publish(new DashboardPanelsChangedEvent());
return;

View File

@ -435,6 +435,20 @@ describe('PanelModel', () => {
});
});
describe('updateGridPos', () => {
it('Should not cause configRev if no change', () => {
model.gridPos = { w: 1, h: 1, x: 1, y: 2 };
model.updateGridPos({ w: 1, h: 1, x: 1, y: 2 });
expect(model.hasChanged).toBe(false);
});
it('Should not cause configRev if gridPos is different', () => {
model.gridPos = { w: 1, h: 1, x: 1, y: 2 };
model.updateGridPos({ w: 10, h: 1, x: 1, y: 2 });
expect(model.hasChanged).toBe(true);
});
});
describe('destroy', () => {
it('Should still preserve last query result', () => {
model.getQueryRunner().useLastResultFrom({

View File

@ -298,10 +298,20 @@ export class PanelModel implements DataConfigSource, IPanelModel {
}
updateGridPos(newPos: GridPos) {
if (
newPos.x === this.gridPos.x &&
newPos.y === this.gridPos.y &&
newPos.h === this.gridPos.h &&
newPos.w === this.gridPos.w
) {
return;
}
this.gridPos.x = newPos.x;
this.gridPos.y = newPos.y;
this.gridPos.w = newPos.w;
this.gridPos.h = newPos.h;
this.configRev++;
}
runAllPanelQueries(