Skip to content

Commit

Permalink
[Lens] Add description property and check duplicate title on save (#6…
Browse files Browse the repository at this point in the history
  • Loading branch information
flash1293 committed Jun 10, 2020
1 parent b3a26b7 commit 524e0d1
Show file tree
Hide file tree
Showing 12 changed files with 102 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,13 @@ import { displayDuplicateTitleConfirmModal } from './display_duplicate_title_con
* @param services
*/
export async function checkForDuplicateTitle(
savedObject: SavedObject,
savedObject: Pick<
SavedObject,
'id' | 'title' | 'getDisplayName' | 'lastSavedTitle' | 'copyOnSave' | 'getEsType'
>,
isTitleDuplicateConfirmed: boolean,
onTitleDuplicate: (() => void) | undefined,
services: SavedObjectKibanaServices
services: Pick<SavedObjectKibanaServices, 'savedObjectsClient' | 'overlays'>
): Promise<true> {
const { savedObjectsClient, overlays } = services;
// Don't check for duplicates if user has already confirmed save with duplicate title
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { confirmModalPromise } from './confirm_modal_promise';
import { SavedObject } from '../../types';

export function displayDuplicateTitleConfirmModal(
savedObject: SavedObject,
savedObject: Pick<SavedObject, 'title' | 'getDisplayName'>,
overlays: OverlayStart
): Promise<true> {
const confirmMessage = i18n.translate(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export interface VisualizationListItem {
stage: 'experimental' | 'beta' | 'production';
savedObjectType: string;
title: string;
description?: string;
typeTitle: string;
}

Expand Down
58 changes: 56 additions & 2 deletions x-pack/plugins/lens/public/app_plugin/app.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*/

import React from 'react';
import { Observable } from 'rxjs';
import { ReactWrapper } from 'enzyme';
import { act } from 'react-dom/test-utils';
import { App } from './app';
Expand All @@ -13,8 +14,11 @@ import { AppMountParameters } from 'kibana/public';
import { Storage } from '../../../../../src/plugins/kibana_utils/public';
import { Document, SavedObjectStore } from '../persistence';
import { mount } from 'enzyme';
import {
SavedObjectSaveModal,
checkForDuplicateTitle,
} from '../../../../../src/plugins/saved_objects/public';
import { createMemoryHistory, History } from 'history';
import { SavedObjectSaveModal } from '../../../../../src/plugins/saved_objects/public';
import {
esFilters,
FilterManager,
Expand All @@ -28,10 +32,20 @@ const dataStartMock = dataPluginMock.createStartContract();
import { navigationPluginMock } from '../../../../../src/plugins/navigation/public/mocks';
import { TopNavMenuData } from '../../../../../src/plugins/navigation/public';
import { coreMock } from 'src/core/public/mocks';
import { Observable } from 'rxjs';

jest.mock('../persistence');
jest.mock('src/core/public');
jest.mock('../../../../../src/plugins/saved_objects/public', () => {
// eslint-disable-next-line no-shadow
const { SavedObjectSaveModal, SavedObjectSaveModalOrigin } = jest.requireActual(
'../../../../../src/plugins/saved_objects/public'
);
return {
SavedObjectSaveModal,
SavedObjectSaveModalOrigin,
checkForDuplicateTitle: jest.fn(),
};
});

const navigationStartMock = navigationPluginMock.createStartContract();

Expand Down Expand Up @@ -644,6 +658,46 @@ describe('Lens App', () => {
});
});

it('checks for duplicate title before saving', async () => {
const args = defaultArgs;
args.editorFrame = frame;
(args.docStorage.save as jest.Mock).mockReturnValue(Promise.resolve({ id: '123' }));

instance = mount(<App {...args} />);

const onChange = frame.mount.mock.calls[0][1].onChange;
await act(async () =>
onChange({
filterableIndexPatterns: [],
doc: ({ id: '123', expression: 'valid expression' } as unknown) as Document,
})
);
instance.update();
await act(async () => {
getButton(instance).run(instance.getDOMNode());
});
instance.update();

const onTitleDuplicate = jest.fn();

await act(async () => {
instance.find(SavedObjectSaveModal).prop('onSave')({
onTitleDuplicate,
isTitleDuplicateConfirmed: false,
newCopyOnSave: false,
newDescription: '',
newTitle: 'test',
});
});

expect(checkForDuplicateTitle).toHaveBeenCalledWith(
expect.objectContaining({ id: '123' }),
false,
onTitleDuplicate,
expect.anything()
);
});

it('does not show the copy button on first save', async () => {
const args = defaultArgs;
args.editorFrame = frame;
Expand Down
26 changes: 25 additions & 1 deletion x-pack/plugins/lens/public/app_plugin/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { KibanaContextProvider } from '../../../../../src/plugins/kibana_react/p
import {
SavedObjectSaveModalOrigin,
OnSaveProps,
checkForDuplicateTitle,
} from '../../../../../src/plugins/saved_objects/public';
import { Document, SavedObjectStore } from '../persistence';
import { EditorFrameInstance } from '../types';
Expand Down Expand Up @@ -252,9 +253,11 @@ export function App({
// state.persistedDoc,
]);

const runSave = (
const runSave = async (
saveProps: Omit<OnSaveProps, 'onTitleDuplicate' | 'newDescription'> & {
returnToOrigin: boolean;
onTitleDuplicate?: OnSaveProps['onTitleDuplicate'];
newDescription?: string;
}
) => {
if (!lastKnownDoc) {
Expand All @@ -276,10 +279,30 @@ export function App({

const doc = {
...lastDocWithoutPinned,
description: saveProps.newDescription,
id: saveProps.newCopyOnSave ? undefined : lastKnownDoc.id,
title: saveProps.newTitle,
};

await checkForDuplicateTitle(
{
...doc,
copyOnSave: saveProps.newCopyOnSave,
lastSavedTitle: lastKnownDoc?.title,
getEsType: () => 'lens',
getDisplayName: () =>
i18n.translate('xpack.lens.app.saveModalType', {
defaultMessage: 'Lens visualization',
}),
},
saveProps.isTitleDuplicateConfirmed,
saveProps.onTitleDuplicate,
{
savedObjectsClient: core.savedObjects.client,
overlays: core.overlays,
}
);

const newlyCreated: boolean = saveProps.newCopyOnSave || !lastKnownDoc?.id;
docStorage
.save(doc)
Expand Down Expand Up @@ -492,6 +515,7 @@ export function App({
documentInfo={{
id: lastKnownDoc.id,
title: lastKnownDoc.title || '',
description: lastKnownDoc.description || '',
}}
objectType={i18n.translate('xpack.lens.app.saveModalType', {
defaultMessage: 'Lens visualization',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export function getSavedObjectFormat({
return {
id: state.persistedId,
title: state.title,
description: state.description,
type: 'lens',
visualizationType: state.visualization.activeId,
expression: expression ? toExpression(expression) : '',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,7 @@ describe('editor_frame state management', () => {
filters: [],
},
title: 'heyo!',
description: 'My lens',
type: 'lens',
visualizationType: 'line',
},
Expand All @@ -406,6 +407,7 @@ describe('editor_frame state management', () => {
},
persistedId: 'b',
title: 'heyo!',
description: 'My lens',
visualization: {
activeId: 'line',
state: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export interface PreviewState {
export interface EditorFrameState extends PreviewState {
persistedId?: string;
title: string;
description?: string;
stagedPreview?: PreviewState;
activeDatasourceId: string | null;
}
Expand Down Expand Up @@ -157,6 +158,7 @@ export const reducer = (state: EditorFrameState, action: Action): EditorFrameSta
...state,
persistedId: action.doc.id,
title: action.doc.title,
description: action.doc.description,
datasourceStates: Object.entries(action.doc.state.datasourceStates).reduce(
(stateMap, [datasourceId, datasourceState]) => ({
...stateMap,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ describe('LensStore', () => {
const { client, store } = testStore('FOO');
const doc = await store.save({
title: 'Hello',
description: 'My doc',
visualizationType: 'bar',
expression: '',
state: {
Expand All @@ -43,6 +44,7 @@ describe('LensStore', () => {
expect(doc).toEqual({
id: 'FOO',
title: 'Hello',
description: 'My doc',
visualizationType: 'bar',
expression: '',
state: {
Expand All @@ -61,6 +63,7 @@ describe('LensStore', () => {
expect(client.create).toHaveBeenCalledTimes(1);
expect(client.create).toHaveBeenCalledWith('lens', {
title: 'Hello',
description: 'My doc',
visualizationType: 'bar',
expression: '',
state: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export interface Document {
type?: string;
visualizationType: string | null;
title: string;
description?: string;
expression: string | null;
state: {
datasourceMetaData: {
Expand Down
3 changes: 2 additions & 1 deletion x-pack/plugins/lens/public/vis_type_alias.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,11 @@ export const getLensAliasConfig = (): VisTypeAlias => ({
searchFields: ['title^3'],
toListItem(savedObject) {
const { id, type, attributes } = savedObject;
const { title } = attributes as { title: string };
const { title, description } = attributes as { title: string; description?: string };
return {
id,
title,
description,
editUrl: getEditPath(id),
editApp: 'lens',
icon: 'lensApp',
Expand Down
3 changes: 3 additions & 0 deletions x-pack/plugins/lens/server/saved_objects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ export function setupSavedObjects(core: CoreSetup) {
title: {
type: 'text',
},
description: {
type: 'text',
},
visualizationType: {
type: 'keyword',
},
Expand Down

0 comments on commit 524e0d1

Please sign in to comment.