From c74385b56617a28c7b374162fc63ff2e7625c98d Mon Sep 17 00:00:00 2001 From: Peter Fitzgibbons Date: Wed, 19 Apr 2023 13:36:46 -0700 Subject: [PATCH 01/18] Fixes * Panel View (legacy) - Duplicate - Rename Signed-off-by: Peter Fitzgibbons --- public/components/custom_panels/custom_panel_table.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/components/custom_panels/custom_panel_table.tsx b/public/components/custom_panels/custom_panel_table.tsx index cf4273de65..a6107953b8 100644 --- a/public/components/custom_panels/custom_panel_table.tsx +++ b/public/components/custom_panels/custom_panel_table.tsx @@ -222,7 +222,7 @@ export const CustomPanelTable = ({ 'Duplicate Dashboard', 'Cancel', 'Duplicate', - selectedCustomPanels[0].title + ' (copy)', + selectedCustomPanels[0].title + ' (copy)x', CREATE_PANEL_MESSAGE ) ); From 4aa38ac84c6689ade3b0429a7dfaaece7892dbc0 Mon Sep 17 00:00:00 2001 From: Peter Fitzgibbons Date: Thu, 20 Apr 2023 09:54:46 -0700 Subject: [PATCH 02/18] Toasts use hook from useOpenSearchDashboards context provider Signed-off-by: Peter Fitzgibbons --- common/types/shared.ts | 5 ++ public/components/app.tsx | 46 +++++++----- public/components/common/toast/index.tsx | 36 +++++++++ .../custom_panels/custom_panel_table.tsx | 75 +++++++++++-------- .../custom_panels/custom_panel_view.tsx | 10 +-- public/components/custom_panels/home.tsx | 26 ++----- .../custom_panels/redux/panel_slice.ts | 2 - public/components/index.tsx | 3 + .../framework/redux/reducers/toast_slice.ts | 40 ++++++++++ public/plugin.ts | 6 ++ 10 files changed, 166 insertions(+), 83 deletions(-) create mode 100644 common/types/shared.ts create mode 100644 public/components/common/toast/index.tsx create mode 100644 public/framework/redux/reducers/toast_slice.ts diff --git a/common/types/shared.ts b/common/types/shared.ts new file mode 100644 index 0000000000..fd0f0a71e1 --- /dev/null +++ b/common/types/shared.ts @@ -0,0 +1,5 @@ +import { CoreStart, ToastsStart } from '../../../../src/core/public'; + +export interface ObservabilityAppServices extends CoreStart { + toasts: ToastsStart; +} diff --git a/public/components/app.tsx b/public/components/app.tsx index 18066f3281..8ea7e3834b 100644 --- a/public/components/app.tsx +++ b/public/components/app.tsx @@ -18,6 +18,8 @@ import { EventAnalytics } from './event_analytics'; import { Home as MetricsHome } from './metrics/index'; import { Main as NotebooksHome } from './notebooks/components/main'; import { Home as TraceAnalyticsHome } from './trace_analytics/home'; +import { ObservabilityAppServices } from '../../common/types/shared'; +import { OpenSearchDashboardsContextProvider } from '../../../../src/plugins/opensearch_dashboards_react/public'; interface ObservabilityAppDeps { CoreStartProp: CoreStart; @@ -28,6 +30,7 @@ interface ObservabilityAppDeps { timestampUtils: any; queryManager: QueryManager; startPage: string; + services: ObservabilityAppServices; } // for cypress to test redux store @@ -53,6 +56,7 @@ export const App = ({ timestampUtils, queryManager, startPage, + services, }: ObservabilityAppDeps) => { const { chrome, http, notifications, savedObjects: coreSavedObjects } = CoreStartProp; const parentBreadcrumb = { @@ -65,26 +69,28 @@ export const App = ({ return ( - - - + + + + + ); diff --git a/public/components/common/toast/index.tsx b/public/components/common/toast/index.tsx new file mode 100644 index 0000000000..ba3959a515 --- /dev/null +++ b/public/components/common/toast/index.tsx @@ -0,0 +1,36 @@ +import React from 'react'; +import { useOpenSearchDashboards } from '../../../../../../src/plugins/opensearch_dashboards_react/public'; +import { ToastInputFields } from '../../../../../../src/core/public'; +import { ObservabilityAppServices } from '../../../../common/types/shared'; + +type Color = 'success' | 'primary' | 'warning' | 'danger' | undefined; + +export const useToast = () => { + const { + services: { toasts }, + } = useOpenSearchDashboards(); + + const setToast = (title: string, color: Color = 'success', text?, side?: string) => { + const newToast: ToastInputFields = { + id: new Date().toISOString(), + title, + text, + }; + switch (color) { + case 'danger': { + toasts.addDanger(newToast); + break; + } + case 'warning': { + toasts.addWarning(newToast); + break; + } + default: { + toasts.addSuccess(newToast); + break; + } + } + }; + + return { setToast }; +}; diff --git a/public/components/custom_panels/custom_panel_table.tsx b/public/components/custom_panels/custom_panel_table.tsx index a6107953b8..9c620227f9 100644 --- a/public/components/custom_panels/custom_panel_table.tsx +++ b/public/components/custom_panels/custom_panel_table.tsx @@ -48,7 +48,6 @@ import { getSampleDataModal } from '../common/helpers/add_sample_modal'; import { pageStyles } from '../../../common/constants/shared'; import { DeleteModal } from '../common/helpers/delete_modal'; import { - clonePanel, createPanel, deletePanels, fetchPanels, @@ -57,6 +56,8 @@ import { renameCustomPanel, selectPanelList, } from './redux/panel_slice'; +import { isNameValid } from './helpers/utils'; +import { useToast } from '../common/toast'; /* * "CustomPanelTable" module, used to view all the saved panels @@ -77,8 +78,6 @@ interface Props { loading: boolean; setBreadcrumbs: (newBreadcrumbs: ChromeBreadcrumb[]) => void; parentBreadcrumbs: EuiBreadcrumb[]; - cloneCustomPanel: (newCustomPanelName: string, customPanelId: string) => void; - deleteCustomPanelList: (customPanelIdList: string[], toastMessage: string) => any; addSamplePanels: () => void; } @@ -86,8 +85,6 @@ export const CustomPanelTable = ({ loading, setBreadcrumbs, parentBreadcrumbs, - cloneCustomPanel, - deleteCustomPanelList, addSamplePanels, }: Props) => { const customPanels = useSelector(selectPanelList); @@ -100,16 +97,13 @@ export const CustomPanelTable = ({ const history = useHistory(); const dispatch = useDispatch(); + const { setToast } = useToast(); useEffect(() => { setBreadcrumbs(parentBreadcrumbs); dispatch(fetchPanels()); }, []); - // useEffect(() => - // console.log({ customPanels, selectedCustomPanels }, [customPanels, selectedCustomPanels]) - // ); - useEffect(() => { const url = window.location.hash.split('/'); if (url[url.length - 1] === 'create') { @@ -126,8 +120,12 @@ export const CustomPanelTable = ({ }; const onCreate = async (newCustomPanelName: string) => { - const newPanel = newPanelTemplate(newCustomPanelName); - dispatch(createPanel(newPanel)); + if (!isNameValid(newCustomPanelName)) { + setToast('Invalid Dashboard name', 'danger'); + } else { + const newPanel = newPanelTemplate(newCustomPanelName); + dispatch(createPanel(newPanel)); + } closeModal(); }; @@ -137,25 +135,35 @@ export const CustomPanelTable = ({ }; const onClone = async (newName: string) => { - let sourcePanel = selectedCustomPanels[0]; - try { - if (!isUuid(sourcePanel.id)) { - // Observability Panel API returns partial record, so for duplication - // we will retrieve the entire record and allow new process to continue. - const legacyFetchResult = await coreRefs.http!.get( - `${CUSTOM_PANELS_API_PREFIX}/panels/${sourcePanel.id}` - ); - sourcePanel = legacyFetchResult.operationalPanel; - } + if (!isNameValid(newName)) { + setToast('Invalid Operational Panel name', 'danger'); + } else { + let sourcePanel = selectedCustomPanels[0]; + try { + if (!isUuid(sourcePanel.id)) { + // Observability Panel API returns partial record, so for duplication + // we will retrieve the entire record and allow new process to continue. + const legacyFetchResult = await coreRefs.http!.get( + `${CUSTOM_PANELS_API_PREFIX}/panels/${sourcePanel.id}` + ); + sourcePanel = legacyFetchResult.operationalPanel; + } - const { id, ...newPanel } = { - ...sourcePanel, - title: newName, - }; + const { id, ...newPanel } = { + ...sourcePanel, + title: newName, + }; - dispatch(createPanel(newPanel)); - } catch (err) { - console.log(err); + await dispatch(createPanel(newPanel)); + + setToast(`Observability Dashboard "${newName}" successfully created!`); + } catch (err) { + setToast( + 'Error cloning Operational Panel, please make sure you have the correct permission.', + 'danger' + ); + console.log(err); + } } closeModal(); }; @@ -166,12 +174,13 @@ export const CustomPanelTable = ({ } successfully deleted!`; try { - dispatch(deletePanels(selectedCustomPanels)); + await dispatch(deletePanels(selectedCustomPanels)); + setToast(toastMessage); } catch (err) { - // setToast( - // 'Error deleting Operational Panels, please make sure you have the correct permission.', - // 'danger' - // ); + setToast( + 'Error deleting Operational Panels, please make sure you have the correct permission.', + 'danger' + ); console.error(err.body?.message || err); } diff --git a/public/components/custom_panels/custom_panel_view.tsx b/public/components/custom_panels/custom_panel_view.tsx index 2384d82efc..d45c5214b9 100644 --- a/public/components/custom_panels/custom_panel_view.tsx +++ b/public/components/custom_panels/custom_panel_view.tsx @@ -70,6 +70,7 @@ import { AddVisualizationPopover } from './helpers/add_visualization_popover'; import { DeleteModal } from '../common/helpers/delete_modal'; import { coreRefs } from '../../framework/core_refs'; import { clonePanel } from './redux/panel_slice'; +import { useToast } from '../common/toast'; /* * "CustomPanelsView" module used to render an Observability Dashboard @@ -104,12 +105,6 @@ interface CustomPanelViewProps { chrome: CoreStart['chrome']; parentBreadcrumbs: EuiBreadcrumb[]; cloneCustomPanel: (clonedCustomPanelName: string, clonedCustomPanelId: string) => Promise; - setToast: ( - title: string, - color?: string, - text?: React.ReactChild | undefined, - side?: string | undefined - ) => void; onEditClick: (savedVisualizationId: string) => any; startTime: string; endTime: string; @@ -138,7 +133,6 @@ export const CustomPanelView = (props: CustomPanelViewProps) => { setEndTime, updateAvailabilityVizId, cloneCustomPanel, - setToast, onEditClick, onAddClick, } = props; @@ -169,6 +163,8 @@ export const CustomPanelView = (props: CustomPanelViewProps) => { const dispatch = useDispatch(); + const { setToast } = useToast(); + const closeHelpFlyout = () => { setAddVizDisabled(false); setHelpIsFlyoutVisible(false); diff --git a/public/components/custom_panels/home.tsx b/public/components/custom_panels/home.tsx index bc75fb6c43..37d34b8fde 100644 --- a/public/components/custom_panels/home.tsx +++ b/public/components/custom_panels/home.tsx @@ -3,9 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { EuiBreadcrumb, EuiGlobalToastList, ShortDate, htmlIdGenerator } from '@elastic/eui'; -import { Toast } from '@elastic/eui/src/components/toast/global_toast_list'; -import React, { ReactChild, useState } from 'react'; +import { EuiBreadcrumb, ShortDate, htmlIdGenerator } from '@elastic/eui'; +import React, { useState } from 'react'; import { useDispatch, batch } from 'react-redux'; // eslint-disable-next-line @osd/eslint/module_migration import { StaticContext } from 'react-router'; @@ -31,6 +30,7 @@ import { init as initPatterns } from '../event_analytics/redux/slices/patterns_s import { init as initQueryResult } from '../event_analytics/redux/slices/query_result_slice'; import { changeQuery, init as initQuery } from '../event_analytics/redux/slices/query_slice'; import { addTab, setSelectedQueryTab } from '../event_analytics/redux/slices/query_tab_slice'; +import { useToast } from '../common/toast'; // import { ObjectFetcher } from '../common/objectFetcher'; @@ -67,14 +67,14 @@ export const Home = ({ coreSavedObjects, setBreadcrumbs, }: PanelHomeProps) => { - const [toasts, setToasts] = useState([]); const [loading, setLoading] = useState(false); - const [toastRightSide, setToastRightSide] = useState(true); const [start, setStart] = useState(''); const [end, setEnd] = useState(''); const dispatch = useDispatch(); + const { setToast } = useToast(); + const customPanelBreadCrumbs = [ ...parentBreadcrumbs, { @@ -83,12 +83,6 @@ export const Home = ({ }, ]; - const setToast = (title: string, color = 'success', text?: ReactChild, side?: string) => { - if (!text) text = ''; - setToastRightSide(!side ? true : false); - setToasts([...toasts, { id: new Date().toISOString(), title, text, color } as Toast]); - }; - const addNewTab = async () => { // get a new tabId const tabId = htmlIdGenerator(TAB_ID_TXT_PFX)(); @@ -176,14 +170,6 @@ export const Home = ({ return ( - { - setToasts(toasts.filter((toast) => toast.id !== removedToast.id)); - }} - side={toastRightSide ? 'right' : 'left'} - toastLifeTimeMs={6000} - /> async (dispatch, getState) => { const newPanel = savedObjectToCustomPanel(newSOPanel); const panelList = getState().customPanel.panelList; dispatch(setPanelList([...panelList, newPanel])); - - window.location.replace(`#/${newPanel.id}`); }; export const clonePanel = (panel, newPanelName) => async (dispatch, getState) => { diff --git a/public/components/index.tsx b/public/components/index.tsx index 8191752974..440f2518a2 100644 --- a/public/components/index.tsx +++ b/public/components/index.tsx @@ -9,6 +9,7 @@ import { QueryManager } from 'common/query_manager'; import { AppMountParameters, CoreStart } from '../../../../src/core/public'; import { AppPluginStartDependencies } from '../types'; import { App } from './app'; +import { ObservabilityAppServices } from '../../common/types/shared'; export const Observability = ( CoreStartProp: CoreStart, @@ -19,6 +20,7 @@ export const Observability = ( savedObjects: any, timestampUtils: any, queryManager: QueryManager, + services: ObservabilityAppServices, startPage: string ) => { ReactDOM.render( @@ -31,6 +33,7 @@ export const Observability = ( timestampUtils={timestampUtils} queryManager={queryManager} startPage={startPage} + services={services} />, AppMountParametersProp.element ); diff --git a/public/framework/redux/reducers/toast_slice.ts b/public/framework/redux/reducers/toast_slice.ts new file mode 100644 index 0000000000..de856535f7 --- /dev/null +++ b/public/framework/redux/reducers/toast_slice.ts @@ -0,0 +1,40 @@ +import { createSlice } from '@reduxjs/toolkit'; + +const toastSlice = createSlice({ + name: 'toast', + initialState: { toasts: [], toastRightSide: true }, + reducers: { + appendToast: (state, action) => { + state.toasts = [...state.toasts, action.payload]; + }, + + setToastRightSide: (state, action) => (state.toastRightSide = action.payload), + + dismissToast: (state, action) => { + state.toasts = state.toasts.filter((t) => t.id !== action.payload.id); + }, + + resetToasts: (state, action) => { + state.toasts = []; + state.toastRightSide = true; + }, + }, +}); + +export const toastReducer = toastSlice.reducer; + +export const { dismissToast, resetToasts } = toastSlice.actions; +const { appendToast, setToastRightSide } = toastSlice.actions; + +export const selectToasts = (rootState) => rootState.toast.toasts; + +export const selectToastRightSide = (rootState) => rootState.toast.toastRightSide; + +export const addToast = (title, color?, textChild?, side?) => (dispatch, getState) => { + const newToast = { id: new Date().toISOString(), title, textChild, color }; + dispatch(appendToast(newToast)); + + if (side) { + dispatch(setToastRightSide(side === 'left')); + } +}; diff --git a/public/plugin.ts b/public/plugin.ts index bb21d79ae3..c415e09345 100644 --- a/public/plugin.ts +++ b/public/plugin.ts @@ -75,6 +75,7 @@ import { ObservabilityStart, SetupDependencies, } from './types'; +import { ObservabilityAppServices } from '../common/types/shared'; export class ObservabilityPlugin implements @@ -135,6 +136,10 @@ export class ObservabilityPlugin const savedObjects = new SavedObjects(coreStart.http); const timestampUtils = new TimestampUtils(dslService, pplService); + const services: ObservabilityAppServices = { + toasts: coreStart.notifications.toasts, + }; + return Observability( coreStart, depsStart as AppPluginStartDependencies, @@ -144,6 +149,7 @@ export class ObservabilityPlugin savedObjects, timestampUtils, qm, + services, startPage ); }; From 20faeda6c4822d3d9520189d461b3565cb6a44db Mon Sep 17 00:00:00 2001 From: Peter Fitzgibbons Date: Fri, 21 Apr 2023 15:51:21 -0700 Subject: [PATCH 03/18] Testing for CustomPanel Toast Signed-off-by: Peter Fitzgibbons --- .cypress/integration/3_panels.spec.ts | 14 +- .../custom_panel_view.test.tsx.snap | 3498 +++++++++-------- .../__tests__/custom_panel_view.test.tsx | 71 +- .../custom_panels/custom_panel_table.tsx | 16 +- public/plugin.ts | 2 +- 5 files changed, 1821 insertions(+), 1780 deletions(-) diff --git a/.cypress/integration/3_panels.spec.ts b/.cypress/integration/3_panels.spec.ts index 252be657a3..df58e2a4e8 100644 --- a/.cypress/integration/3_panels.spec.ts +++ b/.cypress/integration/3_panels.spec.ts @@ -74,7 +74,7 @@ describe('Creating visualizations', () => { }); }); -describe('Testing panels table', () => { +describe.only('Testing panels table', () => { beforeEach(() => { eraseTestPanels(); moveToPanelHome(); @@ -597,6 +597,7 @@ const eraseLegacyPanels = () => { 'osd-xsrf': true, }, }).then((response) => { + console.log("legacy panels to erase", response.body) response.body.panels.map((panel) => { cy.request({ method: 'DELETE', @@ -626,6 +627,7 @@ const eraseSavedObjectPaenls = () => { }, }) .then((response) => { + console.log("saved objects to erase", response.body) response.body.saved_objects.map((soPanel) => { cy.request({ method: 'DELETE', @@ -635,6 +637,9 @@ const eraseSavedObjectPaenls = () => { 'content-type': 'application/json;charset=UTF-8', 'osd-xsrf': true, }, + }).then((response) => { + const deletedId = response; + console.log('erased SO Panel', response) }); }); }); @@ -712,9 +717,10 @@ const openActionsDropdown = () => { }; const selectThePanel = () => { - cy.get('.euiCheckbox__input[title="Select this row"]').then(() => { - cy.get('.euiCheckbox__input[title="Select this row"]').check({ force: true }); - }); + // cy.get('.euiCheckbox__input[title="Select this row"]').then(() => { + cy.get('.euiCheckbox__input[title="Select this row"]').check({ force: true }); + cy.get('.euiTableRow-isSelected').should('exist') + // }); }; const expectToastWith = (title) => { diff --git a/public/components/custom_panels/__tests__/__snapshots__/custom_panel_view.test.tsx.snap b/public/components/custom_panels/__tests__/__snapshots__/custom_panel_view.test.tsx.snap index 05379e0bcd..226fddb62b 100644 --- a/public/components/custom_panels/__tests__/__snapshots__/custom_panel_view.test.tsx.snap +++ b/public/components/custom_panels/__tests__/__snapshots__/custom_panel_view.test.tsx.snap @@ -2,707 +2,589 @@ exports[`Panels View Component renders panel view container with visualizations 1`] = ` - + -
- -
+
+ - -
- -
- -
- + +
+ +
-

- - -
- + + +
-
- -
- - Created on - Invalid date -
- - -
- +
+ +
+ + Created on + Invalid date +
+
+ +
-
- -
- + +
- - - - -
-
- -
+ + + +
+
+ - - Dashboard Actions -
- } - closePopover={[Function]} - display="inlineBlock" - hasArrow={true} - isOpen={false} - ownFocus={true} - panelPaddingSize="none" - withTitle={true} - > -
-
- - - - - -
-
- -
-
- -
- - Add visualization + Dashboard Actions } closePopover={[Function]} display="inlineBlock" hasArrow={true} - id="addVisualizationContextMenu" isOpen={false} ownFocus={true} panelPaddingSize="none" + withTitle={true} >
-
-
-
-
- -
-
-

-
- -
- + + +
+ + + Add visualization + + } + closePopover={[Function]} + display="inlineBlock" + hasArrow={true} + id="addVisualizationContextMenu" + isOpen={false} + ownFocus={true} + panelPaddingSize="none" + > +
+
+ + + + + +
+
+
+
+
+
+
+ +
+
+
+
+ +
-
- -
- + +
+ + PPL + + } + baseQuery="source = " + dslService={ + DSLService { + "fetch": [Function], + "fetchFields": [Function], + "fetchIndices": [Function], + "http": [MockFunction], } - > - PPL - - } - baseQuery="source = " - dslService={ - DSLService { - "fetch": [Function], - "fetchFields": [Function], - "fetchIndices": [Function], - "http": [MockFunction], } - } - getSuggestions={[Function]} - handleQueryChange={[Function]} - handleQuerySearch={[Function]} - isDisabled={true} - key="autocomplete-search-bar" - onItemSelect={[Function]} - placeholder="Use PPL 'where' clauses to add filters on all visualizations [where Carrier = 'OpenSearch-Air']" - possibleCommands={ - Array [ - Object { - "label": "where", - }, - ] - } - query="" - tabId="panels-filter" - tempQuery="" - > -
- - PPL - - } - aria-autocomplete="both" +
- } + aria-autocomplete="both" + aria-labelledby="autocomplete-4-label" + autoCapitalize="off" + autoComplete="off" + autoCorrect="off" + autoFocus={false} + data-test-subj="searchAutocompleteTextArea" + disabled={true} + enterKeyHint="search" fullWidth={true} - inputId="autocomplete-textarea" + id="autocomplete-textarea" + maxLength={512} + onBlur={[Function]} + onChange={[Function]} + onClick={[Function]} + onFocus={[Function]} + onKeyDown={[Function]} + placeholder="Use PPL 'where' clauses to add filters on all visualizations [where Carrier = 'OpenSearch-Air']" + spellCheck="false" + type="search" + value="" > -
+ PPL + + } + fullWidth={true} + inputId="autocomplete-textarea" >
- - - - -
- -
+ - PPL - - -
- -
-
-
-
-
- -
+ PPL + + +
+ + +
+ +
+ + - - -
- -
- - } +
+ +
-
+ } > - - - - + -
-
+ - - + + + +
-
- - -
- } - iconType={false} - isCustom={true} - startDateControl={
} + + +
-
} + iconType={false} + isCustom={true} + startDateControl={
} > - -
- - -
-
- -
- - -
+ Show dates + + + +
+
+ +
+
+ +
+ + - - - - - - - - - - - -
-
-
-
-
-
- -
- - -
- - -
- -
- - -
+ + + + + + +
+ +
+ + +
+ +
+ + +
+ + +
+ +
+ + - -
-

- Start by adding your first visualization -

- -
- - -
+ Start by adding your first visualization + + - + + +
-
- Use PPL Queries to fetch & filter observability data and create visualizations -
- -
-
-
- -
- - -
- - -
+ Use PPL Queries to fetch & filter observability data and create visualizations +
+ +
+ +
+
+
+ + - + + +
-
- - - Add visualization - - } - closePopover={[Function]} - display="inlineBlock" - hasArrow={true} - id="addVisualizationContextMenu" - isOpen={false} - ownFocus={true} - panelPaddingSize="none" + -
-
+ - + } + closePopover={[Function]} + display="inlineBlock" + hasArrow={true} + id="addVisualizationContextMenu" + isOpen={false} + ownFocus={true} + panelPaddingSize="none" + > +
+
+ - - - + + + + +
-
- - -
- -
- - -
- -
- - + +
+ +
+ + +
+ +
+
+ - - - -
- - - - -
- -
-
-
- -
- + maxRows={Infinity} + onDrag={[Function]} + onDragStart={[Function]} + onDragStop={[Function]} + onLayoutChange={[Function]} + onResize={[Function]} + onResizeStart={[Function]} + onResizeStop={[Function]} + preventCollision={false} + rowHeight={150} + style={Object {}} + useCSSTransforms={true} + verticalCompact={true} + width={0} + > +
+ + + + +
+ +
+ + + + +
+
`; diff --git a/public/components/custom_panels/__tests__/custom_panel_view.test.tsx b/public/components/custom_panels/__tests__/custom_panel_view.test.tsx index b37601519f..5a5da64e15 100644 --- a/public/components/custom_panels/__tests__/custom_panel_view.test.tsx +++ b/public/components/custom_panels/__tests__/custom_panel_view.test.tsx @@ -20,13 +20,16 @@ import PPLService from '../../../../public/services/requests/ppl'; import DSLService from '../../../../public/services/requests/dsl'; import { coreStartMock } from '../../../../test/__mocks__/coreMocks'; import { HttpResponse } from '../../../../../../src/core/public'; -import { createStore } from '@reduxjs/toolkit'; +import { applyMiddleware, createStore } from 'redux'; import { rootReducer } from '../../../framework/redux/reducers'; +import thunk from 'redux-thunk'; import { Provider } from 'react-redux'; +import { OpenSearchDashboardsContextProvider } from '../../../../../../src/plugins/opensearch_dashboards_react/public'; describe('Panels View Component', () => { configure({ adapter: new Adapter() }); - const store = createStore(rootReducer); + + const store = createStore(rootReducer, applyMiddleware(thunk)); it('renders panel view container without visualizations', async () => { httpClientMock.get = jest.fn(() => @@ -71,6 +74,26 @@ describe('Panels View Component', () => { page="operationalPanels" /> + + + ); wrapper.update(); @@ -111,27 +134,31 @@ describe('Panels View Component', () => { window.location.assign(`#/event_analytics/explorer/${savedVisId}`); }; + const services = { toasts: { addDanger: (t) => {} } } + const wrapper = mount( - - - + + + + + ); wrapper.update(); diff --git a/public/components/custom_panels/custom_panel_table.tsx b/public/components/custom_panels/custom_panel_table.tsx index 9c620227f9..fb419fa5c6 100644 --- a/public/components/custom_panels/custom_panel_table.tsx +++ b/public/components/custom_panels/custom_panel_table.tsx @@ -91,7 +91,7 @@ export const CustomPanelTable = ({ const [isModalVisible, setIsModalVisible] = useState(false); // Modal Toggle const [modalLayout, setModalLayout] = useState(); // Modal Layout const [isActionsPopoverOpen, setIsActionsPopoverOpen] = useState(false); - const [selectedCustomPanels, setselectedCustomPanels] = useState([]); + const [selectedCustomPanels, setselectedCustomPanels$] = useState([]); const [searchQuery, setSearchQuery] = useState(''); const location = useLocation(); const history = useHistory(); @@ -169,9 +169,8 @@ export const CustomPanelTable = ({ }; const onDelete = async () => { - const toastMessage = `Observability Dashboards ${ - selectedCustomPanels.length > 1 ? 's' : ' ' + selectedCustomPanels[0].title - } successfully deleted!`; + const toastMessage = `Observability Dashboards ${selectedCustomPanels.length > 1 ? 's' : ' ' + selectedCustomPanels[0].title + } successfully deleted!`; try { await dispatch(deletePanels(selectedCustomPanels)); @@ -239,9 +238,8 @@ export const CustomPanelTable = ({ }; const deletePanel = () => { - const customPanelString = `Observability Dashboard${ - selectedCustomPanels.length > 1 ? 's' : '' - }`; + const customPanelString = `Observability Dashboard${selectedCustomPanels.length > 1 ? 's' : '' + }`; setModalLayout( - customPanel.title.toLowerCase().includes(searchQuery.toLowerCase()) - ) + customPanel.title.toLowerCase().includes(searchQuery.toLowerCase()) + ) : customPanels } itemId="id" diff --git a/public/plugin.ts b/public/plugin.ts index c415e09345..06a9946c76 100644 --- a/public/plugin.ts +++ b/public/plugin.ts @@ -79,7 +79,7 @@ import { ObservabilityAppServices } from '../common/types/shared'; export class ObservabilityPlugin implements - Plugin { + Plugin { public setup( core: CoreSetup, setupDeps: SetupDependencies From 67d04e026165ae8eb2b8582c17adb8a579a4a383 Mon Sep 17 00:00:00 2001 From: Shenoy Pratik Date: Wed, 26 Apr 2023 23:44:16 -0700 Subject: [PATCH 04/18] update catches from comments, minor code cleaning Signed-off-by: Shenoy Pratik --- common/constants/shared.ts | 2 +- common/types/shared.ts | 5 -- public/components/app.tsx | 46 ++++++++----------- public/components/common/toast/index.tsx | 15 +++--- .../custom_panels/custom_panel_table.tsx | 21 +++++---- .../custom_panels/custom_panel_view_so.tsx | 2 +- public/components/custom_panels/home.tsx | 1 + .../visualization_flyout.tsx | 2 - .../custom_panels/redux/panel_slice.ts | 6 ++- public/components/index.tsx | 3 -- public/framework/core_refs.ts | 3 +- public/plugin.ts | 24 ++-------- 12 files changed, 52 insertions(+), 78 deletions(-) delete mode 100644 common/types/shared.ts diff --git a/common/constants/shared.ts b/common/constants/shared.ts index 5797994601..e42bd11c60 100644 --- a/common/constants/shared.ts +++ b/common/constants/shared.ts @@ -25,7 +25,7 @@ export const DSL_ENDPOINT = '/_plugins/_dsl'; export const observabilityID = 'observability-logs'; export const observabilityTitle = 'Observability'; -export const observabilityPluginOrder = 6000; +export const observabilityPluginOrder = 1500; export const observabilityApplicationsID = 'observability-applications'; export const observabilityApplicationsTitle = 'Applications'; diff --git a/common/types/shared.ts b/common/types/shared.ts deleted file mode 100644 index fd0f0a71e1..0000000000 --- a/common/types/shared.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { CoreStart, ToastsStart } from '../../../../src/core/public'; - -export interface ObservabilityAppServices extends CoreStart { - toasts: ToastsStart; -} diff --git a/public/components/app.tsx b/public/components/app.tsx index 8ea7e3834b..18066f3281 100644 --- a/public/components/app.tsx +++ b/public/components/app.tsx @@ -18,8 +18,6 @@ import { EventAnalytics } from './event_analytics'; import { Home as MetricsHome } from './metrics/index'; import { Main as NotebooksHome } from './notebooks/components/main'; import { Home as TraceAnalyticsHome } from './trace_analytics/home'; -import { ObservabilityAppServices } from '../../common/types/shared'; -import { OpenSearchDashboardsContextProvider } from '../../../../src/plugins/opensearch_dashboards_react/public'; interface ObservabilityAppDeps { CoreStartProp: CoreStart; @@ -30,7 +28,6 @@ interface ObservabilityAppDeps { timestampUtils: any; queryManager: QueryManager; startPage: string; - services: ObservabilityAppServices; } // for cypress to test redux store @@ -56,7 +53,6 @@ export const App = ({ timestampUtils, queryManager, startPage, - services, }: ObservabilityAppDeps) => { const { chrome, http, notifications, savedObjects: coreSavedObjects } = CoreStartProp; const parentBreadcrumb = { @@ -69,28 +65,26 @@ export const App = ({ return ( - - - - - + + + ); diff --git a/public/components/common/toast/index.tsx b/public/components/common/toast/index.tsx index ba3959a515..6eaef004ce 100644 --- a/public/components/common/toast/index.tsx +++ b/public/components/common/toast/index.tsx @@ -1,16 +1,17 @@ -import React from 'react'; -import { useOpenSearchDashboards } from '../../../../../../src/plugins/opensearch_dashboards_react/public'; +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + import { ToastInputFields } from '../../../../../../src/core/public'; -import { ObservabilityAppServices } from '../../../../common/types/shared'; +import { coreRefs } from '../../../framework/core_refs'; type Color = 'success' | 'primary' | 'warning' | 'danger' | undefined; export const useToast = () => { - const { - services: { toasts }, - } = useOpenSearchDashboards(); + const toasts = coreRefs.toasts!; - const setToast = (title: string, color: Color = 'success', text?, side?: string) => { + const setToast = (title: string, color: Color = 'success', text?: string) => { const newToast: ToastInputFields = { id: new Date().toISOString(), title, diff --git a/public/components/custom_panels/custom_panel_table.tsx b/public/components/custom_panels/custom_panel_table.tsx index fb419fa5c6..76c8662ca1 100644 --- a/public/components/custom_panels/custom_panel_table.tsx +++ b/public/components/custom_panels/custom_panel_table.tsx @@ -91,7 +91,7 @@ export const CustomPanelTable = ({ const [isModalVisible, setIsModalVisible] = useState(false); // Modal Toggle const [modalLayout, setModalLayout] = useState(); // Modal Layout const [isActionsPopoverOpen, setIsActionsPopoverOpen] = useState(false); - const [selectedCustomPanels, setselectedCustomPanels$] = useState([]); + const [selectedCustomPanels, setselectedCustomPanels] = useState([]); const [searchQuery, setSearchQuery] = useState(''); const location = useLocation(); const history = useHistory(); @@ -162,15 +162,16 @@ export const CustomPanelTable = ({ 'Error cloning Operational Panel, please make sure you have the correct permission.', 'danger' ); - console.log(err); + console.error(err); } } closeModal(); }; const onDelete = async () => { - const toastMessage = `Observability Dashboards ${selectedCustomPanels.length > 1 ? 's' : ' ' + selectedCustomPanels[0].title - } successfully deleted!`; + const toastMessage = `Observability Dashboards ${ + selectedCustomPanels.length > 1 ? 's' : ' ' + selectedCustomPanels[0].title + } successfully deleted!`; try { await dispatch(deletePanels(selectedCustomPanels)); @@ -230,7 +231,7 @@ export const CustomPanelTable = ({ 'Duplicate Dashboard', 'Cancel', 'Duplicate', - selectedCustomPanels[0].title + ' (copy)x', + selectedCustomPanels[0].title + ' (copy)', CREATE_PANEL_MESSAGE ) ); @@ -238,8 +239,9 @@ export const CustomPanelTable = ({ }; const deletePanel = () => { - const customPanelString = `Observability Dashboard${selectedCustomPanels.length > 1 ? 's' : '' - }`; + const customPanelString = `Observability Dashboard${ + selectedCustomPanels.length > 1 ? 's' : '' + }`; setModalLayout( >; - // console.log('rendering', { customPanels, selectedCustomPanels }); return (
@@ -410,8 +411,8 @@ export const CustomPanelTable = ({ items={ searchQuery ? customPanels.filter((customPanel) => - customPanel.title.toLowerCase().includes(searchQuery.toLowerCase()) - ) + customPanel.title.toLowerCase().includes(searchQuery.toLowerCase()) + ) : customPanels } itemId="id" diff --git a/public/components/custom_panels/custom_panel_view_so.tsx b/public/components/custom_panels/custom_panel_view_so.tsx index 7fa2beac08..801a91db59 100644 --- a/public/components/custom_panels/custom_panel_view_so.tsx +++ b/public/components/custom_panels/custom_panel_view_so.tsx @@ -363,7 +363,7 @@ export const CustomPanelViewSO = (props: CustomPanelViewProps) => { } if (!isPPLFilterValid(pplFilterValue, setToast)) { - console.log(pplFilterValue); + console.error(pplFilterValue); return; } diff --git a/public/components/custom_panels/home.tsx b/public/components/custom_panels/home.tsx index 37d34b8fde..69e94d61e6 100644 --- a/public/components/custom_panels/home.tsx +++ b/public/components/custom_panels/home.tsx @@ -31,6 +31,7 @@ import { init as initQueryResult } from '../event_analytics/redux/slices/query_r import { changeQuery, init as initQuery } from '../event_analytics/redux/slices/query_slice'; import { addTab, setSelectedQueryTab } from '../event_analytics/redux/slices/query_tab_slice'; import { useToast } from '../common/toast'; +import { coreRefs } from '../../framework/core_refs'; // import { ObjectFetcher } from '../common/objectFetcher'; diff --git a/public/components/custom_panels/panel_modules/visualization_flyout/visualization_flyout.tsx b/public/components/custom_panels/panel_modules/visualization_flyout/visualization_flyout.tsx index 99fb39112f..7240a3171d 100644 --- a/public/components/custom_panels/panel_modules/visualization_flyout/visualization_flyout.tsx +++ b/public/components/custom_panels/panel_modules/visualization_flyout/visualization_flyout.tsx @@ -182,7 +182,6 @@ export const VisaulizationFlyout = ({ }), }) .then(async (res) => { - console.log('addVisualization Replacement', res); setPanelVisualizations(res.visualizations); setToast(`Visualization ${newVisualizationTitle} successfully added!`, 'success'); }) @@ -202,7 +201,6 @@ export const VisaulizationFlyout = ({ }), }) .then(async (res) => { - console.log('addVisualization New', res); setPanelVisualizations(res.visualizations); setToast(`Visualization ${newVisualizationTitle} successfully added!`, 'success'); }) diff --git a/public/components/custom_panels/redux/panel_slice.ts b/public/components/custom_panels/redux/panel_slice.ts index be41f6cc14..b9fae518f4 100644 --- a/public/components/custom_panels/redux/panel_slice.ts +++ b/public/components/custom_panels/redux/panel_slice.ts @@ -145,7 +145,7 @@ export const updatePanel = (panel: CustomPanelType) => async (dispatch, getState const panelList = getState().customPanel.panelList.map((p) => (p.id === panel.id ? panel : p)); dispatch(setPanelList(panelList)); } catch (err) { - console.log('Error updating Dashboard', { err, panel }); + console.error('Error updating Dashboard', { err, panel }); } }; @@ -226,6 +226,8 @@ export const createPanel = (panel) => async (dispatch, getState) => { const newPanel = savedObjectToCustomPanel(newSOPanel); const panelList = getState().customPanel.panelList; dispatch(setPanelList([...panelList, newPanel])); + + window.location.replace(`#/${newPanel.id}`); }; export const clonePanel = (panel, newPanelName) => async (dispatch, getState) => { @@ -272,7 +274,7 @@ export const renameCustomPanel = (editedCustomPanelName: string, id: string) => getState ) => { if (!isNameValid(editedCustomPanelName)) { - console.log('Invalid Observability Dashboard name', 'danger'); + console.error('Invalid Observability Dashboard name', 'danger'); return Promise.reject(); } diff --git a/public/components/index.tsx b/public/components/index.tsx index 440f2518a2..8191752974 100644 --- a/public/components/index.tsx +++ b/public/components/index.tsx @@ -9,7 +9,6 @@ import { QueryManager } from 'common/query_manager'; import { AppMountParameters, CoreStart } from '../../../../src/core/public'; import { AppPluginStartDependencies } from '../types'; import { App } from './app'; -import { ObservabilityAppServices } from '../../common/types/shared'; export const Observability = ( CoreStartProp: CoreStart, @@ -20,7 +19,6 @@ export const Observability = ( savedObjects: any, timestampUtils: any, queryManager: QueryManager, - services: ObservabilityAppServices, startPage: string ) => { ReactDOM.render( @@ -33,7 +31,6 @@ export const Observability = ( timestampUtils={timestampUtils} queryManager={queryManager} startPage={startPage} - services={services} />, AppMountParametersProp.element ); diff --git a/public/framework/core_refs.ts b/public/framework/core_refs.ts index e9a3e0e604..dd2367f19a 100644 --- a/public/framework/core_refs.ts +++ b/public/framework/core_refs.ts @@ -9,7 +9,7 @@ * GitHub history for details. */ -import { HttpStart } from '../../../../src/core/public'; +import { HttpStart, IToasts } from '../../../../src/core/public'; import { SavedObjectsClientContract } from '../../../../src/core/public'; import PPLService from '../services/requests/ppl'; @@ -19,6 +19,7 @@ class CoreRefs { public http?: HttpStart; public savedObjectsClient?: SavedObjectsClientContract; public pplService?: PPLService; + public toasts?: IToasts; private constructor() { // ... } diff --git a/public/plugin.ts b/public/plugin.ts index 06a9946c76..0a94c6273c 100644 --- a/public/plugin.ts +++ b/public/plugin.ts @@ -34,6 +34,7 @@ import { observabilityLogsID, observabilityLogsTitle, observabilityLogsPluginOrder, + observabilityPluginOrder, } from '../common/constants/shared'; import { QueryManager } from '../common/query_manager'; import { VISUALIZATION_SAVED_OBJECT } from '../common/types/observability_saved_object_attributes'; @@ -45,16 +46,9 @@ import { } from '../common/utils'; import { convertLegacyNotebooksUrl } from './components/notebooks/components/helpers/legacy_route_helpers'; import { convertLegacyTraceAnalyticsUrl } from './components/trace_analytics/components/common/legacy_route_helpers'; -// import { uiSettingsService } from '../common/utils'; -// import { QueryManager } from '../common/query_manager'; -import { DashboardSetup } from '../../../src/plugins/dashboard/public'; import { SavedObject } from '../../../src/core/public'; import { coreRefs } from './framework/core_refs'; -// export class ObservabilityPlugin implements Plugin { -// constructor(private initializerContext: PluginInitializerContext) {} - -// public setup(core: CoreSetup, { dashboard }: { dashboard: DashboardSetup }): {} { import { OBSERVABILITY_EMBEDDABLE, OBSERVABILITY_EMBEDDABLE_DESCRIPTION, @@ -68,18 +62,16 @@ import DSLService from './services/requests/dsl'; import PPLService from './services/requests/ppl'; import SavedObjects from './services/saved_objects/event_analytics/saved_objects'; import TimestampUtils from './services/timestamp/timestamp'; -import { observabilityID } from '../common/constants/shared'; import { AppPluginStartDependencies, ObservabilitySetup, ObservabilityStart, SetupDependencies, } from './types'; -import { ObservabilityAppServices } from '../common/types/shared'; export class ObservabilityPlugin implements - Plugin { + Plugin { public setup( core: CoreSetup, setupDeps: SetupDependencies @@ -103,10 +95,6 @@ export class ObservabilityPlugin window.location.assign(convertLegacyTraceAnalyticsUrl(window.location)); } - // // redirect legacy notebooks URL to current URL under observability - // if (window.location.pathname.includes('application_analytics')) { - // window.location.assign(convertLegacyAppAnalyticsUrl(window.location)); - // } const BASE_URL = core.http.basePath.prepend('/app/observability-dashboards#'); setupDeps.dashboard.registerDashboardProvider({ appId: 'observability-panel', @@ -125,7 +113,7 @@ export class ObservabilityPlugin label: i18n.translate('core.ui.observabilityNavList.label', { defaultMessage: 'Observability', }), - order: 1500, + order: observabilityPluginOrder, }, }); @@ -136,10 +124,6 @@ export class ObservabilityPlugin const savedObjects = new SavedObjects(coreStart.http); const timestampUtils = new TimestampUtils(dslService, pplService); - const services: ObservabilityAppServices = { - toasts: coreStart.notifications.toasts, - }; - return Observability( coreStart, depsStart as AppPluginStartDependencies, @@ -149,7 +133,6 @@ export class ObservabilityPlugin savedObjects, timestampUtils, qm, - services, startPage ); }; @@ -246,6 +229,7 @@ export class ObservabilityPlugin coreRefs.http = core.http; coreRefs.savedObjectsClient = core.savedObjects.client; coreRefs.pplService = pplService; + coreRefs.toasts = core.notifications.toasts; return {}; } From 1a1edebc059cc8ebe3310e8d93f247a93750e174 Mon Sep 17 00:00:00 2001 From: Shenoy Pratik Date: Thu, 27 Apr 2023 00:00:56 -0700 Subject: [PATCH 05/18] update tests Signed-off-by: Shenoy Pratik --- .../custom_panel_view.test.tsx.snap | 3504 ++++++++--------- .../__tests__/custom_panel_view.test.tsx | 65 +- test/setup.jest.ts | 1 + 3 files changed, 1768 insertions(+), 1802 deletions(-) diff --git a/public/components/custom_panels/__tests__/__snapshots__/custom_panel_view.test.tsx.snap b/public/components/custom_panels/__tests__/__snapshots__/custom_panel_view.test.tsx.snap index 226fddb62b..05379e0bcd 100644 --- a/public/components/custom_panels/__tests__/__snapshots__/custom_panel_view.test.tsx.snap +++ b/public/components/custom_panels/__tests__/__snapshots__/custom_panel_view.test.tsx.snap @@ -2,589 +2,707 @@ exports[`Panels View Component renders panel view container with visualizations 1`] = ` - - -
- +
+ +
-
- -
- -
- -
+
+ +
+ - + + +
-

- - -
- -
- -
- - Created on - Invalid date -
- - -
+ +
+
+ Created on + Invalid date +

+ + +
+ - -
- -
+
+ - - - - - -
- - + + + + +
+
+ +
-
+ Dashboard Actions + + } + closePopover={[Function]} + display="inlineBlock" + hasArrow={true} + isOpen={false} + ownFocus={true} + panelPaddingSize="none" + withTitle={true} + > +
+
+ + + + + +
+
+ +
+ + +
+ - Dashboard Actions + Add visualization } closePopover={[Function]} display="inlineBlock" hasArrow={true} + id="addVisualizationContextMenu" isOpen={false} ownFocus={true} panelPaddingSize="none" - withTitle={true} >
-
-
- -
- - - Add visualization - - } - closePopover={[Function]} - display="inlineBlock" - hasArrow={true} - id="addVisualizationContextMenu" - isOpen={false} - ownFocus={true} - panelPaddingSize="none" - > -
-
- - - - - -
-
-
-
-
-
-
- -
- -
- - -
+
+ +
+ +
+ + + + +
+ - -
- -
- +
+ - PPL - - } - baseQuery="source = " - dslService={ - DSLService { - "fetch": [Function], - "fetchFields": [Function], - "fetchIndices": [Function], - "http": [MockFunction], } + > + PPL + + } + baseQuery="source = " + dslService={ + DSLService { + "fetch": [Function], + "fetchFields": [Function], + "fetchIndices": [Function], + "http": [MockFunction], } - getSuggestions={[Function]} - handleQueryChange={[Function]} - handleQuerySearch={[Function]} - isDisabled={true} - key="autocomplete-search-bar" - onItemSelect={[Function]} - placeholder="Use PPL 'where' clauses to add filters on all visualizations [where Carrier = 'OpenSearch-Air']" - possibleCommands={ - Array [ - Object { - "label": "where", - }, - ] - } - query="" - tabId="panels-filter" - tempQuery="" + } + getSuggestions={[Function]} + handleQueryChange={[Function]} + handleQuerySearch={[Function]} + isDisabled={true} + key="autocomplete-search-bar" + onItemSelect={[Function]} + placeholder="Use PPL 'where' clauses to add filters on all visualizations [where Carrier = 'OpenSearch-Air']" + possibleCommands={ + Array [ + Object { + "label": "where", + }, + ] + } + query="" + tabId="panels-filter" + tempQuery="" + > +
-
+ PPL + + } + aria-autocomplete="both" aria-labelledby="autocomplete-4-label" - className="aa-Autocomplete" - id="autocomplete-root" - role="combobox" + autoCapitalize="off" + autoComplete="off" + autoCorrect="off" + autoFocus={false} + data-test-subj="searchAutocompleteTextArea" + disabled={true} + enterKeyHint="search" + fullWidth={true} + id="autocomplete-textarea" + maxLength={512} + onBlur={[Function]} + onChange={[Function]} + onClick={[Function]} + onFocus={[Function]} + onKeyDown={[Function]} + placeholder="Use PPL 'where' clauses to add filters on all visualizations [where Carrier = 'OpenSearch-Air']" + spellCheck="false" + type="search" + value="" > - } - aria-autocomplete="both" - aria-labelledby="autocomplete-4-label" - autoCapitalize="off" - autoComplete="off" - autoCorrect="off" - autoFocus={false} - data-test-subj="searchAutocompleteTextArea" - disabled={true} - enterKeyHint="search" fullWidth={true} - id="autocomplete-textarea" - maxLength={512} - onBlur={[Function]} - onChange={[Function]} - onClick={[Function]} - onFocus={[Function]} - onKeyDown={[Function]} - placeholder="Use PPL 'where' clauses to add filters on all visualizations [where Carrier = 'OpenSearch-Air']" - spellCheck="false" - type="search" - value="" + inputId="autocomplete-textarea" > - - PPL - - } - fullWidth={true} - inputId="autocomplete-textarea" +
-
- - - - -
- + + + +
+ + - -
-
-
-
- -
- - + +
+ + +
+ +
+ + +
-
- - -
- -
- - } + +
+ + } + > +
-
- + + } - dateFormat="" - end="now" - isAutoRefreshOnly={false} - isDisabled={false} - isPaused={true} - key="0/.0" - recentlyUsedRanges={Array []} - refreshInterval={0} - start="now-30m" + closePopover={[Function]} + display="inlineBlock" + hasArrow={true} + isOpen={false} + ownFocus={true} + panelPaddingSize="m" > - +
- - - } - closePopover={[Function]} - display="inlineBlock" - hasArrow={true} - isOpen={false} - ownFocus={true} - panelPaddingSize="m" - > -
-
- - - -
+ + + +
- - -
+ + +
+ } + iconType={false} + isCustom={true} + startDateControl={
} > - } - iconType={false} - isCustom={true} - startDateControl={
} +
-
- -
- - -
+ Show dates + + + +
+
+
- -
- - + +
+ + +
-
- - - - - - - - - - - -
- -
- - -
- -
- - -
- - -
- -
- - + + + + + + + +
+ +
+ + +
+ +
+ + +
+ + +
+ +
+ + +
-
- -
+ Start by adding your first visualization + + -

- Start by adding your first visualization -

- -
- - + + +
-
- -
- Use PPL Queries to fetch & filter observability data and create visualizations -
-
-
- -
- -
- - -
- - + +
+ +
+
+
+ + +
+ + +
-
- -
- + Add visualization + + } + closePopover={[Function]} + display="inlineBlock" + hasArrow={true} + id="addVisualizationContextMenu" + isOpen={false} + ownFocus={true} + panelPaddingSize="none" > - +
+ - Add visualization - - } - closePopover={[Function]} - display="inlineBlock" - hasArrow={true} - id="addVisualizationContextMenu" - isOpen={false} - ownFocus={true} - panelPaddingSize="none" - > -
-
- - - - - -
+ + + + +
- - -
- -
- - -
- -
- - + + +
+ +
+
+ +
+ +
+ + + - - - -
- - - - -
- -
- -
- -
- - + } + /> + + + + +
+ +
+ +
+ +
+ `; diff --git a/public/components/custom_panels/__tests__/custom_panel_view.test.tsx b/public/components/custom_panels/__tests__/custom_panel_view.test.tsx index 5a5da64e15..d4eb276e66 100644 --- a/public/components/custom_panels/__tests__/custom_panel_view.test.tsx +++ b/public/components/custom_panels/__tests__/custom_panel_view.test.tsx @@ -24,7 +24,6 @@ import { applyMiddleware, createStore } from 'redux'; import { rootReducer } from '../../../framework/redux/reducers'; import thunk from 'redux-thunk'; import { Provider } from 'react-redux'; -import { OpenSearchDashboardsContextProvider } from '../../../../../../src/plugins/opensearch_dashboards_react/public'; describe('Panels View Component', () => { configure({ adapter: new Adapter() }); @@ -74,26 +73,6 @@ describe('Panels View Component', () => { page="operationalPanels" /> - - - ); wrapper.update(); @@ -134,31 +113,27 @@ describe('Panels View Component', () => { window.location.assign(`#/event_analytics/explorer/${savedVisId}`); }; - const services = { toasts: { addDanger: (t) => {} } } - const wrapper = mount( - - - - - + + + ); wrapper.update(); diff --git a/test/setup.jest.ts b/test/setup.jest.ts index 6bbbefb80e..3a6397fe42 100644 --- a/test/setup.jest.ts +++ b/test/setup.jest.ts @@ -60,3 +60,4 @@ setOSDHttp(coreStartMock.http); setOSDSavedObjectsClient(coreStartMock.savedObjects.client); coreRefs.http = coreStartMock.http; coreRefs.savedObjectsClient = coreStartMock.savedObjects.client; +coreRefs.toasts = coreStartMock.notifications.toasts; From 485b8a8e55ab5a8c5854fe2218cf2cecaadfc462 Mon Sep 17 00:00:00 2001 From: Shenoy Pratik Date: Thu, 27 Apr 2023 00:13:06 -0700 Subject: [PATCH 06/18] remove unused redux slice Signed-off-by: Shenoy Pratik --- .../framework/redux/reducers/toast_slice.ts | 40 ------------------- 1 file changed, 40 deletions(-) delete mode 100644 public/framework/redux/reducers/toast_slice.ts diff --git a/public/framework/redux/reducers/toast_slice.ts b/public/framework/redux/reducers/toast_slice.ts deleted file mode 100644 index de856535f7..0000000000 --- a/public/framework/redux/reducers/toast_slice.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { createSlice } from '@reduxjs/toolkit'; - -const toastSlice = createSlice({ - name: 'toast', - initialState: { toasts: [], toastRightSide: true }, - reducers: { - appendToast: (state, action) => { - state.toasts = [...state.toasts, action.payload]; - }, - - setToastRightSide: (state, action) => (state.toastRightSide = action.payload), - - dismissToast: (state, action) => { - state.toasts = state.toasts.filter((t) => t.id !== action.payload.id); - }, - - resetToasts: (state, action) => { - state.toasts = []; - state.toastRightSide = true; - }, - }, -}); - -export const toastReducer = toastSlice.reducer; - -export const { dismissToast, resetToasts } = toastSlice.actions; -const { appendToast, setToastRightSide } = toastSlice.actions; - -export const selectToasts = (rootState) => rootState.toast.toasts; - -export const selectToastRightSide = (rootState) => rootState.toast.toastRightSide; - -export const addToast = (title, color?, textChild?, side?) => (dispatch, getState) => { - const newToast = { id: new Date().toISOString(), title, textChild, color }; - dispatch(appendToast(newToast)); - - if (side) { - dispatch(setToastRightSide(side === 'left')); - } -}; From 9c6173b88fa9abbd959be99594fdcaa0bd4b3485 Mon Sep 17 00:00:00 2001 From: Shenoy Pratik Date: Thu, 27 Apr 2023 00:19:56 -0700 Subject: [PATCH 07/18] revert cypress changes Signed-off-by: Shenoy Pratik --- .cypress/integration/3_panels.spec.ts | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/.cypress/integration/3_panels.spec.ts b/.cypress/integration/3_panels.spec.ts index df58e2a4e8..87a4ed902b 100644 --- a/.cypress/integration/3_panels.spec.ts +++ b/.cypress/integration/3_panels.spec.ts @@ -74,7 +74,7 @@ describe('Creating visualizations', () => { }); }); -describe.only('Testing panels table', () => { +describe('Testing panels table', () => { beforeEach(() => { eraseTestPanels(); moveToPanelHome(); @@ -597,7 +597,6 @@ const eraseLegacyPanels = () => { 'osd-xsrf': true, }, }).then((response) => { - console.log("legacy panels to erase", response.body) response.body.panels.map((panel) => { cy.request({ method: 'DELETE', @@ -627,7 +626,6 @@ const eraseSavedObjectPaenls = () => { }, }) .then((response) => { - console.log("saved objects to erase", response.body) response.body.saved_objects.map((soPanel) => { cy.request({ method: 'DELETE', @@ -637,9 +635,6 @@ const eraseSavedObjectPaenls = () => { 'content-type': 'application/json;charset=UTF-8', 'osd-xsrf': true, }, - }).then((response) => { - const deletedId = response; - console.log('erased SO Panel', response) }); }); }); @@ -649,8 +644,7 @@ const eraseTestPanels = () => { eraseLegacyPanels(); eraseSavedObjectPaenls(); }; -const uuidRx = - /[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}/; +const uuidRx = /[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}/; const clickCreatePanelButton = () => cy.get('a[data-test-subj="customPanels__createNewPanels"]').click(); @@ -717,10 +711,9 @@ const openActionsDropdown = () => { }; const selectThePanel = () => { - // cy.get('.euiCheckbox__input[title="Select this row"]').then(() => { - cy.get('.euiCheckbox__input[title="Select this row"]').check({ force: true }); - cy.get('.euiTableRow-isSelected').should('exist') - // }); + cy.get('.euiCheckbox__input[title="Select this row"]').then(() => { + cy.get('.euiCheckbox__input[title="Select this row"]').check({ force: true }); + }); }; const expectToastWith = (title) => { From 8d9d08bc2d3701d80bf1b516245b3d0dc4ce00d3 Mon Sep 17 00:00:00 2001 From: Shenoy Pratik Date: Thu, 27 Apr 2023 00:49:53 -0700 Subject: [PATCH 08/18] add toasts to SOflyout Signed-off-by: Shenoy Pratik --- .../custom_panels/custom_panel_view_so.tsx | 10 +-- .../visualization_flyout_so.tsx | 66 ++++++------------- 2 files changed, 22 insertions(+), 54 deletions(-) diff --git a/public/components/custom_panels/custom_panel_view_so.tsx b/public/components/custom_panels/custom_panel_view_so.tsx index 801a91db59..7f9691bb58 100644 --- a/public/components/custom_panels/custom_panel_view_so.tsx +++ b/public/components/custom_panels/custom_panel_view_so.tsx @@ -66,6 +66,7 @@ import { setPanelSt, updatePanel, } from './redux/panel_slice'; +import { useToast } from '../common/toast'; /* * "CustomPanelsView" module used to render an Observability Dashboard @@ -82,7 +83,6 @@ import { * renameCustomPanel: Rename function for the panel * deleteCustomPanel: Delete function for the panel * cloneCustomPanel: Clone function for the panel - * setToast: create Toast function * onEditClick: Edit function for visualization * startTime: Starting time * endTime: Ending time @@ -100,12 +100,6 @@ interface CustomPanelViewProps { chrome: CoreStart['chrome']; parentBreadcrumbs: EuiBreadcrumb[]; cloneCustomPanel: (clonedCustomPanelName: string, clonedCustomPanelId: string) => Promise; - setToast: ( - title: string, - color?: string, - text?: React.ReactChild | undefined, - side?: string | undefined - ) => void; onEditClick: (savedVisualizationId: string) => any; childBreadcrumbs?: EuiBreadcrumb[]; appId?: string; @@ -127,12 +121,12 @@ export const CustomPanelViewSO = (props: CustomPanelViewProps) => { childBreadcrumbs, updateAvailabilityVizId, cloneCustomPanel, - setToast, onEditClick, onAddClick, } = props; const dispatch = useDispatch(); + const { setToast } = useToast(); const panel = useSelector(selectPanel); const [loading, setLoading] = useState(true); diff --git a/public/components/custom_panels/panel_modules/visualization_flyout/visualization_flyout_so.tsx b/public/components/custom_panels/panel_modules/visualization_flyout/visualization_flyout_so.tsx index 96a9b0571c..c606fb567d 100644 --- a/public/components/custom_panels/panel_modules/visualization_flyout/visualization_flyout_so.tsx +++ b/public/components/custom_panels/panel_modules/visualization_flyout/visualization_flyout_so.tsx @@ -57,6 +57,7 @@ import { } from '../../helpers/utils'; import { replaceVizInPanel, selectPanel } from '../../redux/panel_slice'; import './visualization_flyout.scss'; +import { useToast } from '../../../common/toast'; /* * VisaulizationFlyoutSO - This module create a flyout to add visualization for SavedObjects custom Panels @@ -66,7 +67,6 @@ import './visualization_flyout.scss'; * closeFlyout: function to close the flyout * start: start time in date filter * end: end time in date filter - * setToast: function to set toast in the panel * savedObjects: savedObjects core service * pplService: ppl requestor service * setPanelVisualizations: function set the visualization list in panel @@ -81,12 +81,6 @@ interface VisualizationFlyoutSOProps { start: ShortDate; end: ShortDate; http: CoreStart['http']; - setToast: ( - title: string, - color?: string, - text?: React.ReactChild | undefined, - side?: string | undefined - ) => void; savedObjects: CoreStart['savedObjects']; pplService: PPLService; setPanelVisualizations: React.Dispatch>; @@ -104,7 +98,6 @@ export const VisaulizationFlyoutSO = ({ start, end, http, - setToast, savedObjects, pplService, setPanelVisualizations, @@ -113,6 +106,7 @@ export const VisaulizationFlyoutSO = ({ addVisualizationPanel, }: VisualizationFlyoutSOProps) => { const dispatch = useDispatch(); + const { setToast } = useToast(); const panel = useSelector(selectPanel); @@ -166,12 +160,12 @@ export const VisaulizationFlyoutSO = ({ }; const isInputValid = () => { - if (!isDateValid(convertDateTime(start), convertDateTime(end, false), setToast, 'left')) { + if (!isDateValid(convertDateTime(start), convertDateTime(end, false), setToast)) { return false; } if (selectValue === '') { - setToast('Please make a valid selection', 'danger', undefined, 'left'); + setToast('Please make a valid selection', 'danger', undefined); return false; } @@ -182,43 +176,23 @@ export const VisaulizationFlyoutSO = ({ if (!isInputValid()) return; if (isFlyoutReplacement) { - // http - // .post(`${CUSTOM_PANELS_API_PREFIX}/visualizations/replace`, { - // body: JSON.stringify({ - // panelId, - // savedVisualizationId: selectValue, - // oldVisualizationId: replaceVisualizationId, - // }), - // }) - // .then(async (res) => { - // setPanelVisualizations(res.visualizations); - // setToast(`Visualization ${newVisualizationTitle} successfully added!`, 'success'); - // }) - // .catch((err) => { - // setToast(`Error in adding ${newVisualizationTitle} visualization to the panel`, 'danger'); - // console.error(err); - // }); - dispatch(replaceVizInPanel(panel, replaceVisualizationId, selectValue)); + try { + dispatch(replaceVizInPanel(panel, replaceVisualizationId, selectValue)); + setToast(`Visualization ${newVisualizationTitle} successfully added!`, 'success'); + } catch (err) { + setToast(`Error in adding ${newVisualizationTitle} visualization to the panel`, 'danger'); + console.error(err); + } } else { - const visualizationsWithNewPanel = addVisualizationPanel({ - savedVisualizationId: selectValue, - }); - - // http - // .post(`${CUSTOM_PANELS_API_PREFIX}/visualizations`, { - // body: JSON.stringify({ - // panelId, - // savedVisualizationId: selectValue, - // }), - // }) - // .then(async (res) => { - // setPanelVisualizations(res.visualizations); - // setToast(`Visualization ${newVisualizationTitle} successfully added!`, 'success'); - // }) - // .catch((err) => { - // setToast(`Error in adding ${newVisualizationTitle} visualization to the panel`, 'danger'); - // console.error(err); - // }); + try { + const visualizationsWithNewPanel = addVisualizationPanel({ + savedVisualizationId: selectValue, + }); + setToast(`Visualization ${newVisualizationTitle} successfully added!`, 'success'); + } catch (err) { + setToast(`Error in adding ${newVisualizationTitle} visualization to the panel`, 'danger'); + console.error(err); + } } closeFlyout(); }; From 7ffe65572a2e0368a58a2a514f4d179657009596 Mon Sep 17 00:00:00 2001 From: Derek Ho Date: Thu, 27 Apr 2023 10:07:27 -0400 Subject: [PATCH 09/18] fix messaging for multiple delete Signed-off-by: Derek Ho --- public/components/custom_panels/custom_panel_table.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/components/custom_panels/custom_panel_table.tsx b/public/components/custom_panels/custom_panel_table.tsx index 76c8662ca1..8ee50b6be3 100644 --- a/public/components/custom_panels/custom_panel_table.tsx +++ b/public/components/custom_panels/custom_panel_table.tsx @@ -169,7 +169,7 @@ export const CustomPanelTable = ({ }; const onDelete = async () => { - const toastMessage = `Observability Dashboards ${ + const toastMessage = `Observability Dashboard${ selectedCustomPanels.length > 1 ? 's' : ' ' + selectedCustomPanels[0].title } successfully deleted!`; From 0b825888433f36cafb673df5a54444b70db9632d Mon Sep 17 00:00:00 2001 From: Derek Ho Date: Thu, 27 Apr 2023 10:51:06 -0400 Subject: [PATCH 10/18] fix up toast and error handling for create and delete flows Signed-off-by: Derek Ho --- .../custom_panels/custom_panel_table.tsx | 30 ++++-------- .../custom_panels/custom_panel_view_so.tsx | 33 +++++-------- .../custom_panels/redux/panel_slice.ts | 48 +++++++++++++------ 3 files changed, 54 insertions(+), 57 deletions(-) diff --git a/public/components/custom_panels/custom_panel_table.tsx b/public/components/custom_panels/custom_panel_table.tsx index 8ee50b6be3..662f93d932 100644 --- a/public/components/custom_panels/custom_panel_table.tsx +++ b/public/components/custom_panels/custom_panel_table.tsx @@ -124,13 +124,17 @@ export const CustomPanelTable = ({ setToast('Invalid Dashboard name', 'danger'); } else { const newPanel = newPanelTemplate(newCustomPanelName); - dispatch(createPanel(newPanel)); + dispatch(createPanel(newPanel, setToast)); } closeModal(); }; const onRename = async (newCustomPanelName: string) => { - dispatch(renameCustomPanel(newCustomPanelName, selectedCustomPanels[0].id)); + if (!isNameValid(newCustomPanelName)) { + setToast('Invalid Dashboard name', 'danger'); + } else { + dispatch(renameCustomPanel(newCustomPanelName, selectedCustomPanels[0].id)); + } closeModal(); }; @@ -154,12 +158,10 @@ export const CustomPanelTable = ({ title: newName, }; - await dispatch(createPanel(newPanel)); - - setToast(`Observability Dashboard "${newName}" successfully created!`); + dispatch(createPanel(newPanel, setToast)); } catch (err) { setToast( - 'Error cloning Operational Panel, please make sure you have the correct permission.', + 'Error cloning Observability Dashboard, please make sure you have the correct permission.', 'danger' ); console.error(err); @@ -169,21 +171,7 @@ export const CustomPanelTable = ({ }; const onDelete = async () => { - const toastMessage = `Observability Dashboard${ - selectedCustomPanels.length > 1 ? 's' : ' ' + selectedCustomPanels[0].title - } successfully deleted!`; - - try { - await dispatch(deletePanels(selectedCustomPanels)); - setToast(toastMessage); - } catch (err) { - setToast( - 'Error deleting Operational Panels, please make sure you have the correct permission.', - 'danger' - ); - console.error(err.body?.message || err); - } - + dispatch(deletePanels(selectedCustomPanels, setToast)); closeModal(); }; diff --git a/public/components/custom_panels/custom_panel_view_so.tsx b/public/components/custom_panels/custom_panel_view_so.tsx index 7f9691bb58..ebdbb3b42e 100644 --- a/public/components/custom_panels/custom_panel_view_so.tsx +++ b/public/components/custom_panels/custom_panel_view_so.tsx @@ -55,15 +55,10 @@ import { PanelGridSO } from './panel_modules/panel_grid/panel_grid_so'; import { VisaulizationFlyoutSO } from './panel_modules/visualization_flyout/visualization_flyout_so'; import { clonePanel, - createPanel, deletePanels, fetchPanel, - newPanelTemplate, selectPanel, setPanel, - setPanelEt, - setPanelId, - setPanelSt, updatePanel, } from './redux/panel_slice'; import { useToast } from '../common/toast'; @@ -189,20 +184,10 @@ export const CustomPanelViewSO = (props: CustomPanelViewProps) => { }; const onDelete = async () => { - const toastMessage = `Observability Dashboard ${panel.title} successfully deleted!"`; - try { - await dispatch(deletePanels([panel])); - - setTimeout(() => { - window.location.assign(`${last(parentBreadcrumbs)!.href}`); - }, 1000); - } catch (err) { - setToast( - 'Error deleting Operational Panels, please make sure you have the correct permission.', - 'danger' - ); - console.error(err.body?.message || err); - } + dispatch(deletePanels([panel], setToast)); + setTimeout(() => { + window.location.assign(`${last(parentBreadcrumbs)!.href}`); + }, 1000); closeModal(); }; @@ -222,10 +207,10 @@ export const CustomPanelViewSO = (props: CustomPanelViewProps) => { const newPanel = { ...panel, title: newCustomPanelName }; try { dispatch(updatePanel(newPanel)); - setToast(`Operational Panel successfully renamed into "${newCustomPanelName}"`); + setToast(`Observability Dashboard successfully renamed into "${newCustomPanelName}"`); } catch (err) { setToast( - 'Error renaming Operational Panel, please make sure you have the correct permission.', + 'Error renaming Observability Dashboard, please make sure you have the correct permission.', 'danger' ); console.error(err.body.message); @@ -250,7 +235,11 @@ export const CustomPanelViewSO = (props: CustomPanelViewProps) => { }; const onClone = async (newCustomPanelName: string) => { - dispatch(clonePanel(panel, newCustomPanelName)); + try { + await dispatch(clonePanel(panel, newCustomPanelName)); + } catch (err) { + setToast('Error while attempting to Duplicate this Dashboard.', 'danger'); + } closeModal(); }; diff --git a/public/components/custom_panels/redux/panel_slice.ts b/public/components/custom_panels/redux/panel_slice.ts index b9fae518f4..c9713f5829 100644 --- a/public/components/custom_panels/redux/panel_slice.ts +++ b/public/components/custom_panels/redux/panel_slice.ts @@ -211,23 +211,43 @@ const deleteLegacyPanels = (customPanelIdList: string[]) => { return coreRefs.http!.delete(`${CUSTOM_PANELS_API_PREFIX}/panelList/` + concatList); }; -export const deletePanels = (panelsToDelete: CustomPanelType[]) => async (dispatch, getState) => { - const ids = panelsToDelete.map((p) => p.id); - await Promise.all([deleteLegacyPanels(ids), deletePanelSO(ids)]); +export const deletePanels = (panelsToDelete: CustomPanelType[], setToast) => async (dispatch, getState) => { + const toastMessage = `Observability Dashboard${ + panelsToDelete.length > 1 ? 's' : ' ' + panelsToDelete[0].title + } successfully deleted!`; + try { + const ids = panelsToDelete.map((p) => p.id); + await Promise.all([deleteLegacyPanels(ids), deletePanelSO(ids)]); - const panelList: CustomPanelType[] = getState().customPanel.panelList.filter( - (p) => !ids.includes(p.id) - ); - dispatch(setPanelList(panelList)); + const panelList: CustomPanelType[] = getState().customPanel.panelList.filter( + (p) => !ids.includes(p.id) + ); + dispatch(setPanelList(panelList)); + setToast(toastMessage); + } catch (e) { + setToast( + 'Error deleting Observability Dashboards, please make sure you have the correct permission.', + 'danger' + ); + console.error(e); + } }; -export const createPanel = (panel) => async (dispatch, getState) => { - const newSOPanel = await savedObjectPanelsClient.create(panel); - const newPanel = savedObjectToCustomPanel(newSOPanel); - const panelList = getState().customPanel.panelList; - dispatch(setPanelList([...panelList, newPanel])); - - window.location.replace(`#/${newPanel.id}`); +export const createPanel = (panel, setToast) => async (dispatch, getState) => { + try { + const newSOPanel = await savedObjectPanelsClient.create(panel); + const newPanel = savedObjectToCustomPanel(newSOPanel); + const panelList = getState().customPanel.panelList; + dispatch(setPanelList([...panelList, newPanel])); + setToast(`Observability Dashboard "${newPanel.title}" successfully created!`); + window.location.replace(`#/${newPanel.id}`); + } catch (e) { + setToast( + 'Error occurred while creating Observability Dashboard, please make sure you have the correct permission.', + 'danger' + ); + console.error(e); + } }; export const clonePanel = (panel, newPanelName) => async (dispatch, getState) => { From d06d5463711a51a63898bc1fd8149a6a05ccdf7e Mon Sep 17 00:00:00 2001 From: Derek Ho Date: Thu, 27 Apr 2023 10:59:15 -0400 Subject: [PATCH 11/18] fix up clone Signed-off-by: Derek Ho --- .../custom_panels/custom_panel_view.tsx | 19 +-------- .../custom_panels/custom_panel_view_so.tsx | 6 +-- .../custom_panels/redux/panel_slice.ts | 40 +++++++++++-------- 3 files changed, 26 insertions(+), 39 deletions(-) diff --git a/public/components/custom_panels/custom_panel_view.tsx b/public/components/custom_panels/custom_panel_view.tsx index d45c5214b9..ca1940a0c6 100644 --- a/public/components/custom_panels/custom_panel_view.tsx +++ b/public/components/custom_panels/custom_panel_view.tsx @@ -314,24 +314,7 @@ export const CustomPanelView = (props: CustomPanelViewProps) => { }; const onClone = async (newCustomPanelName: string) => { - try { - await dispatch(clonePanel(panel, newCustomPanelName)); - } catch (err) { - setToast('Error while attempting to Duplicate this Dashboard.', 'danger'); - } - - // const newPanel = { - // ...panel, - // title: newCustomPanelName, - // dateCreated: new Date().getTime(), - // dateModified: new Date().getTime(), - // } as PanelType; - // const newSOPanel = await coreRefs.savedObjectsClient!.create( - // CUSTOM_PANELS_SAVED_OBJECT_TYPE, - // newPanel - // ); - // - // window.location.assign(`${last(parentBreadcrumbs)!.href}${newSOPanel.id}`); + dispatch(clonePanel(panel, newCustomPanelName, setToast)); closeModal(); }; diff --git a/public/components/custom_panels/custom_panel_view_so.tsx b/public/components/custom_panels/custom_panel_view_so.tsx index ebdbb3b42e..6d62911554 100644 --- a/public/components/custom_panels/custom_panel_view_so.tsx +++ b/public/components/custom_panels/custom_panel_view_so.tsx @@ -235,11 +235,7 @@ export const CustomPanelViewSO = (props: CustomPanelViewProps) => { }; const onClone = async (newCustomPanelName: string) => { - try { - await dispatch(clonePanel(panel, newCustomPanelName)); - } catch (err) { - setToast('Error while attempting to Duplicate this Dashboard.', 'danger'); - } + dispatch(clonePanel(panel, newCustomPanelName, setToast)); closeModal(); }; diff --git a/public/components/custom_panels/redux/panel_slice.ts b/public/components/custom_panels/redux/panel_slice.ts index c9713f5829..0a4aa47c18 100644 --- a/public/components/custom_panels/redux/panel_slice.ts +++ b/public/components/custom_panels/redux/panel_slice.ts @@ -250,22 +250,30 @@ export const createPanel = (panel, setToast) => async (dispatch, getState) => { } }; -export const clonePanel = (panel, newPanelName) => async (dispatch, getState) => { - const { id, ...panelCopy } = { - ...panel, - title: newPanelName, - dateCreated: new Date().getTime(), - dateModified: new Date().getTime(), - } as PanelType; - - const newSOPanel = await savedObjectPanelsClient.create(panelCopy); - - const newPanel = savedObjectToCustomPanel(newSOPanel); - const panelList = getState().customPanel.panelList; - dispatch(setPanelList([...panelList, newPanel])); - dispatch(setPanel(newPanel)); - - window.location.replace(`#/${newPanel.id}`); +export const clonePanel = (panel, newPanelName, setToast) => async (dispatch, getState) => { + try { + const { id, ...panelCopy } = { + ...panel, + title: newPanelName, + dateCreated: new Date().getTime(), + dateModified: new Date().getTime(), + } as PanelType; + + const newSOPanel = await savedObjectPanelsClient.create(panelCopy); + + const newPanel = savedObjectToCustomPanel(newSOPanel); + const panelList = getState().customPanel.panelList; + dispatch(setPanelList([...panelList, newPanel])); + dispatch(setPanel(newPanel)); + setToast(`Observability Dashboard "${newPanel.title}" successfully created!`); + window.location.replace(`#/${newPanel.id}`); + } catch (e) { + setToast( + 'Error cloning Observability Dashboard, please make sure you have the correct permission.', + 'danger' + ); + console.error(e); + } }; const saveRenamedPanel = async (id, name) => { From d45692f432a54deb2b698b626c01efe6c47166a1 Mon Sep 17 00:00:00 2001 From: Derek Ho Date: Thu, 27 Apr 2023 11:18:44 -0400 Subject: [PATCH 12/18] fix rename in table Signed-off-by: Derek Ho --- .../custom_panels/custom_panel_table.tsx | 2 +- .../custom_panels/custom_panel_view.tsx | 6 ++- .../custom_panels/custom_panel_view_so.tsx | 7 ++- .../custom_panels/redux/panel_slice.ts | 44 +++++++------------ 4 files changed, 28 insertions(+), 31 deletions(-) diff --git a/public/components/custom_panels/custom_panel_table.tsx b/public/components/custom_panels/custom_panel_table.tsx index 662f93d932..18b8a0ab98 100644 --- a/public/components/custom_panels/custom_panel_table.tsx +++ b/public/components/custom_panels/custom_panel_table.tsx @@ -133,7 +133,7 @@ export const CustomPanelTable = ({ if (!isNameValid(newCustomPanelName)) { setToast('Invalid Dashboard name', 'danger'); } else { - dispatch(renameCustomPanel(newCustomPanelName, selectedCustomPanels[0].id)); + dispatch(renameCustomPanel(newCustomPanelName, selectedCustomPanels[0].id, setToast)); } closeModal(); }; diff --git a/public/components/custom_panels/custom_panel_view.tsx b/public/components/custom_panels/custom_panel_view.tsx index ca1940a0c6..ab4657a76f 100644 --- a/public/components/custom_panels/custom_panel_view.tsx +++ b/public/components/custom_panels/custom_panel_view.tsx @@ -314,7 +314,11 @@ export const CustomPanelView = (props: CustomPanelViewProps) => { }; const onClone = async (newCustomPanelName: string) => { - dispatch(clonePanel(panel, newCustomPanelName, setToast)); + if (!isNameValid(newCustomPanelName)) { + setToast('Invalid Operational Panel name', 'danger'); + } else { + dispatch(clonePanel(panel, newCustomPanelName, setToast)); + } closeModal(); }; diff --git a/public/components/custom_panels/custom_panel_view_so.tsx b/public/components/custom_panels/custom_panel_view_so.tsx index 6d62911554..9945a0c714 100644 --- a/public/components/custom_panels/custom_panel_view_so.tsx +++ b/public/components/custom_panels/custom_panel_view_so.tsx @@ -47,6 +47,7 @@ import { getCustomModal } from './helpers/modal_containers'; import { convertDateTime, isDateValid, + isNameValid, isPPLFilterValid, prependRecentlyUsedRange, } from './helpers/utils'; @@ -235,7 +236,11 @@ export const CustomPanelViewSO = (props: CustomPanelViewProps) => { }; const onClone = async (newCustomPanelName: string) => { - dispatch(clonePanel(panel, newCustomPanelName, setToast)); + if (!isNameValid(newCustomPanelName)) { + setToast('Invalid Operational Panel name', 'danger'); + } else { + dispatch(clonePanel(panel, newCustomPanelName, setToast)); + } closeModal(); }; diff --git a/public/components/custom_panels/redux/panel_slice.ts b/public/components/custom_panels/redux/panel_slice.ts index 0a4aa47c18..401dbd4ae3 100644 --- a/public/components/custom_panels/redux/panel_slice.ts +++ b/public/components/custom_panels/redux/panel_slice.ts @@ -297,38 +297,26 @@ const saveRenamedPanelSO = async (id, name) => { }; // Renames an existing CustomPanel -export const renameCustomPanel = (editedCustomPanelName: string, id: string) => async ( +export const renameCustomPanel = (editedCustomPanelName: string, id: string, setToast) => async ( dispatch, getState ) => { - if (!isNameValid(editedCustomPanelName)) { - console.error('Invalid Observability Dashboard name', 'danger'); - return Promise.reject(); + try { + const panel = getState().customPanel.panelList.find((p) => p.id === id); + const updatedPanel = { ...panel, title: editedCustomPanelName }; + if (isUuid(updatedPanel.id)) await updateSavedObjectPanel(updatedPanel); + else await updateLegacyPanel(updatedPanel); + dispatch(setPanel(updatedPanel)); + const panelList = getState().customPanel.panelList.map((p) => (p.id === updatedPanel.id ? updatedPanel : p)); + dispatch(setPanelList(panelList)); + setToast(`Observability Dashboard successfully renamed into "${editedCustomPanelName}"`) + } catch (e) { + setToast( + 'Error renaming Observability Dashboard, please make sure you have the correct permission.', + 'danger' + ); + console.error(e); } - - const panel = getState().customPanel.panelList.find((p) => p.id === id); - const updatedPanel = { ...panel, title: editedCustomPanelName }; - dispatch(updatePanel(updatedPanel)); - - // try { - // // await savePanelFn(editedCustomPanelId, editedCustomPanelName); - - // // setcustomPanelData((prevCustomPanelData) => { - // // const newCustomPanelData = [...prevCustomPanelData]; - // // const renamedCustomPanel = newCustomPanelData.find( - // // (customPanel) => customPanel.id === editedCustomPanelId - // // ); - // // if (renamedCustomPanel) renamedCustomPanel.name = editedCustomPanelName; - // // return newCustomPanelData; - // // }); - // // setToast(`Observability Dashboard successfully renamed into "${editedCustomPanelName}"`); - // } catch (err) { - // console.log( - // 'Error renaming Observability Dashboard, please make sure you have the correct permission.', - // 'danger' - // ); - // console.error(err.body.message); - // } }; /* From 642f5e0c2ab9f6d821c8980a1bed0fd3a57c99a2 Mon Sep 17 00:00:00 2001 From: Derek Ho Date: Thu, 27 Apr 2023 11:25:51 -0400 Subject: [PATCH 13/18] fix rename in custom panel so view Signed-off-by: Derek Ho --- .../custom_panels/custom_panel_view_so.tsx | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/public/components/custom_panels/custom_panel_view_so.tsx b/public/components/custom_panels/custom_panel_view_so.tsx index 9945a0c714..9be7fefb98 100644 --- a/public/components/custom_panels/custom_panel_view_so.tsx +++ b/public/components/custom_panels/custom_panel_view_so.tsx @@ -58,6 +58,7 @@ import { clonePanel, deletePanels, fetchPanel, + renameCustomPanel, selectPanel, setPanel, updatePanel, @@ -205,16 +206,10 @@ export const CustomPanelViewSO = (props: CustomPanelViewProps) => { }; const onRename = async (newCustomPanelName: string) => { - const newPanel = { ...panel, title: newCustomPanelName }; - try { - dispatch(updatePanel(newPanel)); - setToast(`Observability Dashboard successfully renamed into "${newCustomPanelName}"`); - } catch (err) { - setToast( - 'Error renaming Observability Dashboard, please make sure you have the correct permission.', - 'danger' - ); - console.error(err.body.message); + if (!isNameValid(newCustomPanelName)) { + setToast('Invalid Dashboard name', 'danger'); + } else { + dispatch(renameCustomPanel(newCustomPanelName, panel.id, setToast)); } closeModal(); }; From a20c042ad8637eb7f040118f95c72e0b8338cdb1 Mon Sep 17 00:00:00 2001 From: Derek Ho Date: Thu, 27 Apr 2023 14:43:27 -0400 Subject: [PATCH 14/18] fix up panel toasts Signed-off-by: Derek Ho --- common/constants/custom_panels.ts | 2 +- .../custom_panels/custom_panel_table.tsx | 4 +- .../custom_panels/custom_panel_view.tsx | 2 +- .../custom_panels/custom_panel_view_so.tsx | 15 +++---- .../custom_panels/redux/panel_slice.ts | 43 ++++++++----------- 5 files changed, 28 insertions(+), 38 deletions(-) diff --git a/common/constants/custom_panels.ts b/common/constants/custom_panels.ts index 1062d63a29..007aa9c326 100644 --- a/common/constants/custom_panels.ts +++ b/common/constants/custom_panels.ts @@ -6,7 +6,7 @@ export const CUSTOM_PANELS_API_PREFIX = '/api/observability/operational_panels'; export const CUSTOM_PANELS_DOCUMENTATION_URL = 'https://opensearch.org/docs/latest/observability-plugin/operational-panels/'; -export const CREATE_PANEL_MESSAGE = 'Enter a name to describe the purpose of this custom panel.'; +export const CREATE_PANEL_MESSAGE = 'Enter a name to describe the purpose of this observability dashboard.'; export const CUSTOM_PANELS_SAVED_OBJECT_TYPE = 'observability-panel'; diff --git a/public/components/custom_panels/custom_panel_table.tsx b/public/components/custom_panels/custom_panel_table.tsx index 18b8a0ab98..9d2e5e7b33 100644 --- a/public/components/custom_panels/custom_panel_table.tsx +++ b/public/components/custom_panels/custom_panel_table.tsx @@ -133,7 +133,7 @@ export const CustomPanelTable = ({ if (!isNameValid(newCustomPanelName)) { setToast('Invalid Dashboard name', 'danger'); } else { - dispatch(renameCustomPanel(newCustomPanelName, selectedCustomPanels[0].id, setToast)); + dispatch(renameCustomPanel(newCustomPanelName, selectedCustomPanels[0].id)); } closeModal(); }; @@ -171,7 +171,7 @@ export const CustomPanelTable = ({ }; const onDelete = async () => { - dispatch(deletePanels(selectedCustomPanels, setToast)); + dispatch(deletePanels(selectedCustomPanels)); closeModal(); }; diff --git a/public/components/custom_panels/custom_panel_view.tsx b/public/components/custom_panels/custom_panel_view.tsx index ab4657a76f..fd1b35d12b 100644 --- a/public/components/custom_panels/custom_panel_view.tsx +++ b/public/components/custom_panels/custom_panel_view.tsx @@ -317,7 +317,7 @@ export const CustomPanelView = (props: CustomPanelViewProps) => { if (!isNameValid(newCustomPanelName)) { setToast('Invalid Operational Panel name', 'danger'); } else { - dispatch(clonePanel(panel, newCustomPanelName, setToast)); + dispatch(clonePanel(panel, newCustomPanelName)); } closeModal(); }; diff --git a/public/components/custom_panels/custom_panel_view_so.tsx b/public/components/custom_panels/custom_panel_view_so.tsx index 9be7fefb98..b457a7f2f0 100644 --- a/public/components/custom_panels/custom_panel_view_so.tsx +++ b/public/components/custom_panels/custom_panel_view_so.tsx @@ -179,14 +179,14 @@ export const CustomPanelViewSO = (props: CustomPanelViewProps) => { timeProps.end, recentlyUsedRanges ); - dispatch(updatePanel({ ...panel, timeRange: { from: timeProps.start, to: timeProps.end } })); + dispatch(updatePanel({ ...panel, timeRange: { from: timeProps.start, to: timeProps.end } }, '', '')); setRecentlyUsedRanges(updatedRanges.slice(0, 9)); onRefreshFilters(timeProps.start, timeProps.end); }; const onDelete = async () => { - dispatch(deletePanels([panel], setToast)); + dispatch(deletePanels([panel])); setTimeout(() => { window.location.assign(`${last(parentBreadcrumbs)!.href}`); }, 1000); @@ -209,7 +209,7 @@ export const CustomPanelViewSO = (props: CustomPanelViewProps) => { if (!isNameValid(newCustomPanelName)) { setToast('Invalid Dashboard name', 'danger'); } else { - dispatch(renameCustomPanel(newCustomPanelName, panel.id, setToast)); + dispatch(renameCustomPanel(newCustomPanelName, panel.id)); } closeModal(); }; @@ -234,7 +234,7 @@ export const CustomPanelViewSO = (props: CustomPanelViewProps) => { if (!isNameValid(newCustomPanelName)) { setToast('Invalid Operational Panel name', 'danger'); } else { - dispatch(clonePanel(panel, newCustomPanelName, setToast)); + dispatch(clonePanel(panel, newCustomPanelName)); } closeModal(); }; @@ -448,12 +448,7 @@ export const CustomPanelViewSO = (props: CustomPanelViewProps) => { ); const updatedPanel = { ...panel, visualizations: visualizationsWithNewPanel }; - try { - dispatch(updatePanel(updatedPanel)); - } catch (err) { - setToast('Error adding visualization to this Dashboard', 'danger'); - console.error(err?.body?.message || err); - } + dispatch(updatePanel(updatedPanel, '', 'Error adding visualization to this Dashboard')); }; const setPanelVisualizations = (newVis) => { diff --git a/public/components/custom_panels/redux/panel_slice.ts b/public/components/custom_panels/redux/panel_slice.ts index 401dbd4ae3..652d2717d6 100644 --- a/public/components/custom_panels/redux/panel_slice.ts +++ b/public/components/custom_panels/redux/panel_slice.ts @@ -26,6 +26,7 @@ import { addMultipleVisualizations, addVisualizationPanel, } from '../helpers/add_visualization_helper'; +import { useToast } from '../../../../public/components/common/toast'; interface InitialState { id: string; @@ -81,6 +82,8 @@ const normalizedPanel = (panel: CustomPanelType): CustomPanelType => ({ export const selectPanelList = (rootState): CustomPanelType[] => rootState.customPanel.panelList; +const {setToast} = useToast(); + /* ** ASYNC DISPATCH FUNCTIONS */ @@ -136,16 +139,21 @@ export const uuidRx = /^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a export const isUuid = (id) => !!id.match(uuidRx); -export const updatePanel = (panel: CustomPanelType) => async (dispatch, getState) => { +export const updatePanel = (panel: CustomPanelType, onSuccess: string, onFailure: string) => async (dispatch, getState) => { try { if (isUuid(panel.id)) await updateSavedObjectPanel(panel); else await updateLegacyPanel(panel); - + if (onSuccess) { + setToast(onSuccess) + } dispatch(setPanel(panel)); const panelList = getState().customPanel.panelList.map((p) => (p.id === panel.id ? panel : p)); dispatch(setPanelList(panelList)); - } catch (err) { - console.error('Error updating Dashboard', { err, panel }); + } catch (e) { + if (onFailure) { + setToast(onFailure, 'danger') + } + console.error(e); } }; @@ -211,7 +219,7 @@ const deleteLegacyPanels = (customPanelIdList: string[]) => { return coreRefs.http!.delete(`${CUSTOM_PANELS_API_PREFIX}/panelList/` + concatList); }; -export const deletePanels = (panelsToDelete: CustomPanelType[], setToast) => async (dispatch, getState) => { +export const deletePanels = (panelsToDelete: CustomPanelType[]) => async (dispatch, getState) => { const toastMessage = `Observability Dashboard${ panelsToDelete.length > 1 ? 's' : ' ' + panelsToDelete[0].title } successfully deleted!`; @@ -233,7 +241,7 @@ export const deletePanels = (panelsToDelete: CustomPanelType[], setToast) => asy } }; -export const createPanel = (panel, setToast) => async (dispatch, getState) => { +export const createPanel = (panel) => async (dispatch, getState) => { try { const newSOPanel = await savedObjectPanelsClient.create(panel); const newPanel = savedObjectToCustomPanel(newSOPanel); @@ -250,7 +258,7 @@ export const createPanel = (panel, setToast) => async (dispatch, getState) => { } }; -export const clonePanel = (panel, newPanelName, setToast) => async (dispatch, getState) => { +export const clonePanel = (panel, newPanelName) => async (dispatch, getState) => { try { const { id, ...panelCopy } = { ...panel, @@ -297,26 +305,13 @@ const saveRenamedPanelSO = async (id, name) => { }; // Renames an existing CustomPanel -export const renameCustomPanel = (editedCustomPanelName: string, id: string, setToast) => async ( +export const renameCustomPanel = (editedCustomPanelName: string, id: string) => async ( dispatch, getState ) => { - try { - const panel = getState().customPanel.panelList.find((p) => p.id === id); - const updatedPanel = { ...panel, title: editedCustomPanelName }; - if (isUuid(updatedPanel.id)) await updateSavedObjectPanel(updatedPanel); - else await updateLegacyPanel(updatedPanel); - dispatch(setPanel(updatedPanel)); - const panelList = getState().customPanel.panelList.map((p) => (p.id === updatedPanel.id ? updatedPanel : p)); - dispatch(setPanelList(panelList)); - setToast(`Observability Dashboard successfully renamed into "${editedCustomPanelName}"`) - } catch (e) { - setToast( - 'Error renaming Observability Dashboard, please make sure you have the correct permission.', - 'danger' - ); - console.error(e); - } + const panel = getState().customPanel.panelList.find((p) => p.id === id); + const updatedPanel = { ...panel, title: editedCustomPanelName }; + dispatch(updatePanel(updatedPanel, `Operational Panel successfully renamed into "${editedCustomPanelName}"`, 'Error renaming Operational Panel, please make sure you have the correct permission.')) }; /* From 7eba7b4e84712e1a1e9f8c38c34bc867e7be3096 Mon Sep 17 00:00:00 2001 From: Derek Ho Date: Thu, 27 Apr 2023 14:59:46 -0400 Subject: [PATCH 15/18] fix up for flyout Signed-off-by: Derek Ho --- .../custom_panels/custom_panel_view_so.tsx | 23 +++++-------------- .../visualization_flyout_so.tsx | 16 +++---------- .../custom_panels/redux/panel_slice.ts | 9 +++----- 3 files changed, 12 insertions(+), 36 deletions(-) diff --git a/public/components/custom_panels/custom_panel_view_so.tsx b/public/components/custom_panels/custom_panel_view_so.tsx index b457a7f2f0..9ed9abee71 100644 --- a/public/components/custom_panels/custom_panel_view_so.tsx +++ b/public/components/custom_panels/custom_panel_view_so.tsx @@ -362,22 +362,7 @@ export const CustomPanelViewSO = (props: CustomPanelViewProps) => { }; const cloneVisualization = (visualzationTitle: string, savedVisualizationId: string) => { - addVisualizationToCurrentPanel({ savedVisualizationId }); - // http - // .post(`${CUSTOM_PANELS_API_PREFIX}/visualizations`, { - // body: JSON.stringify({ - // panelId, - // savedVisualizationId, - // }), - // }) - // .then(async (res) => { - // setPanelVisualizations(res.visualizations); - // setToast(`Visualization ${visualzationTitle} successfully added!`, 'success'); - // }) - // .catch((err) => { - // setToast(`Error in adding ${visualzationTitle} visualization to the panel`, 'danger'); - // console.error(err); - // }); + addVisualizationToCurrentPanel({ savedVisualizationId, onSuccess: `Visualization ${visualzationTitle} successfully added!`, onFailure: `Error in adding ${visualzationTitle} visualization to the panel` }); }; const cancelButton = ( @@ -435,9 +420,13 @@ export const CustomPanelViewSO = (props: CustomPanelViewProps) => { const addVisualizationToCurrentPanel = async ({ savedVisualizationId, oldVisualizationId, + onSuccess, + onFailure, }: { savedVisualizationId: string; oldVisualizationId?: string; + onSuccess: string; + onFailure: string; }) => { const allVisualizations = panel!.visualizations; @@ -448,7 +437,7 @@ export const CustomPanelViewSO = (props: CustomPanelViewProps) => { ); const updatedPanel = { ...panel, visualizations: visualizationsWithNewPanel }; - dispatch(updatePanel(updatedPanel, '', 'Error adding visualization to this Dashboard')); + dispatch(updatePanel(updatedPanel, onSuccess, onFailure)); }; const setPanelVisualizations = (newVis) => { diff --git a/public/components/custom_panels/panel_modules/visualization_flyout/visualization_flyout_so.tsx b/public/components/custom_panels/panel_modules/visualization_flyout/visualization_flyout_so.tsx index c606fb567d..df61578458 100644 --- a/public/components/custom_panels/panel_modules/visualization_flyout/visualization_flyout_so.tsx +++ b/public/components/custom_panels/panel_modules/visualization_flyout/visualization_flyout_so.tsx @@ -176,23 +176,13 @@ export const VisaulizationFlyoutSO = ({ if (!isInputValid()) return; if (isFlyoutReplacement) { - try { - dispatch(replaceVizInPanel(panel, replaceVisualizationId, selectValue)); - setToast(`Visualization ${newVisualizationTitle} successfully added!`, 'success'); - } catch (err) { - setToast(`Error in adding ${newVisualizationTitle} visualization to the panel`, 'danger'); - console.error(err); - } + dispatch(replaceVizInPanel(panel, replaceVisualizationId, selectValue, newVisualizationTitle)); } else { - try { const visualizationsWithNewPanel = addVisualizationPanel({ savedVisualizationId: selectValue, + onSuccess: `Visualization ${newVisualizationTitle} successfully added!`, + onFailure: `Error in adding ${newVisualizationTitle} visualization to the panel` }); - setToast(`Visualization ${newVisualizationTitle} successfully added!`, 'success'); - } catch (err) { - setToast(`Error in adding ${newVisualizationTitle} visualization to the panel`, 'danger'); - console.error(err); - } } closeFlyout(); }; diff --git a/public/components/custom_panels/redux/panel_slice.ts b/public/components/custom_panels/redux/panel_slice.ts index 652d2717d6..464f90e1e7 100644 --- a/public/components/custom_panels/redux/panel_slice.ts +++ b/public/components/custom_panels/redux/panel_slice.ts @@ -191,7 +191,7 @@ export const addMultipleVizToPanels = (panels, vizIds) => async (dispatch, getSt }); }; -export const replaceVizInPanel = (oldPanel, oldVizId, vizId) => async (dispatch, getState) => { +export const replaceVizInPanel = (oldPanel, oldVizId, vizId, newVisualizationTitle) => async (dispatch, getState) => { const panel = getState().customPanel.panelList.find((p) => p.id === oldPanel.id); const allVisualizations = panel!.visualizations; @@ -199,11 +199,8 @@ export const replaceVizInPanel = (oldPanel, oldVizId, vizId) => async (dispatch, const visualizationsWithNewPanel = addVisualizationPanel(vizId, oldVizId, allVisualizations); const updatedPanel = { ...panel, visualizations: visualizationsWithNewPanel }; - try { - dispatch(updatePanel(updatedPanel)); - } catch (err) { - console.error(err?.body?.message || err); - } + + dispatch(updatePanel(updatedPanel, `Visualization ${newVisualizationTitle} successfully added!`, `Error in adding ${newVisualizationTitle} visualization to the panel`)); }; const deletePanelSO = (customPanelIdList: string[]) => { From dbc7976ef8bbc390ed06d1214ea7d1941c0b4569 Mon Sep 17 00:00:00 2001 From: Derek Ho Date: Thu, 27 Apr 2023 15:38:26 -0400 Subject: [PATCH 16/18] code cleanup Signed-off-by: Derek Ho --- .../custom_panels/custom_panel_table.tsx | 4 ++-- .../custom_panels/custom_panel_view_so.tsx | 20 +++++++++++++++++-- .../visualization_flyout_so.tsx | 4 ---- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/public/components/custom_panels/custom_panel_table.tsx b/public/components/custom_panels/custom_panel_table.tsx index 9d2e5e7b33..4b1fd571d5 100644 --- a/public/components/custom_panels/custom_panel_table.tsx +++ b/public/components/custom_panels/custom_panel_table.tsx @@ -124,7 +124,7 @@ export const CustomPanelTable = ({ setToast('Invalid Dashboard name', 'danger'); } else { const newPanel = newPanelTemplate(newCustomPanelName); - dispatch(createPanel(newPanel, setToast)); + dispatch(createPanel(newPanel)); } closeModal(); }; @@ -158,7 +158,7 @@ export const CustomPanelTable = ({ title: newName, }; - dispatch(createPanel(newPanel, setToast)); + dispatch(createPanel(newPanel)); } catch (err) { setToast( 'Error cloning Observability Dashboard, please make sure you have the correct permission.', diff --git a/public/components/custom_panels/custom_panel_view_so.tsx b/public/components/custom_panels/custom_panel_view_so.tsx index 5c8a065d67..83d8ab55e9 100644 --- a/public/components/custom_panels/custom_panel_view_so.tsx +++ b/public/components/custom_panels/custom_panel_view_so.tsx @@ -66,7 +66,7 @@ import { import { useToast } from '../common/toast'; import PPLService from '../../services/requests/ppl'; import DSLService from '../../services/requests/dsl'; - + /* * "CustomPanelsView" module used to render an Observability Dashboard * @@ -258,10 +258,27 @@ export const CustomPanelViewSO = (props: CustomPanelViewProps) => { }; // toggle between panel edit mode +<<<<<<< HEAD const editPanel = (editType: string) => { setIsEditing(!isEditing); if (editType === 'cancel') dispatch(fetchPanel(panelId)); setEditActionType(editType); +======= + + const startEdit = () => { + setIsEditing(true); + }; + + const applyEdits = useCallback(() => { + dispatch(updatePanel(panel, '', '')); + setIsEditing(false); + setEditActionType('save'); + }, [panel]); + + const cancelEdit = () => { + dispatch(fetchPanel(panelId)); + setIsEditing(false); +>>>>>>> 22977feb (code cleanup) }; const closeFlyout = () => { @@ -446,7 +463,6 @@ export const CustomPanelViewSO = (props: CustomPanelViewProps) => { pplFilterValue={pplFilterValue} start={panel.timeRange.from} end={panel.timeRange.to} - setToast={setToast} http={coreRefs.http!} pplService={pplService} setPanelVisualizations={setPanelVisualizations} diff --git a/public/components/custom_panels/panel_modules/visualization_flyout/visualization_flyout_so.tsx b/public/components/custom_panels/panel_modules/visualization_flyout/visualization_flyout_so.tsx index b0a805ea5a..0b1fff5f75 100644 --- a/public/components/custom_panels/panel_modules/visualization_flyout/visualization_flyout_so.tsx +++ b/public/components/custom_panels/panel_modules/visualization_flyout/visualization_flyout_so.tsx @@ -92,16 +92,12 @@ interface VisualizationFlyoutSOProps { } export const VisaulizationFlyoutSO = ({ - panelId, appId = '', pplFilterValue, closeFlyout, start, end, - http, - savedObjects, pplService, - setPanelVisualizations, isFlyoutReplacement, replaceVisualizationId, addVisualizationPanel, From dfd8582320fca1a8d9469ae7d04639d6895bec99 Mon Sep 17 00:00:00 2001 From: Derek Ho Date: Thu, 27 Apr 2023 15:39:40 -0400 Subject: [PATCH 17/18] finish merge Signed-off-by: Derek Ho --- .../custom_panels/custom_panel_view_so.tsx | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/public/components/custom_panels/custom_panel_view_so.tsx b/public/components/custom_panels/custom_panel_view_so.tsx index 83d8ab55e9..297d0219a6 100644 --- a/public/components/custom_panels/custom_panel_view_so.tsx +++ b/public/components/custom_panels/custom_panel_view_so.tsx @@ -258,27 +258,10 @@ export const CustomPanelViewSO = (props: CustomPanelViewProps) => { }; // toggle between panel edit mode -<<<<<<< HEAD const editPanel = (editType: string) => { setIsEditing(!isEditing); if (editType === 'cancel') dispatch(fetchPanel(panelId)); setEditActionType(editType); -======= - - const startEdit = () => { - setIsEditing(true); - }; - - const applyEdits = useCallback(() => { - dispatch(updatePanel(panel, '', '')); - setIsEditing(false); - setEditActionType('save'); - }, [panel]); - - const cancelEdit = () => { - dispatch(fetchPanel(panelId)); - setIsEditing(false); ->>>>>>> 22977feb (code cleanup) }; const closeFlyout = () => { From 6c9e8674300fc02054cb8887c0b48b19dac054ea Mon Sep 17 00:00:00 2001 From: Derek Ho Date: Thu, 27 Apr 2023 16:46:44 -0400 Subject: [PATCH 18/18] fix up PR comments Signed-off-by: Derek Ho --- common/constants/custom_panels.ts | 2 +- .../custom_panels/custom_panel_view_so.tsx | 12 +++++----- .../custom_panels/redux/panel_slice.ts | 22 ++++++------------- 3 files changed, 14 insertions(+), 22 deletions(-) diff --git a/common/constants/custom_panels.ts b/common/constants/custom_panels.ts index 007aa9c326..f0791d08c3 100644 --- a/common/constants/custom_panels.ts +++ b/common/constants/custom_panels.ts @@ -6,7 +6,7 @@ export const CUSTOM_PANELS_API_PREFIX = '/api/observability/operational_panels'; export const CUSTOM_PANELS_DOCUMENTATION_URL = 'https://opensearch.org/docs/latest/observability-plugin/operational-panels/'; -export const CREATE_PANEL_MESSAGE = 'Enter a name to describe the purpose of this observability dashboard.'; +export const CREATE_PANEL_MESSAGE = 'Enter a name to describe the purpose of this Observability Dashboard.'; export const CUSTOM_PANELS_SAVED_OBJECT_TYPE = 'observability-panel'; diff --git a/public/components/custom_panels/custom_panel_view_so.tsx b/public/components/custom_panels/custom_panel_view_so.tsx index 297d0219a6..65b10416a0 100644 --- a/public/components/custom_panels/custom_panel_view_so.tsx +++ b/public/components/custom_panels/custom_panel_view_so.tsx @@ -354,7 +354,7 @@ export const CustomPanelViewSO = (props: CustomPanelViewProps) => { }; const cloneVisualization = (visualzationTitle: string, savedVisualizationId: string) => { - addVisualizationToCurrentPanel({ savedVisualizationId, onSuccess: `Visualization ${visualzationTitle} successfully added!`, onFailure: `Error in adding ${visualzationTitle} visualization to the panel` }); + addVisualizationToCurrentPanel({ savedVisualizationId, successMsg: `Visualization ${visualzationTitle} successfully added!`, failureMsg: `Error in adding ${visualzationTitle} visualization to the panel` }); }; const cancelButton = ( @@ -412,13 +412,13 @@ export const CustomPanelViewSO = (props: CustomPanelViewProps) => { const addVisualizationToCurrentPanel = async ({ savedVisualizationId, oldVisualizationId, - onSuccess, - onFailure, + successMsg, + failureMsg, }: { savedVisualizationId: string; oldVisualizationId?: string; - onSuccess: string; - onFailure: string; + successMsg: string; + failureMsg: string; }) => { const allVisualizations = panel!.visualizations; @@ -429,7 +429,7 @@ export const CustomPanelViewSO = (props: CustomPanelViewProps) => { ); const updatedPanel = { ...panel, visualizations: visualizationsWithNewPanel }; - dispatch(updatePanel(updatedPanel, onSuccess, onFailure)); + dispatch(updatePanel(updatedPanel, successMsg, failureMsg)); }; const setPanelVisualizations = (newVis) => { diff --git a/public/components/custom_panels/redux/panel_slice.ts b/public/components/custom_panels/redux/panel_slice.ts index 464f90e1e7..ae5f26a062 100644 --- a/public/components/custom_panels/redux/panel_slice.ts +++ b/public/components/custom_panels/redux/panel_slice.ts @@ -139,19 +139,19 @@ export const uuidRx = /^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a export const isUuid = (id) => !!id.match(uuidRx); -export const updatePanel = (panel: CustomPanelType, onSuccess: string, onFailure: string) => async (dispatch, getState) => { +export const updatePanel = (panel: CustomPanelType, successMsg: string, failureMsg: string) => async (dispatch, getState) => { try { if (isUuid(panel.id)) await updateSavedObjectPanel(panel); else await updateLegacyPanel(panel); - if (onSuccess) { - setToast(onSuccess) + if (successMsg) { + setToast(successMsg) } dispatch(setPanel(panel)); const panelList = getState().customPanel.panelList.map((p) => (p.id === panel.id ? panel : p)); dispatch(setPanelList(panelList)); } catch (e) { - if (onFailure) { - setToast(onFailure, 'danger') + if (failureMsg) { + setToast(failureMsg, 'danger') } console.error(e); } @@ -166,11 +166,7 @@ export const addVizToPanels = (panels, vizId) => async (dispatch, getState) => { const visualizationsWithNewPanel = addVisualizationPanel(vizId, undefined, allVisualizations); const updatedPanel = { ...panel, visualizations: visualizationsWithNewPanel }; - try { - dispatch(updatePanel(updatedPanel)); - } catch (err) { - console.error(err?.body?.message || err); - } + dispatch(updatePanel(updatedPanel, '', '')); }); }; @@ -183,11 +179,7 @@ export const addMultipleVizToPanels = (panels, vizIds) => async (dispatch, getSt const visualizationsWithNewPanel = addMultipleVisualizations(vizIds, allVisualizations); const updatedPanel = { ...panel, visualizations: visualizationsWithNewPanel }; - try { - dispatch(updatePanel(updatedPanel)); - } catch (err) { - console.error(err?.body?.message || err); - } + dispatch(updatePanel(updatedPanel, '', '')); }); };