Skip to content

Commit

Permalink
[Maps] allow saving maps to dashboards (elastic#88759)
Browse files Browse the repository at this point in the history
* [Maps] allow saving maps to dashboards

* update saveMap functional test method

* update tags functional test

* review feedback

Co-authored-by: Kibana Machine <[email protected]>
  • Loading branch information
nreese and kibanamachine committed Jan 26, 2021
1 parent 09c4d5a commit 507caae
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 65 deletions.
2 changes: 1 addition & 1 deletion x-pack/plugins/maps/kibana.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,5 @@
"ui": true,
"server": true,
"extraPublicDirs": ["common/constants"],
"requiredBundles": ["kibanaReact", "kibanaUtils", "home", "mapsOss"]
"requiredBundles": ["kibanaReact", "kibanaUtils", "home", "mapsOss", "presentationUtil"]
}
15 changes: 13 additions & 2 deletions x-pack/plugins/maps/public/routes/map_page/saved_map/saved_map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -281,10 +281,12 @@ export class SavedMap {
returnToOrigin,
newTags,
saveByReference,
dashboardId,
}: OnSaveProps & {
returnToOrigin: boolean;
returnToOrigin?: boolean;
newTags?: string[];
saveByReference: boolean;
dashboardId?: string | null;
}) {
if (!this._attributes) {
throw new Error('Invalid usage, must await whenReady before calling save');
Expand Down Expand Up @@ -337,14 +339,23 @@ export class SavedMap {
});
return;
}
this._getStateTransfer().navigateToWithEmbeddablePackage(this._originatingApp, {
await this._getStateTransfer().navigateToWithEmbeddablePackage(this._originatingApp, {
state: {
embeddableId: newCopyOnSave ? undefined : this._embeddableId,
type: MAP_SAVED_OBJECT_TYPE,
input: updatedMapEmbeddableInput,
},
});
return;
} else if (dashboardId) {
await this._getStateTransfer().navigateToWithEmbeddablePackage('dashboards', {
state: {
type: MAP_SAVED_OBJECT_TYPE,
input: updatedMapEmbeddableInput,
},
path: dashboardId === 'new' ? '#/create' : `#/view/${dashboardId}`,
});
return;
}

this._mapEmbeddableInput = updatedMapEmbeddableInput;
Expand Down
108 changes: 62 additions & 46 deletions x-pack/plugins/maps/public/routes/map_page/top_nav_config.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { Adapters } from 'src/plugins/inspector/public';
import {
getCoreChrome,
getMapsCapabilities,
getIsAllowByValueEmbeddables,
getInspector,
getCoreI18n,
getSavedObjectsClient,
Expand All @@ -25,6 +26,7 @@ import {
import { MAP_SAVED_OBJECT_TYPE } from '../../../common/constants';
import { SavedMap } from './saved_map';
import { getMapEmbeddableDisplayName } from '../../../common/i18n_getters';
import { SavedObjectSaveModalDashboard } from '../../../../../../src/plugins/presentation_util/public';

export function getTopNavConfig({
savedMap,
Expand Down Expand Up @@ -139,53 +141,67 @@ export function getTopNavConfig({
/>
) : undefined;

const saveModal = (
<SavedObjectSaveModalOrigin
originatingApp={savedMap.getOriginatingApp()}
getAppNameFromId={savedMap.getAppNameFromId}
onSave={async (props: OnSaveProps & { returnToOrigin: boolean }) => {
try {
await checkForDuplicateTitle(
{
id: props.newCopyOnSave ? undefined : savedMap.getSavedObjectId(),
title: props.newTitle,
copyOnSave: props.newCopyOnSave,
lastSavedTitle: savedMap.getSavedObjectId() ? savedMap.getTitle() : '',
getEsType: () => MAP_SAVED_OBJECT_TYPE,
getDisplayName: getMapEmbeddableDisplayName,
},
props.isTitleDuplicateConfirmed,
props.onTitleDuplicate,
{
savedObjectsClient: getSavedObjectsClient(),
overlays: getCoreOverlays(),
}
);
} catch (e) {
// ignore duplicate title failure, user notified in save modal
return {};
}
const saveModalProps = {
onSave: async (
props: OnSaveProps & { returnToOrigin?: boolean; dashboardId?: string | null }
) => {
try {
await checkForDuplicateTitle(
{
id: props.newCopyOnSave ? undefined : savedMap.getSavedObjectId(),
title: props.newTitle,
copyOnSave: props.newCopyOnSave,
lastSavedTitle: savedMap.getSavedObjectId() ? savedMap.getTitle() : '',
getEsType: () => MAP_SAVED_OBJECT_TYPE,
getDisplayName: getMapEmbeddableDisplayName,
},
props.isTitleDuplicateConfirmed,
props.onTitleDuplicate,
{
savedObjectsClient: getSavedObjectsClient(),
overlays: getCoreOverlays(),
}
);
} catch (e) {
// ignore duplicate title failure, user notified in save modal
return {};
}

await savedMap.save({
...props,
newTags: selectedTags,
saveByReference: !props.dashboardId,
});
// showSaveModal wrapper requires onSave to return an object with an id to close the modal after successful save
return { id: 'id' };
},
onClose: () => {},
documentInfo: {
description: mapDescription,
id: savedMap.getSavedObjectId(),
title: savedMap.getTitle(),
},
objectType: i18n.translate('xpack.maps.topNav.saveModalType', {
defaultMessage: 'map',
}),
};

const saveModal =
savedMap.getOriginatingApp() || !getIsAllowByValueEmbeddables() ? (
<SavedObjectSaveModalOrigin
{...saveModalProps}
originatingApp={savedMap.getOriginatingApp()}
getAppNameFromId={savedMap.getAppNameFromId}
options={tagSelector}
/>
) : (
<SavedObjectSaveModalDashboard
{...saveModalProps}
savedObjectsClient={getSavedObjectsClient()}
tagOptions={tagSelector}
/>
);

await savedMap.save({
...props,
newTags: selectedTags,
saveByReference: true,
});
// showSaveModal wrapper requires onSave to return an object with an id to close the modal after successful save
return { id: 'id' };
}}
onClose={() => {}}
documentInfo={{
description: mapDescription,
id: savedMap.getSavedObjectId(),
title: savedMap.getTitle(),
}}
objectType={i18n.translate('xpack.maps.topNav.saveModalType', {
defaultMessage: 'map',
})}
options={tagSelector}
/>
);
showSaveModal(saveModal, getCoreI18n().Context);
},
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export default function ({ getPageObjects, getService }) {
after(async () => {
await security.testUser.restoreDefaults();
});

describe('new map', () => {
beforeEach(async () => {
await PageObjects.common.navigateToApp('dashboard');
Expand All @@ -55,7 +56,7 @@ export default function ({ getPageObjects, getService }) {
it('should cut the originator and stay in maps application', async () => {
await PageObjects.maps.saveMap(
'map created from dashboard save and return with originator app cut',
true
false
);
await PageObjects.maps.waitForLayersToLoad();
await testSubjects.missingOrFail('mapSaveAndReturnButton');
Expand Down Expand Up @@ -94,7 +95,7 @@ export default function ({ getPageObjects, getService }) {

describe('save as and uncheck return to origin switch', () => {
it('should cut the originator and stay in maps application', async () => {
await PageObjects.maps.saveMap('Clone 2 of map embeddable example', true);
await PageObjects.maps.saveMap('Clone 2 of map embeddable example', false);
await PageObjects.maps.waitForLayersToLoad();
await testSubjects.missingOrFail('mapSaveAndReturnButton');
await testSubjects.existOrFail('mapSaveButton');
Expand Down
18 changes: 7 additions & 11 deletions x-pack/test/functional/page_objects/gis_page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { APP_ID } from '../../../plugins/maps/common/constants';
import { FtrProviderContext } from '../ftr_provider_context';

export function GisPageProvider({ getService, getPageObjects }: FtrProviderContext) {
const PageObjects = getPageObjects(['common', 'header', 'timePicker']);
const PageObjects = getPageObjects(['common', 'header', 'timePicker', 'visualize']);

const log = getService('log');
const testSubjects = getService('testSubjects');
Expand Down Expand Up @@ -148,18 +148,14 @@ export function GisPageProvider({ getService, getPageObjects }: FtrProviderConte
await renderable.waitForRender();
}

async saveMap(name: string, uncheckReturnToOriginModeSwitch = false, tags?: string[]) {
async saveMap(name: string, redirectToOrigin = true, tags?: string[]) {
await testSubjects.click('mapSaveButton');
await testSubjects.setValue('savedObjectTitle', name);
if (uncheckReturnToOriginModeSwitch) {
const redirectToOriginCheckboxExists = await testSubjects.exists(
'returnToOriginModeSwitch'
);
if (!redirectToOriginCheckboxExists) {
throw new Error('Unable to uncheck "returnToOriginModeSwitch", it does not exist.');
}
await testSubjects.setEuiSwitch('returnToOriginModeSwitch', 'uncheck');
}
await PageObjects.visualize.setSaveModalValues(name, {
addToDashboard: false,
redirectToOrigin,
saveAsNew: true,
});
if (tags) {
await testSubjects.click('savedObjectTagSelector');
for (const tagName of tags) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
const listingTable = getService('listingTable');
const testSubjects = getService('testSubjects');
const find = getService('find');
const PageObjects = getPageObjects(['maps', 'tagManagement', 'common']);
const PageObjects = getPageObjects(['maps', 'tagManagement', 'common', 'visualize']);

/**
* Select tags in the searchbar's tag filter.
Expand Down Expand Up @@ -78,7 +78,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
});

it('allows to select tags for a new map', async () => {
await PageObjects.maps.saveMap('my-new-map', false, ['tag-1', 'tag-3']);
await PageObjects.maps.saveMap('my-new-map', true, ['tag-1', 'tag-3']);

await PageObjects.maps.gotoMapListingPage();
await selectFilterTags('tag-1');
Expand All @@ -91,6 +91,10 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {

await testSubjects.click('mapSaveButton');
await testSubjects.setValue('savedObjectTitle', 'map-with-new-tag');
await PageObjects.visualize.setSaveModalValues('map-with-new-tag', {
addToDashboard: false,
saveAsNew: true,
});

await testSubjects.click('savedObjectTagSelector');
await testSubjects.click(`tagSelectorOption-action__create`);
Expand Down Expand Up @@ -127,7 +131,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
it('allows to select tags for an existing map', async () => {
await listingTable.clickItemLink('map', 'map 4 (tag-1)');

await PageObjects.maps.saveMap('map 4 (tag-1)', false, ['tag-3']);
await PageObjects.maps.saveMap('map 4 (tag-1)', true, ['tag-3']);

await PageObjects.maps.gotoMapListingPage();
await selectFilterTags('tag-3');
Expand Down

0 comments on commit 507caae

Please sign in to comment.