From 66d205576c25c9fc336cb7d2572ccf6096bc08e8 Mon Sep 17 00:00:00 2001 From: yujin-emma Date: Fri, 5 Apr 2024 23:13:07 +0000 Subject: [PATCH 01/16] get label from dataSourceOptions Signed-off-by: yujin-emma --- .../data_source_selectable.test.tsx.snap | 52 ++----- .../data_source_selectable.test.tsx | 133 ++++++++++++++++++ .../data_source_selectable.tsx | 124 +++++++++------- .../public/components/utils.ts | 45 +++--- 4 files changed, 234 insertions(+), 120 deletions(-) diff --git a/src/plugins/data_source_management/public/components/data_source_selectable/__snapshots__/data_source_selectable.test.tsx.snap b/src/plugins/data_source_management/public/components/data_source_selectable/__snapshots__/data_source_selectable.test.tsx.snap index f76958715e77..71bc4ab0d412 100644 --- a/src/plugins/data_source_management/public/components/data_source_selectable/__snapshots__/data_source_selectable.test.tsx.snap +++ b/src/plugins/data_source_management/public/components/data_source_selectable/__snapshots__/data_source_selectable.test.tsx.snap @@ -49,23 +49,7 @@ exports[`DataSourceSelectable should filter options if configured 1`] = ` data-test-subj="dataSourceSelectable" isPreFiltered={false} onChange={[Function]} - options={ - Array [ - Object { - "checked": "on", - "id": "", - "label": "Local cluster", - }, - Object { - "id": "test2", - "label": "test2", - }, - Object { - "id": "test3", - "label": "test3", - }, - ] - } + options={Array []} renderOption={[Function]} searchProps={ Object { @@ -96,9 +80,7 @@ exports[`DataSourceSelectable should render normally with local cluster is hidde iconType="database" onClick={[Function]} size="s" - > - - + /> } closePopover={[Function]} @@ -162,9 +144,7 @@ exports[`DataSourceSelectable should render normally with local cluster not hidd iconType="database" onClick={[Function]} size="s" - > - - + /> } closePopover={[Function]} @@ -300,16 +280,11 @@ Object { > @@ -329,24 +304,15 @@ Object {
-
-
-
-
-
+

+ No options available +

diff --git a/src/plugins/data_source_management/public/components/data_source_selectable/data_source_selectable.test.tsx b/src/plugins/data_source_management/public/components/data_source_selectable/data_source_selectable.test.tsx index bbbd40c85857..c17f315a4fd9 100644 --- a/src/plugins/data_source_management/public/components/data_source_selectable/data_source_selectable.test.tsx +++ b/src/plugins/data_source_management/public/components/data_source_selectable/data_source_selectable.test.tsx @@ -169,4 +169,137 @@ describe('DataSourceSelectable', () => { expect(onSelectedDataSource).toHaveBeenCalled(); expect(utils.getDefaultDataSource).toHaveBeenCalled(); }); + + it('should display selected option label normally', async () => { + const onSelectedDataSource = jest.fn(); + const container = render( + ds.attributes.auth.type !== AuthType.NoAuth} + /> + ); + + await nextTick(); + const button = await container.findByTestId('dataSourceSelectableContextMenuHeaderLink'); + expect(button).toHaveTextContent('test2'); + }); + + it('should display Local Cluster when not provide selectedOption', async () => { + const onSelectedDataSource = jest.fn(); + const container = render( + ds.attributes.auth.type !== AuthType.NoAuth} + /> + ); + await nextTick(); + const button = await container.findByTestId('dataSourceSelectableContextMenuHeaderLink'); + expect(button).toHaveTextContent('Local cluster'); + }); + + it('should render normally even only provide dataSourceId', async () => { + const onSelectedDataSource = jest.fn(); + const container = render( + ds.attributes.auth.type !== AuthType.NoAuth} + /> + ); + await nextTick(); + const button = await container.findByTestId('dataSourceSelectableContextMenuHeaderLink'); + expect(button).toHaveTextContent('test2'); + }); + + it('should render warning if provide undefined dataSourceId', async () => { + const onSelectedDataSource = jest.fn(); + const container = render( + ds.attributes.auth.type !== AuthType.NoAuth} + /> + ); + await nextTick(); + const button = await container.findByTestId('dataSourceSelectableContextMenuHeaderLink'); + expect(button).toHaveTextContent(''); + expect(toasts.addWarning).toBeCalledWith('Data source with id is not available'); + }); + + it('should render warning if provide empty object', async () => { + const onSelectedDataSource = jest.fn(); + const container = render( + ds.attributes.auth.type !== AuthType.NoAuth} + /> + ); + await nextTick(); + const button = await container.findByTestId('dataSourceSelectableContextMenuHeaderLink'); + expect(button).toHaveTextContent(''); + expect(toasts.addWarning).toBeCalledWith('Data source with id is not available'); + }); + it('should warning if only provide label', async () => { + const onSelectedDataSource = jest.fn(); + const container = render( + ds.attributes.auth.type !== AuthType.NoAuth} + /> + ); + await nextTick(); + const button = await container.findByTestId('dataSourceSelectableContextMenuHeaderLink'); + expect(toasts.addWarning).toBeCalledWith('Data source with id is not available'); + }); + it('should warning if only provide empty label', async () => { + const onSelectedDataSource = jest.fn(); + const container = render( + ds.attributes.auth.type !== AuthType.NoAuth} + /> + ); + await nextTick(); + const button = await container.findByTestId('dataSourceSelectableContextMenuHeaderLink'); + expect(toasts.addWarning).toBeCalledWith('Data source with id is not available'); + }); }); diff --git a/src/plugins/data_source_management/public/components/data_source_selectable/data_source_selectable.tsx b/src/plugins/data_source_management/public/components/data_source_selectable/data_source_selectable.tsx index 5a4591597ab6..574310292fe0 100644 --- a/src/plugins/data_source_management/public/components/data_source_selectable/data_source_selectable.tsx +++ b/src/plugins/data_source_management/public/components/data_source_selectable/data_source_selectable.tsx @@ -21,7 +21,7 @@ import { SavedObjectsClientContract, ToastsStart, } from 'opensearch-dashboards/public'; -import { getDataSourcesWithFields, getDefaultDataSource } from '../utils'; +import { getDataSourcesWithFields, getDefaultDataSource, getFilteredDataSources } from '../utils'; import { LocalCluster } from '../data_source_selector/data_source_selector'; import { SavedObject } from '../../../../../core/public'; import { DataSourceAttributes } from '../../types'; @@ -43,7 +43,7 @@ interface DataSourceSelectableState { dataSourceOptions: SelectedDataSourceOption[]; isPopoverOpen: boolean; selectedOption?: SelectedDataSourceOption[]; - defaultDataSource: string | null; + defaultDataSource?: string; } interface SelectedDataSourceOption extends DataSourceOption { @@ -81,71 +81,94 @@ export class DataSourceSelectable extends React.Component< this.setState({ ...this.state, isPopoverOpen: false }); } + handleSelectedOption(dataSourceOptions: DataSourceOption[]) { + const [{ id }] = this.props.selectedOption!; + const dsOption = dataSourceOptions.find((ds) => ds.id === id); + if (!dsOption) { + this.props.notifications.addWarning( + i18n.translate('dataSource.fetchDataSourceError', { + defaultMessage: 'Data source with id is not available', + }) + ); + this.setState({ + ...this.state, + selectedOption: [], + }); + this.props.onSelectedDataSources([]); + return; + } + this.setState({ + ...this.state, + selectedOption: [{ id, label: dsOption.label }], + }); + this.props.onSelectedDataSources([{ id, label: dsOption.label }]); + } + + handleDefaultDataSource(dataSourceOptions: DataSourceOption[], defaultDataSource?: string) { + const selectedDataSource = getDefaultDataSource( + dataSourceOptions, + LocalCluster, + defaultDataSource, + this.props.hideLocalCluster + ); + + // no active option, didnot find valid option + if (selectedDataSource.length === 0) { + this.props.notifications.addWarning('No connected data source available.'); + this.props.onSelectedDataSources([]); + return; + } + + this.setState({ + ...this.state, + selectedOption: selectedDataSource, + }); + + this.props.onSelectedDataSources(selectedDataSource); + } + async componentDidMount() { this._isMounted = true; try { - let filteredDataSources: Array> = []; - let dataSourceOptions: DataSourceOption[] = []; - - // Fetch data sources with fields + // 1. Fetch const fetchedDataSources = await getDataSourcesWithFields(this.props.savedObjectsClient, [ 'id', 'title', 'auth.type', ]); - if (fetchedDataSources?.length) { - filteredDataSources = this.props.dataSourceFilter - ? fetchedDataSources.filter((ds) => this.props.dataSourceFilter!(ds)) - : fetchedDataSources; - dataSourceOptions = filteredDataSources - .map((dataSource) => ({ - id: dataSource.id, - label: dataSource.attributes?.title || '', - })) - .sort((a, b) => a.label.toLowerCase().localeCompare(b.label.toLowerCase())); - } + // 2. Process + const dataSourceOptions: DataSourceOption[] = getFilteredDataSources( + fetchedDataSources, + this.props.dataSourceFilter + ); - // Add local cluster to the list of data sources if it is not hidden. + // 3. Add local cluster as option if (!this.props.hideLocalCluster) { dataSourceOptions.unshift(LocalCluster); } const defaultDataSource = this.props.uiSettings?.get('defaultDataSource', null) ?? null; - const selectedDataSource = getDefaultDataSource( - filteredDataSources, - LocalCluster, - this.props.uiSettings, - this.props.hideLocalCluster, - this.props.selectedOption - ); - if (selectedDataSource.length === 0) { - this.props.notifications.addWarning('No connected data source available.'); - } else { - // Update the checked status of the selected data source. - const updatedDataSourceOptions: SelectedDataSourceOption[] = dataSourceOptions.map( - (option) => ({ - ...option, - ...(option.id === selectedDataSource[0].id && { checked: 'on' }), - }) - ); - - if (!this._isMounted) return; - - this.setState({ - ...this.state, - dataSourceOptions: updatedDataSourceOptions, - selectedOption: selectedDataSource, - defaultDataSource, - }); - - this.props.onSelectedDataSources(selectedDataSource); + // 4.1 empty default option, [], just want to show placeholder + // devtool, add sample, tsvb, search relevance + if (this.props.selectedOption?.length === 0) { + // don't trigger callback + return; } - } catch (error) { + + // 4.2 handle active option, [{}] + if (this.props.selectedOption?.length) { + this.handleSelectedOption(dataSourceOptions, fetchedDataSources, defaultDataSource); + return; + } + + // 4.3 handle default data source + this.handleDefaultDataSource(dataSourceOptions, fetchedDataSources, defaultDataSource); + } catch (err) { this.props.notifications.addWarning( i18n.translate('dataSource.fetchDataSourceError', { - defaultMessage: 'Unable to fetch existing data sources', + defaultMessage: 'Unable to fetch existing data sources' + err, }) ); } @@ -183,10 +206,9 @@ export class DataSourceSelectable extends React.Component< size="s" disabled={this.props.disabled || false} > - {(this.state.selectedOption && + {this.state.selectedOption && this.state.selectedOption.length > 0 && - this.state.selectedOption[0].label) || - ''} + this.state.selectedOption[0]?.label} ); diff --git a/src/plugins/data_source_management/public/components/utils.ts b/src/plugins/data_source_management/public/components/utils.ts index be004c989943..ca1d8fbbd4c7 100644 --- a/src/plugins/data_source_management/public/components/utils.ts +++ b/src/plugins/data_source_management/public/components/utils.ts @@ -81,52 +81,45 @@ export async function setFirstDataSourceAsDefault( export function getFilteredDataSources( dataSources: Array>, - filter?: (dataSource: SavedObject) => boolean -) { - return filter ? dataSources.filter((ds) => filter!(ds)) : dataSources; + filter = (ds: SavedObject) => true +): DataSourceOption[] { + return dataSources + .filter((ds) => filter!(ds)) + .map((ds) => ({ + id: ds.id, + label: ds.attributes?.title || '', + })) + .sort((a, b) => a.label.toLowerCase().localeCompare(b.label.toLowerCase())); } export function getDefaultDataSource( - dataSources: Array>, + dataSourcesOptions: DataSourceOption[], LocalCluster: DataSourceOption, - uiSettings?: IUiSettingsClient, - hideLocalCluster?: boolean, - defaultOption?: DataSourceOption[] + defaultDataSourceId?: string, + hideLocalCluster?: boolean ) { - const defaultOptionId = defaultOption?.[0]?.id; - const defaultOptionDataSource = dataSources.find( - (dataSource) => dataSource.id === defaultOptionId - ); - - const defaultDataSourceId = uiSettings?.get('defaultDataSource', null) ?? null; - const defaultDataSourceAfterCheck = dataSources.find( + const defaultDataSourceAfterCheck = dataSourcesOptions.find( (dataSource) => dataSource.id === defaultDataSourceId ); - if (defaultOptionDataSource) { - return [ - { - id: defaultOptionDataSource.id, - label: defaultOption?.[0]?.label || defaultOptionDataSource.attributes?.title, - }, - ]; - } if (defaultDataSourceAfterCheck) { return [ { id: defaultDataSourceAfterCheck.id, - label: defaultDataSourceAfterCheck.attributes?.title || '', + label: defaultDataSourceAfterCheck.label, }, ]; } + if (!hideLocalCluster) { return [LocalCluster]; } - if (dataSources.length > 0) { + + if (dataSourcesOptions.length > 0) { return [ { - id: dataSources[0].id, - label: dataSources[0].attributes.title, + id: dataSourcesOptions[0].id, + label: dataSourcesOptions[0].label, }, ]; } From 372f5cc32c03e9b50a8b84497c6d78a64a0b1852 Mon Sep 17 00:00:00 2001 From: yujin-emma Date: Fri, 5 Apr 2024 23:51:23 +0000 Subject: [PATCH 02/16] update dataSourceOptions Signed-off-by: yujin-emma --- .../data_source_selectable.test.tsx.snap | 43 ++++++++++++++++--- .../data_source_selectable.test.tsx | 19 ++++++++ .../data_source_selectable.tsx | 3 ++ 3 files changed, 58 insertions(+), 7 deletions(-) diff --git a/src/plugins/data_source_management/public/components/data_source_selectable/__snapshots__/data_source_selectable.test.tsx.snap b/src/plugins/data_source_management/public/components/data_source_selectable/__snapshots__/data_source_selectable.test.tsx.snap index 71bc4ab0d412..b3e280753098 100644 --- a/src/plugins/data_source_management/public/components/data_source_selectable/__snapshots__/data_source_selectable.test.tsx.snap +++ b/src/plugins/data_source_management/public/components/data_source_selectable/__snapshots__/data_source_selectable.test.tsx.snap @@ -49,7 +49,22 @@ exports[`DataSourceSelectable should filter options if configured 1`] = ` data-test-subj="dataSourceSelectable" isPreFiltered={false} onChange={[Function]} - options={Array []} + options={ + Array [ + Object { + "id": "", + "label": "Local cluster", + }, + Object { + "id": "test2", + "label": "test2", + }, + Object { + "id": "test3", + "label": "test3", + }, + ] + } renderOption={[Function]} searchProps={ Object { @@ -280,11 +295,16 @@ Object { > @@ -304,15 +324,24 @@ Object {
+
-

- No options available -

+
+
+
+
diff --git a/src/plugins/data_source_management/public/components/data_source_selectable/data_source_selectable.test.tsx b/src/plugins/data_source_management/public/components/data_source_selectable/data_source_selectable.test.tsx index c17f315a4fd9..46c62b516299 100644 --- a/src/plugins/data_source_management/public/components/data_source_selectable/data_source_selectable.test.tsx +++ b/src/plugins/data_source_management/public/components/data_source_selectable/data_source_selectable.test.tsx @@ -302,4 +302,23 @@ describe('DataSourceSelectable', () => { const button = await container.findByTestId('dataSourceSelectableContextMenuHeaderLink'); expect(toasts.addWarning).toBeCalledWith('Data source with id is not available'); }); + + it('should warning if only provide empty array', async () => { + const onSelectedDataSource = jest.fn(); + const container = render( + ds.attributes.auth.type !== AuthType.NoAuth} + /> + ); + await nextTick(); + const button = await container.findByTestId('dataSourceSelectableContextMenuHeaderLink'); + expect(toasts.addWarning).toBeCalledWith('Data source with id is not available'); + }); }); diff --git a/src/plugins/data_source_management/public/components/data_source_selectable/data_source_selectable.tsx b/src/plugins/data_source_management/public/components/data_source_selectable/data_source_selectable.tsx index 574310292fe0..9540b98e2dd4 100644 --- a/src/plugins/data_source_management/public/components/data_source_selectable/data_source_selectable.tsx +++ b/src/plugins/data_source_management/public/components/data_source_selectable/data_source_selectable.tsx @@ -92,6 +92,7 @@ export class DataSourceSelectable extends React.Component< ); this.setState({ ...this.state, + dataSourceOptions, selectedOption: [], }); this.props.onSelectedDataSources([]); @@ -99,6 +100,7 @@ export class DataSourceSelectable extends React.Component< } this.setState({ ...this.state, + dataSourceOptions, selectedOption: [{ id, label: dsOption.label }], }); this.props.onSelectedDataSources([{ id, label: dsOption.label }]); @@ -122,6 +124,7 @@ export class DataSourceSelectable extends React.Component< this.setState({ ...this.state, selectedOption: selectedDataSource, + dataSourceOptions, }); this.props.onSelectedDataSources(selectedDataSource); From 83e0780aa3b240ada91097cc9631a545a0a77c58 Mon Sep 17 00:00:00 2001 From: yujin-emma Date: Fri, 5 Apr 2024 23:52:33 +0000 Subject: [PATCH 03/16] update changelog Signed-off-by: yujin-emma --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index abaf2f303b4a..1bdf4e1fdc74 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -77,6 +77,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - [Multiple Datasource] Enhanced data source selector with default datasource shows as first choice ([#6293](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6293)) - [Multiple Datasource] Add multi data source support to sample vega visualizations ([#6218](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6218)) - [Multiple Datasource] Fetch data source title for DataSourceView when only id is provided ([#6315](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6315) +- [Multiple Datasource] Get data source label when only id is provided in DataSourceSelectable ([#6358](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6358) - [Workspace] Add permission control logic ([#6052](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6052)) - [Multiple Datasource] Add default icon for selectable component and make sure the default datasource shows automatically ([#6327](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6327)) - [Multiple Datasource] Pass selected data sources to plugin consumers when the multi-select component initially loads ([#6333](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6333)) From 0c51068fc5dd942f653e814da0e31b5621eba4a0 Mon Sep 17 00:00:00 2001 From: yujin-emma Date: Sun, 7 Apr 2024 04:22:05 +0000 Subject: [PATCH 04/16] fix failed test Signed-off-by: yujin-emma --- .../data_source_selector.test.tsx.snap | 22 +++++------ .../public/components/utils.test.ts | 39 ++++++++----------- .../data_source_management/public/mocks.ts | 15 +++++++ 3 files changed, 42 insertions(+), 34 deletions(-) diff --git a/src/plugins/data_source_management/public/components/data_source_selector/__snapshots__/data_source_selector.test.tsx.snap b/src/plugins/data_source_management/public/components/data_source_selector/__snapshots__/data_source_selector.test.tsx.snap index 495331156fee..5cb8738e5c17 100644 --- a/src/plugins/data_source_management/public/components/data_source_selector/__snapshots__/data_source_selector.test.tsx.snap +++ b/src/plugins/data_source_management/public/components/data_source_selector/__snapshots__/data_source_selector.test.tsx.snap @@ -80,15 +80,15 @@ exports[`DataSourceSelector: check dataSource options should always place local }, Object { "id": "test1", - "label": "test1", + "label": "", }, Object { "id": "test2", - "label": "test2", + "label": "", }, Object { "id": "test3", - "label": "test3", + "label": "", }, ] } @@ -130,11 +130,11 @@ exports[`DataSourceSelector: check dataSource options should filter options if c }, Object { "id": "test2", - "label": "test2", + "label": "", }, Object { "id": "test3", - "label": "test3", + "label": "", }, ] } @@ -214,15 +214,15 @@ exports[`DataSourceSelector: check dataSource options should hide prepend if rem }, Object { "id": "test1", - "label": "test1", + "label": "", }, Object { "id": "test2", - "label": "test2", + "label": "", }, Object { "id": "test3", - "label": "test3", + "label": "", }, ] } @@ -325,15 +325,15 @@ exports[`DataSourceSelector: check dataSource options should show custom placeho }, Object { "id": "test1", - "label": "test1", + "label": "", }, Object { "id": "test2", - "label": "test2", + "label": "", }, Object { "id": "test3", - "label": "test3", + "label": "", }, ] } diff --git a/src/plugins/data_source_management/public/components/utils.test.ts b/src/plugins/data_source_management/public/components/utils.test.ts index 9dc3a8824cb6..63fac96be57e 100644 --- a/src/plugins/data_source_management/public/components/utils.test.ts +++ b/src/plugins/data_source_management/public/components/utils.test.ts @@ -31,6 +31,7 @@ import { mockUiSettingsCalls, getSingleDataSourceResponse, getDataSource, + getDataSourceOptions, } from '../mocks'; import { AuthType, @@ -518,24 +519,21 @@ describe('DataSourceManagement: Utils.ts', () => { const result = getFilteredDataSources(dataSources); - expect(result).toEqual(dataSources); + expect(result).toEqual([ + { + id: '1', + label: 'DataSource 1', + }, + ]); }); test('should return filtered data sources when a filter is provided', () => { const filter = (dataSource: SavedObject) => dataSource.id === '2'; const result = getFilteredDataSources(getDataSource, filter); - expect(result).toEqual([ { id: '2', - type: '', - references: [], - attributes: { - title: 'DataSource 2', - endpoint: '', - auth: { type: AuthType.NoAuth, credentials: undefined }, - name: AuthType.NoAuth, - }, + label: 'DataSource 2', }, ]); }); @@ -543,39 +541,34 @@ describe('DataSourceManagement: Utils.ts', () => { describe('getDefaultDataSource', () => { const LocalCluster = { id: 'local', label: 'Local Cluster' }; const hideLocalCluster = false; - const defaultOption = [{ id: '2', label: 'Default Option' }]; + const defaultOption = [{ id: '2', label: 'DataSource 2' }]; it('should return the default option if it exists in the data sources', () => { + mockUiSettingsCalls(uiSettings, 'get', '2'); const result = getDefaultDataSource( - getDataSource, + getDataSourceOptions, LocalCluster, - uiSettings, - hideLocalCluster, - defaultOption + '2', + hideLocalCluster ); expect(result).toEqual([defaultOption[0]]); }); it('should return local cluster if it exists and no default options in the data sources', () => { mockUiSettingsCalls(uiSettings, 'get', null); - const result = getDefaultDataSource( - getDataSource, - LocalCluster, - uiSettings, - hideLocalCluster - ); + const result = getDefaultDataSource(getDataSource, LocalCluster, null, hideLocalCluster); expect(result).toEqual([LocalCluster]); }); it('should return the default datasource if hideLocalCluster is false', () => { mockUiSettingsCalls(uiSettings, 'get', '2'); - const result = getDefaultDataSource(getDataSource, LocalCluster, uiSettings, true); + const result = getDefaultDataSource(getDataSourceOptions, LocalCluster, '2', false); expect(result).toEqual([{ id: '2', label: 'DataSource 2' }]); }); it('should return the first data source if no default option, hideLocalCluster is ture and no default datasource', () => { mockUiSettingsCalls(uiSettings, 'get', null); - const result = getDefaultDataSource(getDataSource, LocalCluster, uiSettings, true); + const result = getDefaultDataSource(getDataSourceOptions, LocalCluster, uiSettings, true); expect(result).toEqual([{ id: '1', label: 'DataSource 1' }]); }); }); diff --git a/src/plugins/data_source_management/public/mocks.ts b/src/plugins/data_source_management/public/mocks.ts index acab48df52db..e90ab9c496f7 100644 --- a/src/plugins/data_source_management/public/mocks.ts +++ b/src/plugins/data_source_management/public/mocks.ts @@ -114,6 +114,21 @@ export const getDataSource = [ }, ]; +export const getDataSourceOptions = [ + { + id: '1', + label: 'DataSource 1', + }, + { + id: '2', + label: 'DataSource 2', + }, + { + id: '3', + label: 'DataSource 1', + }, +]; + /* Mock data responses - JSON*/ export const getDataSourcesResponse = { savedObjects: [ From 039dfde95cce895bf8ca294f036c4ec47d95721c Mon Sep 17 00:00:00 2001 From: yujin-emma Date: Mon, 8 Apr 2024 01:13:57 +0000 Subject: [PATCH 05/16] address comments and fix test Signed-off-by: yujin-emma --- .../data_source_selectable.test.tsx | 10 ++++----- .../data_source_selectable.tsx | 21 +++++++------------ 2 files changed, 13 insertions(+), 18 deletions(-) diff --git a/src/plugins/data_source_management/public/components/data_source_selectable/data_source_selectable.test.tsx b/src/plugins/data_source_management/public/components/data_source_selectable/data_source_selectable.test.tsx index 46c62b516299..142b1025b148 100644 --- a/src/plugins/data_source_management/public/components/data_source_selectable/data_source_selectable.test.tsx +++ b/src/plugins/data_source_management/public/components/data_source_selectable/data_source_selectable.test.tsx @@ -244,7 +244,7 @@ describe('DataSourceSelectable', () => { await nextTick(); const button = await container.findByTestId('dataSourceSelectableContextMenuHeaderLink'); expect(button).toHaveTextContent(''); - expect(toasts.addWarning).toBeCalledWith('Data source with id is not available'); + expect(toasts.addWarning).toBeCalledWith('Data source with id: undefined is not available'); }); it('should render warning if provide empty object', async () => { @@ -264,7 +264,7 @@ describe('DataSourceSelectable', () => { await nextTick(); const button = await container.findByTestId('dataSourceSelectableContextMenuHeaderLink'); expect(button).toHaveTextContent(''); - expect(toasts.addWarning).toBeCalledWith('Data source with id is not available'); + expect(toasts.addWarning).toBeCalledWith('Data source with id: undefined is not available'); }); it('should warning if only provide label', async () => { const onSelectedDataSource = jest.fn(); @@ -282,7 +282,7 @@ describe('DataSourceSelectable', () => { ); await nextTick(); const button = await container.findByTestId('dataSourceSelectableContextMenuHeaderLink'); - expect(toasts.addWarning).toBeCalledWith('Data source with id is not available'); + expect(toasts.addWarning).toBeCalledWith('Data source with id: undefined is not available'); }); it('should warning if only provide empty label', async () => { const onSelectedDataSource = jest.fn(); @@ -300,7 +300,7 @@ describe('DataSourceSelectable', () => { ); await nextTick(); const button = await container.findByTestId('dataSourceSelectableContextMenuHeaderLink'); - expect(toasts.addWarning).toBeCalledWith('Data source with id is not available'); + expect(toasts.addWarning).toBeCalledWith('Data source with id: undefined is not available'); }); it('should warning if only provide empty array', async () => { @@ -319,6 +319,6 @@ describe('DataSourceSelectable', () => { ); await nextTick(); const button = await container.findByTestId('dataSourceSelectableContextMenuHeaderLink'); - expect(toasts.addWarning).toBeCalledWith('Data source with id is not available'); + expect(toasts.addWarning).toBeCalledWith('Data source with id: undefined is not available'); }); }); diff --git a/src/plugins/data_source_management/public/components/data_source_selectable/data_source_selectable.tsx b/src/plugins/data_source_management/public/components/data_source_selectable/data_source_selectable.tsx index 9540b98e2dd4..b659728a3628 100644 --- a/src/plugins/data_source_management/public/components/data_source_selectable/data_source_selectable.tsx +++ b/src/plugins/data_source_management/public/components/data_source_selectable/data_source_selectable.tsx @@ -87,7 +87,7 @@ export class DataSourceSelectable extends React.Component< if (!dsOption) { this.props.notifications.addWarning( i18n.translate('dataSource.fetchDataSourceError', { - defaultMessage: 'Data source with id is not available', + defaultMessage: `Data source with id: ${id} is not available`, }) ); this.setState({ @@ -101,7 +101,7 @@ export class DataSourceSelectable extends React.Component< this.setState({ ...this.state, dataSourceOptions, - selectedOption: [{ id, label: dsOption.label }], + selectedOption: [{ id, label: dsOption.label, checked: 'on' }], }); this.props.onSelectedDataSources([{ id, label: dsOption.label }]); } @@ -114,7 +114,7 @@ export class DataSourceSelectable extends React.Component< this.props.hideLocalCluster ); - // no active option, didnot find valid option + // no active option, show warning if (selectedDataSource.length === 0) { this.props.notifications.addWarning('No connected data source available.'); this.props.onSelectedDataSources([]); @@ -133,45 +133,40 @@ export class DataSourceSelectable extends React.Component< async componentDidMount() { this._isMounted = true; try { - // 1. Fetch const fetchedDataSources = await getDataSourcesWithFields(this.props.savedObjectsClient, [ 'id', 'title', 'auth.type', ]); - // 2. Process const dataSourceOptions: DataSourceOption[] = getFilteredDataSources( fetchedDataSources, this.props.dataSourceFilter ); - // 3. Add local cluster as option if (!this.props.hideLocalCluster) { dataSourceOptions.unshift(LocalCluster); } const defaultDataSource = this.props.uiSettings?.get('defaultDataSource', null) ?? null; - // 4.1 empty default option, [], just want to show placeholder - // devtool, add sample, tsvb, search relevance + // no active option pass in, do nothing if (this.props.selectedOption?.length === 0) { - // don't trigger callback + // don't trigger callback since the selectedOption is empty return; } - // 4.2 handle active option, [{}] if (this.props.selectedOption?.length) { this.handleSelectedOption(dataSourceOptions, fetchedDataSources, defaultDataSource); return; } - // 4.3 handle default data source + // handle default data source if there is no valid active option this.handleDefaultDataSource(dataSourceOptions, fetchedDataSources, defaultDataSource); - } catch (err) { + } catch (error) { this.props.notifications.addWarning( i18n.translate('dataSource.fetchDataSourceError', { - defaultMessage: 'Unable to fetch existing data sources' + err, + defaultMessage: 'Unable to fetch existing data sources', }) ); } From f8da881f8114a5355d9badd5ce0eb94cd3a6e0eb Mon Sep 17 00:00:00 2001 From: yujin-emma Date: Mon, 8 Apr 2024 18:19:39 +0000 Subject: [PATCH 06/16] update selected option checked status and udpate snapshot Signed-off-by: yujin-emma --- .../data_source_selectable.test.tsx.snap | 1 + .../data_source_selectable.tsx | 24 +++++++++++++++++-- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/plugins/data_source_management/public/components/data_source_selectable/__snapshots__/data_source_selectable.test.tsx.snap b/src/plugins/data_source_management/public/components/data_source_selectable/__snapshots__/data_source_selectable.test.tsx.snap index b3e280753098..adc2929a7bc8 100644 --- a/src/plugins/data_source_management/public/components/data_source_selectable/__snapshots__/data_source_selectable.test.tsx.snap +++ b/src/plugins/data_source_management/public/components/data_source_selectable/__snapshots__/data_source_selectable.test.tsx.snap @@ -52,6 +52,7 @@ exports[`DataSourceSelectable should filter options if configured 1`] = ` options={ Array [ Object { + "checked": "on", "id": "", "label": "Local cluster", }, diff --git a/src/plugins/data_source_management/public/components/data_source_selectable/data_source_selectable.tsx b/src/plugins/data_source_management/public/components/data_source_selectable/data_source_selectable.tsx index b659728a3628..5a7c24a1889f 100644 --- a/src/plugins/data_source_management/public/components/data_source_selectable/data_source_selectable.tsx +++ b/src/plugins/data_source_management/public/components/data_source_selectable/data_source_selectable.tsx @@ -81,6 +81,17 @@ export class DataSourceSelectable extends React.Component< this.setState({ ...this.state, isPopoverOpen: false }); } + // Update the checked status of the selected data source. + getUpdatedDataSourceOptions( + selectedDataSourceId: string, + dataSourceOptions: DataSourceOption[] + ): SelectedDataSourceOption[] { + return dataSourceOptions.map((option) => ({ + ...option, + ...(option.id === selectedDataSourceId && { checked: 'on' }), + })); + } + handleSelectedOption(dataSourceOptions: DataSourceOption[]) { const [{ id }] = this.props.selectedOption!; const dsOption = dataSourceOptions.find((ds) => ds.id === id); @@ -98,9 +109,13 @@ export class DataSourceSelectable extends React.Component< this.props.onSelectedDataSources([]); return; } + const updatedDataSourceOptions: SelectedDataSourceOption[] = this.getUpdatedDataSourceOptions( + id, + dataSourceOptions + ); this.setState({ ...this.state, - dataSourceOptions, + dataSourceOptions: updatedDataSourceOptions, selectedOption: [{ id, label: dsOption.label, checked: 'on' }], }); this.props.onSelectedDataSources([{ id, label: dsOption.label }]); @@ -121,10 +136,15 @@ export class DataSourceSelectable extends React.Component< return; } + const updatedDataSourceOptions: SelectedDataSourceOption[] = this.getUpdatedDataSourceOptions( + selectedDataSource[0].id, + dataSourceOptions + ); + this.setState({ ...this.state, selectedOption: selectedDataSource, - dataSourceOptions, + dataSourceOptions: updatedDataSourceOptions, }); this.props.onSelectedDataSources(selectedDataSource); From 08633eb1b52286c0d5ce8dd481cb862b33e0271e Mon Sep 17 00:00:00 2001 From: yujin-emma Date: Mon, 8 Apr 2024 19:30:04 +0000 Subject: [PATCH 07/16] update selectable test Signed-off-by: yujin-emma --- config/opensearch_dashboards.yml | 2 +- .../data_source_selectable_example.tsx | 8 ++++ .../data_source_selectable.test.tsx.snap | 4 ++ .../data_source_selectable.test.tsx | 48 +++++++++++++++++-- .../data_source_selectable.tsx | 2 +- .../public/components/utils.ts | 6 ++- 6 files changed, 62 insertions(+), 8 deletions(-) diff --git a/config/opensearch_dashboards.yml b/config/opensearch_dashboards.yml index 40d643b014fd..853a785d26ce 100644 --- a/config/opensearch_dashboards.yml +++ b/config/opensearch_dashboards.yml @@ -247,7 +247,7 @@ # vis_builder.enabled: false # Set the value of this setting to true to enable multiple data source feature. -#data_source.enabled: false +data_source.enabled: true # Set the value of this setting to true to hide local cluster in data source feature. #data_source.hideLocalCluster: false # Set the value of these settings to customize crypto materials to encryption saved credentials diff --git a/examples/multiple_data_source_examples/public/components/data_source_selectable_example.tsx b/examples/multiple_data_source_examples/public/components/data_source_selectable_example.tsx index b81102645de2..345c61c719f0 100644 --- a/examples/multiple_data_source_examples/public/components/data_source_selectable_example.tsx +++ b/examples/multiple_data_source_examples/public/components/data_source_selectable_example.tsx @@ -98,9 +98,17 @@ export const DataSourceSelectableExample = ({ fullWidth: false, savedObjects: savedObjects.client, notifications, + hideLocalCluster: false, + // activeOption: [{id: "fcff3d50-f2f1-11ee-8da7-63a1072e6128"}], // invalid ds only + // activeOption: [{id: "cef124c0-f399-11ee-b119-0769c634fdbe", label: "test2"}], // valid ds only should display test2 + // activeOption: [{id: ""}], // invalid ds only + // activeOption: [], // invalid ds only + // activeOption: [{id: "b707f5b0-f5d7-11ee-9951-911715e0878f"}], onSelectedDataSources: (ds) => { setSelectedDataSources(ds); }, + // dataSourceFilter: (ds) => ds.attributes.title !== 'default-ds' + }} /> ); diff --git a/src/plugins/data_source_management/public/components/data_source_selectable/__snapshots__/data_source_selectable.test.tsx.snap b/src/plugins/data_source_management/public/components/data_source_selectable/__snapshots__/data_source_selectable.test.tsx.snap index adc2929a7bc8..76a01eff0143 100644 --- a/src/plugins/data_source_management/public/components/data_source_selectable/__snapshots__/data_source_selectable.test.tsx.snap +++ b/src/plugins/data_source_management/public/components/data_source_selectable/__snapshots__/data_source_selectable.test.tsx.snap @@ -34,6 +34,7 @@ exports[`DataSourceSelectable should filter options if configured 1`] = ` >
{ @@ -281,7 +281,6 @@ describe('DataSourceSelectable', () => { /> ); await nextTick(); - const button = await container.findByTestId('dataSourceSelectableContextMenuHeaderLink'); expect(toasts.addWarning).toBeCalledWith('Data source with id: undefined is not available'); }); it('should warning if only provide empty label', async () => { @@ -299,7 +298,6 @@ describe('DataSourceSelectable', () => { /> ); await nextTick(); - const button = await container.findByTestId('dataSourceSelectableContextMenuHeaderLink'); expect(toasts.addWarning).toBeCalledWith('Data source with id: undefined is not available'); }); @@ -314,11 +312,51 @@ describe('DataSourceSelectable', () => { hideLocalCluster={true} fullWidth={false} selectedOption={[]} - // dataSourceFilter={(ds) => ds.attributes.auth.type !== AuthType.NoAuth} /> ); await nextTick(); - const button = await container.findByTestId('dataSourceSelectableContextMenuHeaderLink'); expect(toasts.addWarning).toBeCalledWith('Data source with id: undefined is not available'); }); + + it('should render the selected option when pass in the valid dataSourceId', async () => { + const onSelectedDataSource = jest.fn(); + const container = mount( + + ); + await nextTick(); + const containerInstance = container.instance(); + expect(containerInstance.state).toEqual({ + dataSourceOptions: [ + { + id: 'test1', + label: 'test1', + }, + { + checked: 'on', + id: 'test2', + label: 'test2', + }, + { + id: 'test3', + label: 'test3', + }, + ], + defaultDataSource: null, + isPopoverOpen: false, + selectedOption: [ + { + id: 'test2', + label: 'test2', + }, + ], + }); + }); }); diff --git a/src/plugins/data_source_management/public/components/data_source_selectable/data_source_selectable.tsx b/src/plugins/data_source_management/public/components/data_source_selectable/data_source_selectable.tsx index 5a7c24a1889f..433e0130d97f 100644 --- a/src/plugins/data_source_management/public/components/data_source_selectable/data_source_selectable.tsx +++ b/src/plugins/data_source_management/public/components/data_source_selectable/data_source_selectable.tsx @@ -116,7 +116,7 @@ export class DataSourceSelectable extends React.Component< this.setState({ ...this.state, dataSourceOptions: updatedDataSourceOptions, - selectedOption: [{ id, label: dsOption.label, checked: 'on' }], + selectedOption: [{ id, label: dsOption.label }], }); this.props.onSelectedDataSources([{ id, label: dsOption.label }]); } diff --git a/src/plugins/data_source_management/public/components/utils.ts b/src/plugins/data_source_management/public/components/utils.ts index ca1d8fbbd4c7..3f7fc680b5e0 100644 --- a/src/plugins/data_source_management/public/components/utils.ts +++ b/src/plugins/data_source_management/public/components/utils.ts @@ -98,10 +98,11 @@ export function getDefaultDataSource( defaultDataSourceId?: string, hideLocalCluster?: boolean ) { + const defaultDataSourceAfterCheck = dataSourcesOptions.find( (dataSource) => dataSource.id === defaultDataSourceId ); - +// console.log("defaultDataSourceAfterCheck", defaultDataSourceAfterCheck) if (defaultDataSourceAfterCheck) { return [ { @@ -112,8 +113,11 @@ export function getDefaultDataSource( } if (!hideLocalCluster) { + // console.log("hideLocalCluster", hideLocalCluster) + return [LocalCluster]; } + // console.log("dataSourcesOptions[0]", dataSourcesOptions[0]) if (dataSourcesOptions.length > 0) { return [ From 779465c120a1435bd48702d49c97abc30eed9976 Mon Sep 17 00:00:00 2001 From: yujin-emma Date: Mon, 8 Apr 2024 20:16:52 +0000 Subject: [PATCH 08/16] revert example code Signed-off-by: yujin-emma --- .../public/components/data_source_selectable_example.tsx | 8 -------- 1 file changed, 8 deletions(-) diff --git a/examples/multiple_data_source_examples/public/components/data_source_selectable_example.tsx b/examples/multiple_data_source_examples/public/components/data_source_selectable_example.tsx index 345c61c719f0..b81102645de2 100644 --- a/examples/multiple_data_source_examples/public/components/data_source_selectable_example.tsx +++ b/examples/multiple_data_source_examples/public/components/data_source_selectable_example.tsx @@ -98,17 +98,9 @@ export const DataSourceSelectableExample = ({ fullWidth: false, savedObjects: savedObjects.client, notifications, - hideLocalCluster: false, - // activeOption: [{id: "fcff3d50-f2f1-11ee-8da7-63a1072e6128"}], // invalid ds only - // activeOption: [{id: "cef124c0-f399-11ee-b119-0769c634fdbe", label: "test2"}], // valid ds only should display test2 - // activeOption: [{id: ""}], // invalid ds only - // activeOption: [], // invalid ds only - // activeOption: [{id: "b707f5b0-f5d7-11ee-9951-911715e0878f"}], onSelectedDataSources: (ds) => { setSelectedDataSources(ds); }, - // dataSourceFilter: (ds) => ds.attributes.title !== 'default-ds' - }} /> ); From 614a4a2a71c2131de9ac820bc5243590f9498885 Mon Sep 17 00:00:00 2001 From: yujin-emma Date: Mon, 8 Apr 2024 20:17:28 +0000 Subject: [PATCH 09/16] revern config file Signed-off-by: yujin-emma --- config/opensearch_dashboards.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/opensearch_dashboards.yml b/config/opensearch_dashboards.yml index 853a785d26ce..40d643b014fd 100644 --- a/config/opensearch_dashboards.yml +++ b/config/opensearch_dashboards.yml @@ -247,7 +247,7 @@ # vis_builder.enabled: false # Set the value of this setting to true to enable multiple data source feature. -data_source.enabled: true +#data_source.enabled: false # Set the value of this setting to true to hide local cluster in data source feature. #data_source.hideLocalCluster: false # Set the value of these settings to customize crypto materials to encryption saved credentials From 64ca8a666423041ebfcd607268d7ebd358987fe8 Mon Sep 17 00:00:00 2001 From: yujin-emma Date: Mon, 8 Apr 2024 20:28:02 +0000 Subject: [PATCH 10/16] push the utils Signed-off-by: yujin-emma --- src/plugins/data_source_management/public/components/utils.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/plugins/data_source_management/public/components/utils.ts b/src/plugins/data_source_management/public/components/utils.ts index 3f7fc680b5e0..2118fa15d3ad 100644 --- a/src/plugins/data_source_management/public/components/utils.ts +++ b/src/plugins/data_source_management/public/components/utils.ts @@ -98,11 +98,10 @@ export function getDefaultDataSource( defaultDataSourceId?: string, hideLocalCluster?: boolean ) { - const defaultDataSourceAfterCheck = dataSourcesOptions.find( (dataSource) => dataSource.id === defaultDataSourceId ); -// console.log("defaultDataSourceAfterCheck", defaultDataSourceAfterCheck) + // console.log("defaultDataSourceAfterCheck", defaultDataSourceAfterCheck) if (defaultDataSourceAfterCheck) { return [ { From 6f50b19c2c18cdeac867499bc808ddb4025fbb1d Mon Sep 17 00:00:00 2001 From: yujin-emma Date: Mon, 8 Apr 2024 20:59:17 +0000 Subject: [PATCH 11/16] udpate snapshot Signed-off-by: yujin-emma --- .../__snapshots__/data_source_selectable.test.tsx.snap | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/plugins/data_source_management/public/components/data_source_selectable/__snapshots__/data_source_selectable.test.tsx.snap b/src/plugins/data_source_management/public/components/data_source_selectable/__snapshots__/data_source_selectable.test.tsx.snap index 76a01eff0143..adc2929a7bc8 100644 --- a/src/plugins/data_source_management/public/components/data_source_selectable/__snapshots__/data_source_selectable.test.tsx.snap +++ b/src/plugins/data_source_management/public/components/data_source_selectable/__snapshots__/data_source_selectable.test.tsx.snap @@ -34,7 +34,6 @@ exports[`DataSourceSelectable should filter options if configured 1`] = ` >
Date: Mon, 8 Apr 2024 21:13:52 +0000 Subject: [PATCH 12/16] remove console log Signed-off-by: yujin-emma --- src/plugins/data_source_management/public/components/utils.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/plugins/data_source_management/public/components/utils.ts b/src/plugins/data_source_management/public/components/utils.ts index 2118fa15d3ad..6c2bfa3ee55c 100644 --- a/src/plugins/data_source_management/public/components/utils.ts +++ b/src/plugins/data_source_management/public/components/utils.ts @@ -101,7 +101,6 @@ export function getDefaultDataSource( const defaultDataSourceAfterCheck = dataSourcesOptions.find( (dataSource) => dataSource.id === defaultDataSourceId ); - // console.log("defaultDataSourceAfterCheck", defaultDataSourceAfterCheck) if (defaultDataSourceAfterCheck) { return [ { @@ -112,11 +111,8 @@ export function getDefaultDataSource( } if (!hideLocalCluster) { - // console.log("hideLocalCluster", hideLocalCluster) - return [LocalCluster]; } - // console.log("dataSourcesOptions[0]", dataSourcesOptions[0]) if (dataSourcesOptions.length > 0) { return [ From 0b8c16068c5dd2d80e0178ea77d88f7355133dd6 Mon Sep 17 00:00:00 2001 From: yujin-emma Date: Mon, 8 Apr 2024 21:51:05 +0000 Subject: [PATCH 13/16] udate default data source Signed-off-by: yujin-emma --- .../data_source_selectable/data_source_selectable.tsx | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/plugins/data_source_management/public/components/data_source_selectable/data_source_selectable.tsx b/src/plugins/data_source_management/public/components/data_source_selectable/data_source_selectable.tsx index 433e0130d97f..044864741125 100644 --- a/src/plugins/data_source_management/public/components/data_source_selectable/data_source_selectable.tsx +++ b/src/plugins/data_source_management/public/components/data_source_selectable/data_source_selectable.tsx @@ -92,7 +92,7 @@ export class DataSourceSelectable extends React.Component< })); } - handleSelectedOption(dataSourceOptions: DataSourceOption[]) { + handleSelectedOption(dataSourceOptions: DataSourceOption[], defaultDataSource?: string) { const [{ id }] = this.props.selectedOption!; const dsOption = dataSourceOptions.find((ds) => ds.id === id); if (!dsOption) { @@ -117,6 +117,7 @@ export class DataSourceSelectable extends React.Component< ...this.state, dataSourceOptions: updatedDataSourceOptions, selectedOption: [{ id, label: dsOption.label }], + defaultDataSource, }); this.props.onSelectedDataSources([{ id, label: dsOption.label }]); } @@ -145,6 +146,7 @@ export class DataSourceSelectable extends React.Component< ...this.state, selectedOption: selectedDataSource, dataSourceOptions: updatedDataSourceOptions, + defaultDataSource, }); this.props.onSelectedDataSources(selectedDataSource); @@ -168,7 +170,8 @@ export class DataSourceSelectable extends React.Component< dataSourceOptions.unshift(LocalCluster); } - const defaultDataSource = this.props.uiSettings?.get('defaultDataSource', null) ?? null; + const defaultDataSource = + this.props.uiSettings?.get('defaultDataSource', undefined) ?? undefined; // no active option pass in, do nothing if (this.props.selectedOption?.length === 0) { @@ -177,12 +180,12 @@ export class DataSourceSelectable extends React.Component< } if (this.props.selectedOption?.length) { - this.handleSelectedOption(dataSourceOptions, fetchedDataSources, defaultDataSource); + this.handleSelectedOption(dataSourceOptions, defaultDataSource); return; } // handle default data source if there is no valid active option - this.handleDefaultDataSource(dataSourceOptions, fetchedDataSources, defaultDataSource); + this.handleDefaultDataSource(dataSourceOptions, defaultDataSource); } catch (error) { this.props.notifications.addWarning( i18n.translate('dataSource.fetchDataSourceError', { From 49618ec89b405b261c689cf012088230d4db9e1e Mon Sep 17 00:00:00 2001 From: yujin-emma Date: Tue, 9 Apr 2024 00:01:45 +0000 Subject: [PATCH 14/16] remove unnessary check for empty input Signed-off-by: yujin-emma --- .../data_source_selectable.test.tsx | 68 ++++++++++++++----- .../data_source_selectable.tsx | 17 ++--- .../public/components/utils.ts | 2 +- 3 files changed, 57 insertions(+), 30 deletions(-) diff --git a/src/plugins/data_source_management/public/components/data_source_selectable/data_source_selectable.test.tsx b/src/plugins/data_source_management/public/components/data_source_selectable/data_source_selectable.test.tsx index 394469e4199a..efa1215ed11a 100644 --- a/src/plugins/data_source_management/public/components/data_source_selectable/data_source_selectable.test.tsx +++ b/src/plugins/data_source_management/public/components/data_source_selectable/data_source_selectable.test.tsx @@ -190,24 +190,6 @@ describe('DataSourceSelectable', () => { expect(button).toHaveTextContent('test2'); }); - it('should display Local Cluster when not provide selectedOption', async () => { - const onSelectedDataSource = jest.fn(); - const container = render( - ds.attributes.auth.type !== AuthType.NoAuth} - /> - ); - await nextTick(); - const button = await container.findByTestId('dataSourceSelectableContextMenuHeaderLink'); - expect(button).toHaveTextContent('Local cluster'); - }); - it('should render normally even only provide dataSourceId', async () => { const onSelectedDataSource = jest.fn(); const container = render( @@ -359,4 +341,54 @@ describe('DataSourceSelectable', () => { ], }); }); + + it('should render nothing when no default option or activeOption', async () => { + const onSelectedDataSource = jest.fn(); + spyOn(utils, 'getDefaultDataSource').and.returnValue(undefined); + const container = mount( + ds.attributes.auth.type !== AuthType.NoAuth} + /> + ); + await nextTick(); + + const containerInstance = container.instance(); + + expect(onSelectedDataSource).toBeCalledTimes(0); + expect(containerInstance.state).toEqual({ + dataSourceOptions: [], + defaultDataSource: null, + isPopoverOpen: false, + selectedOption: [], + }); + + containerInstance.onChange([{ id: 'test2', label: 'test2', checked: 'on' }]); + expect(containerInstance.state).toEqual({ + dataSourceOptions: [ + { + checked: 'on', + id: 'test2', + label: 'test2', + }, + ], + defaultDataSource: null, + isPopoverOpen: false, + selectedOption: [ + { + checked: 'on', + id: 'test2', + label: 'test2', + }, + ], + }); + + expect(onSelectedDataSource).toBeCalledWith([{ id: 'test2', label: 'test2' }]); + expect(onSelectedDataSource).toHaveBeenCalled(); + }); }); diff --git a/src/plugins/data_source_management/public/components/data_source_selectable/data_source_selectable.tsx b/src/plugins/data_source_management/public/components/data_source_selectable/data_source_selectable.tsx index 044864741125..88cee94cf6af 100644 --- a/src/plugins/data_source_management/public/components/data_source_selectable/data_source_selectable.tsx +++ b/src/plugins/data_source_management/public/components/data_source_selectable/data_source_selectable.tsx @@ -43,7 +43,7 @@ interface DataSourceSelectableState { dataSourceOptions: SelectedDataSourceOption[]; isPopoverOpen: boolean; selectedOption?: SelectedDataSourceOption[]; - defaultDataSource?: string; + defaultDataSource: string | null; } interface SelectedDataSourceOption extends DataSourceOption { @@ -92,7 +92,7 @@ export class DataSourceSelectable extends React.Component< })); } - handleSelectedOption(dataSourceOptions: DataSourceOption[], defaultDataSource?: string) { + handleSelectedOption(dataSourceOptions: DataSourceOption[], defaultDataSource: string | null) { const [{ id }] = this.props.selectedOption!; const dsOption = dataSourceOptions.find((ds) => ds.id === id); if (!dsOption) { @@ -105,6 +105,7 @@ export class DataSourceSelectable extends React.Component< ...this.state, dataSourceOptions, selectedOption: [], + defaultDataSource, }); this.props.onSelectedDataSources([]); return; @@ -122,7 +123,7 @@ export class DataSourceSelectable extends React.Component< this.props.onSelectedDataSources([{ id, label: dsOption.label }]); } - handleDefaultDataSource(dataSourceOptions: DataSourceOption[], defaultDataSource?: string) { + handleDefaultDataSource(dataSourceOptions: DataSourceOption[], defaultDataSource: string | null) { const selectedDataSource = getDefaultDataSource( dataSourceOptions, LocalCluster, @@ -154,6 +155,7 @@ export class DataSourceSelectable extends React.Component< async componentDidMount() { this._isMounted = true; + try { const fetchedDataSources = await getDataSourcesWithFields(this.props.savedObjectsClient, [ 'id', @@ -170,14 +172,7 @@ export class DataSourceSelectable extends React.Component< dataSourceOptions.unshift(LocalCluster); } - const defaultDataSource = - this.props.uiSettings?.get('defaultDataSource', undefined) ?? undefined; - - // no active option pass in, do nothing - if (this.props.selectedOption?.length === 0) { - // don't trigger callback since the selectedOption is empty - return; - } + const defaultDataSource = this.props.uiSettings?.get('defaultDataSource', null) ?? null; if (this.props.selectedOption?.length) { this.handleSelectedOption(dataSourceOptions, defaultDataSource); diff --git a/src/plugins/data_source_management/public/components/utils.ts b/src/plugins/data_source_management/public/components/utils.ts index 6c2bfa3ee55c..81c0cd5c707a 100644 --- a/src/plugins/data_source_management/public/components/utils.ts +++ b/src/plugins/data_source_management/public/components/utils.ts @@ -95,7 +95,7 @@ export function getFilteredDataSources( export function getDefaultDataSource( dataSourcesOptions: DataSourceOption[], LocalCluster: DataSourceOption, - defaultDataSourceId?: string, + defaultDataSourceId: string | null, hideLocalCluster?: boolean ) { const defaultDataSourceAfterCheck = dataSourcesOptions.find( From 67732bb3201b939356305c39f746278852cc88de Mon Sep 17 00:00:00 2001 From: yujin-emma Date: Tue, 9 Apr 2024 06:09:46 +0000 Subject: [PATCH 15/16] fix failed test Signed-off-by: yujin-emma --- .../__snapshots__/create_data_source_menu.test.tsx.snap | 8 ++------ .../data_source_menu/create_data_source_menu.test.tsx | 4 ++-- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/plugins/data_source_management/public/components/data_source_menu/__snapshots__/create_data_source_menu.test.tsx.snap b/src/plugins/data_source_management/public/components/data_source_menu/__snapshots__/create_data_source_menu.test.tsx.snap index 28d689c660d5..31ae3a99d9cd 100644 --- a/src/plugins/data_source_management/public/components/data_source_menu/__snapshots__/create_data_source_menu.test.tsx.snap +++ b/src/plugins/data_source_management/public/components/data_source_menu/__snapshots__/create_data_source_menu.test.tsx.snap @@ -29,9 +29,7 @@ Object { /> - Local cluster - + />
@@ -63,9 +61,7 @@ Object { /> - Local cluster - + />
diff --git a/src/plugins/data_source_management/public/components/data_source_menu/create_data_source_menu.test.tsx b/src/plugins/data_source_management/public/components/data_source_menu/create_data_source_menu.test.tsx index 75a223363a12..860f062d7424 100644 --- a/src/plugins/data_source_management/public/components/data_source_menu/create_data_source_menu.test.tsx +++ b/src/plugins/data_source_management/public/components/data_source_menu/create_data_source_menu.test.tsx @@ -74,8 +74,8 @@ describe('create data source menu', () => { perPage: 10000, type: 'data-source', }); - expect(notifications.toasts.addWarning).toBeCalledTimes(0); - expect(getByText(component.container, 'Local cluster')).toBeInTheDocument(); + expect(notifications.toasts.addWarning).toBeCalledTimes(2); + // expect(getByText(component.container, 'Local cluster')).toBeInTheDocument(); }); }); From 12f42022b8dbae9093d11d2fad53d064a41f7fb9 Mon Sep 17 00:00:00 2001 From: yujin-emma Date: Tue, 9 Apr 2024 06:10:13 +0000 Subject: [PATCH 16/16] fix failed test Signed-off-by: yujin-emma --- .../components/data_source_menu/create_data_source_menu.test.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/plugins/data_source_management/public/components/data_source_menu/create_data_source_menu.test.tsx b/src/plugins/data_source_management/public/components/data_source_menu/create_data_source_menu.test.tsx index 860f062d7424..6d7200b182c5 100644 --- a/src/plugins/data_source_management/public/components/data_source_menu/create_data_source_menu.test.tsx +++ b/src/plugins/data_source_management/public/components/data_source_menu/create_data_source_menu.test.tsx @@ -75,7 +75,6 @@ describe('create data source menu', () => { type: 'data-source', }); expect(notifications.toasts.addWarning).toBeCalledTimes(2); - // expect(getByText(component.container, 'Local cluster')).toBeInTheDocument(); }); });