Skip to content

Commit

Permalink
Add datasource picker component and use it in devtools and tutorial p…
Browse files Browse the repository at this point in the history
…age when multiple datasource is enabled (#5756)

* add datasource picker as a component and use it in devtools and add sample data page

Signed-off-by: Lu Yu <[email protected]>

* add changelog for 5756

Signed-off-by: Lu Yu <[email protected]>

* wraps prepend with i18n and fix typo

Signed-off-by: Lu Yu <[email protected]>

---------

Signed-off-by: Lu Yu <[email protected]>
  • Loading branch information
BionIT authored Jan 31, 2024
1 parent f8ee03a commit b2d2b26
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 111 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
- [Chrome] Introduce registerCollapsibleNavHeader to allow plugins to customize the rendering of nav menu header ([#5244](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5244))
- [Custom Branding] Relative URL should be allowed for logos ([#5572](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5572))
- [Discover] Enhanced the data source selector with added sorting functionality ([#5609](https://github.com/opensearch-project/OpenSearch-Dashboards/issues/5609))
- [Multiple Datasource] Add datasource picker component and use it in devtools and tutorial page when multiple datasource is enabled ([#5756](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5756))

### 🐛 Bug Fixes

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@
"requiredPlugins": ["management", "dataSource", "indexPatternManagement"],
"optionalPlugins": [],
"requiredBundles": ["opensearchDashboardsReact"],
"extraPublicDirs": ["public/components/utils"]
"extraPublicDirs": ["public/components/utils", "public/components/data_source_picker/data_source_picker"]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import React from 'react';
import { getDataSources } from '../utils';
import { i18n } from '@osd/i18n';
import { EuiComboBox } from '@elastic/eui';

export const LocalCluster = {
label: i18n.translate('dataSource.localCluster', {
defaultMessage: 'Local cluster',
}),
id: '',
};

export class DataSourcePicker extends React.Component {
constructor(props) {
super(props);

this.state = {
dataSources: [],
selectedOption: [LocalCluster],
};
}

componentWillUnmount() {
this._isMounted = false;
}

async componentDidMount() {
this._isMounted = true;
getDataSources(this.props.savedObjectsClient)
.then((fetchedDataSources) => {
if (fetchedDataSources?.length) {
const dataSourceOptions = fetchedDataSources.map((dataSource) => ({
id: dataSource.id,
label: dataSource.title,
}));

dataSourceOptions.push(LocalCluster);

if (!this._isMounted) return;
this.setState({
...this.state,
dataSources: dataSourceOptions,
});
}
})
.catch(() => {
this.props.notifications.addWarning(
i18n.translate('dataSource.fetchDataSourceError', {
defaultMessage: 'Unable to fetch existing data sources',
})
);
});
}

onChange(e) {
if (!this._isMounted) return;
this.setState({
selectedOption: e,
});
this.props.onSelectedDataSource(e);
}

render() {
return (
<EuiComboBox
aria-label={i18n.translate('dataSourceComboBoxAriaLabel', {
defaultMessage: 'Select a data source',
})}
placeholder={i18n.translate('dataSourceComboBoxPlaceholder', {
defaultMessage: 'Select a data source',
})}
singleSelection={{ asPlainText: true }}
options={this.state.dataSources}
selectedOptions={this.state.selectedOption}
onChange={(e) => this.onChange(e)}
prepend={i18n.translate('dataSourceComboBoxPrepend', {
defaultMessage: 'Data source',
})}
compressed
isDisabled={this.props.disabled}
/>
);
}
}
66 changes: 8 additions & 58 deletions src/plugins/dev_tools/public/application.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,10 @@
* under the License.
*/

import React, { useEffect, useRef, useState } from 'react';
import React, { useEffect, useRef } from 'react';
import ReactDOM from 'react-dom';
import { HashRouter as Router, Switch, Route, Redirect } from 'react-router-dom';
import {
EuiTab,
EuiTabs,
EuiToolTip,
EuiComboBox,
EuiFlexGroup,
EuiFlexItem,
EuiComboBoxOptionOption,
} from '@elastic/eui';
import { EuiTab, EuiTabs, EuiToolTip, EuiComboBoxOptionOption } from '@elastic/eui';
import { I18nProvider } from '@osd/i18n/react';
import { i18n } from '@osd/i18n';

Expand All @@ -52,9 +44,8 @@ import {
ScopedHistory,
} from 'src/core/public';

import { useEffectOnce } from 'react-use';
// eslint-disable-next-line @osd/eslint/no-restricted-paths
import { getDataSources } from '../../data_source_management/public/components/utils';
import { DataSourcePicker } from '../../data_source_management/public/components/data_source_picker/data_source_picker';
import { DevToolApp } from './dev_tool';
import { DevToolsSetupDependencies } from './plugin';
import { addHelpMenuToAppChrome } from './utils/util';
Expand All @@ -74,11 +65,6 @@ interface MountedDevToolDescriptor {
unmountHandler: () => void;
}

interface DataSourceOption extends EuiComboBoxOptionOption {
id: string;
label: string;
}

function DevToolsWrapper({
devTools,
activeDevTool,
Expand All @@ -88,8 +74,6 @@ function DevToolsWrapper({
dataSourceEnabled,
}: DevToolsWrapperProps) {
const mountedTool = useRef<MountedDevToolDescriptor | null>(null);
const [dataSources, setDataSources] = useState<DataSourceOption[]>([]);
const [selectedOptions, setSelectedOptions] = useState<DataSourceOption[]>([]);

useEffect(
() => () => {
Expand All @@ -100,33 +84,8 @@ function DevToolsWrapper({
[]
);

useEffectOnce(() => {
fetchDataSources();
});

const fetchDataSources = () => {
getDataSources(savedObjects.client)
.then((fetchedDataSources) => {
if (fetchedDataSources?.length) {
const dataSourceOptions = fetchedDataSources.map((dataSource) => ({
id: dataSource.id,
label: dataSource.title,
}));
setDataSources(dataSourceOptions);
}
})
.catch(() => {
toasts.addDanger(
i18n.translate('devTools.devToolWrapper.fetchDataSourceError', {
defaultMessage: 'Unable to fetch existing data sources',
})
);
});
};

const onChange = async (e: Array<EuiComboBoxOptionOption<any>>) => {
const dataSourceId = e[0] ? e[0].id : undefined;
setSelectedOptions(e);
await remount(mountedTool.current!.mountpoint, dataSourceId);
};

Expand Down Expand Up @@ -173,20 +132,11 @@ function DevToolsWrapper({
))}
{dataSourceEnabled ? (
<div className="devAppDataSourcePicker">
<EuiComboBox
aria-label={i18n.translate('devTools.devToolWrapper.DataSourceComboBoxAriaLabel', {
defaultMessage: 'Select a data source',
})}
placeholder={i18n.translate('devTools.devToolWrapper.DataSourceComboBoxPlaceholder', {
defaultMessage: 'Select a data source',
})}
singleSelection={{ asPlainText: true }}
options={dataSources}
selectedOptions={selectedOptions}
onChange={onChange}
prepend="Data source"
compressed
isDisabled={!dataSourceEnabled}
<DataSourcePicker
savedObjectsClient={savedObjects.client}
notifications={toasts}
onSelectedDataSource={onChange}
disabled={!dataSourceEnabled}
/>
</div>
) : null}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import { Synopsis } from './synopsis';
import { SampleDataSetCards } from './sample_data_set_cards';
import { getServices } from '../opensearch_dashboards_services';
// eslint-disable-next-line @osd/eslint/no-restricted-paths
import { getDataSources } from '../../../../data_source_management/public/components/utils';
import { DataSourcePicker } from '../../../../data_source_management/public/components/data_source_picker/data_source_picker';

import {
EuiPage,
Expand All @@ -48,7 +48,6 @@ import {
EuiSpacer,
EuiTitle,
EuiPageBody,
EuiComboBox,
} from '@elastic/eui';

import { getTutorials } from '../load_tutorials';
Expand All @@ -62,9 +61,6 @@ const homeTitle = i18n.translate('home.breadcrumbs.homeTitle', { defaultMessage:
const addDataTitle = i18n.translate('home.breadcrumbs.addDataTitle', {
defaultMessage: 'Add data',
});
const localCluster = i18n.translate('home.dataSource.localCluster', {
defaultMessage: 'Local Cluster',
});

class TutorialDirectoryUi extends React.Component {
constructor(props) {
Expand All @@ -88,7 +84,6 @@ class TutorialDirectoryUi extends React.Component {
tutorialCards: [],
notices: getServices().tutorialService.getDirectoryNotices(),
isDataSourceEnabled: !!getServices().dataSource,
selectedOption: [{ label: localCluster }],
};
}

Expand Down Expand Up @@ -160,31 +155,6 @@ class TutorialDirectoryUi extends React.Component {
return a.name.toLowerCase().localeCompare(b.name.toLowerCase());
});

if (this.state.isDataSourceEnabled) {
getDataSources(getServices().savedObjectsClient)
.then((fetchedDataSources) => {
if (fetchedDataSources?.length) {
const dataSourceOptions = fetchedDataSources.map((dataSource) => ({
id: dataSource.id,
label: dataSource.title,
}));

dataSourceOptions.push({ label: localCluster });
this.setState({
// eslint-disable-line react/no-did-mount-set-state
dataSources: dataSourceOptions,
});
}
})
.catch(() => {
getServices().toastNotifications.addWarning(
i18n.translate('home.dataSource.fetchDataSourceError', {
defaultMessage: 'Unable to fetch existing data sources',
})
);
});
}

this.setState({
// eslint-disable-line react/no-did-mount-set-state
tutorialCards: tutorialCards,
Expand All @@ -197,12 +167,6 @@ class TutorialDirectoryUi extends React.Component {
});
};

onSelectedDataSourceChange = (e) => {
this.setState({ selectedOption: e });
const dataSourceId = e[0] ? e[0].id : undefined;
this.setState({ selectedDataSourceId: dataSourceId });
};

renderTabs = () => {
return this.tabs.map((tab, index) => (
<EuiTab
Expand Down Expand Up @@ -255,25 +219,21 @@ class TutorialDirectoryUi extends React.Component {
);
};

onSelectedDataSourceChange = (e) => {
const dataSourceId = e[0] ? e[0].id : undefined;
this.setState({ selectedDataSourceId: dataSourceId });
};

renderDataSourceSelector = () => {
const { isDataSourceEnabled, dataSources, selectedOption } = this.state;
const { isDataSourceEnabled } = this.state;

return isDataSourceEnabled ? (
<div className="sampledataSourcePicker">
<EuiComboBox
aria-label={i18n.translate('sampleData.DataSourceComboBoxAriaLabel', {
defaultMessage: 'Select a Data Source',
})}
placeholder={i18n.translate('sampleData.DataSourceComboBoxPlaceholder', {
defaultMessage: 'Select a Data Source',
})}
singleSelection={{ asPlainText: true }}
options={dataSources}
selectedOptions={selectedOption}
onChange={this.onSelectedDataSourceChange}
prepend="DataSource"
compressed
isDisabled={!isDataSourceEnabled}
<DataSourcePicker
savedObjectsClient={getServices().savedObjectsClient}
notifications={getServices().toastNotifications}
onSelectedDataSource={this.onSelectedDataSourceChange}
disabled={!isDataSourceEnabled}
/>
</div>
) : null;
Expand Down

0 comments on commit b2d2b26

Please sign in to comment.