forked from binary-com/deriv-app
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[account-v2]/likhith/COJ-667/Create account-closure-form and modal (b…
…inary-com#14208) * feat: created form and modal * fix: failing testcase * fix: ESlint issues * chore: added testcases * feat: added modal and testcases * feat: added modal and testcases * chore: added additional testcases * fix: failing testcase * chore: added testcases for utils * fix: resolved code comments
- Loading branch information
1 parent
183f2e7
commit b05e4b4
Showing
17 changed files
with
726 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
62 changes: 62 additions & 0 deletions
62
packages/account-v2/src/constants/accountClosureReasons.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
import { TAccountClosureReasonsFormValues } from '../utils'; | ||
|
||
export const accountClosureReasons = (): { | ||
label: string; | ||
ref: string; | ||
value: string; | ||
}[] => [ | ||
{ | ||
label: 'I have other financial priorities.', | ||
ref: 'financialPriorities', | ||
value: 'financial-priorities', | ||
}, | ||
{ | ||
label: 'I want to stop myself from trading.', | ||
ref: 'stopTrading', | ||
value: 'stop-trading', | ||
}, | ||
{ | ||
label: "I'm no longer interested in trading.", | ||
ref: 'notInterested', | ||
value: 'not-interested', | ||
}, | ||
{ | ||
label: 'I prefer another trading website.', | ||
ref: 'anotherWebsite', | ||
value: 'another-website', | ||
}, | ||
{ | ||
label: "The platforms aren't user friendly.", | ||
ref: 'notUserFriendly', | ||
value: 'not-user-friendly', | ||
}, | ||
{ | ||
label: 'Making deposits and withdrawals is difficult.', | ||
ref: 'difficultTransactions', | ||
value: 'difficult-transactions', | ||
}, | ||
{ | ||
label: 'The platforms lack key features or functionality.', | ||
ref: 'lackOfFeatures', | ||
value: 'lack-of-features', | ||
}, | ||
{ | ||
label: 'Customer service was unsatisfactory.', | ||
ref: 'unsatisfactoryService', | ||
value: 'unsatisfactory-service', | ||
}, | ||
{ | ||
label: "I'm closing my account for other reasons.", | ||
ref: 'otherReasons', | ||
value: 'other-reasons', | ||
}, | ||
]; | ||
|
||
export const MAX_ALLOWED_REASONS_FOR_CLOSING_ACCOUNT = 3; | ||
export const CHARACTER_LIMIT_FOR_CLOSING_ACCOUNT = 110; | ||
|
||
export type TAccountClosureFormActions = | ||
| { payload: boolean; type: 'displayConfirmModal' } | ||
| { payload: boolean; type: 'displaySuccessModal' } | ||
| { payload: TAccountClosureReasonsFormValues; type: 'disableCheckbox' } | ||
| { payload: TAccountClosureReasonsFormValues; type: 'remainingCharacters' }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
export { | ||
accountClosureReasons, | ||
CHARACTER_LIMIT_FOR_CLOSING_ACCOUNT, | ||
MAX_ALLOWED_REASONS_FOR_CLOSING_ACCOUNT, | ||
type TAccountClosureFormActions, | ||
} from './accountClosureReasons'; | ||
export * from './constants'; |
36 changes: 36 additions & 0 deletions
36
packages/account-v2/src/containers/AccountClosureForm/AccountClosureConfirmModal.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import React from 'react'; | ||
import { StandaloneTriangleExclamationRegularIcon } from '@deriv/quill-icons'; | ||
import { Button, Modal, Text } from '@deriv-com/ui'; | ||
|
||
type TAccountClosureConfirmModalProps = { | ||
handleCancel: () => void; | ||
handleSubmit: () => void; | ||
isModalOpen: boolean; | ||
}; | ||
|
||
export const AccountClosureConfirmModal = ({ | ||
handleCancel, | ||
handleSubmit, | ||
isModalOpen, | ||
}: TAccountClosureConfirmModalProps) => ( | ||
<Modal className='p-24 w-[440px] sm:w-[312px]' isOpen={isModalOpen}> | ||
<Modal.Body className='flex flex-col'> | ||
<StandaloneTriangleExclamationRegularIcon className='self-center fill-status-light-danger' iconSize='2xl' /> | ||
<Text align='center' as='h4' size='md' weight='bold'> | ||
Close your account? | ||
</Text> | ||
<Text align='center' as='p' className='mt-24' size='sm'> | ||
Closing your account will automatically log you out. We shall delete your personal information as soon | ||
as our legal obligations are met. | ||
</Text> | ||
</Modal.Body> | ||
<Modal.Footer className='mt-24 flex gap-x-16 justify-end' hideBorder> | ||
<Button color='black' onClick={handleCancel} rounded='sm' size='md' type='button' variant='outlined'> | ||
Go back | ||
</Button> | ||
<Button color='primary' onClick={handleSubmit} rounded='sm' size='md'> | ||
Close account | ||
</Button> | ||
</Modal.Footer> | ||
</Modal> | ||
); |
176 changes: 176 additions & 0 deletions
176
packages/account-v2/src/containers/AccountClosureForm/AccountClosureForm.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,176 @@ | ||
import React, { Fragment, useReducer, useRef } from 'react'; | ||
import { Field, Form, Formik, FormikProps } from 'formik'; | ||
import { Button, Checkbox, Modal, Text, TextArea } from '@deriv-com/ui'; | ||
import { | ||
ACCOUNT_MODAL_REF, | ||
accountClosureReasons, | ||
CHARACTER_LIMIT_FOR_CLOSING_ACCOUNT, | ||
MAX_ALLOWED_REASONS_FOR_CLOSING_ACCOUNT, | ||
TAccountClosureFormActions, | ||
} from '../../constants'; | ||
import { | ||
getAccountClosureValidationSchema, | ||
TAccountClosureReasonsFormValues, | ||
validateAccountClosure, | ||
} from '../../utils/accountClosureUtils'; | ||
import { AccountClosureConfirmModal } from './AccountClosureConfirmModal'; | ||
import { AccountClosureSuccessModal } from './AccountClosureSuccessModal'; | ||
|
||
export const AccountClosureForm = ({ handleOnBack }: { handleOnBack: () => void }) => { | ||
Modal.setAppElement(ACCOUNT_MODAL_REF); | ||
const reasons = accountClosureReasons(); | ||
const validationSchema = getAccountClosureValidationSchema(); | ||
|
||
const formRef = useRef<FormikProps<TAccountClosureReasonsFormValues>>(null); | ||
|
||
const isReasonNotSelected = !validateAccountClosure( | ||
formRef.current?.values as TAccountClosureReasonsFormValues, | ||
formRef.current?.dirty ?? false | ||
); | ||
|
||
const initialState = { | ||
disableCheckbox: false, | ||
displayConfirmModal: false, | ||
displaySuccessModal: false, | ||
remainingCharacters: CHARACTER_LIMIT_FOR_CLOSING_ACCOUNT, | ||
}; | ||
|
||
const reducer = (state: typeof initialState, action: TAccountClosureFormActions) => { | ||
switch (action.type) { | ||
case 'disableCheckbox': { | ||
const disableCheckbox = | ||
Object.values(action.payload).filter(Boolean).length >= MAX_ALLOWED_REASONS_FOR_CLOSING_ACCOUNT; | ||
return { ...state, disableCheckbox }; | ||
} | ||
|
||
case 'remainingCharacters': { | ||
const { doToImprove, otherTradingPlatforms } = action.payload; | ||
const remainingCharacters = | ||
CHARACTER_LIMIT_FOR_CLOSING_ACCOUNT - | ||
(doToImprove ?? '').concat(otherTradingPlatforms ?? '').length; | ||
return { ...state, remainingCharacters }; | ||
} | ||
case 'displayConfirmModal': { | ||
return { ...state, displayConfirmModal: !state.displayConfirmModal }; | ||
} | ||
case 'displaySuccessModal': { | ||
return { ...state, displaySuccessModal: !state.displaySuccessModal }; | ||
} | ||
default: | ||
return state; | ||
} | ||
}; | ||
|
||
const [state, dispatch] = useReducer(reducer, initialState); | ||
|
||
return ( | ||
<Fragment> | ||
<Formik | ||
initialValues={validationSchema.getDefault()} | ||
innerRef={formRef} | ||
onSubmit={() => dispatch({ payload: true, type: 'displayConfirmModal' })} | ||
validationSchema={validationSchema} | ||
> | ||
{({ dirty, setFieldValue, values }) => ( | ||
<Form> | ||
<section> | ||
<div className='gap-8 flex flex-col my-16'> | ||
{reasons.map(({ label, ref, value }) => ( | ||
<Field | ||
as={Checkbox} | ||
disabled={ | ||
state.disableCheckbox && | ||
!values[ref as keyof TAccountClosureReasonsFormValues] | ||
} | ||
key={value} | ||
label={label} | ||
name={ref} | ||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => { | ||
setFieldValue(ref, event.target.checked); | ||
dispatch({ | ||
payload: { ...values, [ref]: event.target.checked }, | ||
type: 'disableCheckbox', | ||
}); | ||
}} | ||
type='checkbox' | ||
/> | ||
))} | ||
</div> | ||
<Field | ||
aria-label="If you don't mind sharing, which other trading platforms do you use?" | ||
as={TextArea} | ||
className='mb-12' | ||
label="If you don't mind sharing, which other trading platforms do you use?" | ||
name='otherTradingPlatforms' | ||
onChange={(event: React.ChangeEvent<HTMLTextAreaElement>) => { | ||
setFieldValue('otherTradingPlatforms', event.target.value); | ||
dispatch({ | ||
payload: { ...values, otherTradingPlatforms: event.target.value }, | ||
type: 'remainingCharacters', | ||
}); | ||
}} | ||
role='textarea' | ||
textSize='sm' | ||
/> | ||
<Field | ||
aria-label='What could we do to improve?' | ||
as={TextArea} | ||
hint={`Remaining characters: ${state.remainingCharacters}`} | ||
label='What could we do to improve?' | ||
name='doToImprove' | ||
onChange={(event: React.ChangeEvent<HTMLTextAreaElement>) => { | ||
setFieldValue('doToImprove', event.target.value); | ||
dispatch({ | ||
payload: { ...values, doToImprove: event.target.value }, | ||
type: 'remainingCharacters', | ||
}); | ||
}} | ||
role='textarea' | ||
textSize='sm' | ||
/> | ||
</section> | ||
<section className='mt-24 flex gap-x-16 justify-end'> | ||
<Button | ||
color='black' | ||
onClick={handleOnBack} | ||
rounded='sm' | ||
size='md' | ||
type='button' | ||
variant='outlined' | ||
> | ||
Back | ||
</Button> | ||
<Button | ||
color='primary' | ||
disabled={!dirty || isReasonNotSelected} | ||
rounded='sm' | ||
size='md' | ||
type='submit' | ||
variant='contained' | ||
> | ||
Continue | ||
</Button> | ||
</section> | ||
{isReasonNotSelected && ( | ||
<Text as='p' className='mt-16' color='error' size='xs' weight='bold'> | ||
Please select at least one reason | ||
</Text> | ||
)} | ||
</Form> | ||
)} | ||
</Formik> | ||
<AccountClosureConfirmModal | ||
handleCancel={() => dispatch({ payload: false, type: 'displayConfirmModal' })} | ||
handleSubmit={() => { | ||
dispatch({ payload: false, type: 'displayConfirmModal' }); | ||
dispatch({ payload: true, type: 'displaySuccessModal' }); | ||
}} | ||
isModalOpen={state.displayConfirmModal} | ||
/> | ||
<AccountClosureSuccessModal | ||
handleClose={() => dispatch({ payload: false, type: 'displaySuccessModal' })} | ||
isModalOpen={state.displaySuccessModal} | ||
/> | ||
</Fragment> | ||
); | ||
}; |
26 changes: 26 additions & 0 deletions
26
packages/account-v2/src/containers/AccountClosureForm/AccountClosureSuccessModal.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import React from 'react'; | ||
import { Modal, Text } from '@deriv-com/ui'; | ||
|
||
type TAccountClosureSuccessModalProps = { | ||
handleClose: () => void; | ||
isModalOpen: boolean; | ||
}; | ||
|
||
export const AccountClosureSuccessModal = ({ handleClose, isModalOpen }: TAccountClosureSuccessModalProps) => ( | ||
<Modal | ||
className='p-24 w-[440px] sm:w-[312px]' | ||
isOpen={isModalOpen} | ||
onRequestClose={handleClose} | ||
shouldCloseOnEsc | ||
shouldCloseOnOverlayClick | ||
> | ||
<Modal.Body className='flex flex-col'> | ||
<Text align='center' as='p' size='md' weight='bold'> | ||
We're sorry to see you leave. | ||
</Text> | ||
<Text align='center' as='p' size='md' weight='bold'> | ||
Your account is now closed. | ||
</Text> | ||
</Modal.Body> | ||
</Modal> | ||
); |
65 changes: 65 additions & 0 deletions
65
...ccount-v2/src/containers/AccountClosureForm/__tests__/AccountClosureConfirmModal.spec.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
import React from 'react'; | ||
import { Modal } from '@deriv-com/ui'; | ||
import { render, screen } from '@testing-library/react'; | ||
import userEvent from '@testing-library/user-event'; | ||
import { ACCOUNT_MODAL_REF } from '../../../constants'; | ||
import { AccountClosureConfirmModal } from '../AccountClosureConfirmModal'; | ||
|
||
describe('AccountClosureConfirmModal', () => { | ||
let elModalRoot: HTMLElement; | ||
beforeAll(() => { | ||
elModalRoot = document.createElement('div'); | ||
elModalRoot.setAttribute('id', ACCOUNT_MODAL_REF.replace('#', '')); | ||
document.body.appendChild(elModalRoot); | ||
Modal.setAppElement(ACCOUNT_MODAL_REF); | ||
}); | ||
|
||
afterAll(() => { | ||
document.body.removeChild(elModalRoot); | ||
}); | ||
|
||
const handleCancel = jest.fn(); | ||
const handleSubmit = jest.fn(); | ||
|
||
it('should render modal', () => { | ||
render( | ||
<AccountClosureConfirmModal handleCancel={handleCancel} handleSubmit={handleSubmit} isModalOpen={true} /> | ||
); | ||
|
||
expect(screen.getByRole('dialog')).toBeInTheDocument(); | ||
expect(screen.getByRole('button', { name: /Go back/i })).toBeInTheDocument(); | ||
expect(screen.getByRole('button', { name: /Close account/i })).toBeInTheDocument(); | ||
}); | ||
|
||
it('should call handleCancel when clicking Go back', () => { | ||
render( | ||
<AccountClosureConfirmModal handleCancel={handleCancel} handleSubmit={handleSubmit} isModalOpen={true} /> | ||
); | ||
|
||
const goBackButton = screen.getByRole('button', { name: /Go back/i }); | ||
userEvent.click(goBackButton); | ||
|
||
expect(handleCancel).toHaveBeenCalledTimes(1); | ||
}); | ||
|
||
it('should call handleSubmit when clicking Close account', () => { | ||
render( | ||
<AccountClosureConfirmModal handleCancel={handleCancel} handleSubmit={handleSubmit} isModalOpen={true} /> | ||
); | ||
|
||
const closeAccountButton = screen.getByRole('button', { name: /Close account/i }); | ||
userEvent.click(closeAccountButton); | ||
|
||
expect(handleSubmit).toHaveBeenCalledTimes(1); | ||
}); | ||
|
||
it('should not call handleCancel when clicking escape button', () => { | ||
render( | ||
<AccountClosureConfirmModal handleCancel={handleCancel} handleSubmit={handleSubmit} isModalOpen={true} /> | ||
); | ||
|
||
userEvent.keyboard('{esc}'); | ||
|
||
expect(screen.getByRole('dialog')).toBeInTheDocument(); | ||
}); | ||
}); |
Oops, something went wrong.