mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Dashboard: Unsaved changes warning fixes (#54706)
* Quick test of alternative method * change order of if condition again for better readability * Think I have something that is working * Update
This commit is contained in:
parent
ab79976a8d
commit
e5c32c8cc9
@ -6,7 +6,7 @@ import { Subscription } from 'rxjs';
|
|||||||
|
|
||||||
import { FieldConfigSource, GrafanaTheme2 } from '@grafana/data';
|
import { FieldConfigSource, GrafanaTheme2 } from '@grafana/data';
|
||||||
import { selectors } from '@grafana/e2e-selectors';
|
import { selectors } from '@grafana/e2e-selectors';
|
||||||
import { isFetchError, locationService } from '@grafana/runtime';
|
import { locationService } from '@grafana/runtime';
|
||||||
import {
|
import {
|
||||||
HorizontalGroup,
|
HorizontalGroup,
|
||||||
InlineSwitch,
|
InlineSwitch,
|
||||||
@ -31,12 +31,6 @@ import { PanelOptionsChangedEvent, ShowModalReactEvent } from 'app/types/events'
|
|||||||
import { notifyApp } from '../../../../core/actions';
|
import { notifyApp } from '../../../../core/actions';
|
||||||
import { UnlinkModal } from '../../../library-panels/components/UnlinkModal/UnlinkModal';
|
import { UnlinkModal } from '../../../library-panels/components/UnlinkModal/UnlinkModal';
|
||||||
import { isPanelModelLibraryPanel } from '../../../library-panels/guard';
|
import { isPanelModelLibraryPanel } from '../../../library-panels/guard';
|
||||||
import { getLibraryPanelConnectedDashboards } from '../../../library-panels/state/api';
|
|
||||||
import {
|
|
||||||
createPanelLibraryErrorNotification,
|
|
||||||
createPanelLibrarySuccessNotification,
|
|
||||||
saveAndRefreshLibraryPanel,
|
|
||||||
} from '../../../library-panels/utils';
|
|
||||||
import { getVariablesByKey } from '../../../variables/state/selectors';
|
import { getVariablesByKey } from '../../../variables/state/selectors';
|
||||||
import { DashboardPanel } from '../../dashgrid/DashboardPanel';
|
import { DashboardPanel } from '../../dashgrid/DashboardPanel';
|
||||||
import { DashboardModel, PanelModel } from '../../state';
|
import { DashboardModel, PanelModel } from '../../state';
|
||||||
@ -154,22 +148,6 @@ export class PanelEditorUnconnected extends PureComponent<Props> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const connectedDashboards = await getLibraryPanelConnectedDashboards(this.props.panel.libraryPanel.uid);
|
|
||||||
if (
|
|
||||||
connectedDashboards.length === 0 ||
|
|
||||||
(connectedDashboards.length === 1 && connectedDashboards.includes(this.props.dashboard.id))
|
|
||||||
) {
|
|
||||||
try {
|
|
||||||
await saveAndRefreshLibraryPanel(this.props.panel, this.props.dashboard.meta.folderId!);
|
|
||||||
this.props.notifyApp(createPanelLibrarySuccessNotification('Library panel saved'));
|
|
||||||
} catch (err) {
|
|
||||||
if (isFetchError(err)) {
|
|
||||||
this.props.notifyApp(createPanelLibraryErrorNotification(`Error saving library panel: "${err.statusText}"`));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState({ showSaveLibraryPanelModal: true });
|
this.setState({ showSaveLibraryPanelModal: true });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -165,6 +165,45 @@ describe('panelEditor actions', () => {
|
|||||||
expect(sourcePanel.configRev).toEqual(0);
|
expect(sourcePanel.configRev).toEqual(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should apply changes when dashboard was saved from panel edit', async () => {
|
||||||
|
const sourcePanel = new PanelModel({ id: 12, type: 'graph' });
|
||||||
|
sourcePanel.plugin = getPanelPlugin({});
|
||||||
|
sourcePanel.plugin.angularPanelCtrl = undefined;
|
||||||
|
|
||||||
|
const dashboard = new DashboardModel({
|
||||||
|
panels: [{ id: 12, type: 'graph' }],
|
||||||
|
});
|
||||||
|
|
||||||
|
const panel = dashboard.initEditPanel(sourcePanel);
|
||||||
|
|
||||||
|
const state: PanelEditorState = {
|
||||||
|
...initialState(),
|
||||||
|
getPanel: () => panel,
|
||||||
|
getSourcePanel: () => sourcePanel,
|
||||||
|
};
|
||||||
|
|
||||||
|
panel.setProperty('title', 'new title');
|
||||||
|
panel.configRev = 0;
|
||||||
|
panel.hasSavedPanelEditChange = true;
|
||||||
|
|
||||||
|
await thunkTester({
|
||||||
|
panelEditor: state,
|
||||||
|
panels: {},
|
||||||
|
dashboard: {
|
||||||
|
getModel: () => dashboard,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.givenThunk(exitPanelEditor)
|
||||||
|
.whenThunkIsDispatched();
|
||||||
|
|
||||||
|
expect(sourcePanel.configRev).toEqual(1);
|
||||||
|
|
||||||
|
await new Promise((r) => setTimeout(r, 30));
|
||||||
|
|
||||||
|
// expect configRev to be reset to 0 as it was saved
|
||||||
|
expect(sourcePanel.hasChanged).toEqual(false);
|
||||||
|
});
|
||||||
|
|
||||||
it('should apply changes when leaving panel edit with angular panel', async () => {
|
it('should apply changes when leaving panel edit with angular panel', async () => {
|
||||||
const sourcePanel = new PanelModel({ id: 12, type: 'graph' });
|
const sourcePanel = new PanelModel({ id: 12, type: 'graph' });
|
||||||
sourcePanel.plugin = getPanelPlugin({});
|
sourcePanel.plugin = getPanelPlugin({});
|
||||||
|
@ -116,10 +116,7 @@ export function exitPanelEditor(): ThunkResult<void> {
|
|||||||
dashboard.exitPanelEditor();
|
dashboard.exitPanelEditor();
|
||||||
}
|
}
|
||||||
|
|
||||||
// For angular panels we always commit as panel.hasChanged will not have picked up changes done from angular
|
if (hasPanelChangedInPanelEdit(panel) && !shouldDiscardChanges) {
|
||||||
const commitChanges = !shouldDiscardChanges && (panel.hasChanged || panel.isAngularPlugin());
|
|
||||||
|
|
||||||
if (commitChanges) {
|
|
||||||
const modifiedSaveModel = panel.getSaveModel();
|
const modifiedSaveModel = panel.getSaveModel();
|
||||||
const sourcePanel = getSourcePanel();
|
const sourcePanel = getSourcePanel();
|
||||||
const panelTypeChanged = sourcePanel.type !== panel.type;
|
const panelTypeChanged = sourcePanel.type !== panel.type;
|
||||||
@ -143,6 +140,11 @@ export function exitPanelEditor(): ThunkResult<void> {
|
|||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
sourcePanel.getQueryRunner().useLastResultFrom(panel.getQueryRunner());
|
sourcePanel.getQueryRunner().useLastResultFrom(panel.getQueryRunner());
|
||||||
sourcePanel.render();
|
sourcePanel.render();
|
||||||
|
|
||||||
|
// If all changes where saved then reset configRev after applying changes
|
||||||
|
if (panel.hasSavedPanelEditChange && !panel.hasChanged) {
|
||||||
|
sourcePanel.configRev = 0;
|
||||||
|
}
|
||||||
}, 20);
|
}, 20);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,6 +153,10 @@ export function exitPanelEditor(): ThunkResult<void> {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function hasPanelChangedInPanelEdit(panel: PanelModel) {
|
||||||
|
return panel.hasChanged || panel.hasSavedPanelEditChange || panel.isAngularPlugin();
|
||||||
|
}
|
||||||
|
|
||||||
export function updatePanelEditorUIState(uiState: Partial<PanelEditorUIState>): ThunkResult<void> {
|
export function updatePanelEditorUIState(uiState: Partial<PanelEditorUIState>): ThunkResult<void> {
|
||||||
return (dispatch, getStore) => {
|
return (dispatch, getStore) => {
|
||||||
const nextState = { ...getStore().panelEditor.ui, ...uiState };
|
const nextState = { ...getStore().panelEditor.ui, ...uiState };
|
||||||
|
@ -494,6 +494,12 @@ export class DashboardModel implements TimeModel {
|
|||||||
for (const panel of this.panels) {
|
for (const panel of this.panels) {
|
||||||
panel.configRev = 0;
|
panel.configRev = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.panelInEdit) {
|
||||||
|
// Remember that we have a saved a change in panel editor so we apply it when leaving panel edit
|
||||||
|
this.panelInEdit.hasSavedPanelEditChange = this.panelInEdit.configRev > 0;
|
||||||
|
this.panelInEdit.configRev = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hasUnsavedChanges() {
|
hasUnsavedChanges() {
|
||||||
|
@ -70,6 +70,7 @@ const notPersistedProperties: { [str: string]: boolean } = {
|
|||||||
queryRunner: true,
|
queryRunner: true,
|
||||||
replaceVariables: true,
|
replaceVariables: true,
|
||||||
configRev: true,
|
configRev: true,
|
||||||
|
hasSavedPanelEditChange: true,
|
||||||
getDisplayTitle: true,
|
getDisplayTitle: true,
|
||||||
dataSupport: true,
|
dataSupport: true,
|
||||||
key: true,
|
key: true,
|
||||||
@ -170,7 +171,7 @@ export class PanelModel implements DataConfigSource, IPanelModel {
|
|||||||
links?: DataLink[];
|
links?: DataLink[];
|
||||||
declare transparent: boolean;
|
declare transparent: boolean;
|
||||||
|
|
||||||
libraryPanel?: { uid: undefined; name: string } | PanelModelLibraryPanel;
|
libraryPanel?: { uid: undefined; name: string; version?: number } | PanelModelLibraryPanel;
|
||||||
|
|
||||||
autoMigrateFrom?: string;
|
autoMigrateFrom?: string;
|
||||||
|
|
||||||
@ -179,6 +180,7 @@ export class PanelModel implements DataConfigSource, IPanelModel {
|
|||||||
isEditing = false;
|
isEditing = false;
|
||||||
isInView = false;
|
isInView = false;
|
||||||
configRev = 0; // increments when configs change
|
configRev = 0; // increments when configs change
|
||||||
|
hasSavedPanelEditChange?: boolean;
|
||||||
hasRefreshed?: boolean;
|
hasRefreshed?: boolean;
|
||||||
cacheTimeout?: string | null;
|
cacheTimeout?: string | null;
|
||||||
cachedPluginOptions: Record<string, PanelOptionsCache> = {};
|
cachedPluginOptions: Record<string, PanelOptionsCache> = {};
|
||||||
|
@ -45,6 +45,7 @@ function updatePanelModelWithUpdate(panel: PanelModel, updated: LibraryElementDT
|
|||||||
libraryPanel: toPanelModelLibraryPanel(updated),
|
libraryPanel: toPanelModelLibraryPanel(updated),
|
||||||
title: panel.title,
|
title: panel.title,
|
||||||
});
|
});
|
||||||
|
panel.hasSavedPanelEditChange = true;
|
||||||
panel.refresh();
|
panel.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,6 +148,7 @@ export function changeToLibraryPanel(panel: PanelModel, libraryPanel: LibraryEle
|
|||||||
}
|
}
|
||||||
|
|
||||||
panel.configRev = 0;
|
panel.configRev = 0;
|
||||||
|
panel.hasSavedPanelEditChange = true;
|
||||||
panel.refresh();
|
panel.refresh();
|
||||||
|
|
||||||
panel.events.publish(PanelQueriesChangedEvent);
|
panel.events.publish(PanelQueriesChangedEvent);
|
||||||
|
Loading…
Reference in New Issue
Block a user