From 9d62670eef1600478d6da7606112b4a071d04a33 Mon Sep 17 00:00:00 2001 From: Oleg Shur Date: Tue, 20 Apr 2021 16:48:50 +0300 Subject: [PATCH] Add ConfirmationModal common component (#383) * Add ConfirmationModal common component * Update specs --- .../src/components/Connect/List/List.tsx | 8 +- .../src/components/Connect/List/ListItem.tsx | 43 ++++-- .../Connect/List/__tests__/ListItem.spec.tsx | 12 +- .../__snapshots__/ListItem.spec.tsx.snap | 131 ++++++++++-------- .../ConfirmationModal/ConfirmationModal.tsx | 50 +++++++ .../__test__/ConfirmationModal.spec.tsx | 68 +++++++++ 6 files changed, 236 insertions(+), 76 deletions(-) create mode 100644 kafka-ui-react-app/src/components/common/ConfirmationModal/ConfirmationModal.tsx create mode 100644 kafka-ui-react-app/src/components/common/ConfirmationModal/__test__/ConfirmationModal.spec.tsx diff --git a/kafka-ui-react-app/src/components/Connect/List/List.tsx b/kafka-ui-react-app/src/components/Connect/List/List.tsx index 8b886c243f..db84c4772d 100644 --- a/kafka-ui-react-app/src/components/Connect/List/List.tsx +++ b/kafka-ui-react-app/src/components/Connect/List/List.tsx @@ -37,9 +37,11 @@ const List: React.FC = ({ return (
All Connectors -
- Kafka Connect section is under construction. -
+
+
+ Kafka Connect section is under construction. +
+
= ({ }, }) => { const dispatch = useDispatch(); + const [ + isDeleteConnectorConfirmationVisible, + setDeleteConnectorConfirmationVisible, + ] = React.useState(false); const handleDelete = React.useCallback(() => { if (clusterName && connect && name) { dispatch(deleteConnector(clusterName, connect, name)); } + setDeleteConnectorConfirmationVisible(false); }, [clusterName, connect, name]); const runningTasks = React.useMemo(() => { @@ -67,20 +73,31 @@ const ListItem: React.FC = ({ )} - - - - - } - right + +
+ + + + } + right + > + + setDeleteConnectorConfirmationVisible(true)} + > + Remove Connector + + +
+ setDeleteConnectorConfirmationVisible(false)} + onConfirm={handleDelete} > - - - Remove Connector - -
+ Are you sure want to remove {name} connector? + ); diff --git a/kafka-ui-react-app/src/components/Connect/List/__tests__/ListItem.spec.tsx b/kafka-ui-react-app/src/components/Connect/List/__tests__/ListItem.spec.tsx index 7d642038ba..8d7213eb02 100644 --- a/kafka-ui-react-app/src/components/Connect/List/__tests__/ListItem.spec.tsx +++ b/kafka-ui-react-app/src/components/Connect/List/__tests__/ListItem.spec.tsx @@ -8,6 +8,11 @@ import ListItem, { ListItemProps } from '../ListItem'; const store = configureStore(); +jest.mock( + 'components/common/ConfirmationModal/ConfirmationModal', + () => 'mock-ConfirmationModal' +); + describe('Connectors ListItem', () => { const connector = connectorsPayload[0]; const setupWrapper = (props: Partial = {}) => ( @@ -57,7 +62,12 @@ describe('Connectors ListItem', () => { it('handles delete', () => { const wrapper = mount(setupWrapper()); - wrapper.find('DropdownItem a').last().simulate('click'); + expect(wrapper.find('mock-ConfirmationModal').prop('isOpen')).toBeFalsy(); + wrapper.find('DropdownItem').last().simulate('click'); + const modal = wrapper.find('mock-ConfirmationModal'); + expect(modal.prop('isOpen')).toBeTruthy(); + modal.simulate('cancel'); + expect(wrapper.find('mock-ConfirmationModal').prop('isOpen')).toBeFalsy(); }); it('matches snapshot', () => { diff --git a/kafka-ui-react-app/src/components/Connect/List/__tests__/__snapshots__/ListItem.spec.tsx.snap b/kafka-ui-react-app/src/components/Connect/List/__tests__/__snapshots__/ListItem.spec.tsx.snap index 2651ceeba8..62a30c11f3 100644 --- a/kafka-ui-react-app/src/components/Connect/List/__tests__/__snapshots__/ListItem.spec.tsx.snap +++ b/kafka-ui-react-app/src/components/Connect/List/__tests__/__snapshots__/ListItem.spec.tsx.snap @@ -110,77 +110,90 @@ exports[`Connectors ListItem matches snapshot 1`] = ` 2 - - - - - } - right={true} + +
-
+ + + } + right={true} >
- -
- -
- + +
+ + Are you sure want to remove + + hdfs-source-connector + + connector? + diff --git a/kafka-ui-react-app/src/components/common/ConfirmationModal/ConfirmationModal.tsx b/kafka-ui-react-app/src/components/common/ConfirmationModal/ConfirmationModal.tsx new file mode 100644 index 0000000000..672b1c0e0d --- /dev/null +++ b/kafka-ui-react-app/src/components/common/ConfirmationModal/ConfirmationModal.tsx @@ -0,0 +1,50 @@ +import React from 'react'; + +export interface ConfirmationModalProps { + isOpen?: boolean; + title?: React.ReactNode; + onConfirm(): void; + onCancel(): void; +} + +const ConfirmationModal: React.FC = ({ + isOpen, + children, + title, + onCancel, + onConfirm, +}) => { + if (!isOpen) return null; + + return ( +
+ + ); +}; + +export default ConfirmationModal; diff --git a/kafka-ui-react-app/src/components/common/ConfirmationModal/__test__/ConfirmationModal.spec.tsx b/kafka-ui-react-app/src/components/common/ConfirmationModal/__test__/ConfirmationModal.spec.tsx new file mode 100644 index 0000000000..17c2f1aa42 --- /dev/null +++ b/kafka-ui-react-app/src/components/common/ConfirmationModal/__test__/ConfirmationModal.spec.tsx @@ -0,0 +1,68 @@ +import { mount, ReactWrapper } from 'enzyme'; +import React from 'react'; +import ConfirmationModal, { + ConfirmationModalProps, +} from '../ConfirmationModal'; + +const confirmMock = jest.fn(); +const cancelMock = jest.fn(); +const body = 'Please Confirm the action!'; +describe('ConfiramationModal', () => { + const setupWrapper = (props: Partial = {}) => ( + + {body} + + ); + + it('renders nothing', () => { + const wrapper = mount(setupWrapper({ isOpen: false })); + expect(wrapper.exists(ConfirmationModal)).toBeTruthy(); + expect(wrapper.exists('.modal.is-active')).toBeFalsy(); + }); + it('renders modal', () => { + const wrapper = mount(setupWrapper({ isOpen: true })); + expect(wrapper.exists(ConfirmationModal)).toBeTruthy(); + expect(wrapper.exists('.modal.is-active')).toBeTruthy(); + expect(wrapper.find('.modal-card-body').text()).toEqual(body); + }); + it('renders modal with default header', () => { + const wrapper = mount(setupWrapper({ isOpen: true })); + expect(wrapper.find('.modal-card-title').text()).toEqual( + 'Confirm the action' + ); + }); + it('renders modal with custom header', () => { + const title = 'My Custom Header'; + const wrapper = mount(setupWrapper({ isOpen: true, title })); + expect(wrapper.find('.modal-card-title').text()).toEqual(title); + }); + it('handles onConfirm when user clicks confirm button', () => { + const wrapper = mount(setupWrapper({ isOpen: true })); + expect(wrapper.find('.modal-card-foot button').length).toEqual(2); + const cancelBtn = wrapper.find('.modal-card-foot button').at(0); + expect(cancelBtn.text()).toEqual('Confirm'); + cancelBtn.simulate('click'); + expect(cancelMock).toHaveBeenCalledTimes(0); + expect(confirmMock).toHaveBeenCalledTimes(1); + }); + + describe('cancellation', () => { + let wrapper: ReactWrapper; + beforeEach(() => { + wrapper = mount(setupWrapper({ isOpen: true })); + }); + it('handles onCancel when user clicks on modal-background', () => { + wrapper.find('.modal-background').simulate('click'); + expect(cancelMock).toHaveBeenCalledTimes(1); + expect(confirmMock).toHaveBeenCalledTimes(0); + }); + it('handles onCancel when user clicks on Cancel button', () => { + expect(wrapper.find('.modal-card-foot button').length).toEqual(2); + const cancelBtn = wrapper.find('.modal-card-foot button').at(1); + expect(cancelBtn.text()).toEqual('Cancel'); + cancelBtn.simulate('click'); + expect(cancelMock).toHaveBeenCalledTimes(1); + expect(confirmMock).toHaveBeenCalledTimes(0); + }); + }); +});