From e8631e976bc81f6153222fb120d72af5ac5e670c Mon Sep 17 00:00:00 2001 From: Vu Nguyen Date: Wed, 24 Jun 2020 14:01:52 +0700 Subject: [PATCH] refactor: #1325 Refactor developer app revision modal using react-redux hooks (#1823) * refactor: #1325 Refactor app revision modal using react-redux hooks --- ...developer-app-revision-modal.test.tsx.snap | 198 +++--------------- .../developer-app-revision-modal.test.tsx | 136 ++++++------ .../developer-app-revision-modal.tsx | 137 +++++------- 3 files changed, 142 insertions(+), 329 deletions(-) diff --git a/packages/marketplace/src/components/ui/developer-app-revision-modal/__test__/__snapshots__/developer-app-revision-modal.test.tsx.snap b/packages/marketplace/src/components/ui/developer-app-revision-modal/__test__/__snapshots__/developer-app-revision-modal.test.tsx.snap index 1e9512cf10..4ca7a0406c 100644 --- a/packages/marketplace/src/components/ui/developer-app-revision-modal/__test__/__snapshots__/developer-app-revision-modal.test.tsx.snap +++ b/packages/marketplace/src/components/ui/developer-app-revision-modal/__test__/__snapshots__/developer-app-revision-modal.test.tsx.snap @@ -1,23 +1,20 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`DeveloperAppRevisionModal should match a snapshot 1`] = ` - - CANCEL PENDING REVISIONS - + - - - YES, PROCEED - + appId="1" + visible={true} + > + - CANCEL + CANCEL PENDING REVISIONS - - } - title="Please confirm" - visible={false} - > -

- Are you sure you wish to cancel any pending revisions for this App? -

-
- - - All pending revisions for this app have been cancelled. You can now use the ‘Edit Detail’ option to make any additional changes as required. - + } + title="Pending Revisions" + visible={true} + /> -
+
`; diff --git a/packages/marketplace/src/components/ui/developer-app-revision-modal/__test__/developer-app-revision-modal.test.tsx b/packages/marketplace/src/components/ui/developer-app-revision-modal/__test__/developer-app-revision-modal.test.tsx index 9edcdde5e7..6e6de0917e 100644 --- a/packages/marketplace/src/components/ui/developer-app-revision-modal/__test__/developer-app-revision-modal.test.tsx +++ b/packages/marketplace/src/components/ui/developer-app-revision-modal/__test__/developer-app-revision-modal.test.tsx @@ -1,6 +1,9 @@ import * as React from 'react' import { AppDetailState } from '@/reducers/app-detail' -import { shallow } from 'enzyme' +import { mount } from 'enzyme' +import * as ReactRedux from 'react-redux' +import configureStore from 'redux-mock-store' +import appState from '@/reducers/__stubs__/app-state' import { DeveloperAppRevisionModal, @@ -12,10 +15,10 @@ import { backToAppDetailsModal, } from '../developer-app-revision-modal' import { appDetailDataStub } from '@/sagas/__stubs__/app-detail' -import { revisionDetailDataStub } from '@/sagas/__stubs__/revision-detail' -import { appPermissionStub } from '@/sagas/__stubs__/app-permission' -import { revisionsDataStub } from '@/sagas/__stubs__/revisions' -import { integrationTypesStub } from '@/sagas/__stubs__/integration-types' +import { revisionDetailRequestData, revisionDetailClearData, declineRevision } from '@/actions/revision-detail' +import { revisionsRequestData, revisionsClearData } from '@/actions/revisions' +import { appDetailRequestData } from '@/actions/app-detail' +import { LoginIdentity } from '@reapit/cognito-auth' const props: DeveloperAppRevisionModalProps = { appId: '1', @@ -30,106 +33,99 @@ const props: DeveloperAppRevisionModalProps = { }, isStale: false, }, - revisionDetailState: { - loading: false, - error: false, - revisionDetailData: { - data: revisionDetailDataStub.data, - scopes: appPermissionStub, - desktopIntegrationTypes: integrationTypesStub, - }, - approveFormState: 'PENDING', - declineFormState: 'PENDING', - }, - revisionsState: { - loading: false, - revisions: revisionsDataStub, - }, afterClose: jest.fn(), - clearAppRevisionDetail: jest.fn(), - clearAppRevisions: jest.fn(), - declineAppRevision: jest.fn(), - fetchAppDetail: jest.fn(), - fetchAppRevisionDetail: jest.fn(), - fetchAppRevisions: jest.fn(), - loginIdentity: { - adminId: '1', - clientId: '2', - developerId: '3', - email: 'mock@test.com', - name: 'mock user', - userCode: 'mockUserCode', - isAdmin: false, - userTel: '123', - }, } describe('DeveloperAppRevisionModal', () => { + let store + let spyDispatch + beforeEach(() => { + /* mocking store */ + const mockStore = configureStore() + store = mockStore(appState) + spyDispatch = jest.spyOn(ReactRedux, 'useDispatch').mockImplementation(() => store.dispatch) + }) + it('should match a snapshot', () => { - expect(shallow()).toMatchSnapshot() + expect( + mount( + + + , + ), + ).toMatchSnapshot() }) describe('handleUseEffectToFetchAppRevisionDetail', () => { it('should run correctly', () => { - const { appId, appDetailState, fetchAppRevisionDetail, visible } = props + const { appId, appDetailState, visible } = props const { appDetailData } = appDetailState as AppDetailState const appRevisionId = appDetailData?.data.id || '' - const fn = handleUseEffectToFetchAppRevisionDetail(appId, appRevisionId, fetchAppRevisionDetail, visible) + const fn = handleUseEffectToFetchAppRevisionDetail(appId, spyDispatch, visible, appRevisionId) fn() - expect(fetchAppRevisionDetail).toBeCalledWith({ - appId, - appRevisionId, - }) + expect(spyDispatch).toBeCalledWith( + revisionDetailRequestData({ + appId, + appRevisionId, + }), + ) }) }) describe('handleUseEffectToFetchAppRevisions', () => { it('should run correctly', () => { - const { appId, fetchAppRevisions, visible } = props - const fn = handleUseEffectToFetchAppRevisions(appId, fetchAppRevisions, visible) + const { appId, visible } = props + const fn = handleUseEffectToFetchAppRevisions(appId, spyDispatch, visible) fn() - expect(fetchAppRevisions).toBeCalledWith(appId) + expect(spyDispatch).toBeCalledWith(revisionsRequestData({ appId })) }) }) describe('handelePendingRevisionsModalAfterClose', () => { it('should run correctly', () => { - const { afterClose, clearAppRevisions, clearAppRevisionDetail } = props - const fn = handelePendingRevisionsModalAfterClose(afterClose, clearAppRevisions, clearAppRevisionDetail) + const { afterClose } = props + const fn = handelePendingRevisionsModalAfterClose(afterClose, spyDispatch) fn() expect(afterClose).toBeCalled() - expect(clearAppRevisions).toBeCalled() - expect(clearAppRevisionDetail).toBeCalled() + expect(spyDispatch).toBeCalledWith(revisionsClearData(null)) + expect(spyDispatch).toBeCalledWith(revisionDetailClearData(null)) }) }) describe('handleCancelPendingRevisionsButtonClick', () => { it('should run correctly', () => { - const { - declineAppRevision, - appId, - revisionDetailState: { revisionDetailData }, - loginIdentity, - } = props - const appRevisionId = revisionDetailData?.data.id || '' - const fn = handleCancelPendingRevisionsButtonClick(declineAppRevision, appId, appRevisionId, loginIdentity) + const { appId } = props + const appRevisionId = 'test' + const loginIdentity: LoginIdentity = { + adminId: '1', + clientId: '2', + developerId: '3', + email: 'mock@test.com', + name: 'mock user', + userCode: 'mockUserCode', + isAdmin: false, + userTel: '123', + } + const fn = handleCancelPendingRevisionsButtonClick(spyDispatch, appId, appRevisionId, loginIdentity) fn() if (!appRevisionId || !loginIdentity) { expect(handleCancelPendingRevisionsButtonClick).toReturn() } - expect(declineAppRevision).toBeCalledWith({ - appId, - appRevisionId, - name: loginIdentity?.name, - email: loginIdentity?.email, - rejectionReason: 'Developer Cancelled', - }) + expect(spyDispatch).toBeCalledWith( + declineRevision({ + appId, + appRevisionId, + name: loginIdentity?.name, + email: loginIdentity?.email, + rejectionReason: 'Developer Cancelled', + }), + ) }) }) describe('backToAppDetailsModal', () => { it('should run correctly', () => { - const { clearAppRevisions, clearAppRevisionDetail, fetchAppDetail, appId } = props - const fn = backToAppDetailsModal(fetchAppDetail, clearAppRevisions, clearAppRevisionDetail, appId) + const { appId } = props + const fn = backToAppDetailsModal(appId, spyDispatch) fn() - expect(clearAppRevisions).toBeCalled() - expect(clearAppRevisionDetail).toBeCalled() - expect(fetchAppDetail).toBeCalledWith(appId) + expect(spyDispatch).toBeCalledWith(revisionsClearData(null)) + expect(spyDispatch).toBeCalledWith(revisionDetailClearData(null)) + expect(spyDispatch).toBeCalledWith(appDetailRequestData({ id: appId })) }) }) }) diff --git a/packages/marketplace/src/components/ui/developer-app-revision-modal/developer-app-revision-modal.tsx b/packages/marketplace/src/components/ui/developer-app-revision-modal/developer-app-revision-modal.tsx index cf80b31325..31c3909dd1 100644 --- a/packages/marketplace/src/components/ui/developer-app-revision-modal/developer-app-revision-modal.tsx +++ b/packages/marketplace/src/components/ui/developer-app-revision-modal/developer-app-revision-modal.tsx @@ -1,25 +1,18 @@ import * as React from 'react' import { Dispatch } from 'redux' -import { connect } from 'react-redux' -import { ReduxState } from '@/types/core' +import { useSelector, useDispatch } from 'react-redux' import { appDetailRequestData } from '@/actions/app-detail' -import { - revisionDetailRequestData, - RevisionDetailRequestParams, - declineRevision, - RevisionDeclineRequestParams, - revisionDetailClearData, -} from '@/actions/revision-detail' +import { revisionDetailRequestData, declineRevision, revisionDetailClearData } from '@/actions/revision-detail' import { revisionsRequestData, revisionsClearData } from '@/actions/revisions' import { AppDetailState } from '@/reducers/app-detail' -import { RevisionsState } from '@/reducers/revisions' -import { RevisionDetailState } from '@/reducers/revision-detail' import { LoginIdentity } from '@reapit/cognito-auth' import { Modal, Loader, Button } from '@reapit/elements' import AppRevisionComparison from '../app-revision-comparison/app-revision-comparison' import CallToAction from '@/components/ui/call-to-action' import { DeveloperAppDetailState } from '@/reducers/developer' +import { selectAppRevisions, selectAppRevisionDetail } from '@/selector/app-revisions' +import { selectLoginIdentity } from '@/selector/auth' export interface OwnProps { visible: boolean @@ -29,25 +22,16 @@ export interface OwnProps { onCancelSuccess?: () => void } -export interface DeveloperAppModalMappedProps { - revisionsState: RevisionsState - revisionDetailState: RevisionDetailState - loginIdentity?: LoginIdentity -} - -export interface DeveloperAppModalMappedAction { - fetchAppDetail: (id: string) => void - fetchAppRevisions: (appId: string) => void - fetchAppRevisionDetail: (params: RevisionDetailRequestParams) => void - declineAppRevision: (params: RevisionDeclineRequestParams) => void - clearAppRevisionDetail: () => void - clearAppRevisions: () => void +export type DeveloperAppRevisionModalProps = { + visible: boolean + appId: string + appDetailState: DeveloperAppDetailState | AppDetailState + afterClose: () => void + onCancelSuccess?: () => void } -export type DeveloperAppRevisionModalProps = OwnProps & DeveloperAppModalMappedProps & DeveloperAppModalMappedAction - export const handleCancelPendingRevisionsButtonClick = ( - declineAppRevision: (params: RevisionDeclineRequestParams) => void, + dispatch: Dispatch, appId: string, appRevisionId?: string, loginIdentity?: LoginIdentity, @@ -58,45 +42,52 @@ export const handleCancelPendingRevisionsButtonClick = ( return } const { name, email } = loginIdentity - declineAppRevision({ - appId, - appRevisionId, - name, - email, - rejectionReason: 'Developer Cancelled', - callback: onCancelSuccess, - }) + dispatch( + declineRevision({ + appId, + appRevisionId, + name, + email, + rejectionReason: 'Developer Cancelled', + callback: onCancelSuccess, + }), + ) } } -export const handelePendingRevisionsModalAfterClose = (afterClose, clearAppRevisions, clearAppRevisionDetail) => { +export const handelePendingRevisionsModalAfterClose = (afterClose: () => void, dispatch: Dispatch) => { return () => { afterClose() - clearAppRevisions() - clearAppRevisionDetail() + dispatch(revisionsClearData(null)) + dispatch(revisionDetailClearData(null)) } } -export const backToAppDetailsModal = (fetchAppDetail, clearAppRevisions, clearAppRevisionDetail, appId) => { +export const backToAppDetailsModal = (appId: string, dispatch: Dispatch) => { return () => { - clearAppRevisions() - clearAppRevisionDetail() - fetchAppDetail(appId) + dispatch(revisionsClearData(null)) + dispatch(revisionDetailClearData(null)) + dispatch(appDetailRequestData({ id: appId })) } } -export const handleUseEffectToFetchAppRevisions = (appId, fetchAppRevisions, visible) => { +export const handleUseEffectToFetchAppRevisions = (appId: string, dispatch: Dispatch, visible: boolean) => { return () => { if (appId && visible) { - fetchAppRevisions(appId) + dispatch(revisionsRequestData({ appId })) } } } -export const handleUseEffectToFetchAppRevisionDetail = (appId, appRevisionId, fetchAppRevisionDetail, visible) => { +export const handleUseEffectToFetchAppRevisionDetail = ( + appId: string, + dispatch: Dispatch, + visible: boolean, + appRevisionId?: string, +) => { return () => { if (appId && appRevisionId && visible) { - fetchAppRevisionDetail({ appId, appRevisionId }) + dispatch(revisionDetailRequestData({ appId, appRevisionId })) } } } @@ -104,20 +95,14 @@ export const handleUseEffectToFetchAppRevisionDetail = (appId, appRevisionId, fe export const DeveloperAppRevisionModal: React.FC = ({ visible, appId, - revisionDetailState, - revisionsState, appDetailState, - loginIdentity, - fetchAppDetail, - fetchAppRevisions, - fetchAppRevisionDetail, - declineAppRevision, - clearAppRevisionDetail, - clearAppRevisions, afterClose, onCancelSuccess, }) => { - const { revisions } = revisionsState + const dispatch = useDispatch() + const revisions = useSelector(selectAppRevisions) + const revisionDetailState = useSelector(selectAppRevisionDetail) + const loginIdentity = useSelector(selectLoginIdentity) const revisionsData = revisions?.data const latestAppRevisionId = revisionsData && revisionsData[0].id const { declineFormState, revisionDetailData } = revisionDetailState @@ -131,22 +116,20 @@ export const DeveloperAppRevisionModal: React.FC const [isConfirmationModalVisible, setIsConfirmationModalVisible] = React.useState(false) - React.useEffect(handleUseEffectToFetchAppRevisions(appId, fetchAppRevisions, visible), [ + React.useEffect(handleUseEffectToFetchAppRevisions(appId, dispatch, visible), [appId, dispatch, visible]) + + React.useEffect(handleUseEffectToFetchAppRevisionDetail(appId, dispatch, visible, latestAppRevisionId), [ appId, - fetchAppRevisions, + latestAppRevisionId, + dispatch, visible, ]) - React.useEffect( - handleUseEffectToFetchAppRevisionDetail(appId, latestAppRevisionId, fetchAppRevisionDetail, visible), - [appId, latestAppRevisionId, fetchAppRevisionDetail, visible], - ) - return ( type="button" loading={isDeclining} onClick={handleCancelPendingRevisionsButtonClick( - declineAppRevision, + dispatch, appId, latestAppRevisionId, loginIdentity, @@ -194,15 +177,12 @@ export const DeveloperAppRevisionModal: React.FC

Are you sure you wish to cancel any pending revisions for this App?

- + All pending revisions for this app have been cancelled. You can now use the ‘Edit Detail’ option to make any @@ -214,19 +194,4 @@ export const DeveloperAppRevisionModal: React.FC ) } -const mapStateToProps = (state: ReduxState) => ({ - revisionsState: state.revisions, - revisionDetailState: state.revisionDetail, - loginIdentity: state.auth.loginSession?.loginIdentity, -}) - -const mapDispatchToProps = (dispatch: Dispatch) => ({ - fetchAppDetail: (id: string) => dispatch(appDetailRequestData({ id })), - fetchAppRevisions: (appId: string) => dispatch(revisionsRequestData({ appId })), - fetchAppRevisionDetail: param => dispatch(revisionDetailRequestData(param)), - declineAppRevision: params => dispatch(declineRevision(params)), - clearAppRevisionDetail: () => dispatch(revisionDetailClearData(null)), - clearAppRevisions: () => dispatch(revisionsClearData(null)), -}) - -export default connect(mapStateToProps, mapDispatchToProps)(DeveloperAppRevisionModal) +export default DeveloperAppRevisionModal