Skip to content

Commit

Permalink
[VizBuilder] Create a new wizard directly on a dashboard (opensearch-…
Browse files Browse the repository at this point in the history
…project#2384)

* Create a new wizard directly on a dashboard

After selecting a dashboard, create a new wizard directly from the dashboard and have the option to add to that dashboard.

Signed-off-by: abbyhu2000 <abigailhu2000@gmail.com>

* hook change to solve browser warning

Signed-off-by: abbyhu2000 <abigailhu2000@gmail.com>

* Move onsave out from getTopNavConfig function and added unit tests

Signed-off-by: abbyhu2000 <abigailhu2000@gmail.com>

* Add changelog

Signed-off-by: abbyhu2000 <abigailhu2000@gmail.com>

Signed-off-by: abbyhu2000 <abigailhu2000@gmail.com>
  • Loading branch information
abbyhu2000 committed Oct 28, 2022
1 parent b17f5c4 commit 55cc59b
Show file tree
Hide file tree
Showing 7 changed files with 350 additions and 93 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
* [Multi DataSource] UX enhacement on index pattern management stack ([#2505](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2505))
* [Multi DataSource] UX enhancement on Data source management stack ([#2521](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2521))
* [Multi DataSource] UX enhancement on Update stored password modal for Data source management stack ([#2532](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2532))
* [Viz Builder] Create a new wizard directly on a dashboard ([#2384](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2384))

### 🐛 Bug Fixes

Expand Down
36 changes: 21 additions & 15 deletions src/plugins/wizard/public/application/components/top_nav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/

import React, { useMemo } from 'react';
import React, { useEffect, useState } from 'react';
import { useParams } from 'react-router-dom';
import { useUnmount } from 'react-use';
import { PLUGIN_ID } from '../../../common';
Expand All @@ -17,6 +17,7 @@ import { useTypedSelector, useTypedDispatch } from '../utils/state_management';
import { setEditorState } from '../utils/state_management/metadata_slice';
import { useCanSave } from '../utils/use/use_can_save';
import { saveStateToSavedObject } from '../../saved_visualizations/transforms';
import { TopNavMenuData } from '../../../../navigation/public';

export const TopNav = () => {
// id will only be set for the edit route
Expand All @@ -34,27 +35,32 @@ export const TopNav = () => {
const saveDisabledReason = useCanSave();
const savedWizardVis = useSavedWizardVis(visualizationIdFromUrl);
const { selected: indexPattern } = useIndexPatterns();
const [config, setConfig] = useState<TopNavMenuData[] | undefined>();

const config = useMemo(() => {
if (!savedWizardVis || !indexPattern) return;
useEffect(() => {
const getConfig = () => {
if (!savedWizardVis || !indexPattern) return;

return getTopNavConfig(
{
visualizationIdFromUrl,
savedWizardVis: saveStateToSavedObject(savedWizardVis, rootState, indexPattern),
saveDisabledReason,
dispatch,
},
services
);
return getTopNavConfig(
{
visualizationIdFromUrl,
savedWizardVis: saveStateToSavedObject(savedWizardVis, rootState, indexPattern),
saveDisabledReason,
dispatch,
},
services
);
};

setConfig(getConfig());
}, [
rootState,
savedWizardVis,
services,
visualizationIdFromUrl,
rootState,
indexPattern,
saveDisabledReason,
dispatch,
services,
indexPattern,
]);

// reset validity before component destroyed
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import { WizardServices } from '../../types';
import { getOnSave } from './get_top_nav_config';
import { createWizardServicesMock } from './mocks';

describe('getOnSave', () => {
let savedWizardVis: any;
let originatingApp: string | undefined;
let visualizationIdFromUrl: string;
let dispatch: any;
let mockServices: jest.Mocked<WizardServices>;
let onSaveProps: {
newTitle: string;
newCopyOnSave: boolean;
isTitleDuplicateConfirmed: boolean;
onTitleDuplicate: any;
newDescription: string;
returnToOrigin: boolean;
};

beforeEach(() => {
savedWizardVis = {
id: '1',
title: 'save wizard wiz title',
description: '',
visualizationState: '',
styleState: '',
version: 0,
copyOnSave: true,
searchSourceFields: {},
save: jest.fn().mockReturnValue('1'),
};
originatingApp = '';
visualizationIdFromUrl = '';
dispatch = jest.fn();
mockServices = createWizardServicesMock();

onSaveProps = {
newTitle: 'new title',
newCopyOnSave: false,
isTitleDuplicateConfirmed: false,
onTitleDuplicate: jest.fn(),
newDescription: 'new description',
returnToOrigin: true,
};
});

test('return undefined when savedWizardVis is null', async () => {
savedWizardVis = null;
const onSave = getOnSave(
savedWizardVis,
originatingApp,
visualizationIdFromUrl,
dispatch,
mockServices
);
const onSaveResult = await onSave(onSaveProps);

expect(onSaveResult).toBeUndefined();
});

test('savedWizardVis get saved correctly', async () => {
const onSave = getOnSave(
savedWizardVis,
originatingApp,
visualizationIdFromUrl,
dispatch,
mockServices
);
const onSaveReturn = await onSave(onSaveProps);
expect(savedWizardVis).toMatchInlineSnapshot(`
Object {
"copyOnSave": false,
"description": "new description",
"id": "1",
"save": [MockFunction] {
"calls": Array [
Array [
Object {
"confirmOverwrite": false,
"isTitleDuplicateConfirmed": false,
"onTitleDuplicate": [MockFunction],
"returnToOrigin": true,
},
],
],
"results": Array [
Object {
"type": "return",
"value": "1",
},
],
},
"searchSourceFields": Object {},
"styleState": "",
"title": "new title",
"version": 0,
"visualizationState": "",
}
`);
expect(onSaveReturn?.id).toBe('1');
});

test('savedWizardVis does not change title with a null id', async () => {
savedWizardVis.save = jest.fn().mockReturnValue(null);
const onSave = getOnSave(
savedWizardVis,
originatingApp,
visualizationIdFromUrl,
dispatch,
mockServices
);
const onSaveResult = await onSave(onSaveProps);
expect(savedWizardVis.title).toBe('save wizard wiz title');
expect(onSaveResult?.id).toBeNull();
});

test('create a new wizard from dashboard', async () => {
savedWizardVis.id = undefined;
savedWizardVis.save = jest.fn().mockReturnValue('2');
originatingApp = 'dashboard';
onSaveProps.returnToOrigin = true;

const onSave = getOnSave(
savedWizardVis,
originatingApp,
visualizationIdFromUrl,
dispatch,
mockServices
);
const onSaveResult = await onSave(onSaveProps);
expect(onSaveResult?.id).toBe('2');
expect(dispatch).toBeCalledTimes(0);
});

test('edit an exising wizard from dashboard', async () => {
savedWizardVis.copyOnSave = false;
onSaveProps.newDescription = 'new description after editing';
originatingApp = 'dashboard';
onSaveProps.returnToOrigin = true;
const onSave = getOnSave(
savedWizardVis,
originatingApp,
visualizationIdFromUrl,
dispatch,
mockServices
);
const onSaveResult = await onSave(onSaveProps);
expect(onSaveResult?.id).toBe('1');
expect(mockServices.application.navigateToApp).toBeCalledTimes(1);
expect(savedWizardVis.description).toBe('new description after editing');
});
});
Loading

0 comments on commit 55cc59b

Please sign in to comment.