From 0095681d251bffd8c28ace6ef467afe905b4caa9 Mon Sep 17 00:00:00 2001 From: Devon Thomson Date: Fri, 19 Feb 2021 12:34:01 -0500 Subject: [PATCH] [Time to Visualize] Stay in Edit Mode After Dashboard Quicksave (#91729) * Make quicksave function stay in edit mode --- .../public/application/dashboard_app.tsx | 1 + .../application/dashboard_state_manager.ts | 11 ++- .../public/application/lib/save_dashboard.ts | 6 +- .../application/listing/confirm_overlays.tsx | 47 +++++----- .../listing/dashboard_unsaved_listing.tsx | 18 ++-- .../application/top_nav/dashboard_top_nav.tsx | 27 +++--- .../application/top_nav/get_top_nav_config.ts | 54 ++++++----- .../dashboard/public/dashboard_strings.ts | 90 ++++++++++++------- .../public/top_nav_menu/top_nav_menu_data.tsx | 1 + .../public/top_nav_menu/top_nav_menu_item.tsx | 1 + .../apps/dashboard/dashboard_save.ts | 6 +- .../translations/translations/ja-JP.json | 2 - .../translations/translations/zh-CN.json | 2 - 13 files changed, 156 insertions(+), 110 deletions(-) diff --git a/src/plugins/dashboard/public/application/dashboard_app.tsx b/src/plugins/dashboard/public/application/dashboard_app.tsx index f659fa002e922..8466cf009db9d 100644 --- a/src/plugins/dashboard/public/application/dashboard_app.tsx +++ b/src/plugins/dashboard/public/application/dashboard_app.tsx @@ -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) { diff --git a/src/plugins/dashboard/public/application/dashboard_state_manager.ts b/src/plugins/dashboard/public/application/dashboard_state_manager.ts index e4b2afa8a46ea..7f3f347e6e3ae 100644 --- a/src/plugins/dashboard/public/application/dashboard_state_manager.ts +++ b/src/plugins/dashboard/public/application/dashboard_state_manager.ts @@ -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. @@ -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 }); + } } /** diff --git a/src/plugins/dashboard/public/application/lib/save_dashboard.ts b/src/plugins/dashboard/public/application/lib/save_dashboard.ts index 80392f61946cd..6913fcda4c8e2 100644 --- a/src/plugins/dashboard/public/application/lib/save_dashboard.ts +++ b/src/plugins/dashboard/public/application/lib/save_dashboard.ts @@ -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 @@ -23,7 +25,7 @@ export function saveDashboard( toJson: (obj: any) => string, timeFilter: TimefilterContract, dashboardStateManager: DashboardStateManager, - saveOptions: SavedObjectSaveOpts + saveOptions: SavedDashboardSaveOpts ): Promise { const savedDashboard = dashboardStateManager.savedDashboard; const appState = dashboardStateManager.appState; @@ -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; diff --git a/src/plugins/dashboard/public/application/listing/confirm_overlays.tsx b/src/plugins/dashboard/public/application/listing/confirm_overlays.tsx index d302bb4216bc4..b1e9af32ccd19 100644 --- a/src/plugins/dashboard/public/application/listing/confirm_overlays.tsx +++ b/src/plugins/dashboard/public/application/listing/confirm_overlays.tsx @@ -18,21 +18,23 @@ 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) { @@ -40,8 +42,6 @@ export const confirmDiscardUnsavedChanges = ( } }); -export type DiscardOrKeepSelection = 'cancel' | 'discard' | 'keep'; - export const confirmDiscardOrKeepUnsavedChanges = ( overlays: OverlayStart ): Promise => { @@ -50,11 +50,13 @@ export const confirmDiscardOrKeepUnsavedChanges = ( toMountPoint( <> - {leaveConfirmStrings.getLeaveEditModeTitle()} + + {leaveEditModeConfirmStrings.getLeaveEditModeTitle()} + - {leaveConfirmStrings.getLeaveEditModeSubtitle()} + {leaveEditModeConfirmStrings.getLeaveEditModeSubtitle()} @@ -62,33 +64,34 @@ export const confirmDiscardOrKeepUnsavedChanges = ( data-test-subj="dashboardDiscardConfirmCancel" onClick={() => session.close()} > - {leaveConfirmStrings.getCancelButtonText()} + {leaveEditModeConfirmStrings.getLeaveEditModeCancelButtonText()} { session.close(); - resolve('keep'); + resolve('discard'); }} > - {leaveConfirmStrings.getKeepChangesText()} + {leaveEditModeConfirmStrings.getLeaveEditModeDiscardButtonText()} { session.close(); - resolve('discard'); + resolve('keep'); }} > - {leaveConfirmStrings.getConfirmButtonText()} + {leaveEditModeConfirmStrings.getLeaveEditModeKeepChangesText()} ), { 'data-test-subj': 'dashboardDiscardConfirmModal', + maxWidth: 550, } ); }); diff --git a/src/plugins/dashboard/public/application/listing/dashboard_unsaved_listing.tsx b/src/plugins/dashboard/public/application/listing/dashboard_unsaved_listing.tsx index db50cfb638d64..66e8b2348490a 100644 --- a/src/plugins/dashboard/public/application/listing/dashboard_unsaved_listing.tsx +++ b/src/plugins/dashboard/public/application/listing/dashboard_unsaved_listing.tsx @@ -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'; @@ -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] ); diff --git a/src/plugins/dashboard/public/application/top_nav/dashboard_top_nav.tsx b/src/plugins/dashboard/public/application/top_nav/dashboard_top_nav.tsx index 11fb7f0cb56ff..d279a6c219c9d 100644 --- a/src/plugins/dashboard/public/application/top_nav/dashboard_top_nav.tsx +++ b/src/plugins/dashboard/public/application/top_nav/dashboard_top_nav.tsx @@ -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 '../..'; @@ -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; @@ -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, @@ -98,6 +96,7 @@ export function DashboardTopNav({ } = useKibana().services; const [state, setState] = useState({ chromeIsVisible: false }); + const [isSaveInProgress, setIsSaveInProgress] = useState(false); useEffect(() => { const visibleSubscription = chrome.getIsVisible$().subscribe((chromeIsVisible) => { @@ -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 @@ -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) { @@ -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 }; @@ -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); @@ -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(); @@ -467,6 +469,7 @@ export function DashboardTopNav({ hideWriteControls: dashboardCapabilities.hideWriteControls, isNewDashboard: !savedDashboard.id, isDirty: dashboardStateManager.isDirty, + isSaveInProgress, }); const badges = unsavedChanges diff --git a/src/plugins/dashboard/public/application/top_nav/get_top_nav_config.ts b/src/plugins/dashboard/public/application/top_nav/get_top_nav_config.ts index 26eea1b5f718d..801ab54eb9839 100644 --- a/src/plugins/dashboard/public/application/top_nav/get_top_nav_config.ts +++ b/src/plugins/dashboard/public/application/top_nav/get_top_nav_config.ts @@ -10,6 +10,7 @@ import { i18n } from '@kbn/i18n'; import { ViewMode } from '../../services/embeddable'; import { TopNavIds } from './top_nav_ids'; import { NavAction } from '../../types'; +import { TopNavMenuData } from '../../../../navigation/public'; /** * @param actions - A mapping of TopNavIds to an action function that should run when the @@ -20,7 +21,12 @@ import { NavAction } from '../../types'; export function getTopNavConfig( dashboardMode: ViewMode, actions: { [key: string]: NavAction }, - options: { hideWriteControls: boolean; isNewDashboard: boolean; isDirty: boolean } + options: { + hideWriteControls: boolean; + isNewDashboard: boolean; + isDirty: boolean; + isSaveInProgress?: boolean; + } ) { switch (dashboardMode) { case ViewMode.VIEW: @@ -36,20 +42,17 @@ export function getTopNavConfig( getEditConfig(actions[TopNavIds.ENTER_EDIT_MODE]), ]; case ViewMode.EDIT: - return options.isNewDashboard - ? [ - getOptionsConfig(actions[TopNavIds.OPTIONS]), - getShareConfig(actions[TopNavIds.SHARE]), - getViewConfig(actions[TopNavIds.EXIT_EDIT_MODE]), - getSaveConfig(actions[TopNavIds.SAVE], options.isNewDashboard), - ] - : [ - getOptionsConfig(actions[TopNavIds.OPTIONS]), - getShareConfig(actions[TopNavIds.SHARE]), - getViewConfig(actions[TopNavIds.EXIT_EDIT_MODE]), - getSaveConfig(actions[TopNavIds.SAVE]), - getQuickSave(actions[TopNavIds.QUICK_SAVE]), - ]; + const disableButton = options.isSaveInProgress; + const navItems: TopNavMenuData[] = [ + getOptionsConfig(actions[TopNavIds.OPTIONS], disableButton), + getShareConfig(actions[TopNavIds.SHARE], disableButton), + getViewConfig(actions[TopNavIds.EXIT_EDIT_MODE], disableButton), + getSaveConfig(actions[TopNavIds.SAVE], options.isNewDashboard, disableButton), + ]; + if (!options.isNewDashboard) { + navItems.push(getQuickSave(actions[TopNavIds.QUICK_SAVE], disableButton, options.isDirty)); + } + return navItems; default: return []; } @@ -106,9 +109,12 @@ function getEditConfig(action: NavAction) { /** * @returns {kbnTopNavConfig} */ -function getQuickSave(action: NavAction) { +function getQuickSave(action: NavAction, isLoading?: boolean, isDirty?: boolean) { return { + isLoading, + disableButton: !isDirty, id: 'quick-save', + iconType: 'save', emphasize: true, label: getSaveButtonLabel(), description: i18n.translate('dashboard.topNave.saveConfigDescription', { @@ -122,10 +128,12 @@ function getQuickSave(action: NavAction) { /** * @returns {kbnTopNavConfig} */ -function getSaveConfig(action: NavAction, isNewDashboard = false) { +function getSaveConfig(action: NavAction, isNewDashboard = false, disableButton?: boolean) { return { + disableButton, id: 'save', label: isNewDashboard ? getSaveButtonLabel() : getSaveAsButtonLabel(), + iconType: isNewDashboard ? 'save' : undefined, description: i18n.translate('dashboard.topNave.saveAsConfigDescription', { defaultMessage: 'Save as a new dashboard', }), @@ -138,11 +146,12 @@ function getSaveConfig(action: NavAction, isNewDashboard = false) { /** * @returns {kbnTopNavConfig} */ -function getViewConfig(action: NavAction) { +function getViewConfig(action: NavAction, disableButton?: boolean) { return { + disableButton, id: 'cancel', label: i18n.translate('dashboard.topNave.cancelButtonAriaLabel', { - defaultMessage: 'cancel', + defaultMessage: 'Return', }), description: i18n.translate('dashboard.topNave.viewConfigDescription', { defaultMessage: 'Switch to view-only mode', @@ -172,7 +181,7 @@ function getCloneConfig(action: NavAction) { /** * @returns {kbnTopNavConfig} */ -function getShareConfig(action: NavAction | undefined) { +function getShareConfig(action: NavAction | undefined, disableButton?: boolean) { return { id: 'share', label: i18n.translate('dashboard.topNave.shareButtonAriaLabel', { @@ -184,15 +193,16 @@ function getShareConfig(action: NavAction | undefined) { testId: 'shareTopNavButton', run: action ?? (() => {}), // disable the Share button if no action specified - disableButton: !action, + disableButton: !action || disableButton, }; } /** * @returns {kbnTopNavConfig} */ -function getOptionsConfig(action: NavAction) { +function getOptionsConfig(action: NavAction, disableButton?: boolean) { return { + disableButton, id: 'options', label: i18n.translate('dashboard.topNave.optionsButtonAriaLabel', { defaultMessage: 'options', diff --git a/src/plugins/dashboard/public/dashboard_strings.ts b/src/plugins/dashboard/public/dashboard_strings.ts index dad347b176c7e..79a59d0cfa605 100644 --- a/src/plugins/dashboard/public/dashboard_strings.ts +++ b/src/plugins/dashboard/public/dashboard_strings.ts @@ -199,6 +199,25 @@ export const getNewDashboardTitle = () => defaultMessage: 'New Dashboard', }); +export const getDashboard60Warning = () => + i18n.translate('dashboard.urlWasRemovedInSixZeroWarningMessage', { + defaultMessage: 'The url "dashboard/create" was removed in 6.0. Please update your bookmarks.', + }); + +export const dashboardReadonlyBadge = { + getText: () => + i18n.translate('dashboard.badge.readOnly.text', { + defaultMessage: 'Read only', + }), + getTooltip: () => + i18n.translate('dashboard.badge.readOnly.tooltip', { + defaultMessage: 'Unable to save dashboards', + }), +}; + +/* + Modals +*/ export const shareModalStrings = { getTopMenuCheckbox: () => i18n.translate('dashboard.embedUrlParamExtension.topMenu', { @@ -222,22 +241,6 @@ export const shareModalStrings = { }), }; -export const getDashboard60Warning = () => - i18n.translate('dashboard.urlWasRemovedInSixZeroWarningMessage', { - defaultMessage: 'The url "dashboard/create" was removed in 6.0. Please update your bookmarks.', - }); - -export const dashboardReadonlyBadge = { - getText: () => - i18n.translate('dashboard.badge.readOnly.text', { - defaultMessage: 'Read only', - }), - getTooltip: () => - i18n.translate('dashboard.badge.readOnly.tooltip', { - defaultMessage: 'Unable to save dashboards', - }), -}; - export const leaveConfirmStrings = { getLeaveTitle: () => i18n.translate('dashboard.appLeaveConfirmModal.unsavedChangesTitle', { @@ -247,33 +250,51 @@ export const leaveConfirmStrings = { i18n.translate('dashboard.appLeaveConfirmModal.unsavedChangesSubtitle', { defaultMessage: 'Leave Dashboard with unsaved work?', }), - getKeepChangesText: () => - i18n.translate('dashboard.appLeaveConfirmModal.keepUnsavedChangesButtonLabel', { - defaultMessage: 'Keep unsaved changes', + getLeaveCancelButtonText: () => + i18n.translate('dashboard.appLeaveConfirmModal.cancelButtonLabel', { + defaultMessage: 'Cancel', }), +}; + +export const leaveEditModeConfirmStrings = { getLeaveEditModeTitle: () => - i18n.translate('dashboard.changeViewModeConfirmModal.leaveEditMode', { - defaultMessage: 'Leave edit mode with unsaved work?', + i18n.translate('dashboard.changeViewModeConfirmModal.leaveEditModeTitle', { + defaultMessage: 'You have unsaved changes', }), getLeaveEditModeSubtitle: () => - i18n.translate('dashboard.changeViewModeConfirmModal.discardChangesOptionalDescription', { - defaultMessage: `If you discard your changes, there's no getting them back.`, + i18n.translate('dashboard.changeViewModeConfirmModal.description', { + defaultMessage: `You can keep or discard your changes on return to view mode. You can't recover discarded changes.`, + }), + getLeaveEditModeKeepChangesText: () => + i18n.translate('dashboard.changeViewModeConfirmModal.keepUnsavedChangesButtonLabel', { + defaultMessage: 'Keep changes', + }), + getLeaveEditModeDiscardButtonText: () => + i18n.translate('dashboard.changeViewModeConfirmModal.confirmButtonLabel', { + defaultMessage: 'Discard changes', + }), + getLeaveEditModeCancelButtonText: () => + i18n.translate('dashboard.changeViewModeConfirmModal.cancelButtonLabel', { + defaultMessage: 'Continue editing', }), +}; + +export const discardConfirmStrings = { getDiscardTitle: () => - i18n.translate('dashboard.changeViewModeConfirmModal.discardChangesTitle', { + i18n.translate('dashboard.discardChangesConfirmModal.discardChangesTitle', { defaultMessage: 'Discard changes to dashboard?', }), getDiscardSubtitle: () => - i18n.translate('dashboard.changeViewModeConfirmModal.discardChangesDescription', { + i18n.translate('dashboard.discardChangesConfirmModal.discardChangesDescription', { defaultMessage: `Once you discard your changes, there's no getting them back.`, }), - getConfirmButtonText: () => - i18n.translate('dashboard.changeViewModeConfirmModal.confirmButtonLabel', { + getDiscardConfirmButtonText: () => + i18n.translate('dashboard.discardChangesConfirmModal.confirmButtonLabel', { defaultMessage: 'Discard changes', }), - getCancelButtonText: () => - i18n.translate('dashboard.changeViewModeConfirmModal.cancelButtonLabel', { - defaultMessage: 'Continue editing', + getDiscardCancelButtonText: () => + i18n.translate('dashboard.discardChangesConfirmModal.cancelButtonLabel', { + defaultMessage: 'Cancel', }), }; @@ -290,13 +311,20 @@ export const createConfirmStrings = { i18n.translate('dashboard.createConfirmModal.confirmButtonLabel', { defaultMessage: 'Start over', }), - getContinueButtonText: () => leaveConfirmStrings.getCancelButtonText(), + getContinueButtonText: () => + i18n.translate('dashboard.createConfirmModal.continueButtonLabel', { + defaultMessage: 'Continue editing', + }), getCancelButtonText: () => i18n.translate('dashboard.createConfirmModal.cancelButtonLabel', { defaultMessage: 'Cancel', }), }; +/* + Error Messages +*/ + export const panelStorageErrorStrings = { getPanelsGetError: (message: string) => i18n.translate('dashboard.panelStorageError.getError', { diff --git a/src/plugins/navigation/public/top_nav_menu/top_nav_menu_data.tsx b/src/plugins/navigation/public/top_nav_menu/top_nav_menu_data.tsx index 3a54c7ed01185..b6b056134361a 100644 --- a/src/plugins/navigation/public/top_nav_menu/top_nav_menu_data.tsx +++ b/src/plugins/navigation/public/top_nav_menu/top_nav_menu_data.tsx @@ -20,6 +20,7 @@ export interface TopNavMenuData { disableButton?: boolean | (() => boolean); tooltip?: string | (() => string | undefined); emphasize?: boolean; + isLoading?: boolean; iconType?: string; iconSide?: EuiButtonProps['iconSide']; } diff --git a/src/plugins/navigation/public/top_nav_menu/top_nav_menu_item.tsx b/src/plugins/navigation/public/top_nav_menu/top_nav_menu_item.tsx index ec91452badf36..523bf07f828c9 100644 --- a/src/plugins/navigation/public/top_nav_menu/top_nav_menu_item.tsx +++ b/src/plugins/navigation/public/top_nav_menu/top_nav_menu_item.tsx @@ -30,6 +30,7 @@ export function TopNavMenuItem(props: TopNavMenuData) { const commonButtonProps = { isDisabled: isDisabled(), onClick: handleClick, + isLoading: props.isLoading, iconType: props.iconType, iconSide: props.iconSide, 'data-test-subj': props.testId, diff --git a/test/functional/apps/dashboard/dashboard_save.ts b/test/functional/apps/dashboard/dashboard_save.ts index d1320b064b6d1..0a0a2fc1dd286 100644 --- a/test/functional/apps/dashboard/dashboard_save.ts +++ b/test/functional/apps/dashboard/dashboard_save.ts @@ -130,7 +130,11 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.dashboard.clickQuickSave(); await testSubjects.existOrFail('saveDashboardSuccess'); - await testSubjects.existOrFail('dashboardEditMode'); + }); + + it('Stays in edit mode after performing a quick save', async function () { + await PageObjects.header.waitUntilLoadingHasFinished(); + await testSubjects.existOrFail('dashboardQuickSaveMenuItem'); }); }); } diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 3a07e83950149..443caadfb301a 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -589,8 +589,6 @@ "dashboard.badge.readOnly.tooltip": "ダッシュボードを保存できません", "dashboard.changeViewModeConfirmModal.cancelButtonLabel": "編集を続行", "dashboard.changeViewModeConfirmModal.confirmButtonLabel": "変更を破棄", - "dashboard.changeViewModeConfirmModal.discardChangesDescription": "変更を破棄すると、元に戻すことはできません。", - "dashboard.changeViewModeConfirmModal.discardChangesTitle": "ダッシュボードへの変更を破棄しますか?", "dashboard.cloneModal.cloneDashboardTitleAriaLabel": "クローンダッシュボードタイトル", "dashboard.dashboardAppBreadcrumbsTitle": "ダッシュボード", "dashboard.dashboardGrid.toast.unableToLoadDashboardDangerMessage": "ダッシュボードが読み込めません。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 0e8ede769a5d2..67ba6cb695bce 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -589,8 +589,6 @@ "dashboard.badge.readOnly.tooltip": "无法保存仪表板", "dashboard.changeViewModeConfirmModal.cancelButtonLabel": "继续编辑", "dashboard.changeViewModeConfirmModal.confirmButtonLabel": "放弃更改", - "dashboard.changeViewModeConfirmModal.discardChangesDescription": "放弃更改后,它们将无法恢复。", - "dashboard.changeViewModeConfirmModal.discardChangesTitle": "放弃对仪表板的更改?", "dashboard.cloneModal.cloneDashboardTitleAriaLabel": "克隆仪表板标题", "dashboard.dashboardAppBreadcrumbsTitle": "仪表板", "dashboard.dashboardGrid.toast.unableToLoadDashboardDangerMessage": "无法加载仪表板。",