[7.x] [Dashboard] Adds Save as button to top menu (#90320) (#91208)

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Catherine Liu 2021-02-11 15:02:11 -08:00 committed by GitHub
parent ee4b72ee29
commit f5f6bc17c1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 129 additions and 81 deletions

View file

@ -321,6 +321,33 @@ export function DashboardTopNav({
dashboardStateManager,
]);
const runQuickSave = useCallback(async () => {
const currentTitle = dashboardStateManager.getTitle();
const currentDescription = dashboardStateManager.getDescription();
const currentTimeRestore = dashboardStateManager.getTimeRestore();
let currentTags: string[] = [];
if (savedObjectsTagging) {
const dashboard = dashboardStateManager.savedDashboard;
if (savedObjectsTagging.ui.hasTagDecoration(dashboard)) {
currentTags = dashboard.getTags();
}
}
save({}).then((response: SaveResult) => {
// If the save wasn't successful, put the original values back.
if (!(response as { id: string }).id) {
dashboardStateManager.setTitle(currentTitle);
dashboardStateManager.setDescription(currentDescription);
dashboardStateManager.setTimeRestore(currentTimeRestore);
if (savedObjectsTagging) {
dashboardStateManager.setTags(currentTags);
}
}
return response;
});
}, [save, savedObjectsTagging, dashboardStateManager]);
const runClone = useCallback(() => {
const currentTitle = dashboardStateManager.getTitle();
const onClone = async (
@ -356,9 +383,8 @@ export function DashboardTopNav({
[TopNavIds.ENTER_EDIT_MODE]: () => onChangeViewMode(ViewMode.EDIT),
[TopNavIds.DISCARD_CHANGES]: onDiscardChanges,
[TopNavIds.SAVE]: runSave,
[TopNavIds.QUICK_SAVE]: runQuickSave,
[TopNavIds.CLONE]: runClone,
[TopNavIds.ADD_EXISTING]: addFromLibrary,
[TopNavIds.VISUALIZE]: createNew,
[TopNavIds.OPTIONS]: (anchorElement) => {
showOptionsPopover({
anchorElement,
@ -394,10 +420,9 @@ export function DashboardTopNav({
onDiscardChanges,
onChangeViewMode,
savedDashboard,
addFromLibrary,
createNew,
runClone,
runSave,
runQuickSave,
share,
]);
@ -419,11 +444,11 @@ export function DashboardTopNav({
const showFilterBar = shouldShowFilterBar(Boolean(embedSettings?.forceHideFilterBar));
const showSearchBar = showQueryBar || showFilterBar;
const topNav = getTopNavConfig(
viewMode,
dashboardTopNavActions,
dashboardCapabilities.hideWriteControls
);
const topNav = getTopNavConfig(viewMode, dashboardTopNavActions, {
hideWriteControls: dashboardCapabilities.hideWriteControls,
isNewDashboard: !savedDashboard.id,
isDirty: dashboardStateManager.isDirty,
});
return {
appName: 'dashboard',

View file

@ -20,11 +20,11 @@ import { NavAction } from '../../types';
export function getTopNavConfig(
dashboardMode: ViewMode,
actions: { [key: string]: NavAction },
hideWriteControls: boolean
options: { hideWriteControls: boolean; isNewDashboard: boolean; isDirty: boolean }
) {
switch (dashboardMode) {
case ViewMode.VIEW:
return hideWriteControls
return options.hideWriteControls
? [
getFullScreenConfig(actions[TopNavIds.FULL_SCREEN]),
getShareConfig(actions[TopNavIds.SHARE]),
@ -36,20 +36,39 @@ export function getTopNavConfig(
getEditConfig(actions[TopNavIds.ENTER_EDIT_MODE]),
];
case ViewMode.EDIT:
return [
getOptionsConfig(actions[TopNavIds.OPTIONS]),
getShareConfig(actions[TopNavIds.SHARE]),
getAddConfig(actions[TopNavIds.ADD_EXISTING]),
getViewConfig(actions[TopNavIds.EXIT_EDIT_MODE]),
getDiscardConfig(actions[TopNavIds.DISCARD_CHANGES]),
getSaveConfig(actions[TopNavIds.SAVE]),
getCreateNewConfig(actions[TopNavIds.VISUALIZE]),
];
return options.isNewDashboard
? [
getOptionsConfig(actions[TopNavIds.OPTIONS]),
getShareConfig(actions[TopNavIds.SHARE]),
getViewConfig(actions[TopNavIds.EXIT_EDIT_MODE]),
getDiscardConfig(actions[TopNavIds.DISCARD_CHANGES]),
getSaveConfig(actions[TopNavIds.SAVE], options.isNewDashboard),
]
: [
getOptionsConfig(actions[TopNavIds.OPTIONS]),
getShareConfig(actions[TopNavIds.SHARE]),
getViewConfig(actions[TopNavIds.EXIT_EDIT_MODE]),
getDiscardConfig(actions[TopNavIds.DISCARD_CHANGES]),
getSaveConfig(actions[TopNavIds.SAVE]),
getQuickSave(actions[TopNavIds.QUICK_SAVE]),
];
default:
return [];
}
}
function getSaveButtonLabel() {
return i18n.translate('dashboard.topNave.saveButtonAriaLabel', {
defaultMessage: 'save',
});
}
function getSaveAsButtonLabel() {
return i18n.translate('dashboard.topNave.saveAsButtonAriaLabel', {
defaultMessage: 'save as',
});
}
function getFullScreenConfig(action: NavAction) {
return {
id: 'full-screen',
@ -89,17 +108,32 @@ function getEditConfig(action: NavAction) {
/**
* @returns {kbnTopNavConfig}
*/
function getSaveConfig(action: NavAction) {
function getQuickSave(action: NavAction) {
return {
id: 'quick-save',
emphasize: true,
label: getSaveButtonLabel(),
description: i18n.translate('dashboard.topNave.saveConfigDescription', {
defaultMessage: 'Quick save your dashboard without any prompts',
}),
testId: 'dashboardQuickSaveMenuItem',
run: action,
};
}
/**
* @returns {kbnTopNavConfig}
*/
function getSaveConfig(action: NavAction, isNewDashboard = false) {
return {
id: 'save',
label: i18n.translate('dashboard.topNave.saveButtonAriaLabel', {
defaultMessage: 'save',
}),
description: i18n.translate('dashboard.topNave.saveConfigDescription', {
defaultMessage: 'Save your dashboard',
label: isNewDashboard ? getSaveButtonLabel() : getSaveAsButtonLabel(),
description: i18n.translate('dashboard.topNave.saveAsConfigDescription', {
defaultMessage: 'Save as a new dashboard',
}),
testId: 'dashboardSaveMenuItem',
run: action,
emphasize: isNewDashboard,
};
}
@ -157,42 +191,6 @@ function getCloneConfig(action: NavAction) {
/**
* @returns {kbnTopNavConfig}
*/
function getAddConfig(action: NavAction) {
return {
id: 'add',
label: i18n.translate('dashboard.topNave.addButtonAriaLabel', {
defaultMessage: 'Library',
}),
description: i18n.translate('dashboard.topNave.addConfigDescription', {
defaultMessage: 'Add an existing visualization to the dashboard',
}),
testId: 'dashboardAddPanelButton',
run: action,
};
}
/**
* @returns {kbnTopNavConfig}
*/
function getCreateNewConfig(action: NavAction) {
return {
emphasize: true,
iconType: 'plusInCircleFilled',
id: 'addNew',
label: i18n.translate('dashboard.topNave.addNewButtonAriaLabel', {
defaultMessage: 'Create panel',
}),
description: i18n.translate('dashboard.topNave.addNewConfigDescription', {
defaultMessage: 'Create a new panel on this dashboard',
}),
testId: 'dashboardAddNewPanelButton',
run: action,
};
}
// /**
// * @returns {kbnTopNavConfig}
// */
function getShareConfig(action: NavAction | undefined) {
return {
id: 'share',

View file

@ -10,7 +10,7 @@ exports[`Storyshots components/PanelToolbar default 1`] = `
>
<button
className="euiButton euiButton--primary euiButton--small euiButton--fill"
data-test-subj="addVisualizationButton"
data-test-subj="dashboardAddNewPanelButton"
disabled={false}
onClick={[Function]}
style={
@ -41,6 +41,7 @@ exports[`Storyshots components/PanelToolbar default 1`] = `
>
<button
className="euiButton euiButton--text euiButton--small panelToolbarButton"
data-test-subj="dashboardAddPanelButton"
disabled={false}
onClick={[Function]}
style={

View file

@ -26,7 +26,7 @@ export const PanelToolbar: FC<Props> = ({ onAddPanelClick, onLibraryClick }) =>
size="s"
iconType="plusInCircleFilled"
onClick={onAddPanelClick}
data-test-subj="addVisualizationButton"
data-test-subj="dashboardAddNewPanelButton"
>
{i18n.translate('dashboard.panelToolbar.addPanelButtonLabel', {
defaultMessage: 'Create panel',
@ -40,6 +40,7 @@ export const PanelToolbar: FC<Props> = ({ onAddPanelClick, onLibraryClick }) =>
className="panelToolbarButton"
iconType="folderOpen"
onClick={onLibraryClick}
data-test-subj="dashboardAddPanelButton"
>
{i18n.translate('dashboard.panelToolbar.libraryButtonLabel', {
defaultMessage: 'Add from library',

View file

@ -9,12 +9,11 @@
export const TopNavIds = {
SHARE: 'share',
OPTIONS: 'options',
QUICK_SAVE: 'quickSave',
SAVE: 'save',
EXIT_EDIT_MODE: 'exitEditMode',
ENTER_EDIT_MODE: 'enterEditMode',
DISCARD_CHANGES: 'discard',
CLONE: 'clone',
FULL_SCREEN: 'fullScreenMode',
VISUALIZE: 'visualize',
ADD_EXISTING: 'addExisting',
};

View file

@ -11,6 +11,7 @@ import { FtrProviderContext } from '../../ftr_provider_context';
export default function ({ getService, getPageObjects }: FtrProviderContext) {
const PageObjects = getPageObjects(['dashboard', 'header']);
const listingTable = getService('listingTable');
const testSubjects = getService('testSubjects');
// FLAKY: https://github.com/elastic/kibana/issues/89476
describe.skip('dashboard save', function describeIndexTests() {
@ -112,5 +113,24 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await listingTable.searchAndExpectItemsCount('dashboard', dashboardNameEnterKey, 1);
});
it('Does not show quick save menu item on a new dashboard', async function () {
await PageObjects.dashboard.gotoDashboardLandingPage();
await PageObjects.dashboard.clickNewDashboard();
await PageObjects.dashboard.expectMissingQuickSaveOption();
});
it('Does not show dashboard save modal when on quick save', async function () {
await PageObjects.dashboard.gotoDashboardLandingPage();
await PageObjects.dashboard.clickNewDashboard();
await PageObjects.dashboard.saveDashboard('test quick save');
await PageObjects.dashboard.switchToEditMode();
await PageObjects.dashboard.expectExistsQuickSaveOption();
await PageObjects.dashboard.clickQuickSave();
await testSubjects.existOrFail('saveDashboardSuccess');
await testSubjects.existOrFail('dashboardEditMode');
});
});
}

View file

@ -48,8 +48,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
});
it('should add new visualization from dashboard', async () => {
await testSubjects.exists('addVisualizationButton');
await testSubjects.click('addVisualizationButton');
await testSubjects.exists('dashboardAddNewPanelButton');
await testSubjects.click('dashboardAddNewPanelButton');
await dashboardVisualizations.createAndAddMarkdown({
name: 'Dashboard Test Markdown',
markdown: 'Markdown text',

View file

@ -248,6 +248,11 @@ export function DashboardPageProvider({ getService, getPageObjects }: FtrProvide
await testSubjects.click('dashboardDiscardChanges');
}
public async clickQuickSave() {
log.debug('clickQuickSave');
await testSubjects.click('dashboardQuickSaveMenuItem');
}
public async clickNewDashboard(continueEditing = false) {
await listingTable.clickNewButton('createDashboardPromptButton');
if (await testSubjects.exists('dashboardCreateConfirm')) {
@ -583,6 +588,13 @@ export function DashboardPageProvider({ getService, getPageObjects }: FtrProvide
await testSubjects.missingOrFail('dashboardSaveMenuItem');
}
public async expectMissingQuickSaveOption() {
await testSubjects.missingOrFail('dashboardQuickSaveMenuItem');
}
public async expectExistsQuickSaveOption() {
await testSubjects.existOrFail('dashboardQuickSaveMenuItem');
}
public async getNotLoadedVisualizations(vizList: string[]) {
const checkList = [];
for (const name of vizList) {

View file

@ -89,7 +89,7 @@ export function DashboardVisualizationProvider({ getService, getPageObjects }: F
async clickAddVisualizationButton() {
log.debug('DashboardVisualizations.clickAddVisualizationButton');
await testSubjects.click('addVisualizationButton');
await testSubjects.click('dashboardAddNewPanelButton');
}
async isNewVisDialogShowing() {

View file

@ -47,8 +47,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
it('adding a metric visualization', async function () {
const originalPanelCount = await PageObjects.dashboard.getPanelCount();
expect(originalPanelCount).to.eql(0);
await testSubjects.exists('addVisualizationButton');
await testSubjects.click('addVisualizationButton');
await testSubjects.exists('dashboardAddNewPanelButton');
await testSubjects.click('dashboardAddNewPanelButton');
await dashboardVisualizations.createAndEmbedMetric('Embedding Vis Test');
await PageObjects.dashboard.waitForRenderComplete();
await dashboardExpect.metricValuesExist(['0']);

View file

@ -660,10 +660,6 @@
"dashboard.topNav.saveModal.storeTimeWithDashboardFormRowHelpText": "有効化すると、ダッシュボードが読み込まれるごとに現在選択された時刻の時間フィルターが変更されます。",
"dashboard.topNav.saveModal.storeTimeWithDashboardFormRowLabel": "ダッシュボードに時刻を保存",
"dashboard.topNav.showCloneModal.dashboardCopyTitle": "{title}のコピー",
"dashboard.topNave.addButtonAriaLabel": "ライブラリ",
"dashboard.topNave.addConfigDescription": "既存のビジュアライゼーションをダッシュボードに追加",
"dashboard.topNave.addNewButtonAriaLabel": "パネルの作成",
"dashboard.topNave.addNewConfigDescription": "このダッシュボードに新規パネルを作成",
"dashboard.topNave.cancelButtonAriaLabel": "キャンセル",
"dashboard.topNave.cloneButtonAriaLabel": "クローンを作成",
"dashboard.topNave.cloneConfigDescription": "ダッシュボードのコピーを作成します",

View file

@ -660,11 +660,7 @@
"dashboard.topNav.saveModal.storeTimeWithDashboardFormRowHelpText": "每次加载此仪表板时,都会将时间筛选更改为当前选定的时间。",
"dashboard.topNav.saveModal.storeTimeWithDashboardFormRowLabel": "将时间随仪表板保存",
"dashboard.topNav.showCloneModal.dashboardCopyTitle": "{title} 副本",
"dashboard.topNave.addButtonAriaLabel": "库",
"dashboard.topNave.addConfigDescription": "将现有可视化添加到仪表板",
"dashboard.topNave.cancelButtonAriaLabel": "取消",
"dashboard.topNave.addNewButtonAriaLabel": "创建面板",
"dashboard.topNave.addNewConfigDescription": "在此仪表板上创建新的面板",
"dashboard.topNave.cloneButtonAriaLabel": "克隆",
"dashboard.topNave.cloneConfigDescription": "创建仪表板的副本",
"dashboard.topNave.editButtonAriaLabel": "编辑",

View file

@ -29,8 +29,8 @@ export default function ({ getPageObjects, getService }) {
it('adds Lens visualization to empty dashboard', async () => {
const title = 'Dashboard Test Lens';
await testSubjects.exists('addVisualizationButton');
await testSubjects.click('addVisualizationButton');
await testSubjects.exists('dashboardAddNewPanelButton');
await testSubjects.click('dashboardAddNewPanelButton');
await dashboardVisualizations.ensureNewVisualizationDialogIsShowing();
await PageObjects.lens.createAndAddLensFromDashboard({ title, redirectToOrigin: true });
await PageObjects.dashboard.waitForRenderComplete();