Skip to content

Commit

Permalink
Adds initial type service (opensearch-project#1260)
Browse files Browse the repository at this point in the history
Signed-off-by: Ashwin Pc <[email protected]>
  • Loading branch information
ashwin-pc committed Mar 29, 2022
1 parent 68a1e38 commit a685985
Show file tree
Hide file tree
Showing 16 changed files with 396 additions and 51 deletions.
28 changes: 1 addition & 27 deletions src/plugins/wizard/public/application/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,17 @@
* SPDX-License-Identifier: Apache-2.0
*/

import React, { useEffect } from 'react';
import React from 'react';
import { I18nProvider } from '@osd/i18n/react';
import { EuiPage } from '@elastic/eui';
import { DataPublicPluginStart } from '../../../data/public';
import { SideNav } from './components/side_nav';
import { DragDropProvider } from './utils/drag_drop/drag_drop_context';
import { useOpenSearchDashboards } from '../../../opensearch_dashboards_react/public';
import { WizardServices } from '../types';
import { Workspace } from './components/workspace';

import './app.scss';
import { TopNav } from './components/top_nav';
import { useTypedDispatch } from './utils/state_management';
import { setIndexPattern } from './utils/state_management/datasource_slice';

export const WizardApp = () => {
const {
services: { data },
} = useOpenSearchDashboards<WizardServices>();

useIndexPattern(data);

// Render the application DOM.
return (
<I18nProvider>
Expand All @@ -38,18 +27,3 @@ export const WizardApp = () => {
</I18nProvider>
);
};

// TODO: Temporary. Need to update it fetch the index pattern cohesively
function useIndexPattern(data: DataPublicPluginStart) {
const dispatch = useTypedDispatch();

useEffect(() => {
const fetchIndexPattern = async () => {
const defaultIndexPattern = await data.indexPatterns.getDefault();
if (defaultIndexPattern) {
dispatch(setIndexPattern(defaultIndexPattern));
}
};
fetchIndexPattern();
}, [data.indexPatterns, dispatch]);
}
88 changes: 82 additions & 6 deletions src/plugins/wizard/public/application/components/workspace.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,22 @@
* SPDX-License-Identifier: Apache-2.0
*/

import { EuiButton, EuiEmptyPrompt, EuiFlexGroup, EuiFlexItem, EuiPanel } from '@elastic/eui';
import React, { FC } from 'react';
import {
EuiButton,
EuiContextMenu,
EuiContextMenuPanelItemDescriptor,
EuiEmptyPrompt,
EuiFlexGroup,
EuiFlexItem,
EuiIcon,
EuiPanel,
EuiPopover,
} from '@elastic/eui';
import React, { FC, useState, useMemo } from 'react';
import { useOpenSearchDashboards } from '../../../../opensearch_dashboards_react/public';
import { WizardServices } from '../../types';
import { useTypedDispatch, useTypedSelector } from '../utils/state_management';
import { setActiveVisualization } from '../utils/state_management/visualization_slice';

import './workspace.scss';

Expand All @@ -13,10 +27,7 @@ export const Workspace: FC = ({ children }) => {
<section className="wizWorkspace">
<EuiFlexGroup className="wizCanvasControls">
<EuiFlexItem grow={false}>
{/* TODO: This is the temporary view of the selected chard, should be replaced by dropdown */}
<EuiButton iconType="visBarVertical" disabled>
Bar
</EuiButton>
<TypeSelectorPopover />
</EuiFlexItem>
</EuiFlexGroup>
<EuiPanel className="wizCanvas">
Expand All @@ -35,3 +46,68 @@ export const Workspace: FC = ({ children }) => {
</section>
);
};

const TypeSelectorPopover = () => {
const [isPopoverOpen, setPopover] = useState(false);
const { activeVisualization: activeVisualizationId } = useTypedSelector(
(state) => state.visualization
);
const {
services: { types },
} = useOpenSearchDashboards<WizardServices>();
const dispatch = useTypedDispatch();

// TODO: Error if no active visualization
const activeVisualization = types.get(activeVisualizationId || '');
const visualizationTypes = types.all();

const onButtonClick = () => {
setPopover(!isPopoverOpen);
};

const closePopover = () => {
setPopover(false);
};

const panels = useMemo(
() => [
{
id: 0,
title: 'Chart types',
items: visualizationTypes.map(
({ name, title, icon, description }): EuiContextMenuPanelItemDescriptor => ({
name: title,
icon: <EuiIcon type={icon} />,
onClick: () => {
closePopover();
dispatch(setActiveVisualization(name));
},
toolTipContent: description,
toolTipPosition: 'right',
})
),
},
],
[dispatch, visualizationTypes]
);

const button = (
<EuiButton iconType={activeVisualization?.icon} onClick={onButtonClick}>
{activeVisualization?.title}
</EuiButton>
);

return (
<EuiPopover
id="contextMenuExample"
ownFocus
button={button}
isOpen={isPopoverOpen}
closePopover={closePopover}
panelPaddingSize="none"
anchorPosition="downLeft"
>
<EuiContextMenu initialPanelId={0} panels={panels} />
</EuiPopover>
);
};
5 changes: 3 additions & 2 deletions src/plugins/wizard/public/application/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,16 @@ import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter as Router } from 'react-router-dom';
import { Provider as ReduxProvider } from 'react-redux';
import { Store } from 'redux';
import { AppMountParameters } from '../../../../core/public';
import { WizardServices } from '../types';
import { WizardApp } from './app';
import { OpenSearchDashboardsContextProvider } from '../../../opensearch_dashboards_react/public';
import { store } from './utils/state_management';

export const renderApp = (
{ appBasePath, element }: AppMountParameters,
services: WizardServices
services: WizardServices,
store: Store
) => {
ReactDOM.render(
<Router basename={appBasePath}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import { createSlice, PayloadAction } from '@reduxjs/toolkit';
import { IndexPattern } from 'src/plugins/data/common';
import { WizardServices } from '../../../types';

import { IndexPatternField, OSD_FIELD_TYPES } from '../../../../../data/public';

Expand All @@ -22,6 +23,18 @@ const initialState: DataSourceState = {
searchField: '',
};

export const getPreloadedState = async ({ data }: WizardServices): Promise<DataSourceState> => {
const preloadedState = { ...initialState };

const defaultIndexPattern = await data.indexPatterns.getDefault();
if (defaultIndexPattern) {
preloadedState.indexPattern = defaultIndexPattern;
preloadedState.visualizableFields = defaultIndexPattern.fields.filter(isVisualizable);
}

return preloadedState;
};

export const slice = createSlice({
name: 'dataSource',
initialState,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import { PreloadedState } from '@reduxjs/toolkit';
import { WizardServices } from '../../..';
import { getPreloadedState as getPreloadedDatasourceState } from './datasource_slice';
import { getPreloadedState as getPreloadedVisualizationState } from './visualization_slice';
import { RootState } from './store';

export const getPreloadedState = async (
services: WizardServices
): Promise<PreloadedState<RootState>> => {
const dataSourceState = await getPreloadedDatasourceState(services);
const visualizationState = await getPreloadedVisualizationState(services);

return {
dataSource: dataSourceState,
visualization: visualizationState,
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,32 @@
* SPDX-License-Identifier: Apache-2.0
*/

import { configureStore } from '@reduxjs/toolkit';
import { combineReducers, configureStore, PreloadedState } from '@reduxjs/toolkit';
import { reducer as dataSourceReducer } from './datasource_slice';
import { reducer as configReducer } from './config_slice';
import { reducer as visualizationReducer } from './visualization_slice';
import { WizardServices } from '../../..';
import { getPreloadedState } from './preload';

export const store = configureStore({
reducer: {
dataSource: dataSourceReducer,
config: configReducer,
},
const rootReducer = combineReducers({
dataSource: dataSourceReducer,
config: configReducer,
visualization: visualizationReducer,
});

export const configurePreloadedStore = (preloadedState: PreloadedState<RootState>) => {
return configureStore({
reducer: rootReducer,
preloadedState,
});
};

export const getPreloadedStore = async (services: WizardServices) => {
const preloadedState = await getPreloadedState(services);
return configurePreloadedStore(preloadedState);
};

// Infer the `RootState` and `AppDispatch` types from the store itself
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
export type RootState = ReturnType<typeof rootReducer>;
type Store = ReturnType<typeof configurePreloadedStore>;
export type AppDispatch = Store['dispatch'];
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import { createSlice, PayloadAction } from '@reduxjs/toolkit';
import { WizardServices } from '../../../types';

interface VisualizationState {
activeVisualization: string | null;
}

const initialState: VisualizationState = {
activeVisualization: null,
};

export const getPreloadedState = async ({ types }: WizardServices): Promise<VisualizationState> => {
const preloadedState = { ...initialState };

const defaultVisualization = types.all()[0];
if (defaultVisualization) {
preloadedState.activeVisualization = defaultVisualization.name;
}

return preloadedState;
};

export const slice = createSlice({
name: 'visualization',
initialState,
reducers: {
setActiveVisualization: (state, action: PayloadAction<string>) => {
state.activeVisualization = action.payload;
},
},
});

export const { reducer } = slice;
export const { setActiveVisualization } = slice.actions;
22 changes: 20 additions & 2 deletions src/plugins/wizard/public/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,24 @@ import {
WizardPluginSetupDependencies,
WizardPluginStartDependencies,
WizardServices,
WizardSetup,
} from './types';
import { PLUGIN_NAME } from '../common';
import { TypeService } from './services/type_service';
import { getPreloadedStore } from './application/utils/state_management';

export class WizardPlugin
implements Plugin<void, void, WizardPluginSetupDependencies, WizardPluginStartDependencies> {
implements
Plugin<WizardSetup, void, WizardPluginSetupDependencies, WizardPluginStartDependencies> {
private typeService = new TypeService();

constructor(public initializerContext: PluginInitializerContext) {}

public setup(
core: CoreSetup<WizardPluginStartDependencies>,
{ visualizations }: WizardPluginSetupDependencies
) {
const typeService = this.typeService;
// Register the plugin to core
core.application.register({
id: 'wizard',
Expand All @@ -39,23 +46,30 @@ export class WizardPlugin
const [coreStart, pluginsStart] = await core.getStartServices();
const { data, savedObjects, navigation } = pluginsStart;

const { registerDefaultTypes } = await import('./visualizations');
registerDefaultTypes(typeService.setup());

const services: WizardServices = {
...coreStart,
toastNotifications: coreStart.notifications.toasts,
data,
savedObjectsPublic: savedObjects,
navigation,
setHeaderActionMenu: params.setHeaderActionMenu,
types: typeService.start(),
};

// make sure the index pattern list is up to date
data.indexPatterns.clearCache();
// make sure a default index pattern exists
// if not, the page will be redirected to management and visualize won't be rendered
// TODO: Add the redirect
await pluginsStart.data.indexPatterns.ensureDefaultIndexPattern();

const store = await getPreloadedStore(services);

// Render the application
return renderApp(params, services);
return renderApp(params, services, store);
},
});

Expand All @@ -72,6 +86,10 @@ export class WizardPlugin
aliasApp: 'wizard',
aliasPath: '#/',
});

return {
...typeService.setup(),
};
}

public start(core: CoreStart) {}
Expand Down
6 changes: 6 additions & 0 deletions src/plugins/wizard/public/services/type_service/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

export * from './type_service';
Loading

0 comments on commit a685985

Please sign in to comment.