From e33e23207aed47be00a9bc4483876d89f4af0739 Mon Sep 17 00:00:00 2001 From: Kapian1234 Date: Tue, 13 Aug 2024 11:51:25 +0800 Subject: [PATCH 1/9] Update the appearance of Associate data sources Signed-off-by: Kapian1234 --- .../components/workspace_form/constants.ts | 2 +- .../select_data_source_panel.test.tsx | 19 +++ .../select_data_source_panel.tsx | 137 +++++------------- 3 files changed, 55 insertions(+), 103 deletions(-) diff --git a/src/plugins/workspace/public/components/workspace_form/constants.ts b/src/plugins/workspace/public/components/workspace_form/constants.ts index 2a2c7142d6f0..6e64f9c964ef 100644 --- a/src/plugins/workspace/public/components/workspace_form/constants.ts +++ b/src/plugins/workspace/public/components/workspace_form/constants.ts @@ -42,7 +42,7 @@ export const workspaceUseCaseTitle = i18n.translate('workspace.form.workspaceUse }); export const selectDataSourceTitle = i18n.translate('workspace.form.selectDataSource.title', { - defaultMessage: 'Associate data source', + defaultMessage: 'Associate data sources', }); export const usersAndPermissionsTitle = i18n.translate('workspace.form.usersAndPermissions.title', { diff --git a/src/plugins/workspace/public/components/workspace_form/select_data_source_panel.test.tsx b/src/plugins/workspace/public/components/workspace_form/select_data_source_panel.test.tsx index 2890333f1268..b66d8f3d72b3 100644 --- a/src/plugins/workspace/public/components/workspace_form/select_data_source_panel.test.tsx +++ b/src/plugins/workspace/public/components/workspace_form/select_data_source_panel.test.tsx @@ -39,6 +39,25 @@ const setup = ({ }; describe('SelectDataSourcePanel', () => { + beforeEach(() => { + const originalOffsetHeight = Object.getOwnPropertyDescriptor( + HTMLElement.prototype, + 'offsetHeight' + ); + const originalOffsetWidth = Object.getOwnPropertyDescriptor( + HTMLElement.prototype, + 'offsetWidth' + ); + Object.defineProperty(HTMLElement.prototype, 'offsetHeight', { + configurable: true, + value: 600, + }); + Object.defineProperty(HTMLElement.prototype, 'offsetWidth', { + configurable: true, + value: 600, + }); + }); + it('should render consistent data sources when selected data sources passed', () => { const { getByText } = setup({ selectedDataSources: dataSources }); diff --git a/src/plugins/workspace/public/components/workspace_form/select_data_source_panel.tsx b/src/plugins/workspace/public/components/workspace_form/select_data_source_panel.tsx index 9c990d7e7195..c1c7f9c1bd7a 100644 --- a/src/plugins/workspace/public/components/workspace_form/select_data_source_panel.tsx +++ b/src/plugins/workspace/public/components/workspace_form/select_data_source_panel.tsx @@ -4,17 +4,7 @@ */ import React, { useCallback, useEffect, useState } from 'react'; -import { - EuiSmallButton, - EuiCompressedFormRow, - EuiSpacer, - EuiFlexGroup, - EuiFlexItem, - EuiButtonIcon, - EuiCompressedComboBox, - EuiComboBoxOptionOption, - EuiFormLabel, -} from '@elastic/eui'; +import { EuiSpacer, EuiFormLabel, EuiSelectable, EuiText, EuiSelectableOption } from '@elastic/eui'; import { i18n } from '@osd/i18n'; import { SavedObjectsStart } from '../../../../../core/public'; import { getDataSourcesList } from '../../utils'; @@ -34,7 +24,7 @@ export const SelectDataSourcePanel = ({ selectedDataSources, savedObjects, }: SelectDataSourcePanelProps) => { - const [dataSourcesOptions, setDataSourcesOptions] = useState([]); + const [dataSourcesOptions, setDataSourcesOptions] = useState([]); useEffect(() => { if (!savedObjects) return; getDataSourcesList(savedObjects.client, ['*']).then((result) => { @@ -42,110 +32,53 @@ export const SelectDataSourcePanel = ({ label: title, value: id, })); + setDataSourcesOptions(options); }); }, [savedObjects, setDataSourcesOptions]); - const handleAddNewOne = useCallback(() => { - onChange?.([ - ...selectedDataSources, - { - title: '', - id: '', - }, - ]); - }, [onChange, selectedDataSources]); const handleSelect = useCallback( - (selectedOptions, index) => { - const newOption = selectedOptions[0] - ? // Select new data source - { - title: selectedOptions[0].label, - id: selectedOptions[0].value, - } - : // Click reset button - { - title: '', - id: '', - }; - const newSelectedOptions = [...selectedDataSources]; - newSelectedOptions.splice(index, 1, newOption); - - onChange(newSelectedOptions); - }, - [onChange, selectedDataSources] - ); - - const handleDelete = useCallback( - (index) => { - const newSelectedOptions = [...selectedDataSources]; - newSelectedOptions.splice(index, 1); - + (newOptions) => { + setDataSourcesOptions(newOptions); + const newSelectedOptions = []; + for (const option of newOptions) { + if (option.checked === 'on') + newSelectedOptions.push({ title: option.label, id: option.value }); + } onChange(newSelectedOptions); }, - [onChange, selectedDataSources] + [onChange] ); return (
- {i18n.translate('workspace.form.selectDataSource.subTitle', { - defaultMessage: 'Data source', - })} + + {i18n.translate('workspace.form.selectDataSource.subTitle', { + defaultMessage: 'Add data sources that will be available in the workspace', + })} + - - {selectedDataSources.map(({ id, title }, index) => ( - - - - handleSelect(selectedOptions, index)} - placeholder="Select" - /> - - - handleDelete(index)} - isDisabled={false} - /> - - - - ))} - - + - {i18n.translate('workspace.form.selectDataSourcePanel.addNew', { - defaultMessage: 'Add New', - })} - + {(list, search) => ( + <> + {search} + {list} + + )} +
); }; From 9e1a9194f8c93892b36aebb2627a690ad94cead7 Mon Sep 17 00:00:00 2001 From: Kapian1234 Date: Tue, 13 Aug 2024 16:54:42 +0800 Subject: [PATCH 2/9] Modify tests Signed-off-by: Kapian1234 --- .../select_data_source_panel.test.tsx | 69 +++++++------------ .../select_data_source_panel.tsx | 12 ++-- 2 files changed, 31 insertions(+), 50 deletions(-) diff --git a/src/plugins/workspace/public/components/workspace_form/select_data_source_panel.test.tsx b/src/plugins/workspace/public/components/workspace_form/select_data_source_panel.test.tsx index b66d8f3d72b3..55813f5c4099 100644 --- a/src/plugins/workspace/public/components/workspace_form/select_data_source_panel.test.tsx +++ b/src/plugins/workspace/public/components/workspace_form/select_data_source_panel.test.tsx @@ -4,9 +4,10 @@ */ import React from 'react'; -import { fireEvent, render, act } from '@testing-library/react'; +import { fireEvent, render, act, waitFor } from '@testing-library/react'; import { SelectDataSourcePanel, SelectDataSourcePanelProps } from './select_data_source_panel'; import { coreMock } from '../../../../../core/public/mocks'; +import { log } from 'console'; const dataSources = [ { @@ -39,15 +40,12 @@ const setup = ({ }; describe('SelectDataSourcePanel', () => { + const originalOffsetHeight = Object.getOwnPropertyDescriptor( + HTMLElement.prototype, + 'offsetHeight' + ); + const originalOffsetWidth = Object.getOwnPropertyDescriptor(HTMLElement.prototype, 'offsetWidth'); beforeEach(() => { - const originalOffsetHeight = Object.getOwnPropertyDescriptor( - HTMLElement.prototype, - 'offsetHeight' - ); - const originalOffsetWidth = Object.getOwnPropertyDescriptor( - HTMLElement.prototype, - 'offsetWidth' - ); Object.defineProperty(HTMLElement.prototype, 'offsetHeight', { configurable: true, value: 600, @@ -58,51 +56,34 @@ describe('SelectDataSourcePanel', () => { }); }); - it('should render consistent data sources when selected data sources passed', () => { - const { getByText } = setup({ selectedDataSources: dataSources }); - - expect(getByText(dataSources[0].title)).toBeInTheDocument(); - expect(getByText(dataSources[1].title)).toBeInTheDocument(); + afterEach(() => { + Object.defineProperty( + HTMLElement.prototype, + 'offsetHeight', + originalOffsetHeight as PropertyDescriptor + ); + Object.defineProperty( + HTMLElement.prototype, + 'offsetWidth', + originalOffsetWidth as PropertyDescriptor + ); }); - it('should call onChange when clicking add new data source button', () => { - const onChangeMock = jest.fn(); - const { getByTestId } = setup({ onChange: onChangeMock }); + it('should render consistent data sources when selected data sources passed', async () => { + const { getByText } = await setup({ selectedDataSources: [] }); - expect(onChangeMock).not.toHaveBeenCalled(); - fireEvent.click(getByTestId('workspaceForm-select-dataSource-addNew')); - expect(onChangeMock).toHaveBeenCalledWith([ - { - id: '', - title: '', - }, - ]); + expect(getByText(dataSources[0].title)).toBeInTheDocument(); + expect(getByText(dataSources[1].title)).toBeInTheDocument(); }); - it('should call onChange when updating selected data sources in combo box', async () => { + it('should call onChange when updating selected data sources in selectable', async () => { const onChangeMock = jest.fn(); - const { getByTitle, getByText } = setup({ + const { getByTitle } = await setup({ onChange: onChangeMock, - selectedDataSources: [{ id: '', title: '' }], + selectedDataSources: [], }); expect(onChangeMock).not.toHaveBeenCalled(); - await act(() => { - fireEvent.click(getByText('Select')); - }); fireEvent.click(getByTitle(dataSources[0].title)); expect(onChangeMock).toHaveBeenCalledWith([{ id: 'id1', title: 'title1' }]); }); - - it('should call onChange when deleting selected data source', async () => { - const onChangeMock = jest.fn(); - const { getByLabelText } = setup({ - onChange: onChangeMock, - selectedDataSources: [{ id: '', title: '' }], - }); - expect(onChangeMock).not.toHaveBeenCalled(); - await act(() => { - fireEvent.click(getByLabelText('Delete data source')); - }); - expect(onChangeMock).toHaveBeenCalledWith([]); - }); }); diff --git a/src/plugins/workspace/public/components/workspace_form/select_data_source_panel.tsx b/src/plugins/workspace/public/components/workspace_form/select_data_source_panel.tsx index c1c7f9c1bd7a..ec047dfe31f0 100644 --- a/src/plugins/workspace/public/components/workspace_form/select_data_source_panel.tsx +++ b/src/plugins/workspace/public/components/workspace_form/select_data_source_panel.tsx @@ -38,14 +38,14 @@ export const SelectDataSourcePanel = ({ }, [savedObjects, setDataSourcesOptions]); const handleSelect = useCallback( - (newOptions) => { - setDataSourcesOptions(newOptions); - const newSelectedOptions = []; - for (const option of newOptions) { + (options) => { + setDataSourcesOptions(options); + const selectedOptions = []; + for (const option of options) { if (option.checked === 'on') - newSelectedOptions.push({ title: option.label, id: option.value }); + selectedOptions.push({ title: option.label, id: option.value }); } - onChange(newSelectedOptions); + onChange(selectedOptions); }, [onChange] ); From 45de84932fd860a18a0a31e977a987dec60c0da9 Mon Sep 17 00:00:00 2001 From: Kapian1234 Date: Tue, 13 Aug 2024 17:20:50 +0800 Subject: [PATCH 3/9] Modify tests Signed-off-by: Kapian1234 --- .../workspace_form/select_data_source_panel.test.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/plugins/workspace/public/components/workspace_form/select_data_source_panel.test.tsx b/src/plugins/workspace/public/components/workspace_form/select_data_source_panel.test.tsx index 55813f5c4099..eb3aa0394b82 100644 --- a/src/plugins/workspace/public/components/workspace_form/select_data_source_panel.test.tsx +++ b/src/plugins/workspace/public/components/workspace_form/select_data_source_panel.test.tsx @@ -4,10 +4,9 @@ */ import React from 'react'; -import { fireEvent, render, act, waitFor } from '@testing-library/react'; +import { fireEvent, render } from '@testing-library/react'; import { SelectDataSourcePanel, SelectDataSourcePanelProps } from './select_data_source_panel'; import { coreMock } from '../../../../../core/public/mocks'; -import { log } from 'console'; const dataSources = [ { From 96cecac5b4529a9683bbd6e70fbf065e6d6203f6 Mon Sep 17 00:00:00 2001 From: "opensearch-changeset-bot[bot]" <154024398+opensearch-changeset-bot[bot]@users.noreply.github.com> Date: Tue, 13 Aug 2024 09:27:17 +0000 Subject: [PATCH 4/9] Changeset file for PR #7696 created/updated --- changelogs/fragments/7696.yml | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 changelogs/fragments/7696.yml diff --git a/changelogs/fragments/7696.yml b/changelogs/fragments/7696.yml new file mode 100644 index 000000000000..d8c8485a0306 --- /dev/null +++ b/changelogs/fragments/7696.yml @@ -0,0 +1,2 @@ +feat: +- Refactor Associate data sources to support multi-select ([#7696](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/7696)) \ No newline at end of file From 262070f5abd70c4eb669690741e94122d29adadf Mon Sep 17 00:00:00 2001 From: Kapian1234 Date: Thu, 15 Aug 2024 16:03:04 +0800 Subject: [PATCH 5/9] Modify tests Signed-off-by: Kapian1234 --- .../workspace_creator.test.tsx | 56 +++++++++---------- .../workspace_updater.test.tsx | 6 +- .../workspace_form/workspace_form.test.tsx | 6 +- 3 files changed, 29 insertions(+), 39 deletions(-) diff --git a/src/plugins/workspace/public/components/workspace_creator/workspace_creator.test.tsx b/src/plugins/workspace/public/components/workspace_creator/workspace_creator.test.tsx index 24692db47229..8cc03e8912ee 100644 --- a/src/plugins/workspace/public/components/workspace_creator/workspace_creator.test.tsx +++ b/src/plugins/workspace/public/components/workspace_creator/workspace_creator.test.tsx @@ -274,39 +274,33 @@ describe('WorkspaceCreator', () => { }); it('create workspace with customized selected dataSources', async () => { - const { getByTestId, getByTitle, getByText } = render( - - ); - const nameInput = getByTestId('workspaceForm-workspaceDetails-nameInputText'); - fireEvent.input(nameInput, { - target: { value: 'test workspace name' }, - }); - fireEvent.click(getByTestId('workspaceUseCase-observability')); - fireEvent.click(getByTestId('workspaceForm-select-dataSource-addNew')); - fireEvent.click(getByTestId('workspaceForm-select-dataSource-comboBox')); - await act(() => { - fireEvent.click(getByText('Select')); - }); - fireEvent.click(getByTitle(dataSourcesList[0].title)); + const { getByTestId, getByText } = render(); - fireEvent.click(getByTestId('workspaceForm-bottomBar-createButton')); - expect(workspaceClientCreate).toHaveBeenCalledWith( - expect.objectContaining({ - name: 'test workspace name', - }), - { - dataSources: ['id1'], - permissions: { - library_write: { - users: ['%me%'], - }, - write: { - users: ['%me%'], + waitFor(() => { + const nameInput = getByTestId('workspaceForm-workspaceDetails-nameInputText'); + fireEvent.input(nameInput, { + target: { value: 'test workspace name' }, + }); + fireEvent.click(getByTestId('workspaceUseCase-observability')); + fireEvent.click(getByText(dataSourcesList[0].title)); + + fireEvent.click(getByTestId('workspaceForm-bottomBar-createButton')); + expect(workspaceClientCreate).toHaveBeenCalledWith( + expect.objectContaining({ + name: 'test workspace name', + }), + { + dataSources: ['id1'], + permissions: { + library_write: { + users: ['%me%'], + }, + write: { + users: ['%me%'], + }, }, - }, - } - ); - await waitFor(() => { + } + ); expect(notificationToastsAddSuccess).toHaveBeenCalled(); }); expect(notificationToastsAddDanger).not.toHaveBeenCalled(); diff --git a/src/plugins/workspace/public/components/workspace_detail/workspace_updater.test.tsx b/src/plugins/workspace/public/components/workspace_detail/workspace_updater.test.tsx index e2ecbd0a32cd..fb93d79aab73 100644 --- a/src/plugins/workspace/public/components/workspace_detail/workspace_updater.test.tsx +++ b/src/plugins/workspace/public/components/workspace_detail/workspace_updater.test.tsx @@ -223,10 +223,6 @@ describe('WorkspaceUpdater', () => { fireEvent.click(getByTestId('workspaceUseCase-observability')); fireEvent.click(getByTestId('workspaceUseCase-analytics')); - act(() => { - fireEvent.click(getAllByLabelText('Delete data source')[0]); - }); - fireEvent.click(getByTestId('workspaceForm-bottomBar-updateButton')); expect(workspaceClientUpdate).toHaveBeenCalledWith( expect.any(String), @@ -245,7 +241,7 @@ describe('WorkspaceUpdater', () => { users: ['foo'], }, }, - dataSources: ['id2'], + dataSources: ['id1', 'id2'], } ); await waitFor(() => { diff --git a/src/plugins/workspace/public/components/workspace_form/workspace_form.test.tsx b/src/plugins/workspace/public/components/workspace_form/workspace_form.test.tsx index 05191dbd189a..7fc6a59f1865 100644 --- a/src/plugins/workspace/public/components/workspace_form/workspace_form.test.tsx +++ b/src/plugins/workspace/public/components/workspace_form/workspace_form.test.tsx @@ -53,17 +53,17 @@ describe('WorkspaceForm', () => { it('should enable data source panel for dashboard admin and when data source is enabled', () => { const { getByText } = setup(true, mockDataSourceManagementSetup); - expect(getByText('Associate data source')).toBeInTheDocument(); + expect(getByText('Associate data sources')).toBeInTheDocument(); }); it('should not display data source panel for non dashboard admin', () => { const { queryByText } = setup(false, mockDataSourceManagementSetup); - expect(queryByText('Associate data source')).not.toBeInTheDocument(); + expect(queryByText('Associate data sources')).not.toBeInTheDocument(); }); it('should not display data source panel when data source is disabled', () => { const { queryByText } = setup(true, undefined); - expect(queryByText('Associate data source')).not.toBeInTheDocument(); + expect(queryByText('Associate data sources')).not.toBeInTheDocument(); }); }); From f819ce6fdb659c2d22ff5ac7125ef3f7bb91071c Mon Sep 17 00:00:00 2001 From: Kapian1234 Date: Wed, 21 Aug 2024 11:17:29 +0800 Subject: [PATCH 6/9] add table for data sources Signed-off-by: Kapian1234 --- .../association_data_source_modal.tsx | 18 +-- ...reatepage_opensearch_connections_table.tsx | 127 ++++++++--------- .../select_data_source_panel.tsx | 134 ++---------------- 3 files changed, 75 insertions(+), 204 deletions(-) diff --git a/src/plugins/workspace/public/components/workspace_detail/association_data_source_modal.tsx b/src/plugins/workspace/public/components/workspace_detail/association_data_source_modal.tsx index 999ac80f7ad0..b44e98ee2b65 100644 --- a/src/plugins/workspace/public/components/workspace_detail/association_data_source_modal.tsx +++ b/src/plugins/workspace/public/components/workspace_detail/association_data_source_modal.tsx @@ -15,6 +15,7 @@ import { EuiModalHeader, EuiModalHeaderTitle, EuiSelectableOption, + EuiSpacer, } from '@elastic/eui'; import { FormattedMessage } from 'react-intl'; import { getDataSourcesList } from '../../utils'; @@ -64,21 +65,20 @@ export const AssociationDataSourceModal = ({ -

- -

+
- + + - + diff --git a/src/plugins/workspace/public/components/workspace_form/createpage_opensearch_connections_table.tsx b/src/plugins/workspace/public/components/workspace_form/createpage_opensearch_connections_table.tsx index 451f6db4a478..62cc977e6393 100644 --- a/src/plugins/workspace/public/components/workspace_form/createpage_opensearch_connections_table.tsx +++ b/src/plugins/workspace/public/components/workspace_form/createpage_opensearch_connections_table.tsx @@ -3,82 +3,65 @@ * SPDX-License-Identifier: Apache-2.0 */ -import React, { useMemo, useState } from 'react'; +import React, { Dispatch, SetStateAction, useState } from 'react'; import { EuiSpacer, - EuiButton, EuiFlexItem, EuiFlexGroup, - EuiFieldSearch, EuiInMemoryTable, EuiBasicTableColumn, EuiTableSelectionType, EuiTableActionsColumnType, - EuiConfirmModal, + EuiSmallButton, } from '@elastic/eui'; import { i18n } from '@osd/i18n'; import { DataSource } from '../../../common/types'; -import { useWorkspaceFormContext } from './workspace_form_context'; interface OpenSearchConnectionTableProps { assignedDataSources: DataSource[]; isDashboardAdmin: boolean; - setIsLoading: React.Dispatch>; + setAssignedDataSources: (value: DataSource[]) => void; + setModalVisible: Dispatch>; } export const CreatePageOpenSearchConnectionTable = ({ assignedDataSources, + setAssignedDataSources, isDashboardAdmin, - setIsLoading, + setModalVisible, }: OpenSearchConnectionTableProps) => { - const { formData, setSelectedDataSources } = useWorkspaceFormContext(); - const [searchTerm, setSearchTerm] = useState(''); - const [SelectedItems, setSelectedItems] = useState([]); - const [assignItems, setAssignItems] = useState([]); - const [modalVisible, setModalVisible] = useState(false); + const [selectedItems, setSelectedItems] = useState([]); - const filteredDataSources = useMemo( - () => - assignedDataSources.filter((dataSource) => - dataSource.title.toLowerCase().includes(searchTerm.toLowerCase()) - ), - [searchTerm, assignedDataSources] - ); - - const onSelectionChange = (selectedItems: DataSource[]) => { - setSelectedItems(selectedItems); - setAssignItems(selectedItems); + const onSelectionChange = (currentSelectedItems: DataSource[]) => { + setSelectedItems(currentSelectedItems); }; - const handleUnassignDataSources = async (dataSources: DataSource[]) => { - setIsLoading(true); - setModalVisible(false); - const { selectedDataSources } = formData; - const savedDataSources = (selectedDataSources ?? [])?.filter( + const handleUnassignDataSources = (dataSources: DataSource[]) => { + const savedDataSources = (assignedDataSources ?? [])?.filter( ({ id }: DataSource) => !dataSources.some((item) => item.id === id) ); - setSelectedDataSources(savedDataSources); - setIsLoading(false); + setAssignedDataSources(savedDataSources); + setSelectedItems(savedDataSources); }; const columns: Array> = [ { field: 'title', - name: i18n.translate('workspace.detail.dataSources.table.title', { + name: i18n.translate('workspace.creator.dataSources.table.title', { defaultMessage: 'Title', }), truncateText: true, }, { field: 'dataSourceEngineType', - name: i18n.translate('workspace.detail.dataSources.table.type', { + name: i18n.translate('workspace.creator.dataSources.table.type', { defaultMessage: 'Type', }), truncateText: true, }, { field: 'description', - name: i18n.translate('workspace.detail.dataSources.table.description', { + name: i18n.translate('workspace.creator.dataSources.table.description', { defaultMessage: 'Description', }), truncateText: true, @@ -86,17 +69,17 @@ export const CreatePageOpenSearchConnectionTable = ({ ...(isDashboardAdmin ? [ { - name: i18n.translate('workspace.detail.dataSources.table.actions', { + name: i18n.translate('workspace.creator.dataSources.table.actions', { defaultMessage: 'Actions', }), actions: [ { - name: i18n.translate('workspace.detail.dataSources.table.actions.remove.name', { + name: i18n.translate('workspace.creator.dataSources.table.actions.remove.name', { defaultMessage: 'Remove association', }), isPrimary: true, description: i18n.translate( - 'workspace.detail.dataSources.table.actions.remove.description', + 'workspace.creator.dataSources.table.actions.remove.description', { defaultMessage: 'Remove association', } @@ -104,10 +87,9 @@ export const CreatePageOpenSearchConnectionTable = ({ icon: 'unlink', type: 'icon', onClick: (item: DataSource) => { - setAssignItems([item]); - setModalVisible(true); + handleUnassignDataSources([item]); }, - 'data-test-subj': 'workspace-detail-dataSources-table-actions-remove', + 'data-test-subj': 'workspace-creator-dataSources-table-actions-remove', }, ], } as EuiTableActionsColumnType, @@ -120,39 +102,44 @@ export const CreatePageOpenSearchConnectionTable = ({ onSelectionChange, }; + const associationButton = ( + setModalVisible(true)} + data-test-subj="workspace-creator-dataSources-assign-button" + > + {i18n.translate('workspace.form.selectDataSourcePanel.addNew', { + defaultMessage: 'Add New', + })} + + ); + + const removeButton = ( + { + handleUnassignDataSources(selectedItems); + }} + data-test-subj="workspace-creator-dataSources-assign-button" + > + {i18n.translate('workspace.form.selectDataSourcePanel.remove', { + defaultMessage: 'Remove Selected', + })} + + ); + return ( <> - - - {modalVisible && ( - { - setModalVisible(false); - }} - onConfirm={() => { - handleUnassignDataSources(assignItems); - }} - cancelButtonText={i18n.translate('workspace.detail.dataSources.modal.cancelButton', { - defaultMessage: 'Cancel', - })} - confirmButtonText={i18n.translate('workspace.detail.dataSources.Modal.confirmButton', { - defaultMessage: 'Remove connections', - })} - buttonColor="danger" - defaultFocusedButton="confirm" + + {selectedItems.length > 0 && {removeButton}} + {isDashboardAdmin && {associationButton}} + + + {assignedDataSources.length > 0 && ( + )} diff --git a/src/plugins/workspace/public/components/workspace_form/select_data_source_panel.tsx b/src/plugins/workspace/public/components/workspace_form/select_data_source_panel.tsx index 3746382e51e3..e192d17f4899 100644 --- a/src/plugins/workspace/public/components/workspace_form/select_data_source_panel.tsx +++ b/src/plugins/workspace/public/components/workspace_form/select_data_source_panel.tsx @@ -3,27 +3,13 @@ * SPDX-License-Identifier: Apache-2.0 */ -import React, { useCallback, useEffect, useState } from 'react'; -import { - EuiSpacer, - EuiFormLabel, - EuiSelectable, - EuiText, - EuiSelectableOption, - EuiPopover, - EuiSmallButton, - EuiFlexGroup, - EuiFlexItem, - EuiLoadingSpinner, -} from '@elastic/eui'; +import React, { useState } from 'react'; +import { EuiSpacer, EuiFormLabel, EuiText, EuiFlexItem } from '@elastic/eui'; import { i18n } from '@osd/i18n'; import { SavedObjectsStart } from '../../../../../core/public'; -import { getDataSourcesList } from '../../utils'; import { DataSource } from '../../../common/types'; import { WorkspaceFormError } from './types'; import { AssociationDataSourceModal } from '../workspace_detail/association_data_source_modal'; -import { useWorkspaceFormContext } from './workspace_form_context'; -import { OpenSearchConnectionTable } from '../workspace_detail/opensearch_connections_table'; import { CreatePageOpenSearchConnectionTable } from './createpage_opensearch_connections_table'; export interface SelectDataSourcePanelProps { @@ -41,77 +27,22 @@ export const SelectDataSourcePanel = ({ savedObjects, isDashboardAdmin, }: SelectDataSourcePanelProps) => { - const [dataSourcesOptions, setDataSourcesOptions] = useState([]); - const [isVisible, setIsVisible] = useState(false); - const [dataSourcesOptionsInTable, setDataSourcesOptionsInTable] = useState([]); - const [isLoading, setIsLoading] = useState(false); - useEffect(() => { - if (!savedObjects) return; - getDataSourcesList(savedObjects.client, ['*']).then((result) => { - const options = result.map((option) => ({ - label: option.title, - value: option.id, - description: option.description, - type: option.dataSourceEngineType, - })); - - setDataSourcesOptions(options); - }); - }, [savedObjects, setDataSourcesOptions]); - - const handleSelect = useCallback( - (options) => { - setDataSourcesOptions(options); - const selectedOptions = []; - for (const option of options) { - if (option.checked === 'on') - selectedOptions.push({ title: option.label, id: option.value }); - } - onChange(selectedOptions); - }, - [onChange] - ); + const [modalVisible, setModalVisible] = useState(false); const handleAssignDataSources = async (dataSources: DataSource[]) => { - setIsVisible(false); + setModalVisible(false); const savedDataSources: DataSource[] = [...assignedDataSources, ...dataSources]; onChange(savedDataSources); }; - const associationButton = ( - setIsVisible(true)} - isLoading={isLoading} - // data-test-subj="workspace-detail-dataSources-assign-button" - > - {i18n.translate('workspace.form.selectDataSourcePanel.addNew', { - defaultMessage: 'Add New', - })} - - ); - - const loadingMessage = ( -
- - - - {i18n.translate('workspace.detail.dataSources.noAssociation.message', { - defaultMessage: 'Loading OpenSearch connections...', - })} - -
- ); - const renderTableContent = () => { - if (isLoading) { - return loadingMessage; - } return ( ); }; @@ -126,59 +57,12 @@ export const SelectDataSourcePanel = ({
- {/* { - setIsDataSourceAssociateListOpen((current) => !current); - }} - data-test-subj={`workspaceForm-permissionSettingPanel-addNew`} - color="primary" - iconType="plusInCircle" - > - {i18n.translate('workspace.form.selectDataSourcePanel.addNew', { - defaultMessage: 'Add New', - })} - - } - closePopover={() => { - setIsDataSourceAssociateListOpen(false); - }} - isOpen={isDataSourceAssociateListOpen} - > - { - setDataSourcesOptions(options); - }} - > - {(list, search) => ( - <> - {search} - {list} - - )} - - */} - - {isDashboardAdmin && {associationButton}} - - - {assignedDataSources.length > 0 && renderTableContent()} - {isVisible && ( + {renderTableContent()} + {modalVisible && ( setIsVisible(false)} + closeModal={() => setModalVisible(false)} handleAssignDataSources={handleAssignDataSources} /> )} From 4e9b8fb2cd190545f42a2216ddb8404e51784223 Mon Sep 17 00:00:00 2001 From: Kapian1234 Date: Wed, 21 Aug 2024 12:28:55 +0800 Subject: [PATCH 7/9] Modify tests Signed-off-by: Kapian1234 --- .../select_data_source_panel.test.tsx | 10 ++--- ...reatepage_opensearch_connections_table.tsx | 2 +- .../select_data_source_panel.test.tsx | 45 +++++++++++++++---- .../select_data_source_panel.tsx | 1 - 4 files changed, 42 insertions(+), 16 deletions(-) diff --git a/src/plugins/workspace/public/components/workspace_detail/select_data_source_panel.test.tsx b/src/plugins/workspace/public/components/workspace_detail/select_data_source_panel.test.tsx index 6a81482096ec..943201c41e14 100644 --- a/src/plugins/workspace/public/components/workspace_detail/select_data_source_panel.test.tsx +++ b/src/plugins/workspace/public/components/workspace_detail/select_data_source_panel.test.tsx @@ -141,7 +141,7 @@ describe('WorkspaceDetail', () => { getByText('Add OpenSearch connections that will be available in the workspace.') ).toBeInTheDocument(); expect(getByText('Close')).toBeInTheDocument(); - expect(getByText('Save changes')).toBeInTheDocument(); + expect(getByText('Associate data sources')).toBeInTheDocument(); expect(getByText('ds-2-title')).toBeInTheDocument(); }); fireEvent.click(getByText('Close')); @@ -171,11 +171,11 @@ describe('WorkspaceDetail', () => { getByText('Add OpenSearch connections that will be available in the workspace.') ).toBeInTheDocument(); expect(getByText('Close')).toBeInTheDocument(); - expect(getByText('Save changes')).toBeInTheDocument(); + expect(getByText('Associate data sources')).toBeInTheDocument(); expect(getByText('ds-2-title')).toBeInTheDocument(); }); fireEvent.click(getByText('ds-2-title')); - fireEvent.click(getByText('Save changes')); + fireEvent.click(getByText('Associate data sources')); await waitFor(() => { expect(notificationToastsAddSuccess).toHaveBeenCalled(); }); @@ -205,11 +205,11 @@ describe('WorkspaceDetail', () => { getByText('Add OpenSearch connections that will be available in the workspace.') ).toBeInTheDocument(); expect(getByText('Close')).toBeInTheDocument(); - expect(getByText('Save changes')).toBeInTheDocument(); + expect(getByText('Associate data sources')).toBeInTheDocument(); expect(getByText('ds-2-title')).toBeInTheDocument(); }); fireEvent.click(getByText('ds-2-title')); - fireEvent.click(getByText('Save changes')); + fireEvent.click(getByText('Associate data sources')); await waitFor(() => { expect(notificationToastsAddDanger).toHaveBeenCalled(); }); diff --git a/src/plugins/workspace/public/components/workspace_form/createpage_opensearch_connections_table.tsx b/src/plugins/workspace/public/components/workspace_form/createpage_opensearch_connections_table.tsx index 62cc977e6393..33108b46d2b5 100644 --- a/src/plugins/workspace/public/components/workspace_form/createpage_opensearch_connections_table.tsx +++ b/src/plugins/workspace/public/components/workspace_form/createpage_opensearch_connections_table.tsx @@ -108,7 +108,7 @@ export const CreatePageOpenSearchConnectionTable = ({ data-test-subj="workspace-creator-dataSources-assign-button" > {i18n.translate('workspace.form.selectDataSourcePanel.addNew', { - defaultMessage: 'Add New', + defaultMessage: 'Add data sources', })} ); diff --git a/src/plugins/workspace/public/components/workspace_form/select_data_source_panel.test.tsx b/src/plugins/workspace/public/components/workspace_form/select_data_source_panel.test.tsx index eb3aa0394b82..21fe714d9987 100644 --- a/src/plugins/workspace/public/components/workspace_form/select_data_source_panel.test.tsx +++ b/src/plugins/workspace/public/components/workspace_form/select_data_source_panel.test.tsx @@ -4,7 +4,7 @@ */ import React from 'react'; -import { fireEvent, render } from '@testing-library/react'; +import { fireEvent, render, waitFor } from '@testing-library/react'; import { SelectDataSourcePanel, SelectDataSourcePanelProps } from './select_data_source_panel'; import { coreMock } from '../../../../../core/public/mocks'; @@ -24,16 +24,18 @@ const mockCoreStart = coreMock.createStart(); const setup = ({ savedObjects = mockCoreStart.savedObjects, - selectedDataSources = [], + assignedDataSources = [], onChange = jest.fn(), errors = undefined, + isDashboardAdmin = true, }: Partial) => { return render( ); }; @@ -68,18 +70,43 @@ describe('SelectDataSourcePanel', () => { ); }); - it('should render consistent data sources when selected data sources passed', async () => { - const { getByText } = await setup({ selectedDataSources: [] }); + it('should click on Add data sources button', async () => { + const { getByText } = setup({}); + expect(getByText('Add data sources')).toBeInTheDocument(); - expect(getByText(dataSources[0].title)).toBeInTheDocument(); - expect(getByText(dataSources[1].title)).toBeInTheDocument(); + fireEvent.click(getByText('Add data sources')); + await waitFor(() => { + expect( + getByText('Add OpenSearch connections that will be available in the workspace.') + ).toBeInTheDocument(); + expect(getByText('Close')).toBeInTheDocument(); + expect(getByText('Associate data sources')).toBeInTheDocument(); + expect(getByText(dataSources[0].title)).toBeInTheDocument(); + }); + fireEvent.click(getByText('Close')); + }); + + it('should render consistent data sources when assigned data sources passed', async () => { + const { getByText } = setup({ assignedDataSources: [] }); + fireEvent.click(getByText('Add data sources')); + await waitFor(() => { + expect(getByText(dataSources[0].title)).toBeInTheDocument(); + expect(getByText(dataSources[1].title)).toBeInTheDocument(); + }); + fireEvent.click(getByText(dataSources[0].title)); + fireEvent.click(getByText(dataSources[1].title)); + await waitFor(() => { + fireEvent.click(getByText('Associate data sources')); + expect(getByText(dataSources[0].title)).toBeInTheDocument(); + expect(getByText(dataSources[1].title)).toBeInTheDocument(); + }); }); - it('should call onChange when updating selected data sources in selectable', async () => { + it('should call onChange when updating assigned data sources', async () => { const onChangeMock = jest.fn(); const { getByTitle } = await setup({ onChange: onChangeMock, - selectedDataSources: [], + assignedDataSources: [], }); expect(onChangeMock).not.toHaveBeenCalled(); fireEvent.click(getByTitle(dataSources[0].title)); diff --git a/src/plugins/workspace/public/components/workspace_form/select_data_source_panel.tsx b/src/plugins/workspace/public/components/workspace_form/select_data_source_panel.tsx index e192d17f4899..ced22ee11ba7 100644 --- a/src/plugins/workspace/public/components/workspace_form/select_data_source_panel.tsx +++ b/src/plugins/workspace/public/components/workspace_form/select_data_source_panel.tsx @@ -39,7 +39,6 @@ export const SelectDataSourcePanel = ({ return ( Date: Wed, 21 Aug 2024 14:34:39 +0800 Subject: [PATCH 8/9] Modify tests Signed-off-by: Kapian1234 --- ...reatepage_opensearch_connections_table.tsx | 2 +- .../select_data_source_panel.test.tsx | 32 +++++++++++++++++-- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/src/plugins/workspace/public/components/workspace_form/createpage_opensearch_connections_table.tsx b/src/plugins/workspace/public/components/workspace_form/createpage_opensearch_connections_table.tsx index 33108b46d2b5..9cc41ab51a8d 100644 --- a/src/plugins/workspace/public/components/workspace_form/createpage_opensearch_connections_table.tsx +++ b/src/plugins/workspace/public/components/workspace_form/createpage_opensearch_connections_table.tsx @@ -122,7 +122,7 @@ export const CreatePageOpenSearchConnectionTable = ({ data-test-subj="workspace-creator-dataSources-assign-button" > {i18n.translate('workspace.form.selectDataSourcePanel.remove', { - defaultMessage: 'Remove Selected', + defaultMessage: 'Remove selected', })} ); diff --git a/src/plugins/workspace/public/components/workspace_form/select_data_source_panel.test.tsx b/src/plugins/workspace/public/components/workspace_form/select_data_source_panel.test.tsx index 21fe714d9987..45b2e2c4445e 100644 --- a/src/plugins/workspace/public/components/workspace_form/select_data_source_panel.test.tsx +++ b/src/plugins/workspace/public/components/workspace_form/select_data_source_panel.test.tsx @@ -4,7 +4,7 @@ */ import React from 'react'; -import { fireEvent, render, waitFor } from '@testing-library/react'; +import { fireEvent, render, waitFor, screen } from '@testing-library/react'; import { SelectDataSourcePanel, SelectDataSourcePanelProps } from './select_data_source_panel'; import { coreMock } from '../../../../../core/public/mocks'; @@ -104,12 +104,38 @@ describe('SelectDataSourcePanel', () => { it('should call onChange when updating assigned data sources', async () => { const onChangeMock = jest.fn(); - const { getByTitle } = await setup({ + const { getByText } = await setup({ onChange: onChangeMock, assignedDataSources: [], }); expect(onChangeMock).not.toHaveBeenCalled(); - fireEvent.click(getByTitle(dataSources[0].title)); + fireEvent.click(getByText('Add data sources')); + await waitFor(() => { + expect(getByText(dataSources[0].title)).toBeInTheDocument(); + }); + fireEvent.click(getByText(dataSources[0].title)); + fireEvent.click(getByText('Associate data sources')); expect(onChangeMock).toHaveBeenCalledWith([{ id: 'id1', title: 'title1' }]); }); + + it('should call onChange when remove assigned data sources', async () => { + const onChangeMock = jest.fn(); + const { getByText, getAllByTestId } = await setup({ + onChange: onChangeMock, + assignedDataSources: dataSources, + }); + expect(onChangeMock).not.toHaveBeenCalled(); + + // Remove by unlink icon + const button = getAllByTestId('workspace-creator-dataSources-table-actions-remove')[0]; + fireEvent.click(button); + expect(onChangeMock).toHaveBeenCalledWith([{ id: 'id2', title: 'title2' }]); + + // Remove by clicking the checkbox and remove button + const checkbox = screen.getAllByRole('checkbox')[0]; + fireEvent.click(checkbox); + expect(getByText('Remove selected')).toBeInTheDocument(); + fireEvent.click(getByText('Remove selected')); + expect(onChangeMock).toHaveBeenCalledWith([]); + }); }); From cd503094172d60f7eff378d9a4aa788c1befa9fb Mon Sep 17 00:00:00 2001 From: Kapian1234 Date: Wed, 21 Aug 2024 15:59:45 +0800 Subject: [PATCH 9/9] Modify tests Signed-off-by: Kapian1234 --- .../workspace_form/select_data_source_panel.test.tsx | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/plugins/workspace/public/components/workspace_form/select_data_source_panel.test.tsx b/src/plugins/workspace/public/components/workspace_form/select_data_source_panel.test.tsx index 45b2e2c4445e..c919995a4e90 100644 --- a/src/plugins/workspace/public/components/workspace_form/select_data_source_panel.test.tsx +++ b/src/plugins/workspace/public/components/workspace_form/select_data_source_panel.test.tsx @@ -93,13 +93,6 @@ describe('SelectDataSourcePanel', () => { expect(getByText(dataSources[0].title)).toBeInTheDocument(); expect(getByText(dataSources[1].title)).toBeInTheDocument(); }); - fireEvent.click(getByText(dataSources[0].title)); - fireEvent.click(getByText(dataSources[1].title)); - await waitFor(() => { - fireEvent.click(getByText('Associate data sources')); - expect(getByText(dataSources[0].title)).toBeInTheDocument(); - expect(getByText(dataSources[1].title)).toBeInTheDocument(); - }); }); it('should call onChange when updating assigned data sources', async () => {