From 3726ca044e8148c6b329a4aaa921707b0f4ae558 Mon Sep 17 00:00:00 2001 From: Ashwin P Chandran Date: Tue, 26 Jul 2022 09:18:19 -0700 Subject: [PATCH] [D&D] Adds autosave while editing aggregation (#1953) * fest: Adds autosave while editing agg Signed-off-by: Ashwin Pc * fix: autosave order Signed-off-by: Ashwin Pc * fix: spelling Co-authored-by: Josh Romero Co-authored-by: Josh Romero --- .../components/data_tab/config_panel.tsx | 6 ++- .../components/data_tab/secondary_panel.tsx | 40 ++++++++++++---- .../components/data_tab/use/use_dropbox.tsx | 8 ++-- .../application/components/workspace.tsx | 2 +- .../utils/state_management/metadata_slice.ts | 48 +++++++++++++++++++ .../utils/state_management/preload.ts | 3 ++ .../utils/state_management/store.ts | 2 + .../state_management/visualization_slice.ts | 13 ++--- 8 files changed, 97 insertions(+), 25 deletions(-) create mode 100644 src/plugins/wizard/public/application/utils/state_management/metadata_slice.ts diff --git a/src/plugins/wizard/public/application/components/data_tab/config_panel.tsx b/src/plugins/wizard/public/application/components/data_tab/config_panel.tsx index 1c0e67693471..be0ec12bbff2 100644 --- a/src/plugins/wizard/public/application/components/data_tab/config_panel.tsx +++ b/src/plugins/wizard/public/application/components/data_tab/config_panel.tsx @@ -13,7 +13,9 @@ import { SecondaryPanel } from './secondary_panel'; export function ConfigPanel() { const vizType = useVisualizationType(); - const draftAgg = useTypedSelector((state) => state.visualization.activeVisualization?.draftAgg); + const editingState = useTypedSelector( + (state) => state.visualization.activeVisualization?.draftAgg + ); const schemas = vizType.ui.containerConfig.data.schemas; if (!schemas) return null; @@ -21,7 +23,7 @@ export function ConfigPanel() { const mainPanel = mapSchemaToAggPanel(schemas); return ( - +
{mainPanel}
diff --git a/src/plugins/wizard/public/application/components/data_tab/secondary_panel.tsx b/src/plugins/wizard/public/application/components/data_tab/secondary_panel.tsx index 21bdf256422d..a0fa9c160227 100644 --- a/src/plugins/wizard/public/application/components/data_tab/secondary_panel.tsx +++ b/src/plugins/wizard/public/application/components/data_tab/secondary_panel.tsx @@ -12,11 +12,14 @@ import { useIndexPatterns, useVisualizationType } from '../../utils/use'; import { useOpenSearchDashboards } from '../../../../../opensearch_dashboards_react/public'; import { WizardServices } from '../../../types'; import { IAggType } from '../../../../../data/public'; -import { saveAgg, editAgg } from '../../utils/state_management/visualization_slice'; +import { saveDraftAgg, editDraftAgg } from '../../utils/state_management/visualization_slice'; +import { setValid } from '../../utils/state_management/metadata_slice'; + +const EDITOR_KEY = 'CONFIG_PANEL'; export function SecondaryPanel() { const draftAgg = useTypedSelector((state) => state.visualization.activeVisualization!.draftAgg); - const [valid, setValid] = useState(true); + const valid = useTypedSelector((state) => state.metadata.editorState.valid[EDITOR_KEY]); const [touched, setTouched] = useState(false); const dispatch = useTypedDispatch(); const vizType = useVisualizationType(); @@ -46,9 +49,26 @@ export function SecondaryPanel() { const showAggParamEditor = !!(aggConfig && indexPattern); const closeMenu = useCallback(() => { - // Save the agg if valid else discard - dispatch(saveAgg(valid)); - }, [dispatch, valid]); + dispatch(editDraftAgg(undefined)); + }, [dispatch]); + + const handleSetValid = useCallback( + (isValid: boolean) => { + // Set validity state globally + dispatch( + setValid({ + key: EDITOR_KEY, + valid: isValid, + }) + ); + + // Autosave changes if valid + if (valid) { + dispatch(saveDraftAgg()); + } + }, + [dispatch, valid] + ); return (
@@ -58,7 +78,7 @@ export function SecondaryPanel() { className="wizConfig__aggEditor" agg={aggConfig!} indexPattern={indexPattern!} - setValidity={setValid} + setValidity={handleSetValid} setTouched={setTouched} schemas={schemas} formIsTouched={false} @@ -66,8 +86,8 @@ export function SecondaryPanel() { metricAggs={[]} state={{ data: {}, - description: 'Falalala', - title: 'Title for the aggParams', + description: '', + title: '', }} setAggParamValue={function ( aggId: string, @@ -75,11 +95,11 @@ export function SecondaryPanel() { value: any ): void { aggConfig.params[paramName] = value; - dispatch(editAgg(aggConfig.serialize())); + dispatch(editDraftAgg(aggConfig.serialize())); }} onAggTypeChange={function (aggId: string, aggType: IAggType): void { aggConfig.type = aggType; - dispatch(editAgg(aggConfig.serialize())); + dispatch(editDraftAgg(aggConfig.serialize())); }} /> )} diff --git a/src/plugins/wizard/public/application/components/data_tab/use/use_dropbox.tsx b/src/plugins/wizard/public/application/components/data_tab/use/use_dropbox.tsx index decad4a2c334..af9122d8ebd7 100644 --- a/src/plugins/wizard/public/application/components/data_tab/use/use_dropbox.tsx +++ b/src/plugins/wizard/public/application/components/data_tab/use/use_dropbox.tsx @@ -12,7 +12,7 @@ import { useTypedDispatch, useTypedSelector } from '../../../utils/state_managem import { DropboxDisplay, DropboxProps } from '../dropbox'; import { useDrop } from '../../../utils/drag_drop'; import { - editAgg, + editDraftAgg, reorderAgg, updateAggConfigParams, } from '../../../utils/state_management/visualization_slice'; @@ -88,18 +88,18 @@ export const useDropbox = (props: UseDropboxProps): DropboxProps => { throw new Error('Missing new aggConfig'); } - dispatch(editAgg(newAggConfig.serialize())); + dispatch(editDraftAgg(newAggConfig.serialize())); }, [aggConfigs, aggService, aggs, dispatch, indexPattern, schema.name]); const onEditField = useCallback( - (aggId) => { + (aggId: string) => { const aggConfig = aggConfigs?.aggs.find((agg) => agg.id === aggId); if (!aggConfig) { throw new Error('Could not find agg in aggConfigs'); } - dispatch(editAgg(aggConfig.serialize())); + dispatch(editDraftAgg(aggConfig.serialize())); }, [aggConfigs?.aggs, dispatch] ); diff --git a/src/plugins/wizard/public/application/components/workspace.tsx b/src/plugins/wizard/public/application/components/workspace.tsx index 39d75a7c43d2..ab320c8257ad 100644 --- a/src/plugins/wizard/public/application/components/workspace.tsx +++ b/src/plugins/wizard/public/application/components/workspace.tsx @@ -95,7 +95,7 @@ export const Workspace: FC = ({ children }) => { ) : ( Drop some fields to start} + title={

Add a field to start

} body={ <>

Drag a field to the configuration panel to generate a visualization.

diff --git a/src/plugins/wizard/public/application/utils/state_management/metadata_slice.ts b/src/plugins/wizard/public/application/utils/state_management/metadata_slice.ts new file mode 100644 index 000000000000..6e0902220079 --- /dev/null +++ b/src/plugins/wizard/public/application/utils/state_management/metadata_slice.ts @@ -0,0 +1,48 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { createSlice, PayloadAction } from '@reduxjs/toolkit'; +import { WizardServices } from '../../../types'; + +export interface MetadataState { + editorState: { + valid: { + // Validity for each section in the editor + [key: string]: boolean; + }; + }; +} + +const initialState: MetadataState = { + editorState: { + valid: {}, + }, +}; + +export const getPreloadedState = async ({ + types, + data, +}: WizardServices): Promise => { + const preloadedState = { ...initialState }; + + return preloadedState; +}; + +export const slice = createSlice({ + name: 'metadata', + initialState, + reducers: { + setValid: (state, action: PayloadAction<{ key: string; valid: boolean }>) => { + const { key, valid } = action.payload; + state.editorState.valid[key] = valid; + }, + setState: (_state, action: PayloadAction) => { + return action.payload; + }, + }, +}); + +export const { reducer } = slice; +export const { setValid, setState } = slice.actions; diff --git a/src/plugins/wizard/public/application/utils/state_management/preload.ts b/src/plugins/wizard/public/application/utils/state_management/preload.ts index d9cefa21a064..1d96608e33cc 100644 --- a/src/plugins/wizard/public/application/utils/state_management/preload.ts +++ b/src/plugins/wizard/public/application/utils/state_management/preload.ts @@ -7,6 +7,7 @@ import { PreloadedState } from '@reduxjs/toolkit'; import { WizardServices } from '../../..'; import { getPreloadedState as getPreloadedStyleState } from './style_slice'; import { getPreloadedState as getPreloadedVisualizationState } from './visualization_slice'; +import { getPreloadedState as getPreloadedMetadataState } from './metadata_slice'; import { RootState } from './store'; export const getPreloadedState = async ( @@ -14,9 +15,11 @@ export const getPreloadedState = async ( ): Promise> => { const styleState = await getPreloadedStyleState(services); const visualizationState = await getPreloadedVisualizationState(services); + const metadataState = await getPreloadedMetadataState(services); return { style: styleState, visualization: visualizationState, + metadata: metadataState, }; }; diff --git a/src/plugins/wizard/public/application/utils/state_management/store.ts b/src/plugins/wizard/public/application/utils/state_management/store.ts index d8fff094cd1e..932a5f4d8914 100644 --- a/src/plugins/wizard/public/application/utils/state_management/store.ts +++ b/src/plugins/wizard/public/application/utils/state_management/store.ts @@ -6,12 +6,14 @@ import { combineReducers, configureStore, PreloadedState } from '@reduxjs/toolkit'; import { reducer as styleReducer } from './style_slice'; import { reducer as visualizationReducer } from './visualization_slice'; +import { reducer as metadataReducer } from './metadata_slice'; import { WizardServices } from '../../..'; import { getPreloadedState } from './preload'; const rootReducer = combineReducers({ style: styleReducer, visualization: visualizationReducer, + metadata: metadataReducer, }); export const configurePreloadedStore = (preloadedState: PreloadedState) => { diff --git a/src/plugins/wizard/public/application/utils/state_management/visualization_slice.ts b/src/plugins/wizard/public/application/utils/state_management/visualization_slice.ts index 06c865e14395..fe1277f33432 100644 --- a/src/plugins/wizard/public/application/utils/state_management/visualization_slice.ts +++ b/src/plugins/wizard/public/application/utils/state_management/visualization_slice.ts @@ -59,15 +59,13 @@ export const slice = createSlice({ setSearchField: (state, action: PayloadAction) => { state.searchField = action.payload; }, - editAgg: (state, action: PayloadAction) => { + editDraftAgg: (state, action: PayloadAction) => { state.activeVisualization!.draftAgg = action.payload; }, - saveAgg: (state, action: PayloadAction) => { - const saveDraft = action.payload; + saveDraftAgg: (state, action: PayloadAction) => { const draftAgg = state.activeVisualization!.draftAgg; - // Delete the aggConfigParam if the save is not true - if (saveDraft && draftAgg) { + if (draftAgg) { const aggIndex = state.activeVisualization!.aggConfigParams.findIndex( (agg) => agg.id === draftAgg.id ); @@ -78,7 +76,6 @@ export const slice = createSlice({ state.activeVisualization!.aggConfigParams.splice(aggIndex, 1, draftAgg); } } - delete state.activeVisualization!.draftAgg; }, reorderAgg: ( state, @@ -116,9 +113,9 @@ export const { setActiveVisualization, setIndexPattern, setSearchField, - editAgg, + editDraftAgg, + saveDraftAgg, updateAggConfigParams, - saveAgg, reorderAgg, setState, } = slice.actions;