Library Panels: Improve unsaved changes handling (#32026)

* Library Panels: Improve unsaved changes handling
This commit is contained in:
kay delaney 2021-03-25 13:11:09 +00:00 committed by GitHub
parent 093e5947f4
commit 298ff3ea01
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 38 additions and 22 deletions

View File

@ -1,5 +1,6 @@
import React, { PureComponent } from 'react'; import React, { PureComponent } from 'react';
import { connect, ConnectedProps } from 'react-redux'; import { connect, ConnectedProps } from 'react-redux';
import { Prompt } from 'react-router-dom';
import AutoSizer from 'react-virtualized-auto-sizer'; import AutoSizer from 'react-virtualized-auto-sizer';
import { css, cx } from 'emotion'; import { css, cx } from 'emotion';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
@ -29,6 +30,7 @@ import { DashboardPanel } from '../../dashgrid/DashboardPanel';
import { import {
exitPanelEditor, exitPanelEditor,
discardPanelChanges,
initPanelEditor, initPanelEditor,
panelEditorCleanUp, panelEditorCleanUp,
updatePanelEditorUIState, updatePanelEditorUIState,
@ -36,7 +38,6 @@ import {
} from './state/actions'; } from './state/actions';
import { updateTimeZoneForSession } from 'app/features/profile/state/reducers'; import { updateTimeZoneForSession } from 'app/features/profile/state/reducers';
import { setDiscardChanges } from './state/reducers';
import { getPanelEditorTabs } from './state/selectors'; import { getPanelEditorTabs } from './state/selectors';
import { getPanelStateById } from '../../state/selectors'; import { getPanelStateById } from '../../state/selectors';
@ -83,7 +84,7 @@ const mapDispatchToProps = {
exitPanelEditor, exitPanelEditor,
updateSourcePanel, updateSourcePanel,
panelEditorCleanUp, panelEditorCleanUp,
setDiscardChanges, discardPanelChanges,
updatePanelEditorUIState, updatePanelEditorUIState,
updateTimeZoneForSession, updateTimeZoneForSession,
notifyApp, notifyApp,
@ -118,15 +119,18 @@ export class PanelEditorUnconnected extends PureComponent<Props> {
this.forceUpdate(); this.forceUpdate();
}; };
onDiscard = () => { onBack = () => {
this.props.setDiscardChanges(true);
locationService.partial({ locationService.partial({
editPanel: null, editPanel: null,
tab: null, tab: null,
}); });
}; };
onDiscard = () => {
this.props.discardPanelChanges();
this.onBack();
};
onOpenDashboardSettings = () => { onOpenDashboardSettings = () => {
locationService.partial({ locationService.partial({
editview: 'settings', editview: 'settings',
@ -148,12 +152,11 @@ export class PanelEditorUnconnected extends PureComponent<Props> {
return; return;
} }
if (this.props.panel.libraryPanel.meta.connectedDashboards === 0) {
return;
}
const connectedDashboards = await getLibraryPanelConnectedDashboards(this.props.panel.libraryPanel.uid); const connectedDashboards = await getLibraryPanelConnectedDashboards(this.props.panel.libraryPanel.uid);
if (connectedDashboards.length === 1 && connectedDashboards.indexOf(this.props.dashboard.id) !== -1) { if (
connectedDashboards.length === 0 ||
(connectedDashboards.length === 1 && connectedDashboards.includes(this.props.dashboard.id))
) {
try { try {
await saveAndRefreshLibraryPanel(this.props.panel, this.props.dashboard.meta.folderId!); await saveAndRefreshLibraryPanel(this.props.panel, this.props.dashboard.meta.folderId!);
this.props.updateSourcePanel(this.props.panel); this.props.updateSourcePanel(this.props.panel);
@ -327,12 +330,7 @@ export class PanelEditorUnconnected extends PureComponent<Props> {
Save Save
</ToolbarButton> </ToolbarButton>
), ),
<ToolbarButton <ToolbarButton onClick={this.onBack} variant="primary" title="Apply changes and go back to dashboard" key="apply">
onClick={this.props.exitPanelEditor}
variant="primary"
title="Apply changes and go back to dashboard"
key="apply"
>
Apply Apply
</ToolbarButton>, </ToolbarButton>,
]; ];
@ -408,6 +406,18 @@ export class PanelEditorUnconnected extends PureComponent<Props> {
return ( return (
<div className={styles.wrapper} aria-label={selectors.components.PanelEditor.General.content}> <div className={styles.wrapper} aria-label={selectors.components.PanelEditor.General.content}>
<Prompt
when={true}
message={(location) => {
const searchParams = new URLSearchParams(location.search);
if (!this.props.panel.libraryPanel || !this.props.panel.hasChanged || searchParams.has('editPanel')) {
return true;
}
exitPanelEditor();
return false;
}}
/>
<PageToolbar title={`${dashboard.title} / Edit Panel`} onGoBack={exitPanelEditor}> <PageToolbar title={`${dashboard.title} / Edit Panel`} onGoBack={exitPanelEditor}>
{this.renderEditorActions()} {this.renderEditorActions()}
</PageToolbar> </PageToolbar>

View File

@ -42,19 +42,25 @@ export function updateSourcePanel(sourcePanel: PanelModel): ThunkResult<void> {
}; };
} }
export function discardPanelChanges(): ThunkResult<void> {
return async (dispatch, getStore) => {
const { getPanel } = getStore().panelEditor;
getPanel().hasChanged = false;
dispatch(setDiscardChanges(true));
};
}
export function exitPanelEditor(): ThunkResult<void> { export function exitPanelEditor(): ThunkResult<void> {
return async (dispatch, getStore) => { return async (dispatch, getStore) => {
const dashboard = getStore().dashboard.getModel(); const dashboard = getStore().dashboard.getModel();
const { getPanel, shouldDiscardChanges } = getStore().panelEditor; const { getPanel, shouldDiscardChanges } = getStore().panelEditor;
const onConfirm = () => locationService.partial({ editPanel: null, tab: null }); const onConfirm = () => locationService.partial({ editPanel: null, tab: null });
const panel = getPanel();
const onDiscard = () => { const onDiscard = () => {
dispatch(setDiscardChanges(true)); dispatch(discardPanelChanges());
onConfirm(); onConfirm();
}; };
const panel = getPanel();
if (shouldDiscardChanges || !panel.libraryPanel) { if (shouldDiscardChanges || !panel.libraryPanel) {
onConfirm(); onConfirm();
return; return;

View File

@ -109,12 +109,12 @@ export const SaveLibraryPanelModal: React.FC<Props> = ({
> >
Update all Update all
</Button> </Button>
<Button variant="secondary" onClick={onDismiss}>
Cancel
</Button>
<Button variant="destructive" onClick={discardAndClose}> <Button variant="destructive" onClick={discardAndClose}>
Discard Discard
</Button> </Button>
<Button variant="secondary" onClick={onDismiss}>
Cancel
</Button>
</HorizontalGroup> </HorizontalGroup>
</div> </div>
</Modal> </Modal>