Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Time to Visualize] Stay in Edit Mode After Dashboard Quicksave #91729

Merged
1 change: 1 addition & 0 deletions src/plugins/dashboard/public/application/dashboard_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,7 @@ export function DashboardApp({
}}
viewMode={viewMode}
lastDashboardId={savedDashboardId}
clearUnsavedChanges={() => setUnsavedChanges(false)}
timefilter={data.query.timefilter.timefilter}
onQuerySubmit={(_payload, isUpdate) => {
if (isUpdate === false) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@ export class DashboardStateManager {
/**
* Resets the state back to the last saved version of the dashboard.
*/
public resetState() {
public resetState(resetViewMode: boolean) {
// In order to show the correct warning, we have to store the unsaved
// title on the dashboard object. We should fix this at some point, but this is how all the other object
// save panels work at the moment.
Expand All @@ -366,9 +366,14 @@ export class DashboardStateManager {
this.stateDefaults.query = this.lastSavedDashboardFilters.query;
// Need to make a copy to ensure they are not overwritten.
this.stateDefaults.filters = [...this.getLastSavedFilterBars()];

this.isDirty = false;
this.stateContainer.set(this.stateDefaults);

if (resetViewMode) {
this.stateContainer.set(this.stateDefaults);
} else {
const currentViewMode = this.stateContainer.get().viewMode;
this.stateContainer.set({ ...this.stateDefaults, viewMode: currentViewMode });
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import { SavedObjectSaveOpts } from '../../services/saved_objects';
import { updateSavedDashboard } from './update_saved_dashboard';
import { DashboardStateManager } from '../dashboard_state_manager';

export type SavedDashboardSaveOpts = SavedObjectSaveOpts & { stayInEditMode?: boolean };

/**
* Saves the dashboard.
* @param toJson A custom toJson function. Used because the previous code used
Expand All @@ -23,7 +25,7 @@ export function saveDashboard(
toJson: (obj: any) => string,
timeFilter: TimefilterContract,
dashboardStateManager: DashboardStateManager,
saveOptions: SavedObjectSaveOpts
saveOptions: SavedDashboardSaveOpts
): Promise<string> {
const savedDashboard = dashboardStateManager.savedDashboard;
const appState = dashboardStateManager.appState;
Expand All @@ -36,7 +38,7 @@ export function saveDashboard(
// reset state only when save() was successful
// e.g. save() could be interrupted if title is duplicated and not confirmed
dashboardStateManager.lastSavedDashboardFilters = dashboardStateManager.getFilterState();
dashboardStateManager.resetState();
dashboardStateManager.resetState(!saveOptions.stayInEditMode);
}

return id;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,30 +18,30 @@ import {
} from '@elastic/eui';
import React from 'react';
import { OverlayStart } from '../../../../../core/public';
import { createConfirmStrings, leaveConfirmStrings } from '../../dashboard_strings';
import {
createConfirmStrings,
discardConfirmStrings,
leaveEditModeConfirmStrings,
} from '../../dashboard_strings';
import { toMountPoint } from '../../services/kibana_react';

export const confirmDiscardUnsavedChanges = (
overlays: OverlayStart,
discardCallback: () => void,
cancelButtonText = leaveConfirmStrings.getCancelButtonText()
) =>
export type DiscardOrKeepSelection = 'cancel' | 'discard' | 'keep';

export const confirmDiscardUnsavedChanges = (overlays: OverlayStart, discardCallback: () => void) =>
overlays
.openConfirm(leaveConfirmStrings.getDiscardSubtitle(), {
confirmButtonText: leaveConfirmStrings.getConfirmButtonText(),
cancelButtonText,
.openConfirm(discardConfirmStrings.getDiscardSubtitle(), {
confirmButtonText: discardConfirmStrings.getDiscardConfirmButtonText(),
cancelButtonText: discardConfirmStrings.getDiscardCancelButtonText(),
buttonColor: 'danger',
defaultFocusedButton: EUI_MODAL_CANCEL_BUTTON,
title: leaveConfirmStrings.getDiscardTitle(),
title: discardConfirmStrings.getDiscardTitle(),
})
.then((isConfirmed) => {
if (isConfirmed) {
discardCallback();
}
});

export type DiscardOrKeepSelection = 'cancel' | 'discard' | 'keep';

export const confirmDiscardOrKeepUnsavedChanges = (
overlays: OverlayStart
): Promise<DiscardOrKeepSelection> => {
Expand All @@ -50,45 +50,48 @@ export const confirmDiscardOrKeepUnsavedChanges = (
toMountPoint(
<>
<EuiModalHeader data-test-subj="dashboardDiscardConfirm">
<EuiModalHeaderTitle>{leaveConfirmStrings.getLeaveEditModeTitle()}</EuiModalHeaderTitle>
<EuiModalHeaderTitle>
{leaveEditModeConfirmStrings.getLeaveEditModeTitle()}
</EuiModalHeaderTitle>
</EuiModalHeader>

<EuiModalBody>
<EuiText>{leaveConfirmStrings.getLeaveEditModeSubtitle()}</EuiText>
<EuiText>{leaveEditModeConfirmStrings.getLeaveEditModeSubtitle()}</EuiText>
</EuiModalBody>

<EuiModalFooter>
<EuiButtonEmpty
data-test-subj="dashboardDiscardConfirmCancel"
onClick={() => session.close()}
>
{leaveConfirmStrings.getCancelButtonText()}
{leaveEditModeConfirmStrings.getLeaveEditModeCancelButtonText()}
</EuiButtonEmpty>
<EuiButtonEmpty
data-test-subj="dashboardDiscardConfirmKeep"
color="danger"
data-test-subj="dashboardDiscardConfirmDiscard"
onClick={() => {
session.close();
resolve('keep');
resolve('discard');
}}
>
{leaveConfirmStrings.getKeepChangesText()}
{leaveEditModeConfirmStrings.getLeaveEditModeDiscardButtonText()}
</EuiButtonEmpty>
<EuiButton
fill
color="danger"
data-test-subj="dashboardDiscardConfirmDiscard"
data-test-subj="dashboardDiscardConfirmKeep"
onClick={() => {
session.close();
resolve('discard');
resolve('keep');
}}
>
{leaveConfirmStrings.getConfirmButtonText()}
{leaveEditModeConfirmStrings.getLeaveEditModeKeepChangesText()}
</EuiButton>
</EuiModalFooter>
</>
),
{
'data-test-subj': 'dashboardDiscardConfirmModal',
maxWidth: 550,
}
);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,7 @@ import {
} from '@elastic/eui';
import React, { useCallback, useEffect, useState } from 'react';
import { DashboardSavedObject } from '../..';
import {
createConfirmStrings,
dashboardUnsavedListingStrings,
getNewDashboardTitle,
} from '../../dashboard_strings';
import { dashboardUnsavedListingStrings, getNewDashboardTitle } from '../../dashboard_strings';
import { useKibana } from '../../services/kibana_react';
import { DASHBOARD_PANELS_UNSAVED_ID } from '../lib/dashboard_panel_storage';
import { DashboardAppServices, DashboardRedirect } from '../types';
Expand Down Expand Up @@ -136,14 +132,10 @@ export const DashboardUnsavedListing = ({

const onDiscard = useCallback(
(id?: string) => {
confirmDiscardUnsavedChanges(
overlays,
() => {
dashboardPanelStorage.clearPanels(id);
refreshUnsavedDashboards();
},
createConfirmStrings.getCancelButtonText()
);
confirmDiscardUnsavedChanges(overlays, () => {
dashboardPanelStorage.clearPanels(id);
refreshUnsavedDashboards();
});
},
[overlays, refreshUnsavedDashboards, dashboardPanelStorage]
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,7 @@ import {
openAddPanelFlyout,
ViewMode,
} from '../../services/embeddable';
import {
getSavedObjectFinder,
SavedObjectSaveOpts,
SaveResult,
showSaveModal,
} from '../../services/saved_objects';
import { getSavedObjectFinder, SaveResult, showSaveModal } from '../../services/saved_objects';

import { NavAction } from '../../types';
import { DashboardSavedObject } from '../..';
Expand All @@ -48,6 +43,7 @@ import { OverlayRef } from '../../../../../core/public';
import { getNewDashboardTitle, unsavedChangesBadge } from '../../dashboard_strings';
import { DASHBOARD_PANELS_UNSAVED_ID } from '../lib/dashboard_panel_storage';
import { DashboardContainer } from '..';
import { SavedDashboardSaveOpts } from '../lib/save_dashboard';

export interface DashboardTopNavState {
chromeIsVisible: boolean;
Expand All @@ -64,13 +60,15 @@ export interface DashboardTopNavProps {
timefilter: TimefilterContract;
indexPatterns: IndexPattern[];
redirectTo: DashboardRedirect;
unsavedChanges?: boolean;
unsavedChanges: boolean;
clearUnsavedChanges: () => void;
lastDashboardId?: string;
viewMode: ViewMode;
}

export function DashboardTopNav({
dashboardStateManager,
clearUnsavedChanges,
dashboardContainer,
lastDashboardId,
unsavedChanges,
Expand Down Expand Up @@ -98,6 +96,7 @@ export function DashboardTopNav({
} = useKibana<DashboardAppServices>().services;

const [state, setState] = useState<DashboardTopNavState>({ chromeIsVisible: false });
const [isSaveInProgress, setIsSaveInProgress] = useState(false);

useEffect(() => {
const visibleSubscription = chrome.getIsVisible$().subscribe((chromeIsVisible) => {
Expand Down Expand Up @@ -177,7 +176,7 @@ export function DashboardTopNav({
}

function discardChanges() {
dashboardStateManager.resetState();
dashboardStateManager.resetState(true);
dashboardStateManager.clearUnsavedPanels();

// We need to do a hard reset of the timepicker. appState will not reload like
Expand Down Expand Up @@ -222,7 +221,7 @@ export function DashboardTopNav({
* @resolved {String} - The id of the doc
*/
const save = useCallback(
async (saveOptions: SavedObjectSaveOpts) => {
async (saveOptions: SavedDashboardSaveOpts) => {
return saveDashboard(angular.toJson, timefilter, dashboardStateManager, saveOptions)
.then(function (id) {
if (id) {
Expand All @@ -239,7 +238,6 @@ export function DashboardTopNav({
redirectTo({ destination: 'dashboard', id, useReplace: !lastDashboardId });
} else {
chrome.docTitle.change(dashboardStateManager.savedDashboard.lastSavedTitle);
dashboardStateManager.switchViewMode(ViewMode.VIEW);
}
}
return { id };
Expand Down Expand Up @@ -355,7 +353,8 @@ export function DashboardTopNav({
}
}

save({}).then((response: SaveResult) => {
setIsSaveInProgress(true);
save({ stayInEditMode: true }).then((response: SaveResult) => {
// If the save wasn't successful, put the original values back.
if (!(response as { id: string }).id) {
dashboardStateManager.setTitle(currentTitle);
Expand All @@ -364,10 +363,13 @@ export function DashboardTopNav({
if (savedObjectsTagging) {
dashboardStateManager.setTags(currentTags);
}
} else {
clearUnsavedChanges();
}
setIsSaveInProgress(false);
return response;
});
}, [save, savedObjectsTagging, dashboardStateManager]);
}, [save, savedObjectsTagging, dashboardStateManager, clearUnsavedChanges]);

const runClone = useCallback(() => {
const currentTitle = dashboardStateManager.getTitle();
Expand Down Expand Up @@ -467,6 +469,7 @@ export function DashboardTopNav({
hideWriteControls: dashboardCapabilities.hideWriteControls,
isNewDashboard: !savedDashboard.id,
isDirty: dashboardStateManager.isDirty,
isSaveInProgress,
});

const badges = unsavedChanges
Expand Down
Loading