Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds initial type service #1260

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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