mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[Dashboard] Add reset button (#154872)
Closes https://github.com/elastic/kibana/issues/132457 ## Summary This PR adds a reset button to the dashboard top navigation in both edit and view mode - when clicked, if the dashboard has unsaved changes, it will revert the dashboard to the last saved state: https://user-images.githubusercontent.com/8698078/232918433-97bac4b0-7472-49e9-9eb3-2cb7c9e6edf6.mov > **Note** > The above video contains some old copy for the modals. Please refer to "All copy changes" below to see the updated copy. Note that, by adding more buttons to the top nav bar, we are increasing the risk of someone hitting [this accessibility issue](https://github.com/elastic/kibana/issues/154414) (where the breadcrumbs get completely overlapped before the browser is small enough for the navigation to collapse) - this will either be addressed on the Shared UX side or will be addressed as part of our [nav bar redesign](https://github.com/elastic/kibana/issues/154945) 👍 ### All Copy Changes - **Listing Page** | Before | After | |--------|-------| |  |  | |  |  | - **Dashboard - _Edit Mode_** | Before | After | |--------|-------| |  |  | - **Dashboard - _View Mode_** | Before | After | |--------|-------| | N/A since you couldn't discard changes from view mode previously |  | ### Flaky Test Runner - `test/functional/apps/dashboard/group1/dashboard_unsaved_state.ts` <a href="https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/2144"><img src="https://user-images.githubusercontent.com/8698078/232622312-8532bc7e-a699-45ee-862d-739d116c5dba.png"></a> - `test/functional/apps/dashboard_elements/controls/options_list/options_list_dashboard_interaction.ts` <a href="https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/2143"><img src="https://user-images.githubusercontent.com/8698078/232615061-f01439e8-3a69-4190-8b6f-1926e1fa776a.png"></a> ### Checklist - [x] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md) - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios - [x] Any UI touched in this PR is usable by keyboard only (learn more about [keyboard accessibility](https://webaim.org/techniques/keyboard/)) - [x] Any UI touched in this PR does not create any new axe failures (run axe in browser: [FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/), [Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US)) - [x] This renders correctly on smaller devices using a responsive layout. (You can test this [in your browser](https://www.browserstack.com/guide/responsive-testing-on-local-server)) (Refer above) - [x] This was checked for [cross-browser compatibility](https://www.elastic.co/support/matrix#matrix_browsers) ### For maintainers - [ ] This was checked for breaking API changes and was [labeled appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)
This commit is contained in:
parent
17c6b6ce33
commit
29ffa177c1
12 changed files with 171 additions and 81 deletions
|
@ -182,6 +182,14 @@ export const topNavStrings = {
|
|||
defaultMessage: 'Save as a new dashboard',
|
||||
}),
|
||||
},
|
||||
resetChanges: {
|
||||
label: i18n.translate('dashboard.topNave.resetChangesButtonAriaLabel', {
|
||||
defaultMessage: 'Reset',
|
||||
}),
|
||||
description: i18n.translate('dashboard.topNave.resetChangesConfigDescription', {
|
||||
defaultMessage: 'Reset changes to dashboard',
|
||||
}),
|
||||
},
|
||||
switchToViewMode: {
|
||||
label: i18n.translate('dashboard.topNave.cancelButtonAriaLabel', {
|
||||
defaultMessage: 'Switch to view mode',
|
||||
|
|
|
@ -52,6 +52,7 @@ export const useDashboardMenuItems = ({
|
|||
const hasOverlays = dashboard.select((state) => state.componentState.hasOverlays);
|
||||
const lastSavedId = dashboard.select((state) => state.componentState.lastSavedId);
|
||||
const dashboardTitle = dashboard.select((state) => state.explicitInput.title);
|
||||
const viewMode = dashboard.select((state) => state.explicitInput.viewMode);
|
||||
|
||||
/**
|
||||
* Show the Dashboard app's share menu
|
||||
|
@ -109,21 +110,26 @@ export const useDashboardMenuItems = ({
|
|||
}, [maybeRedirect, dashboard]);
|
||||
|
||||
/**
|
||||
* Returns to view mode. If the dashboard has unsaved changes shows a warning and resets to last saved state.
|
||||
* Show the dashboard's "Confirm reset changes" modal. If confirmed:
|
||||
* (1) reset the dashboard to the last saved state, and
|
||||
* (2) if `switchToViewMode` is `true`, set the dashboard to view mode.
|
||||
*/
|
||||
const returnToViewMode = useCallback(() => {
|
||||
dashboard.clearOverlays();
|
||||
if (hasUnsavedChanges) {
|
||||
confirmDiscardUnsavedChanges(() => {
|
||||
batch(() => {
|
||||
dashboard.resetToLastSavedState();
|
||||
dashboard.dispatch.setViewMode(ViewMode.VIEW);
|
||||
});
|
||||
});
|
||||
return;
|
||||
}
|
||||
dashboard.dispatch.setViewMode(ViewMode.VIEW);
|
||||
}, [dashboard, hasUnsavedChanges]);
|
||||
const resetChanges = useCallback(
|
||||
(switchToViewMode: boolean = false) => {
|
||||
dashboard.clearOverlays();
|
||||
if (hasUnsavedChanges) {
|
||||
confirmDiscardUnsavedChanges(() => {
|
||||
batch(() => {
|
||||
dashboard.resetToLastSavedState();
|
||||
if (switchToViewMode) dashboard.dispatch.setViewMode(ViewMode.VIEW);
|
||||
});
|
||||
}, viewMode);
|
||||
} else {
|
||||
if (switchToViewMode) dashboard.dispatch.setViewMode(ViewMode.VIEW);
|
||||
}
|
||||
},
|
||||
[dashboard, hasUnsavedChanges, viewMode]
|
||||
);
|
||||
|
||||
/**
|
||||
* Register all of the top nav configs that can be used by dashboard.
|
||||
|
@ -184,7 +190,7 @@ export const useDashboardMenuItems = ({
|
|||
id: 'cancel',
|
||||
disableButton: isSaveInProgress || !lastSavedId || hasOverlays,
|
||||
testId: 'dashboardViewOnlyMode',
|
||||
run: () => returnToViewMode(),
|
||||
run: () => resetChanges(true),
|
||||
} as TopNavMenuData,
|
||||
|
||||
share: {
|
||||
|
@ -215,9 +221,9 @@ export const useDashboardMenuItems = ({
|
|||
quickSaveDashboard,
|
||||
hasUnsavedChanges,
|
||||
isSaveInProgress,
|
||||
returnToViewMode,
|
||||
saveDashboardAs,
|
||||
setIsLabsShown,
|
||||
resetChanges,
|
||||
hasOverlays,
|
||||
lastSavedId,
|
||||
isLabsShown,
|
||||
|
@ -226,27 +232,53 @@ export const useDashboardMenuItems = ({
|
|||
clone,
|
||||
]);
|
||||
|
||||
const resetChangesMenuItem = useMemo(() => {
|
||||
return {
|
||||
...topNavStrings.resetChanges,
|
||||
id: 'reset',
|
||||
testId: 'dashboardDiscardChangesMenuItem',
|
||||
disableButton:
|
||||
!hasUnsavedChanges ||
|
||||
hasOverlays ||
|
||||
(viewMode === ViewMode.EDIT && (isSaveInProgress || !lastSavedId)),
|
||||
run: () => resetChanges(),
|
||||
};
|
||||
}, [hasOverlays, lastSavedId, resetChanges, viewMode, isSaveInProgress, hasUnsavedChanges]);
|
||||
|
||||
/**
|
||||
* Build ordered menus for view and edit mode.
|
||||
*/
|
||||
const viewModeTopNavConfig = useMemo(() => {
|
||||
const labsMenuItem = isLabsEnabled ? [menuItems.labs] : [];
|
||||
const shareMenuItem = share ? [menuItems.share] : [];
|
||||
const writePermissionsMenuItems = showWriteControls ? [menuItems.clone, menuItems.edit] : [];
|
||||
return [...labsMenuItem, menuItems.fullScreen, ...shareMenuItem, ...writePermissionsMenuItems];
|
||||
}, [menuItems, share, showWriteControls, isLabsEnabled]);
|
||||
const cloneMenuItem = showWriteControls ? [menuItems.clone] : [];
|
||||
const editMenuItem = showWriteControls ? [menuItems.edit] : [];
|
||||
return [
|
||||
...labsMenuItem,
|
||||
menuItems.fullScreen,
|
||||
...shareMenuItem,
|
||||
...cloneMenuItem,
|
||||
resetChangesMenuItem,
|
||||
...editMenuItem,
|
||||
];
|
||||
}, [menuItems, share, showWriteControls, resetChangesMenuItem, isLabsEnabled]);
|
||||
|
||||
const editModeTopNavConfig = useMemo(() => {
|
||||
const labsMenuItem = isLabsEnabled ? [menuItems.labs] : [];
|
||||
const shareMenuItem = share ? [menuItems.share] : [];
|
||||
const editModeItems: TopNavMenuData[] = [];
|
||||
if (lastSavedId) {
|
||||
editModeItems.push(menuItems.saveAs, menuItems.switchToViewMode, menuItems.quickSave);
|
||||
editModeItems.push(
|
||||
menuItems.saveAs,
|
||||
menuItems.switchToViewMode,
|
||||
resetChangesMenuItem,
|
||||
menuItems.quickSave
|
||||
);
|
||||
} else {
|
||||
editModeItems.push(menuItems.switchToViewMode, menuItems.saveAs);
|
||||
}
|
||||
return [...labsMenuItem, menuItems.settings, ...shareMenuItem, ...editModeItems];
|
||||
}, [lastSavedId, menuItems, share, isLabsEnabled]);
|
||||
}, [lastSavedId, menuItems, share, resetChangesMenuItem, isLabsEnabled]);
|
||||
|
||||
return { viewModeTopNavConfig, editModeTopNavConfig };
|
||||
};
|
||||
|
|
|
@ -124,7 +124,10 @@ export const dashboardContainerReducers = {
|
|||
},
|
||||
|
||||
resetToLastSavedInput: (state: DashboardReduxState) => {
|
||||
state.explicitInput = state.componentState.lastSavedInput;
|
||||
state.explicitInput = {
|
||||
...state.componentState.lastSavedInput,
|
||||
viewMode: state.explicitInput.viewMode, // keep current view mode when resetting
|
||||
};
|
||||
},
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { ViewMode } from '@kbn/embeddable-plugin/public';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
export const dashboardListingErrorStrings = {
|
||||
|
@ -91,32 +92,32 @@ export const dashboardUnsavedListingStrings = {
|
|||
defaultMessage: 'Continue editing',
|
||||
}),
|
||||
getDiscardAriaLabel: (title: string) =>
|
||||
i18n.translate('dashboard.listing.unsaved.discardAria', {
|
||||
defaultMessage: 'Discard changes to {title}',
|
||||
i18n.translate('dashboard.listing.unsaved.resetAria', {
|
||||
defaultMessage: 'Reset changes to {title}',
|
||||
values: { title },
|
||||
}),
|
||||
getDiscardTitle: () =>
|
||||
i18n.translate('dashboard.listing.unsaved.discardTitle', {
|
||||
defaultMessage: 'Discard changes',
|
||||
i18n.translate('dashboard.listing.unsaved.resetTitle', {
|
||||
defaultMessage: 'Reset changes',
|
||||
}),
|
||||
};
|
||||
|
||||
export const discardConfirmStrings = {
|
||||
getDiscardTitle: () =>
|
||||
i18n.translate('dashboard.discardChangesConfirmModal.discardChangesTitle', {
|
||||
defaultMessage: 'Discard changes to dashboard?',
|
||||
export const resetConfirmStrings = {
|
||||
getResetTitle: () =>
|
||||
i18n.translate('dashboard.resetChangesConfirmModal.resetChangesTitle', {
|
||||
defaultMessage: 'Reset dashboard?',
|
||||
}),
|
||||
getDiscardSubtitle: () =>
|
||||
i18n.translate('dashboard.discardChangesConfirmModal.discardChangesDescription', {
|
||||
defaultMessage: `Once you discard your changes, there's no getting them back.`,
|
||||
}),
|
||||
getDiscardConfirmButtonText: () =>
|
||||
i18n.translate('dashboard.discardChangesConfirmModal.confirmButtonLabel', {
|
||||
defaultMessage: 'Discard changes',
|
||||
}),
|
||||
getDiscardCancelButtonText: () =>
|
||||
i18n.translate('dashboard.discardChangesConfirmModal.cancelButtonLabel', {
|
||||
defaultMessage: 'Cancel',
|
||||
getResetSubtitle: (viewMode: ViewMode) =>
|
||||
viewMode === ViewMode.EDIT
|
||||
? i18n.translate('dashboard.discardChangesConfirmModal.discardChangesDescription', {
|
||||
defaultMessage: `All unsaved changes will be lost.`,
|
||||
})
|
||||
: i18n.translate('dashboard.resetChangesConfirmModal.resetChangesDescription', {
|
||||
defaultMessage: `This dashboard will return to its last saved state. You might lose changes to filters and queries.`,
|
||||
}),
|
||||
getResetConfirmButtonText: () =>
|
||||
i18n.translate('dashboard.resetChangesConfirmModal.confirmButtonLabel', {
|
||||
defaultMessage: 'Reset dashboard',
|
||||
}),
|
||||
};
|
||||
|
||||
|
|
|
@ -20,24 +20,28 @@ import {
|
|||
EuiText,
|
||||
EUI_MODAL_CANCEL_BUTTON,
|
||||
} from '@elastic/eui';
|
||||
import { ViewMode } from '@kbn/embeddable-plugin/public';
|
||||
import { toMountPoint } from '@kbn/kibana-react-plugin/public';
|
||||
|
||||
import { pluginServices } from '../services/plugin_services';
|
||||
import { createConfirmStrings, discardConfirmStrings } from './_dashboard_listing_strings';
|
||||
import { createConfirmStrings, resetConfirmStrings } from './_dashboard_listing_strings';
|
||||
|
||||
export type DiscardOrKeepSelection = 'cancel' | 'discard' | 'keep';
|
||||
|
||||
export const confirmDiscardUnsavedChanges = (discardCallback: () => void) => {
|
||||
export const confirmDiscardUnsavedChanges = (
|
||||
discardCallback: () => void,
|
||||
viewMode: ViewMode = ViewMode.EDIT // we want to show the danger modal on the listing page
|
||||
) => {
|
||||
const {
|
||||
overlays: { openConfirm },
|
||||
} = pluginServices.getServices();
|
||||
|
||||
openConfirm(discardConfirmStrings.getDiscardSubtitle(), {
|
||||
confirmButtonText: discardConfirmStrings.getDiscardConfirmButtonText(),
|
||||
cancelButtonText: discardConfirmStrings.getDiscardCancelButtonText(),
|
||||
buttonColor: 'danger',
|
||||
openConfirm(resetConfirmStrings.getResetSubtitle(viewMode), {
|
||||
confirmButtonText: resetConfirmStrings.getResetConfirmButtonText(),
|
||||
buttonColor: viewMode === ViewMode.EDIT ? 'danger' : 'primary',
|
||||
maxWidth: 500,
|
||||
defaultFocusedButton: EUI_MODAL_CANCEL_BUTTON,
|
||||
title: discardConfirmStrings.getDiscardTitle(),
|
||||
title: resetConfirmStrings.getResetTitle(),
|
||||
}).then((isConfirmed) => {
|
||||
if (isConfirmed) {
|
||||
discardCallback();
|
||||
|
|
|
@ -129,6 +129,10 @@ export class CustomizePanelAction implements Action<CustomizePanelActionContext>
|
|||
{
|
||||
size: 's',
|
||||
'data-test-subj': 'customizePanel',
|
||||
onClose: (overlayRef) => {
|
||||
if (overlayTracker) overlayTracker.clearOverlays();
|
||||
overlayRef.close();
|
||||
},
|
||||
}
|
||||
);
|
||||
overlayTracker?.openOverlay(handle);
|
||||
|
|
|
@ -85,10 +85,14 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
await validateQueryAndFilter();
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
// discard changes made in view mode
|
||||
await PageObjects.dashboard.switchToEditMode();
|
||||
await PageObjects.dashboard.clickCancelOutOfEditMode();
|
||||
it('can discard changes', async () => {
|
||||
await PageObjects.dashboard.clickDiscardChanges();
|
||||
await PageObjects.dashboard.waitForRenderComplete();
|
||||
|
||||
const query = await queryBar.getQueryString();
|
||||
expect(query).to.eql('');
|
||||
const filterCount = await filterBar.getFilterCount();
|
||||
expect(filterCount).to.eql(0);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -140,8 +144,21 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
expect(currentPanelCount).to.eql(unsavedPanelCount);
|
||||
});
|
||||
|
||||
it('resets to original panel count after discarding changes', async () => {
|
||||
it('can discard changes', async () => {
|
||||
unsavedPanelCount = await PageObjects.dashboard.getPanelCount();
|
||||
expect(unsavedPanelCount).to.eql(originalPanelCount + 2);
|
||||
|
||||
await PageObjects.dashboard.clickDiscardChanges();
|
||||
const currentPanelCount = await PageObjects.dashboard.getPanelCount();
|
||||
expect(currentPanelCount).to.eql(originalPanelCount);
|
||||
});
|
||||
|
||||
it('resets to original panel count after switching to view mode and discarding changes', async () => {
|
||||
await addPanels();
|
||||
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||
unsavedPanelCount = await PageObjects.dashboard.getPanelCount();
|
||||
expect(unsavedPanelCount).to.eql(originalPanelCount + 2);
|
||||
|
||||
await PageObjects.dashboard.clickCancelOutOfEditMode();
|
||||
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||
const currentPanelCount = await PageObjects.dashboard.getPanelCount();
|
||||
|
|
|
@ -257,21 +257,36 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
await dashboard.clearUnsavedChanges();
|
||||
});
|
||||
|
||||
it('changes to selections can be discarded', async () => {
|
||||
await dashboardControls.optionsListOpenPopover(controlId);
|
||||
await dashboardControls.optionsListPopoverSelectOption('bark');
|
||||
await dashboardControls.optionsListEnsurePopoverIsClosed(controlId);
|
||||
let selections = await dashboardControls.optionsListGetSelectionsString(controlId);
|
||||
expect(selections).to.equal('hiss, grr, bark');
|
||||
describe('discarding changes', async () => {
|
||||
describe('changes can be discarded', async () => {
|
||||
let selections = '';
|
||||
|
||||
await dashboard.clickCancelOutOfEditMode();
|
||||
selections = await dashboardControls.optionsListGetSelectionsString(controlId);
|
||||
expect(selections).to.equal('hiss, grr');
|
||||
});
|
||||
beforeEach(async () => {
|
||||
await dashboardControls.optionsListOpenPopover(controlId);
|
||||
await dashboardControls.optionsListPopoverSelectOption('bark');
|
||||
await dashboardControls.optionsListEnsurePopoverIsClosed(controlId);
|
||||
selections = await dashboardControls.optionsListGetSelectionsString(controlId);
|
||||
expect(selections).to.equal('hiss, grr, bark');
|
||||
});
|
||||
|
||||
it('dashboard does not load with unsaved changes when changes are discarded', async () => {
|
||||
await dashboard.switchToEditMode();
|
||||
await testSubjects.missingOrFail('dashboardUnsavedChangesBadge');
|
||||
afterEach(async () => {
|
||||
selections = await dashboardControls.optionsListGetSelectionsString(controlId);
|
||||
expect(selections).to.equal('hiss, grr');
|
||||
});
|
||||
|
||||
it('by clicking the discard changes button', async () => {
|
||||
await dashboard.clickDiscardChanges();
|
||||
});
|
||||
|
||||
it('by switching to view mode', async () => {
|
||||
await dashboard.clickCancelOutOfEditMode();
|
||||
});
|
||||
});
|
||||
|
||||
it('dashboard does not load with unsaved changes when changes are discarded', async () => {
|
||||
await dashboard.switchToEditMode();
|
||||
await testSubjects.missingOrFail('dashboardUnsavedChangesBadge');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -309,6 +309,18 @@ export class DashboardPageObject extends FtrService {
|
|||
}
|
||||
}
|
||||
|
||||
public async clickDiscardChanges(accept = true) {
|
||||
await this.retry.try(async () => {
|
||||
await this.expectDiscardChangesButtonEnabled();
|
||||
this.log.debug('clickDiscardChanges');
|
||||
await this.testSubjects.click('dashboardDiscardChangesMenuItem');
|
||||
});
|
||||
await this.common.expectConfirmModalOpenState(true);
|
||||
if (accept) {
|
||||
await this.common.clickConfirmOnModal();
|
||||
}
|
||||
}
|
||||
|
||||
public async clickQuickSave() {
|
||||
await this.retry.try(async () => {
|
||||
await this.expectQuickSaveButtonEnabled();
|
||||
|
@ -734,6 +746,15 @@ export class DashboardPageObject extends FtrService {
|
|||
await this.testSubjects.existOrFail('dashboardQuickSaveMenuItem');
|
||||
}
|
||||
|
||||
public async expectDiscardChangesButtonEnabled() {
|
||||
this.log.debug('expectDiscardChangesButtonEnabled');
|
||||
const quickSaveButton = await this.testSubjects.find('dashboardDiscardChangesMenuItem');
|
||||
const isDisabled = await quickSaveButton.getAttribute('disabled');
|
||||
if (isDisabled) {
|
||||
throw new Error('Discard changes button disabled');
|
||||
}
|
||||
}
|
||||
|
||||
public async expectQuickSaveButtonEnabled() {
|
||||
this.log.debug('expectQuickSaveButtonEnabled');
|
||||
const quickSaveButton = await this.testSubjects.find('dashboardQuickSaveMenuItem');
|
||||
|
|
|
@ -1090,7 +1090,6 @@
|
|||
"dashboard.addPanel.newEmbeddableAddedSuccessMessageTitle": "{savedObjectName} a été ajouté",
|
||||
"dashboard.addPanel.savedObjectAddedToContainerSuccessMessageTitle": "{savedObjectName} a été ajouté",
|
||||
"dashboard.listing.createNewDashboard.newToKibanaDescription": "Vous êtes nouveau sur Kibana ? {sampleDataInstallLink} pour découvrir l'application.",
|
||||
"dashboard.listing.unsaved.discardAria": "Ignorer les modifications apportées à {title}",
|
||||
"dashboard.listing.unsaved.editAria": "Continuer à modifier {title}",
|
||||
"dashboard.listing.unsaved.unsavedChangesTitle": "Vous avez des modifications non enregistrées dans le {dash} suivant :",
|
||||
"dashboard.loadingError.dashboardGridErrorMessage": "Impossible de charger le tableau de bord : {message}",
|
||||
|
@ -1127,10 +1126,7 @@
|
|||
"dashboard.dashboardPageTitle": "Tableaux de bord",
|
||||
"dashboard.dashboardWasSavedSuccessMessage": "Le tableau de bord \"{dashTitle}\" a été enregistré.",
|
||||
"dashboard.deleteError.toastDescription": "Erreur rencontrée lors de la suppression du tableau de bord",
|
||||
"dashboard.discardChangesConfirmModal.cancelButtonLabel": "Annuler",
|
||||
"dashboard.discardChangesConfirmModal.confirmButtonLabel": "Abandonner les modifications",
|
||||
"dashboard.discardChangesConfirmModal.discardChangesDescription": "Une fois les modifications ignorées, vous ne pourrez pas les récupérer.",
|
||||
"dashboard.discardChangesConfirmModal.discardChangesTitle": "Ignorer les modifications apportées au tableau de bord ?",
|
||||
"dashboard.editingToolbar.addControlButtonTitle": "Ajouter un contrôle",
|
||||
"dashboard.editingToolbar.addTimeSliderControlButtonTitle": "Ajouter un contrôle de curseur temporel",
|
||||
"dashboard.editingToolbar.controlsButtonTitle": "Contrôles",
|
||||
|
@ -1164,7 +1160,6 @@
|
|||
"dashboard.listing.readonlyNoItemsTitle": "Aucun tableau de bord à afficher",
|
||||
"dashboard.listing.table.entityName": "tableau de bord",
|
||||
"dashboard.listing.table.entityNamePlural": "tableaux de bord",
|
||||
"dashboard.listing.unsaved.discardTitle": "Abandonner les modifications",
|
||||
"dashboard.listing.unsaved.editTitle": "Poursuivre les modifications",
|
||||
"dashboard.listing.unsaved.loading": "Chargement",
|
||||
"dashboard.loadingError.dashboardNotFound": "Le tableau de bord demandé est introuvable.",
|
||||
|
|
|
@ -1090,7 +1090,6 @@
|
|||
"dashboard.addPanel.newEmbeddableAddedSuccessMessageTitle": "{savedObjectName}が追加されました",
|
||||
"dashboard.addPanel.savedObjectAddedToContainerSuccessMessageTitle": "{savedObjectName}が追加されました",
|
||||
"dashboard.listing.createNewDashboard.newToKibanaDescription": "Kibana は初心者ですか?{sampleDataInstallLink}してお試しください。",
|
||||
"dashboard.listing.unsaved.discardAria": "{title}の変更を破棄",
|
||||
"dashboard.listing.unsaved.editAria": "{title}の編集を続行",
|
||||
"dashboard.listing.unsaved.unsavedChangesTitle": "次の{dash}には保存されていない変更があります:",
|
||||
"dashboard.loadingError.dashboardGridErrorMessage": "ダッシュボードが読み込めません:{message}",
|
||||
|
@ -1127,10 +1126,7 @@
|
|||
"dashboard.dashboardPageTitle": "ダッシュボード",
|
||||
"dashboard.dashboardWasSavedSuccessMessage": "ダッシュボード「{dashTitle}」が保存されました。",
|
||||
"dashboard.deleteError.toastDescription": "ダッシュボードの削除中にエラーが発生しました",
|
||||
"dashboard.discardChangesConfirmModal.cancelButtonLabel": "キャンセル",
|
||||
"dashboard.discardChangesConfirmModal.confirmButtonLabel": "変更を破棄",
|
||||
"dashboard.discardChangesConfirmModal.discardChangesDescription": "変更を破棄すると、元に戻すことはできません。",
|
||||
"dashboard.discardChangesConfirmModal.discardChangesTitle": "ダッシュボードへの変更を破棄しますか?",
|
||||
"dashboard.editingToolbar.addControlButtonTitle": "コントロールを追加",
|
||||
"dashboard.editingToolbar.addTimeSliderControlButtonTitle": "時間スライダーコントロールを追加",
|
||||
"dashboard.editingToolbar.controlsButtonTitle": "コントロール",
|
||||
|
@ -1164,7 +1160,6 @@
|
|||
"dashboard.listing.readonlyNoItemsTitle": "表示するダッシュボードがありません",
|
||||
"dashboard.listing.table.entityName": "ダッシュボード",
|
||||
"dashboard.listing.table.entityNamePlural": "ダッシュボード",
|
||||
"dashboard.listing.unsaved.discardTitle": "変更を破棄",
|
||||
"dashboard.listing.unsaved.editTitle": "編集を続行",
|
||||
"dashboard.listing.unsaved.loading": "読み込み中",
|
||||
"dashboard.loadingError.dashboardNotFound": "リクエストされたダッシュボードが見つかりませんでした。",
|
||||
|
|
|
@ -1090,7 +1090,6 @@
|
|||
"dashboard.addPanel.newEmbeddableAddedSuccessMessageTitle": "{savedObjectName} 已添加",
|
||||
"dashboard.addPanel.savedObjectAddedToContainerSuccessMessageTitle": "{savedObjectName} 已添加",
|
||||
"dashboard.listing.createNewDashboard.newToKibanaDescription": "Kibana 新手?{sampleDataInstallLink}来试用一下。",
|
||||
"dashboard.listing.unsaved.discardAria": "丢弃对 {title} 的更改",
|
||||
"dashboard.listing.unsaved.editAria": "继续编辑 {title}",
|
||||
"dashboard.listing.unsaved.unsavedChangesTitle": "在以下 {dash} 中有未保存更改:",
|
||||
"dashboard.loadingError.dashboardGridErrorMessage": "无法加载仪表板:{message}",
|
||||
|
@ -1127,10 +1126,7 @@
|
|||
"dashboard.dashboardPageTitle": "仪表板",
|
||||
"dashboard.dashboardWasSavedSuccessMessage": "仪表板“{dashTitle}”已保存",
|
||||
"dashboard.deleteError.toastDescription": "删除仪表板时发生错误",
|
||||
"dashboard.discardChangesConfirmModal.cancelButtonLabel": "取消",
|
||||
"dashboard.discardChangesConfirmModal.confirmButtonLabel": "放弃更改",
|
||||
"dashboard.discardChangesConfirmModal.discardChangesDescription": "放弃更改后,它们将无法恢复。",
|
||||
"dashboard.discardChangesConfirmModal.discardChangesTitle": "放弃对仪表板所做的更改?",
|
||||
"dashboard.editingToolbar.addControlButtonTitle": "添加控件",
|
||||
"dashboard.editingToolbar.addTimeSliderControlButtonTitle": "添加时间滑块控件",
|
||||
"dashboard.editingToolbar.controlsButtonTitle": "控件",
|
||||
|
@ -1164,7 +1160,6 @@
|
|||
"dashboard.listing.readonlyNoItemsTitle": "没有可查看的仪表板",
|
||||
"dashboard.listing.table.entityName": "仪表板",
|
||||
"dashboard.listing.table.entityNamePlural": "仪表板",
|
||||
"dashboard.listing.unsaved.discardTitle": "放弃更改",
|
||||
"dashboard.listing.unsaved.editTitle": "继续编辑",
|
||||
"dashboard.listing.unsaved.loading": "正在加载",
|
||||
"dashboard.loadingError.dashboardNotFound": "找不到请求的仪表板。",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue