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

Add datasource picker component and use it in devtools and tutorial page when multiple datasource is enabled #5756

Merged
merged 3 commits into from
Jan 31, 2024
Merged
Show file tree
Hide file tree
Changes from 2 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
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,87 @@
/*
* 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',
})
);
});
}

onChang(e) {
BionIT marked this conversation as resolved.
Show resolved Hide resolved
if (!this._isMounted) return;
this.setState({
selectedOption: e,
});
this.props.onSelectedDataSource(e);
BionIT marked this conversation as resolved.
Show resolved Hide resolved
}

render() {
return (
<EuiComboBox
aria-label={i18n.translate('dataSourceComboBoxAriaLabel', {
defaultMessage: 'Select a data source',
BionIT marked this conversation as resolved.
Show resolved Hide resolved
})}
placeholder={i18n.translate('dataSourceComboBoxPlaceholder', {
defaultMessage: 'Select a data source',
})}
singleSelection={{ asPlainText: true }}
options={this.state.dataSources}
selectedOptions={this.state.selectedOption}
onChange={(e) => this.onChang(e)}
BionIT marked this conversation as resolved.
Show resolved Hide resolved
prepend="Data source"
BionIT marked this conversation as resolved.
Show resolved Hide resolved
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
Loading