From b2d2b26cf7d33b3d3dba1b13434f86469aa7c4eb Mon Sep 17 00:00:00 2001 From: Lu Yu Date: Tue, 30 Jan 2024 17:07:24 -0800 Subject: [PATCH] Add datasource picker component and use it in devtools and tutorial page 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 * add changelog for 5756 Signed-off-by: Lu Yu * wraps prepend with i18n and fix typo Signed-off-by: Lu Yu --------- Signed-off-by: Lu Yu --- CHANGELOG.md | 1 + .../opensearch_dashboards.json | 2 +- .../data_source_picker/data_source_picker.js | 89 +++++++++++++++++++ src/plugins/dev_tools/public/application.tsx | 66 ++------------ .../components/tutorial_directory.js | 64 +++---------- 5 files changed, 111 insertions(+), 111 deletions(-) create mode 100644 src/plugins/data_source_management/public/components/data_source_picker/data_source_picker.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 1580a367da8a..85935a0a360e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/src/plugins/data_source_management/opensearch_dashboards.json b/src/plugins/data_source_management/opensearch_dashboards.json index 58e81a337e7d..6d0ed5b98d6a 100644 --- a/src/plugins/data_source_management/opensearch_dashboards.json +++ b/src/plugins/data_source_management/opensearch_dashboards.json @@ -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"] } diff --git a/src/plugins/data_source_management/public/components/data_source_picker/data_source_picker.js b/src/plugins/data_source_management/public/components/data_source_picker/data_source_picker.js new file mode 100644 index 000000000000..2747a5f886ce --- /dev/null +++ b/src/plugins/data_source_management/public/components/data_source_picker/data_source_picker.js @@ -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 ( + this.onChange(e)} + prepend={i18n.translate('dataSourceComboBoxPrepend', { + defaultMessage: 'Data source', + })} + compressed + isDisabled={this.props.disabled} + /> + ); + } +} diff --git a/src/plugins/dev_tools/public/application.tsx b/src/plugins/dev_tools/public/application.tsx index 3e5e6991634e..e03733927d7a 100644 --- a/src/plugins/dev_tools/public/application.tsx +++ b/src/plugins/dev_tools/public/application.tsx @@ -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'; @@ -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'; @@ -74,11 +65,6 @@ interface MountedDevToolDescriptor { unmountHandler: () => void; } -interface DataSourceOption extends EuiComboBoxOptionOption { - id: string; - label: string; -} - function DevToolsWrapper({ devTools, activeDevTool, @@ -88,8 +74,6 @@ function DevToolsWrapper({ dataSourceEnabled, }: DevToolsWrapperProps) { const mountedTool = useRef(null); - const [dataSources, setDataSources] = useState([]); - const [selectedOptions, setSelectedOptions] = useState([]); useEffect( () => () => { @@ -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>) => { const dataSourceId = e[0] ? e[0].id : undefined; - setSelectedOptions(e); await remount(mountedTool.current!.mountpoint, dataSourceId); }; @@ -173,20 +132,11 @@ function DevToolsWrapper({ ))} {dataSourceEnabled ? (
-
) : null} diff --git a/src/plugins/home/public/application/components/tutorial_directory.js b/src/plugins/home/public/application/components/tutorial_directory.js index 0dcdc3ec775f..3e8ba2d10c80 100644 --- a/src/plugins/home/public/application/components/tutorial_directory.js +++ b/src/plugins/home/public/application/components/tutorial_directory.js @@ -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, @@ -48,7 +48,6 @@ import { EuiSpacer, EuiTitle, EuiPageBody, - EuiComboBox, } from '@elastic/eui'; import { getTutorials } from '../load_tutorials'; @@ -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) { @@ -88,7 +84,6 @@ class TutorialDirectoryUi extends React.Component { tutorialCards: [], notices: getServices().tutorialService.getDirectoryNotices(), isDataSourceEnabled: !!getServices().dataSource, - selectedOption: [{ label: localCluster }], }; } @@ -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, @@ -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) => ( { + 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 ? (
-
) : null;