Skip to content

Commit 32b107c

Browse files
authored
Merge pull request #156 from nada-deriv/nada/FEQ-2381/leave-filter-modal
Nada/FEQ-2381/feat: add cancel modal for filter
2 parents 42277ac + 489312e commit 32b107c

File tree

10 files changed

+199
-35
lines changed

10 files changed

+199
-35
lines changed

src/components/Modals/FilterModal/FilterModal.scss

+9
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,13 @@
99
z-index: 1;
1010
height: calc(100vh - 8rem);
1111
}
12+
13+
& .mobile-wrapper {
14+
&__header {
15+
gap: 0;
16+
span {
17+
margin: 0 auto;
18+
}
19+
}
20+
}
1221
}

src/components/Modals/FilterModal/FilterModal.tsx

+65-30
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import { useEffect, useState } from 'react';
2+
import clsx from 'clsx';
23
import { FullPageMobileWrapper, PageReturn } from '@/components';
3-
import { api } from '@/hooks';
4+
import { api, useModalManager } from '@/hooks';
45
import { LabelPairedChevronRightLgRegularIcon } from '@deriv/quill-icons';
56
import { useTranslations } from '@deriv-com/translations';
67
import { Modal, Text, ToggleSwitch, useDevice } from '@deriv-com/ui';
8+
import { LeaveFilterModal } from '../LeaveFilterModal';
79
import { FilterModalContent } from './FilterModalContent';
810
import { FilterModalFooter } from './FilterModalFooter';
911
import './FilterModal.scss';
@@ -25,13 +27,15 @@ const FilterModal = ({
2527
selectedPaymentMethods,
2628
setSelectedPaymentMethods,
2729
}: TFilterModalProps) => {
30+
const { hideModal, isModalOpenFor, showModal } = useModalManager();
2831
const { data } = api.paymentMethods.useGet();
2932
const { localize } = useTranslations();
3033
const [showPaymentMethods, setShowPaymentMethods] = useState<boolean>(false);
3134
const [isMatching, setIsMatching] = useState<boolean>(isToggled);
3235
const [paymentMethods, setPaymentMethods] = useState<string[]>(selectedPaymentMethods);
3336
const [paymentMethodNames, setPaymentMethodNames] = useState<string>('All');
3437
const { isMobile } = useDevice();
38+
const [isHidden, setIsHidden] = useState(false);
3539

3640
const filterOptions = [
3741
{
@@ -83,11 +87,28 @@ const FilterModal = ({
8387
}
8488
}, [data, paymentMethods]);
8589

90+
const closeCancelModal = (hideAll = false) => {
91+
hideModal({ shouldHideAllModals: hideAll });
92+
if (!hideAll) {
93+
setIsHidden(false);
94+
}
95+
};
96+
97+
const onClickClose = () => {
98+
const isValid = (showPaymentMethods && hasSamePaymentMethods) || (!showPaymentMethods && hasSameFilters);
99+
if (isValid) {
100+
onRequestClose();
101+
} else {
102+
setIsHidden(true);
103+
showModal('LeaveFilterModal');
104+
}
105+
};
106+
86107
if (isMobile && isModalOpen) {
87108
return (
88109
<FullPageMobileWrapper
89110
className='filter-modal'
90-
onBack={showPaymentMethods ? () => setShowPaymentMethods(false) : onRequestClose}
111+
onBack={showPaymentMethods ? () => setShowPaymentMethods(false) : onClickClose}
91112
renderFooter={() => (
92113
<FilterModalFooter
93114
hasSameFilters={hasSameFilters}
@@ -103,46 +124,60 @@ const FilterModal = ({
103124
{headerText}
104125
</Text>
105126
)}
127+
shouldShowBackIcon={showPaymentMethods}
128+
shouldShowCloseIcon
106129
>
107130
<FilterModalContent
108131
filterOptions={filterOptions}
109132
paymentMethods={paymentMethods}
110133
setPaymentMethods={setPaymentMethods}
111134
showPaymentMethods={showPaymentMethods}
112135
/>
136+
{isModalOpenFor('LeaveFilterModal') && (
137+
<LeaveFilterModal isModalOpen onRequestClose={closeCancelModal} />
138+
)}
113139
</FullPageMobileWrapper>
114140
);
115141
}
116142

117143
return (
118-
<Modal ariaHideApp={false} className='filter-modal' isOpen={isModalOpen} onRequestClose={onRequestClose}>
119-
<Modal.Header onRequestClose={onRequestClose}>
120-
<PageReturn
121-
onClick={() => setShowPaymentMethods(false)}
122-
pageTitle={headerText}
123-
shouldHideBackButton={!showPaymentMethods}
124-
weight='bold'
125-
/>
126-
</Modal.Header>
127-
<Modal.Body>
128-
<FilterModalContent
129-
filterOptions={filterOptions}
130-
paymentMethods={paymentMethods}
131-
setPaymentMethods={setPaymentMethods}
132-
showPaymentMethods={showPaymentMethods}
133-
/>
134-
</Modal.Body>
135-
<Modal.Footer className='p-0'>
136-
<FilterModalFooter
137-
hasSameFilters={hasSameFilters}
138-
hasSamePaymentMethods={hasSamePaymentMethods}
139-
onApplyConfirm={onApplyConfirm}
140-
onResetClear={onResetClear}
141-
paymentMethods={paymentMethods}
142-
showPaymentMethods={showPaymentMethods}
143-
/>
144-
</Modal.Footer>
145-
</Modal>
144+
<>
145+
<Modal
146+
ariaHideApp={false}
147+
className={clsx('filter-modal', { hidden: isHidden })}
148+
isOpen={isModalOpen}
149+
onRequestClose={onClickClose}
150+
style={{ overlay: { background: isHidden ? 'transparent' : 'rgba(0, 0, 0, 0.72)', zIndex: 9999 } }}
151+
>
152+
<Modal.Header onRequestClose={onClickClose}>
153+
<PageReturn
154+
onClick={() => setShowPaymentMethods(false)}
155+
pageTitle={headerText}
156+
shouldHideBackButton={!showPaymentMethods}
157+
weight='bold'
158+
/>
159+
</Modal.Header>
160+
<Modal.Body>
161+
<FilterModalContent
162+
filterOptions={filterOptions}
163+
paymentMethods={paymentMethods}
164+
setPaymentMethods={setPaymentMethods}
165+
showPaymentMethods={showPaymentMethods}
166+
/>
167+
</Modal.Body>
168+
<Modal.Footer className='p-0'>
169+
<FilterModalFooter
170+
hasSameFilters={hasSameFilters}
171+
hasSamePaymentMethods={hasSamePaymentMethods}
172+
onApplyConfirm={onApplyConfirm}
173+
onResetClear={onResetClear}
174+
paymentMethods={paymentMethods}
175+
showPaymentMethods={showPaymentMethods}
176+
/>
177+
</Modal.Footer>
178+
</Modal>
179+
{isModalOpenFor('LeaveFilterModal') && <LeaveFilterModal isModalOpen onRequestClose={closeCancelModal} />}
180+
</>
146181
);
147182
};
148183

src/components/Modals/FilterModal/FilterModalContent/FilterModalContent.tsx

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import clsx from 'clsx';
2-
import { Text } from '@deriv-com/ui';
2+
import { Text, useDevice } from '@deriv-com/ui';
33
import { FilterModalPaymentMethods } from '../FilterModalPaymentMethods';
44

55
type TFilterModalContentProps = {
@@ -20,6 +20,8 @@ const FilterModalContent = ({
2020
setPaymentMethods,
2121
showPaymentMethods,
2222
}: TFilterModalContentProps) => {
23+
const { isMobile } = useDevice();
24+
const textSize = isMobile ? 'md' : 'sm';
2325
return (
2426
<>
2527
{showPaymentMethods ? (
@@ -41,8 +43,8 @@ const FilterModalContent = ({
4143
onClick={option.onClick}
4244
>
4345
<div className='flex flex-col'>
44-
<Text size='sm'>{option.text}</Text>
45-
<Text color='less-prominent' size='sm'>
46+
<Text size={textSize}>{option.text}</Text>
47+
<Text color='less-prominent' size={textSize}>
4648
{option.subtext}
4749
</Text>
4850
</div>

src/components/Modals/FilterModal/FilterModalPaymentMethods/FilterModalPaymentMethods.scss

+11-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
.filter-modal-payment-methods {
22
.deriv-input {
33
border: none;
4-
border-bottom: 1px solid #f2f3f4;
4+
border-bottom: 1px solid #d6dadb;
55
border-radius: 0;
66
// stylelint-disable-next-line declaration-no-important
77
padding: 1.5rem !important;
@@ -15,5 +15,15 @@
1515
// stylelint-disable-next-line declaration-no-important
1616
left: 4rem !important;
1717
}
18+
19+
&--general {
20+
&:hover {
21+
&:not(:disabled) {
22+
&:not(:focus-within) {
23+
border-bottom: 1px solid #d6dadb;
24+
}
25+
}
26+
}
27+
}
1828
}
1929
}

src/components/Modals/FilterModal/__tests__/FilterModal.spec.tsx

+8-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@ let mockData: { display_name: string; id: string }[] | undefined = [
2323
},
2424
];
2525

26+
const mockModalManager = {
27+
hideModal: jest.fn(),
28+
isModalOpenFor: jest.fn().mockReturnValue(false),
29+
showModal: jest.fn(),
30+
};
31+
2632
jest.mock('@/hooks', () => ({
2733
api: {
2834
paymentMethods: {
@@ -31,6 +37,7 @@ jest.mock('@/hooks', () => ({
3137
})),
3238
},
3339
},
40+
useModalManager: jest.fn(() => mockModalManager),
3441
}));
3542

3643
jest.mock('@deriv-com/ui', () => ({
@@ -307,7 +314,7 @@ describe('<FilterModal />', () => {
307314
const paymentMethodsText = screen.getByText('Payment methods');
308315
await user.click(paymentMethodsText);
309316

310-
const backButton = screen.getByTestId('dt_mobile_wrapper_button');
317+
const backButton = screen.getAllByTestId('dt_mobile_wrapper_button')[0];
311318
await user.click(backButton);
312319

313320
expect(screen.getByText('Filter')).toBeInTheDocument();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
.leave-filter-modal {
2+
@include default-modal;
3+
4+
&__header {
5+
@include mobile {
6+
padding-left: 1.6rem;
7+
}
8+
}
9+
10+
&__body {
11+
padding: 1rem 2.4rem;
12+
}
13+
14+
&__footer {
15+
gap: 0.8rem;
16+
}
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { Localize } from '@deriv-com/translations';
2+
import { Button, Modal, Text, useDevice } from '@deriv-com/ui';
3+
import './LeaveFilterModal.scss';
4+
5+
type TLeaveFilterModalProps = {
6+
isModalOpen: boolean;
7+
onRequestClose: (shouldHide?: boolean) => void;
8+
};
9+
10+
const LeaveFilterModal = ({ isModalOpen, onRequestClose }: TLeaveFilterModalProps) => {
11+
const { isMobile } = useDevice();
12+
const textSize = isMobile ? 'md' : 'sm';
13+
return (
14+
<Modal
15+
ariaHideApp={false}
16+
className='leave-filter-modal'
17+
isOpen={isModalOpen}
18+
shouldCloseOnOverlayClick={false}
19+
>
20+
<Modal.Header className='leave-filter-modal__header' hideBorder hideCloseIcon>
21+
<Text size={isMobile ? 'lg' : 'md'} weight='bold'>
22+
<Localize i18n_default_text='Leave page?' />
23+
</Text>
24+
</Modal.Header>
25+
26+
<Modal.Body className='leave-filter-modal__body'>
27+
<Text size={textSize}>
28+
<Localize i18n_default_text='Are you sure you want to leave this page? Changes made will not be saved.' />
29+
</Text>
30+
</Modal.Body>
31+
<Modal.Footer className='leave-filter-modal__footer' hideBorder>
32+
<Button
33+
className='border-2'
34+
color='black'
35+
onClick={() => onRequestClose(false)}
36+
size='lg'
37+
textSize={textSize}
38+
variant='outlined'
39+
>
40+
<Localize i18n_default_text='Cancel' />
41+
</Button>
42+
<Button onClick={() => onRequestClose(true)} size='lg' textSize={textSize}>
43+
<Localize i18n_default_text='Leave page' />
44+
</Button>
45+
</Modal.Footer>
46+
</Modal>
47+
);
48+
};
49+
50+
export default LeaveFilterModal;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { render, screen } from '@testing-library/react';
2+
import userEvent from '@testing-library/user-event';
3+
import LeaveFilterModal from '../LeaveFilterModal';
4+
5+
const mockProps = {
6+
isModalOpen: true,
7+
onRequestClose: jest.fn(),
8+
};
9+
10+
jest.mock('@deriv-com/ui', () => ({
11+
...jest.requireActual('@deriv-com/ui'),
12+
useDevice: () => ({ isMobile: false }),
13+
}));
14+
15+
describe('LeaveFilterModal', () => {
16+
it('should render the modal as expected', () => {
17+
render(<LeaveFilterModal {...mockProps} />);
18+
expect(screen.getByText('Leave page?')).toBeInTheDocument();
19+
});
20+
21+
it('should call onRequestClose with false when cancel button is clicked', async () => {
22+
render(<LeaveFilterModal {...mockProps} />);
23+
await userEvent.click(screen.getByRole('button', { name: 'Cancel' }));
24+
expect(mockProps.onRequestClose).toHaveBeenCalledWith(false);
25+
});
26+
27+
it('should call onRequestClose with true when leave page button is clicked', async () => {
28+
render(<LeaveFilterModal {...mockProps} />);
29+
await userEvent.click(screen.getByRole('button', { name: 'Leave page' }));
30+
expect(mockProps.onRequestClose).toHaveBeenCalledWith(true);
31+
});
32+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default as LeaveFilterModal } from './LeaveFilterModal';

src/components/Modals/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export * from './EmailVerificationModal';
1717
export * from './ErrorModal';
1818
export * from './FilterModal';
1919
export * from './InvalidVerificationLinkModal';
20+
export * from './LeaveFilterModal';
2021
export * from './LoadingModal';
2122
export * from './MyAdsDeleteModal';
2223
export * from './NicknameModal';

0 commit comments

Comments
 (0)