diff --git a/CHANGELOG.md b/CHANGELOG.md
index df35a1f20ba..b891b05c35f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -49,6 +49,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
* Add updated_at column to objects' tables ([#1218](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/1218))
* [Viz Builder] State validation before dispatching and loading ([#2351](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2351))
* [Viz Builder] Create a new wizard directly on a dashboard ([#2384](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2384))
+* [Multi DataSource] UX enhacement on index pattern management stack ([#2505]https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2505))
### 🐛 Bug Fixes
diff --git a/src/plugins/index_pattern_management/opensearch_dashboards.json b/src/plugins/index_pattern_management/opensearch_dashboards.json
index 1efd05cb1a4..611f122c8c1 100644
--- a/src/plugins/index_pattern_management/opensearch_dashboards.json
+++ b/src/plugins/index_pattern_management/opensearch_dashboards.json
@@ -5,5 +5,5 @@
"ui": true,
"optionalPlugins": ["dataSource"],
"requiredPlugins": ["management", "data", "urlForwarding"],
- "requiredBundles": ["opensearchDashboardsReact", "opensearchDashboardsUtils", "savedObjects"]
+ "requiredBundles": ["opensearchDashboardsReact", "opensearchDashboardsUtils"]
}
diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_data_source/components/header/__snapshots__/header.test.tsx.snap b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_data_source/components/header/__snapshots__/header.test.tsx.snap
index e15b9428b45..6f6073906ee 100644
--- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_data_source/components/header/__snapshots__/header.test.tsx.snap
+++ b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_data_source/components/header/__snapshots__/header.test.tsx.snap
@@ -1,238 +1,388 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Header should render data source finder when choose to use data source 1`] = `
-
-
-
-
+
`;
exports[`Header should render normally 1`] = `
-
-
-
-
+
`;
diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_data_source/components/header/header.test.tsx b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_data_source/components/header/header.test.tsx
index 0805f6a5f78..dc8b72341b8 100644
--- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_data_source/components/header/header.test.tsx
+++ b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_data_source/components/header/header.test.tsx
@@ -6,13 +6,24 @@
import React from 'react';
import { Header } from '../header';
import { shallow } from 'enzyme';
+import { shallowWithIntl } from 'test_utils/enzyme_helpers';
+
+jest.mock('../../../../../../../../../plugins/opensearch_dashboards_react/public', () => ({
+ useOpenSearchDashboards: jest.fn().mockReturnValue({
+ services: {
+ notifications: { toast: { addWarning: jest.fn() } },
+ },
+ }),
+}));
+
+afterAll(() => jest.clearAllMocks());
describe('Header', () => {
it('should render normally', () => {
- const component = shallow(
+ const component = shallowWithIntl(
{}}
- dataSourceRef={{ type: 'type', id: 'id' }!}
+ dataSourceRef={{ type: 'type', id: 'id', title: 'title' }!}
goToNextStep={() => {}}
isNextStepDisabled={true}
stepInfo={{ totalStepNumber: 0, currentStepNumber: 0 }}
@@ -22,11 +33,11 @@ describe('Header', () => {
expect(component).toMatchSnapshot();
});
- it('should render data source finder when choose to use data source', () => {
- const component = shallow(
+ it('should render existing data sources list when choose to use data source', () => {
+ const component = shallowWithIntl(
{}}
- dataSourceRef={{ type: 'type', id: 'id' }!}
+ dataSourceRef={{ type: 'type', id: 'id', title: 'title' }!}
goToNextStep={() => {}}
isNextStepDisabled={true}
stepInfo={{ totalStepNumber: 0, currentStepNumber: 0 }}
@@ -45,10 +56,10 @@ describe('Header', () => {
});
it('should disable next step before select data source', () => {
- const component = shallow(
+ const component = shallowWithIntl(
{}}
- dataSourceRef={{ type: 'type', id: 'id' }!}
+ dataSourceRef={{ type: 'type', id: 'id', title: 'title' }!}
goToNextStep={() => {}}
isNextStepDisabled={true}
stepInfo={{ totalStepNumber: 0, currentStepNumber: 0 }}
@@ -71,10 +82,10 @@ describe('Header', () => {
});
it('should enable next step when pick default option', () => {
- const component = shallow(
+ const component = shallowWithIntl(
{}}
- dataSourceRef={{ type: 'type', id: 'id' }!}
+ dataSourceRef={{ type: 'type', id: 'id', title: 'title' }!}
goToNextStep={() => {}}
isNextStepDisabled={true}
stepInfo={{ totalStepNumber: 0, currentStepNumber: 0 }}
diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_data_source/components/header/header.tsx b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_data_source/components/header/header.tsx
index ea463a97a70..75f641b7016 100644
--- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_data_source/components/header/header.tsx
+++ b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_data_source/components/header/header.tsx
@@ -3,49 +3,88 @@
* SPDX-License-Identifier: Apache-2.0
*/
-import React, { useState } from 'react';
+import React, { Fragment, useState } from 'react';
import './header.scss';
import {
- EuiTitle,
- EuiSpacer,
- EuiText,
- EuiFlexItem,
EuiButton,
EuiFlexGroup,
+ EuiFlexItem,
EuiRadio,
+ EuiSelectable,
+ EuiSpacer,
+ EuiText,
+ EuiTitle,
} from '@elastic/eui';
-import { FormattedMessage } from '@osd/i18n/react';
import { i18n } from '@osd/i18n';
+import { FormattedMessage } from '@osd/i18n/react';
+import { useEffectOnce } from 'react-use';
import {
DataSourceRef,
IndexPatternManagmentContext,
} from 'src/plugins/index_pattern_management/public/types';
-import { SavedObjectFinderUi } from '../../../../../../../../../plugins/saved_objects/public';
import { useOpenSearchDashboards } from '../../../../../../../../../plugins/opensearch_dashboards_react/public';
-import { StepInfo } from '../../../../types';
+import { getDataSources } from '../../../../../../components/utils';
+import { DataSourceTableItem, StepInfo } from '../../../../types';
+import { LoadingState } from '../../../loading_state';
interface HeaderProps {
- onDataSourceSelected: (id: string, type: string) => void;
+ onDataSourceSelected: (id: string, type: string, title: string) => void;
dataSourceRef: DataSourceRef;
goToNextStep: (dataSourceRef: DataSourceRef) => void;
isNextStepDisabled: boolean;
stepInfo: StepInfo;
}
-const DATA_SOURCE_PAGE_SIZE = 5;
-
export const Header: React.FC = (props: HeaderProps) => {
const { dataSourceRef, onDataSourceSelected, goToNextStep, isNextStepDisabled, stepInfo } = props;
const { currentStepNumber, totalStepNumber } = stepInfo;
const [defaultChecked, setDefaultChecked] = useState(true);
const [dataSourceChecked, setDataSourceChecked] = useState(false);
+ const [dataSources, setDataSources] = useState([]);
+ const [isLoading, setIsLoading] = useState(false);
+
+ const {
+ savedObjects,
+ notifications: { toasts },
+ } = useOpenSearchDashboards().services;
+
+ useEffectOnce(() => {
+ fetchDataSources();
+ });
+
+ const fetchDataSources = () => {
+ setIsLoading(true);
+ getDataSources(savedObjects.client)
+ .then((fetchedDataSources: DataSourceTableItem[]) => {
+ setIsLoading(false);
+ if (fetchedDataSources?.length) {
+ setDataSources(fetchedDataSources);
+ }
+ })
+ .catch(() => {
+ toasts.addWarning(
+ i18n.translate(
+ 'indexPatternManagement.createIndexPattern.stepDataSource.fetchDataSourceError',
+ {
+ defaultMessage: 'Unable to find existing data sources',
+ }
+ )
+ );
+ });
+ };
- const { savedObjects, uiSettings } = useOpenSearchDashboards<
- IndexPatternManagmentContext
- >().services;
+ const onSelectedDataSource = (options: DataSourceTableItem[]) => {
+ const selectedDataSource = options.find(({ checked }) => checked);
+ setDataSources(options);
+ onDataSourceSelected(
+ selectedDataSource!.id,
+ selectedDataSource!.type,
+ selectedDataSource!.title
+ );
+ };
const onChangeDefaultChecked = (e) => {
setDefaultChecked(e.target.checked);
@@ -58,111 +97,119 @@ export const Header: React.FC = (props: HeaderProps) => {
};
return (
-
-
-
+ <>
+
+
+
+
+
+
+
-
-
-
-
+
+
+ }
+ checked={defaultChecked}
+ onChange={(e) => onChangeDefaultChecked(e)}
+ compressed
/>
-
-
-
- }
- checked={defaultChecked}
- onChange={(e) => onChangeDefaultChecked(e)}
- compressed
- />
-
-
+
+
+ }
+ checked={dataSourceChecked}
+ onChange={(e) => onChangeDataSourceChecked(e)}
+ compressed
/>
-
-
-
- }
- checked={dataSourceChecked}
- onChange={(e) => onChangeDataSourceChecked(e)}
- compressed
- />
-
-
-
- {dataSourceChecked && (
-
-
-
+ {dataSourceChecked && (
+
+
+
}
- )}
- savedObjectMetaData={[
- {
- type: 'data-source',
- getIconForSavedObject: () => 'apps', // todo: #2034
- name: i18n.translate(
- 'indexPatternManagement.createIndexPattern.searchSelection.savedObjectType.dataSource',
+ searchable
+ searchProps={{
+ 'data-test-subj': 'selectDataSources',
+ placeholder: i18n.translate(
+ 'indexPatternManagement.createIndexPattern.stepDataSource.searchPlaceHolder',
{
- defaultMessage: 'Data Source',
+ defaultMessage: 'Search data sources',
}
),
- },
- ]}
- fixedPageSize={DATA_SOURCE_PAGE_SIZE}
- uiSettings={uiSettings}
- savedObjects={savedObjects}
- />
-
- )}
-
-
-
- goToNextStep(dataSourceRef)}
- isDisabled={isNextStepDisabled && !defaultChecked}
- >
-
-
-
-
-
+ isInvalid: !!dataSources,
+ }}
+ singleSelection={'always'}
+ options={dataSources}
+ onChange={(newOptions) => onSelectedDataSource(newOptions)}
+ >
+ {(list, search) => (
+
+ {search}
+
+ {list}
+
+ )}
+
+
+ )}
+
+
+
+ goToNextStep(dataSourceRef)}
+ isDisabled={isNextStepDisabled && !defaultChecked}
+ >
+
+
+
+
+
+
+ {isLoading ? : null}
+ >
);
};
diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_data_source/step_data_source.tsx b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_data_source/step_data_source.tsx
index 5e1b1c92f1a..fa91b455ae6 100644
--- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_data_source/step_data_source.tsx
+++ b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_data_source/step_data_source.tsx
@@ -21,8 +21,8 @@ export const StepDataSource = (props: StepDataSourceProps) => {
const [selectedDataSource, setSelectedDataSource] = useState();
const [isNextStepDisabled, setIsNextStepDisabled] = useState(true);
- const onDataSourceSelected = (id: string, selectedType: string) => {
- const selected = { id, type: selectedType };
+ const onDataSourceSelected = (id: string, selectedType: string, title: string) => {
+ const selected = { id, type: selectedType, title };
setSelectedDataSource(selected);
setIsNextStepDisabled(false);
diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_index_pattern/components/header/header.scss b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_index_pattern/components/header/header.scss
new file mode 100644
index 00000000000..a71690db08c
--- /dev/null
+++ b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_index_pattern/components/header/header.scss
@@ -0,0 +1,5 @@
+.dataSourceIndexPatternDot {
+ margin-top: 45px;
+ margin-left: 5px;
+ margin-right: 5px;
+}
diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_index_pattern/components/header/header.tsx b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_index_pattern/components/header/header.tsx
index b4e1803a8d2..5e8db4ac920 100644
--- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_index_pattern/components/header/header.tsx
+++ b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_index_pattern/components/header/header.tsx
@@ -29,6 +29,7 @@
*/
import React from 'react';
+import './header.scss';
import {
EuiTitle,
@@ -45,7 +46,12 @@ import {
import { i18n } from '@osd/i18n';
import { FormattedMessage } from '@osd/i18n/react';
+import {
+ DataSourceRef,
+ IndexPatternManagmentContext,
+} from 'src/plugins/index_pattern_management/public/types';
import { StepInfo } from '../../../../types';
+import { useOpenSearchDashboards } from '../../../../../../../../../plugins/opensearch_dashboards_react/public';
interface HeaderProps {
isInputInvalid: boolean;
@@ -59,6 +65,7 @@ interface HeaderProps {
onChangeIncludingSystemIndices: (event: EuiSwitchEvent) => void;
isIncludingSystemIndices: boolean;
stepInfo: StepInfo;
+ dataSourceRef?: DataSourceRef;
}
export const Header: React.FC = ({
@@ -73,101 +80,176 @@ export const Header: React.FC = ({
onChangeIncludingSystemIndices,
isIncludingSystemIndices,
stepInfo,
+ dataSourceRef,
...rest
-}) => (
-
-
-
-
-
-
-
-
-
-
- {
+ const { dataSourceEnabled } = useOpenSearchDashboards().services;
+
+ return (
+
+
+
+
+
+
+
+
+
+
+ {dataSourceEnabled
+ ? renderDataSourceAndIndexPatternInput(
+ isInputInvalid,
+ errors,
+ characterList,
+ query,
+ onQueryChanged,
+ dataSourceRef!
+ )
+ : renderIndexPatternInput(
+ isInputInvalid,
+ errors,
+ characterList,
+ query,
+ onQueryChanged
+ )}
+ {showSystemIndices ? (
+
+
+ }
+ id="checkboxShowSystemIndices"
+ checked={isIncludingSystemIndices}
+ onChange={onChangeIncludingSystemIndices}
+ data-test-subj="showSystemAndHiddenIndices"
+ />
+
+ ) : null}
+
+
+
+
+ goToNextStep(query)}
+ isDisabled={isNextStepDisabled}
+ data-test-subj="createIndexPatternGoToStep2Button"
+ >
- }
- isInvalid={isInputInvalid}
- error={errors}
- helpText={
- <>
- * }}
- />{' '}
- {characterList} }}
- />
- >
- }
- >
-
+
+
+
+
+ );
+};
- {showSystemIndices ? (
-
-
- }
- id="checkboxShowSystemIndices"
- checked={isIncludingSystemIndices}
- onChange={onChangeIncludingSystemIndices}
- data-test-subj="showSystemAndHiddenIndices"
- />
-
- ) : null}
-
-
-
-
- goToNextStep(query)}
- isDisabled={isNextStepDisabled}
- data-test-subj="createIndexPatternGoToStep2Button"
- >
+const renderIndexPatternInput = (
+ isInputInvalid: boolean,
+ errors: any,
+ characterList: string,
+ query: string,
+ onQueryChanged: (e: React.ChangeEvent) => void
+) => {
+ return (
+
+ }
+ isInvalid={isInputInvalid}
+ error={errors}
+ helpText={
+ <>
+ * }}
+ />{' '}
+ {characterList} }}
+ />
+ >
+ }
+ >
+
+
+ );
+};
+
+const renderDataSourceAndIndexPatternInput = (
+ isInputInvalid: boolean,
+ errors: any,
+ characterList: string,
+ query: string,
+ onQueryChanged: (e: React.ChangeEvent) => void,
+ dataSourceRef: DataSourceRef
+) => {
+ return (
+
+
+
-
+ }
+ isInvalid={isInputInvalid}
+ error={errors}
+ >
+
+ {`.`}
+
+ {renderIndexPatternInput(isInputInvalid, errors, characterList, query, onQueryChanged)}
+
-
-);
+ );
+};
diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_index_pattern/step_index_pattern.tsx b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_index_pattern/step_index_pattern.tsx
index cf6d9f1398f..e42fc3218dd 100644
--- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_index_pattern/step_index_pattern.tsx
+++ b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_index_pattern/step_index_pattern.tsx
@@ -352,7 +352,7 @@ export class StepIndexPattern extends Component
);
}
diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/types.ts b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/types.ts
index 9705bbc4b3e..917abd60f7f 100644
--- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/types.ts
+++ b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/types.ts
@@ -84,3 +84,12 @@ export interface StepInfo {
totalStepNumber: number;
currentStepNumber: number;
}
+
+export interface DataSourceTableItem {
+ id: string;
+ type: string;
+ title: string;
+ sort: string;
+ checked?: 'on' | 'off';
+ label: string;
+}
diff --git a/src/plugins/index_pattern_management/public/components/utils.ts b/src/plugins/index_pattern_management/public/components/utils.ts
index 4d9ba4b396d..3929563fdc1 100644
--- a/src/plugins/index_pattern_management/public/components/utils.ts
+++ b/src/plugins/index_pattern_management/public/components/utils.ts
@@ -30,6 +30,7 @@
import { IIndexPattern } from 'src/plugins/data/public';
import { SavedObjectsClientContract } from 'src/core/public';
+import { DataSourceAttributes } from 'src/plugins/data_source/common/data_sources';
import { IndexPatternManagementStart } from '../plugin';
export async function getIndexPatterns(
@@ -79,3 +80,31 @@ export async function getIndexPatterns(
) || []
);
}
+
+export async function getDataSources(savedObjectsClient: SavedObjectsClientContract) {
+ return (
+ savedObjectsClient
+ .find({
+ type: 'data-source',
+ fields: ['title', 'type'],
+ perPage: 10000,
+ })
+ .then((response) =>
+ response.savedObjects
+ .map((dataSource) => {
+ const id = dataSource.id;
+ const type = dataSource.type;
+ const title = dataSource.get('title');
+
+ return {
+ id,
+ title,
+ type,
+ label: title,
+ sort: `${title}`,
+ };
+ })
+ .sort((a, b) => a.sort.localeCompare(b.sort))
+ ) || []
+ );
+}
diff --git a/src/plugins/index_pattern_management/public/types.ts b/src/plugins/index_pattern_management/public/types.ts
index 4c23651ec84..bc96fc15c8e 100644
--- a/src/plugins/index_pattern_management/public/types.ts
+++ b/src/plugins/index_pattern_management/public/types.ts
@@ -70,4 +70,4 @@ export enum MlCardState {
ENABLED,
}
-export type DataSourceRef = Pick;
+export type DataSourceRef = { title: string } & Pick;