From b117fd374d553c3382bd8a63e49bd98fb654916d Mon Sep 17 00:00:00 2001 From: Lu Yu Date: Mon, 22 Apr 2024 11:21:53 -0700 Subject: [PATCH] [Multiple Datasource] Add new error database icon to handle error state for data source component (#6570) * add error icon and add popover Signed-off-by: Lu Yu * fix tests Signed-off-by: Lu Yu * fix typo Signed-off-by: Lu Yu --------- Signed-off-by: Lu Yu --- .../custom_database_icon/error_icon.tsx | 24 ++++++ .../components/custom_database_icon/index.ts | 5 ++ .../data_source_aggregated_view.tsx | 4 +- .../data_source_error_menu.tsx | 82 +++++++++++++++++-- .../create_data_source_menu.test.tsx.snap | 48 +++++++---- .../create_data_source_menu.test.tsx | 2 +- .../data_source_menu/data_source_menu.tsx | 2 + .../data_source_multi_selectable.test.tsx | 2 +- .../data_source_multi_selectable.tsx | 9 +- .../data_source_selectable.tsx | 4 +- .../data_source_view.test.tsx | 2 +- .../data_source_view/data_source_view.tsx | 2 +- .../public/components/toast_button/index.ts | 5 ++ .../components/toast_button/reload_button.tsx | 23 ++++++ .../public/components/utils.test.ts | 2 +- .../public/components/utils.ts | 14 ++-- 16 files changed, 197 insertions(+), 33 deletions(-) create mode 100644 src/plugins/data_source_management/public/components/custom_database_icon/error_icon.tsx create mode 100644 src/plugins/data_source_management/public/components/custom_database_icon/index.ts create mode 100644 src/plugins/data_source_management/public/components/toast_button/index.ts create mode 100644 src/plugins/data_source_management/public/components/toast_button/reload_button.tsx diff --git a/src/plugins/data_source_management/public/components/custom_database_icon/error_icon.tsx b/src/plugins/data_source_management/public/components/custom_database_icon/error_icon.tsx new file mode 100644 index 000000000000..6d92a1bfb964 --- /dev/null +++ b/src/plugins/data_source_management/public/components/custom_database_icon/error_icon.tsx @@ -0,0 +1,24 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ +import React from 'react'; + +export const ErrorIcon = () => { + return ( + + + + + ); +}; diff --git a/src/plugins/data_source_management/public/components/custom_database_icon/index.ts b/src/plugins/data_source_management/public/components/custom_database_icon/index.ts new file mode 100644 index 000000000000..506c9ee84980 --- /dev/null +++ b/src/plugins/data_source_management/public/components/custom_database_icon/index.ts @@ -0,0 +1,5 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ +export { ErrorIcon } from './error_icon'; diff --git a/src/plugins/data_source_management/public/components/data_source_aggregated_view/data_source_aggregated_view.tsx b/src/plugins/data_source_management/public/components/data_source_aggregated_view/data_source_aggregated_view.tsx index 775d5e5c3e67..4aa8a31e848e 100644 --- a/src/plugins/data_source_management/public/components/data_source_aggregated_view/data_source_aggregated_view.tsx +++ b/src/plugins/data_source_management/public/components/data_source_aggregated_view/data_source_aggregated_view.tsx @@ -14,6 +14,7 @@ import { } from '@elastic/eui'; import { i18n } from '@osd/i18n'; import { + ApplicationStart, IUiSettingsClient, SavedObjectsClientContract, ToastsStart, @@ -38,6 +39,7 @@ interface DataSourceAggregatedViewProps { dataSourceFilter?: (dataSource: SavedObject) => boolean; displayAllCompatibleDataSources: boolean; uiSettings?: IUiSettingsClient; + application?: ApplicationStart; } interface DataSourceAggregatedViewState extends DataSourceBaseState { @@ -133,7 +135,7 @@ export class DataSourceAggregatedView extends React.Component< return ; } if (this.state.showError) { - return ; + return ; } const button = ( { + const [showPopover, setShowPopover] = useState(false); + + const refreshButton = ( + window.location.reload()} + > + {i18n.translate('dataSourcesManagement.dataSourceErrorMenu.refreshPage', { + defaultMessage: 'Refresh the page', + })} + + ); + + const iconButton = ( + } + size="s" + onClick={() => setShowPopover(!showPopover)} + /> + ); -export const DataSourceErrorMenu = () => { return ( <> - - Error + setShowPopover(false)} + panelPaddingSize="none" + anchorPosition="downLeft" + data-test-subj={'dataSourceErrorPopover'} + > + + + + {i18n.translate('dataSourcesManagement.dataSourceErrorMenu.text', { + defaultMessage: 'Failed to fetch data sources', + })} + + + + + {refreshButton} + + + ); }; 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 c705db9194b0..0bce35917c56 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 @@ -5,33 +5,53 @@ Object { "asFragment": [Function], "baseElement":
-
- Error +
, "container":
-
- Error +
, 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 df299687928b..fa9485b9948f 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 @@ -85,7 +85,7 @@ describe('create data source menu', () => { perPage: 10000, type: 'data-source', }); - expect(notifications.toasts.addWarning).toBeCalledTimes(2); + expect(notifications.toasts.add).toBeCalledTimes(2); }); }); diff --git a/src/plugins/data_source_management/public/components/data_source_menu/data_source_menu.tsx b/src/plugins/data_source_management/public/components/data_source_menu/data_source_menu.tsx index 453d110e9741..60776af403d2 100644 --- a/src/plugins/data_source_management/public/components/data_source_menu/data_source_menu.tsx +++ b/src/plugins/data_source_management/public/components/data_source_menu/data_source_menu.tsx @@ -57,6 +57,7 @@ export function DataSourceMenu(props: DataSourceMenuProps): ReactElement | notifications={notifications!.toasts} onSelectedDataSources={onSelectedDataSources!} uiSettings={uiSettings} + application={application} /> ); } @@ -108,6 +109,7 @@ export function DataSourceMenu(props: DataSourceMenuProps): ReactElement | dataSourceFilter={dataSourceFilter} displayAllCompatibleDataSources={displayAllCompatibleDataSources} uiSettings={uiSettings} + application={application} /> ); } diff --git a/src/plugins/data_source_management/public/components/data_source_multi_selectable/data_source_multi_selectable.test.tsx b/src/plugins/data_source_management/public/components/data_source_multi_selectable/data_source_multi_selectable.test.tsx index 448eb404d995..957080dbd1ab 100644 --- a/src/plugins/data_source_management/public/components/data_source_multi_selectable/data_source_multi_selectable.test.tsx +++ b/src/plugins/data_source_management/public/components/data_source_multi_selectable/data_source_multi_selectable.test.tsx @@ -90,7 +90,7 @@ describe('DataSourceMultiSelectable', () => { /> ); await nextTick(); - expect(toasts.addWarning).toBeCalledTimes(1); + expect(toasts.add).toBeCalledTimes(1); }); it('should callback when onChange happens', async () => { diff --git a/src/plugins/data_source_management/public/components/data_source_multi_selectable/data_source_multi_selectable.tsx b/src/plugins/data_source_management/public/components/data_source_multi_selectable/data_source_multi_selectable.tsx index 8e842f70b4f5..ddc9477061ce 100644 --- a/src/plugins/data_source_management/public/components/data_source_multi_selectable/data_source_multi_selectable.tsx +++ b/src/plugins/data_source_management/public/components/data_source_multi_selectable/data_source_multi_selectable.tsx @@ -4,7 +4,11 @@ */ import React from 'react'; -import { SavedObjectsClientContract, ToastsStart } from 'opensearch-dashboards/public'; +import { + ApplicationStart, + SavedObjectsClientContract, + ToastsStart, +} from 'opensearch-dashboards/public'; import { IUiSettingsClient } from 'src/core/public'; import { DataSourceFilterGroup, SelectedDataSourceOption } from './data_source_filter_group'; import { NoDataSource } from '../no_data_source'; @@ -19,6 +23,7 @@ export interface DataSourceMultiSeletableProps { hideLocalCluster: boolean; fullWidth: boolean; uiSettings?: IUiSettingsClient; + application?: ApplicationStart; } interface DataSourceMultiSeletableState extends DataSourceBaseState { @@ -114,7 +119,7 @@ export class DataSourceMultiSelectable extends React.Component< return ; } if (this.state.showError) { - return ; + return ; } return ( ); } + if (this.state.showError) { - return ; + return ; } + const button = ( <> { /> ); expect(component).toMatchSnapshot(); - expect(toasts.addWarning).toBeCalledTimes(1); + expect(toasts.add).toBeCalledTimes(1); expect(utils.getDataSourceById).toBeCalledTimes(1); }); diff --git a/src/plugins/data_source_management/public/components/data_source_view/data_source_view.tsx b/src/plugins/data_source_management/public/components/data_source_view/data_source_view.tsx index 75596b215b08..1c7e93b5929c 100644 --- a/src/plugins/data_source_management/public/components/data_source_view/data_source_view.tsx +++ b/src/plugins/data_source_management/public/components/data_source_view/data_source_view.tsx @@ -154,7 +154,7 @@ export class DataSourceView extends React.Component; + return ; } const label = this.state.selectedOption.length > 0 ? this.state.selectedOption[0].label : ''; const options = diff --git a/src/plugins/data_source_management/public/components/toast_button/index.ts b/src/plugins/data_source_management/public/components/toast_button/index.ts new file mode 100644 index 000000000000..fd881fc3d882 --- /dev/null +++ b/src/plugins/data_source_management/public/components/toast_button/index.ts @@ -0,0 +1,5 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ +export { getReloadButton } from './reload_button'; diff --git a/src/plugins/data_source_management/public/components/toast_button/reload_button.tsx b/src/plugins/data_source_management/public/components/toast_button/reload_button.tsx new file mode 100644 index 000000000000..5f63d76f473f --- /dev/null +++ b/src/plugins/data_source_management/public/components/toast_button/reload_button.tsx @@ -0,0 +1,23 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ +import { EuiButton, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import React from 'react'; +import { i18n } from '@osd/i18n'; + +export const getReloadButton = () => { + return ( + <> + + + window.location.reload()}> + {i18n.translate('dataSourceMenu.requiresPageReloadToastButtonLabel', { + defaultMessage: 'Refresh the page', + })} + + + + + ); +}; 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 cc553037188b..3a9443b9183f 100644 --- a/src/plugins/data_source_management/public/components/utils.test.ts +++ b/src/plugins/data_source_management/public/components/utils.test.ts @@ -79,7 +79,7 @@ describe('DataSourceManagement: Utils.ts', () => { const changeStateMock = jest.fn(); handleDataSourceFetchError(changeStateMock, toasts); expect(changeStateMock).toBeCalledWith({ showError: true }); - expect(toasts.addWarning).toHaveBeenCalledWith(`Failed to fetch data source`); + expect(toasts.add).toBeCalledTimes(1); }); }); diff --git a/src/plugins/data_source_management/public/components/utils.ts b/src/plugins/data_source_management/public/components/utils.ts index a9f428f5cfa7..7af0a7df0555 100644 --- a/src/plugins/data_source_management/public/components/utils.ts +++ b/src/plugins/data_source_management/public/components/utils.ts @@ -22,6 +22,8 @@ import { AuthenticationMethodRegistry } from '../auth_registry'; import { DataSourceOption } from './data_source_menu/types'; import { DataSourceGroupLabelOption } from './data_source_menu/types'; import { createGetterSetter } from '../../../opensearch_dashboards_utils/public'; +import { toMountPoint } from '../../../opensearch_dashboards_react/public'; +import { getReloadButton } from './toast_button'; export async function getDataSources(savedObjectsClient: SavedObjectsClientContract) { return savedObjectsClient @@ -282,11 +284,13 @@ export const handleDataSourceFetchError = ( ) => { changeState({ showError: true }); if (callback) callback([]); - notifications.addWarning( - i18n.translate('dataSource.fetchDataSourceError', { - defaultMessage: 'Failed to fetch data source', - }) - ); + notifications.add({ + title: i18n.translate('dataSource.fetchDataSourceError', { + defaultMessage: 'Failed to fetch data sources', + }), + text: toMountPoint(getReloadButton()), + color: 'danger', + }); }; interface DataSourceOptionGroupLabel {