Dashboards: Only refresh panels on panel edit exit if refresh is set (#67652)

Closes #66838

Co-authored-by: Torkel Ödegaard <torkel@grafana.com>
This commit is contained in:
kay delaney 2023-05-10 14:12:55 +01:00 committed by GitHub
parent e51b92991d
commit 7801cf6585
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 108 additions and 90 deletions

View File

@ -2372,8 +2372,7 @@ exports[`better eslint`] = {
[0, 0, 0, "Unexpected any. Specify a different type.", "1"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"],
[0, 0, 0, "Unexpected any. Specify a different type.", "2"], [0, 0, 0, "Unexpected any. Specify a different type.", "2"],
[0, 0, 0, "Unexpected any. Specify a different type.", "3"], [0, 0, 0, "Unexpected any. Specify a different type.", "3"],
[0, 0, 0, "Unexpected any. Specify a different type.", "4"], [0, 0, 0, "Unexpected any. Specify a different type.", "4"]
[0, 0, 0, "Unexpected any. Specify a different type.", "5"]
], ],
"public/app/features/dashboard/state/DashboardMigrator.test.ts:5381": [ "public/app/features/dashboard/state/DashboardMigrator.test.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "0"],

View File

@ -287,19 +287,16 @@ describe('timeSrv', () => {
}); });
}); });
describe('pauseAutoRefresh', () => {
it('should set autoRefreshPaused to true', () => {
_dashboard.refresh = '10s';
timeSrv.pauseAutoRefresh();
expect(timeSrv.autoRefreshPaused).toBe(true);
});
});
describe('resumeAutoRefresh', () => { describe('resumeAutoRefresh', () => {
it('should set refresh to empty value', () => { it('should set auto-refresh interval', () => {
timeSrv.autoRefreshPaused = true; timeSrv.setAutoRefresh('10s');
expect(timeSrv.refreshTimer).not.toBeUndefined();
timeSrv.stopAutoRefresh();
expect(timeSrv.refreshTimer).toBeUndefined();
timeSrv.resumeAutoRefresh(); timeSrv.resumeAutoRefresh();
expect(timeSrv.autoRefreshPaused).toBe(false); expect(timeSrv.refreshTimer).not.toBeUndefined();
}); });
}); });

View File

@ -23,9 +23,8 @@ import { getRefreshFromUrl } from '../utils/getRefreshFromUrl';
export class TimeSrv { export class TimeSrv {
time: any; time: any;
refreshTimer: any; refreshTimer: number | undefined;
refresh: any; refresh: any;
autoRefreshPaused = false;
oldRefresh: string | null | undefined; oldRefresh: string | null | undefined;
timeModel?: TimeModel; timeModel?: TimeModel;
timeAtLoad: any; timeAtLoad: any;
@ -235,9 +234,9 @@ export class TimeSrv {
const validInterval = this.contextSrv.getValidInterval(interval); const validInterval = this.contextSrv.getValidInterval(interval);
const intervalMs = rangeUtil.intervalToMs(validInterval); const intervalMs = rangeUtil.intervalToMs(validInterval);
this.refreshTimer = setTimeout(() => { this.refreshTimer = window.setTimeout(() => {
this.startNextRefreshTimer(intervalMs); this.startNextRefreshTimer(intervalMs);
!this.autoRefreshPaused && this.refreshTimeModel(); this.refreshTimeModel();
}, intervalMs); }, intervalMs);
const refresh = this.contextSrv.getValidInterval(interval); const refresh = this.contextSrv.getValidInterval(interval);
@ -252,10 +251,10 @@ export class TimeSrv {
} }
private startNextRefreshTimer(afterMs: number) { private startNextRefreshTimer(afterMs: number) {
this.refreshTimer = setTimeout(() => { this.refreshTimer = window.setTimeout(() => {
this.startNextRefreshTimer(afterMs); this.startNextRefreshTimer(afterMs);
if (this.contextSrv.isGrafanaVisible()) { if (this.contextSrv.isGrafanaVisible()) {
!this.autoRefreshPaused && this.refreshTimeModel(); this.refreshTimeModel();
} else { } else {
this.autoRefreshBlocked = true; this.autoRefreshBlocked = true;
} }
@ -264,18 +263,14 @@ export class TimeSrv {
stopAutoRefresh() { stopAutoRefresh() {
clearTimeout(this.refreshTimer); clearTimeout(this.refreshTimer);
} this.refreshTimer = undefined;
// store timeModel refresh value and pause auto-refresh in some places
// i.e panel edit
pauseAutoRefresh() {
this.autoRefreshPaused = true;
} }
// resume auto-refresh based on old dashboard refresh property // resume auto-refresh based on old dashboard refresh property
resumeAutoRefresh() { resumeAutoRefresh() {
this.autoRefreshPaused = false; if (this.timeModel?.refresh) {
this.refreshTimeModel(); this.setAutoRefresh(this.timeModel.refresh);
}
} }
setTime(time: RawTimeRange, updateUrl = true) { setTime(time: RawTimeRange, updateUrl = true) {

View File

@ -1,6 +1,6 @@
import { keys as _keys } from 'lodash'; import { keys as _keys } from 'lodash';
import { VariableHide } from '@grafana/data'; import { dateTime, TimeRange, VariableHide } from '@grafana/data';
import { defaultVariableModel } from '@grafana/schema'; import { defaultVariableModel } from '@grafana/schema';
import { contextSrv } from 'app/core/services/context_srv'; import { contextSrv } from 'app/core/services/context_srv';
@ -1158,8 +1158,35 @@ describe('exitViewPanel', () => {
}); });
}); });
describe('exitPanelEditor', () => { describe('when initEditPanel is called', () => {
function getTestContext(pauseAutoRefresh = false) { function getTestContext() {
const dashboard = createDashboardModelFixture();
const timeSrvMock = {
pauseAutoRefresh: jest.fn(),
resumeAutoRefresh: jest.fn(),
stopAutoRefresh: jest.fn(),
} as unknown as TimeSrv;
setTimeSrv(timeSrvMock);
return { dashboard, timeSrvMock };
}
it('should set panelInEdit', () => {
const { dashboard } = getTestContext();
dashboard.addPanel({ type: 'timeseries' });
dashboard.initEditPanel(dashboard.panels[0]);
expect(dashboard.panelInEdit).not.toBeUndefined();
});
it('should stop auto-refresh', () => {
const { dashboard, timeSrvMock } = getTestContext();
dashboard.addPanel({ type: 'timeseries' });
dashboard.initEditPanel(dashboard.panels[0]);
expect(timeSrvMock.stopAutoRefresh).toHaveBeenCalled();
});
});
describe('when exitPanelEditor is called', () => {
function getTestContext() {
const panel = new PanelModel({ destroy: jest.fn() }); const panel = new PanelModel({ destroy: jest.fn() });
const dashboard = createDashboardModelFixture(); const dashboard = createDashboardModelFixture();
const timeSrvMock = { const timeSrvMock = {
@ -1169,70 +1196,54 @@ describe('exitPanelEditor', () => {
} as unknown as TimeSrv; } as unknown as TimeSrv;
dashboard.startRefresh = jest.fn(); dashboard.startRefresh = jest.fn();
dashboard.panelInEdit = panel; dashboard.panelInEdit = panel;
if (pauseAutoRefresh) {
timeSrvMock.autoRefreshPaused = true;
}
setTimeSrv(timeSrvMock); setTimeSrv(timeSrvMock);
return { dashboard, panel, timeSrvMock }; return { dashboard, panel, timeSrvMock };
} }
describe('when called', () => { it('should set panelInEdit to undefined', () => {
it('then panelInEdit is set to undefined', () => { const { dashboard } = getTestContext();
const { dashboard } = getTestContext();
dashboard.exitPanelEditor(); dashboard.exitPanelEditor();
expect(dashboard.panelInEdit).toBeUndefined(); expect(dashboard.panelInEdit).toBeUndefined();
}); });
it('then destroy is called on panel', () => { it('should destroy panel', () => {
const { dashboard, panel } = getTestContext(); const { dashboard, panel } = getTestContext();
dashboard.exitPanelEditor(); dashboard.exitPanelEditor();
expect(panel.destroy).toHaveBeenCalled(); expect(panel.destroy).toHaveBeenCalled();
}); });
it('then startRefresh is not called', () => { it('should not call startRefresh', () => {
const { dashboard } = getTestContext(); const { dashboard } = getTestContext();
dashboard.exitPanelEditor(); dashboard.exitPanelEditor();
expect(dashboard.startRefresh).not.toHaveBeenCalled(); expect(dashboard.startRefresh).not.toHaveBeenCalled();
}); });
it('then auto refresh property is resumed', () => { it('should call startRefresh if time range changed during edit', () => {
const { dashboard, timeSrvMock } = getTestContext(true); const { dashboard } = getTestContext();
dashboard.exitPanelEditor();
expect(timeSrvMock.resumeAutoRefresh).toHaveBeenCalled(); const range: TimeRange = {
}); from: dateTime(new Date().getTime()).subtract(1, 'minutes'),
}); to: dateTime(new Date().getTime()),
}); raw: {
from: 'now-1m',
describe('initEditPanel', () => { to: 'now',
function getTestContext() { },
const dashboard = createDashboardModelFixture(); };
const timeSrvMock = { dashboard.timeRangeUpdated(range);
pauseAutoRefresh: jest.fn(), dashboard.exitPanelEditor();
resumeAutoRefresh: jest.fn(),
} as unknown as TimeSrv; expect(dashboard.startRefresh).toHaveBeenCalled();
setTimeSrv(timeSrvMock); });
return { dashboard, timeSrvMock };
} it('then auto refresh property is resumed', () => {
const { dashboard, timeSrvMock } = getTestContext();
describe('when called', () => { dashboard.exitPanelEditor();
it('then panelInEdit is not undefined', () => { expect(timeSrvMock.resumeAutoRefresh).toHaveBeenCalled();
const { dashboard } = getTestContext();
dashboard.addPanel({ type: 'timeseries' });
dashboard.initEditPanel(dashboard.panels[0]);
expect(dashboard.panelInEdit).not.toBeUndefined();
});
it('then auto-refresh is paused', () => {
const { dashboard, timeSrvMock } = getTestContext();
dashboard.addPanel({ type: 'timeseries' });
dashboard.initEditPanel(dashboard.panels[0]);
expect(timeSrvMock.pauseAutoRefresh).toHaveBeenCalled();
});
}); });
}); });

View File

@ -101,6 +101,7 @@ export class DashboardModel implements TimeModel {
private panelsAffectedByVariableChange: number[] | null; private panelsAffectedByVariableChange: number[] | null;
private appEventsSubscription: Subscription; private appEventsSubscription: Subscription;
private lastRefresh: number; private lastRefresh: number;
private timeRangeUpdatedDuringEdit = false;
// ------------------ // ------------------
// not persisted // not persisted
@ -126,6 +127,7 @@ export class DashboardModel implements TimeModel {
appEventsSubscription: true, appEventsSubscription: true,
panelsAffectedByVariableChange: true, panelsAffectedByVariableChange: true,
lastRefresh: true, lastRefresh: true,
timeRangeUpdatedDuringEdit: true,
}; };
constructor( constructor(
@ -379,6 +381,10 @@ export class DashboardModel implements TimeModel {
timeRangeUpdated(timeRange: TimeRange) { timeRangeUpdated(timeRange: TimeRange) {
this.events.publish(new TimeRangeUpdatedEvent(timeRange)); this.events.publish(new TimeRangeUpdatedEvent(timeRange));
dispatch(onTimeRangeUpdated(this.uid, timeRange)); dispatch(onTimeRangeUpdated(this.uid, timeRange));
if (this.panelInEdit) {
this.timeRangeUpdatedDuringEdit = true;
}
} }
startRefresh(event: VariablesChangedEvent = { refreshAll: true, panelIds: [] }) { startRefresh(event: VariablesChangedEvent = { refreshAll: true, panelIds: [] }) {
@ -417,11 +423,28 @@ export class DashboardModel implements TimeModel {
} }
initEditPanel(sourcePanel: PanelModel): PanelModel { initEditPanel(sourcePanel: PanelModel): PanelModel {
getTimeSrv().pauseAutoRefresh(); getTimeSrv().stopAutoRefresh();
this.panelInEdit = sourcePanel.getEditClone(); this.panelInEdit = sourcePanel.getEditClone();
this.timeRangeUpdatedDuringEdit = false;
return this.panelInEdit; return this.panelInEdit;
} }
exitPanelEditor() {
this.panelInEdit!.destroy();
this.panelInEdit = undefined;
getTimeSrv().resumeAutoRefresh();
if (this.panelsAffectedByVariableChange || this.timeRangeUpdatedDuringEdit) {
this.startRefresh({
panelIds: this.panelsAffectedByVariableChange ?? [],
refreshAll: this.timeRangeUpdatedDuringEdit,
});
this.panelsAffectedByVariableChange = null;
this.timeRangeUpdatedDuringEdit = false;
}
}
initViewPanel(panel: PanelModel) { initViewPanel(panel: PanelModel) {
this.panelInView = panel; this.panelInView = panel;
panel.setIsViewing(true); panel.setIsViewing(true);
@ -433,13 +456,6 @@ export class DashboardModel implements TimeModel {
this.refreshIfPanelsAffectedByVariableChange(); this.refreshIfPanelsAffectedByVariableChange();
} }
exitPanelEditor() {
this.panelInEdit!.destroy();
this.panelInEdit = undefined;
getTimeSrv().resumeAutoRefresh();
this.refreshIfPanelsAffectedByVariableChange();
}
private refreshIfPanelsAffectedByVariableChange() { private refreshIfPanelsAffectedByVariableChange() {
if (!this.panelsAffectedByVariableChange) { if (!this.panelsAffectedByVariableChange) {
return; return;