Skip to content

Commit 0ae2b0a

Browse files
authored
[FEQ] P2P-V2 update advert (deriv-com#14509)
* feat: update advert * fix: failing test fix * fix: style fixes * fix: route -changes
1 parent 220baf3 commit 0ae2b0a

File tree

31 files changed

+586
-188
lines changed

31 files changed

+586
-188
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
.p2p-v2-ad-cancel-create-edit-modal {
2+
border-radius: 8px;
3+
width: 44rem;
4+
height: fit-content;
5+
@include mobile {
6+
max-width: calc(100vw - 3.2rem);
7+
}
8+
9+
&__header {
10+
height: unset;
11+
padding: 2.4rem;
12+
@include mobile {
13+
padding: 1.6rem;
14+
}
15+
}
16+
&__body {
17+
padding: 0.8rem 2.4rem;
18+
@include mobile {
19+
padding: 0.8rem 1.6rem;
20+
}
21+
}
22+
23+
&__footer {
24+
gap: 0.8rem;
25+
padding-bottom: 2.4rem;
26+
27+
@include mobile {
28+
padding-bottom: 1.6rem;
29+
}
30+
}
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import React from 'react';
2+
import { useHistory } from 'react-router-dom';
3+
import { MY_ADS_URL } from '@/constants';
4+
import { useQueryString } from '@/hooks';
5+
import { Button, Modal, Text, useDevice } from '@deriv-com/ui';
6+
import './AdCancelCreateEditModal.scss';
7+
8+
type TAdCancelCreateEditModalProps = {
9+
isModalOpen: boolean;
10+
onRequestClose: () => void;
11+
};
12+
13+
const AdCancelCreateEditModal = ({ isModalOpen, onRequestClose }: TAdCancelCreateEditModalProps) => {
14+
const { isMobile } = useDevice();
15+
const history = useHistory();
16+
const { queryString } = useQueryString();
17+
const { advertId = '' } = queryString;
18+
const isEdit = !!advertId;
19+
const textSize = isMobile ? 'md' : 'sm';
20+
return (
21+
<Modal
22+
ariaHideApp={false}
23+
className='p2p-v2-ad-cancel-create-edit-modal'
24+
isOpen={isModalOpen}
25+
shouldCloseOnOverlayClick={false}
26+
>
27+
<Modal.Header className='p2p-v2-ad-cancel-create-edit-modal__header' hideBorder hideCloseIcon>
28+
<Text weight='bold'>{isEdit ? 'Cancel your edits?' : 'Cancel ad creation?'}</Text>
29+
</Modal.Header>
30+
<Modal.Body className='p2p-v2-ad-cancel-create-edit-modal__body'>
31+
<Text size='sm'>
32+
{isEdit
33+
? `If you choose to cancel, the edited details will be lost.`
34+
: `If you choose to cancel, the details you've entered will be lost.`}
35+
</Text>
36+
</Modal.Body>
37+
<Modal.Footer className='p2p-v2-ad-cancel-create-edit-modal__footer' hideBorder>
38+
<Button
39+
color='black'
40+
onClick={() => history.push(MY_ADS_URL)}
41+
size='lg'
42+
textSize={textSize}
43+
variant='outlined'
44+
>
45+
Cancel
46+
</Button>
47+
<Button onClick={onRequestClose} size='lg' textSize={textSize}>
48+
Don’t cancel
49+
</Button>
50+
</Modal.Footer>
51+
</Modal>
52+
);
53+
};
54+
55+
export default AdCancelCreateEditModal;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import React from 'react';
2+
import { MY_ADS_URL } from '@/constants';
3+
import { useQueryString } from '@/hooks';
4+
import { render, screen } from '@testing-library/react';
5+
import userEvent from '@testing-library/user-event';
6+
import AdCancelCreateEditModal from '../AdCancelCreateEditModal';
7+
8+
jest.mock('@deriv-com/ui', () => ({
9+
...jest.requireActual('@deriv-com/ui'),
10+
useDevice: () => ({ isMobile: false }),
11+
}));
12+
13+
jest.mock('@/hooks', () => ({
14+
...jest.requireActual('@/hooks'),
15+
useQueryString: jest.fn().mockReturnValue({ queryString: { advertId: '' } }),
16+
}));
17+
18+
const mockUseQueryString = useQueryString as jest.Mock;
19+
20+
const mockFn = jest.fn();
21+
jest.mock('react-router-dom', () => ({
22+
...jest.requireActual('react-router-dom'),
23+
useHistory: () => ({ push: mockFn }),
24+
}));
25+
26+
const mockProps = {
27+
isModalOpen: true,
28+
onRequestClose: jest.fn(),
29+
};
30+
31+
describe('AdCancelCreateEditModal', () => {
32+
it('should render the component as expected', () => {
33+
render(<AdCancelCreateEditModal {...mockProps} />);
34+
expect(screen.getByText('Cancel ad creation?')).toBeInTheDocument();
35+
});
36+
it('should render the component as expected when isEdit is true', () => {
37+
mockUseQueryString.mockReturnValueOnce({ queryString: { advertId: '123' } });
38+
render(<AdCancelCreateEditModal {...mockProps} />);
39+
expect(screen.getByText('Cancel your edits?')).toBeInTheDocument();
40+
});
41+
it('should redirect to my ads page on clicking cancel button', () => {
42+
render(<AdCancelCreateEditModal {...mockProps} />);
43+
const button = screen.getByRole('button', { name: 'Cancel' });
44+
userEvent.click(button);
45+
expect(mockFn).toHaveBeenCalledWith(MY_ADS_URL);
46+
});
47+
it("should close the modal on clicking don't cancel button", () => {
48+
render(<AdCancelCreateEditModal {...mockProps} />);
49+
const button = screen.getByRole('button', { name: 'Don’t cancel' });
50+
userEvent.click(button);
51+
expect(mockProps.onRequestClose).toHaveBeenCalledTimes(1);
52+
});
53+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default as AdCancelCreateEditModal } from './AdCancelCreateEditModal';

packages/p2p-v2/src/components/Modals/AdCreateEditErrorModal/AdCreateEditErrorModal.tsx

+9-5
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import './AdCreateEditErrorModal.scss';
55

66
type TAdCreateEditErrorModalProps = {
77
errorCode?: ErrorCodes;
8+
errorMessage?: string;
89
isModalOpen: boolean;
910
onRequestClose: () => void;
1011
};
@@ -31,7 +32,12 @@ const errorContent: ErrorContent = {
3132
},
3233
};
3334

34-
const AdCreateEditErrorModal = ({ errorCode, isModalOpen, onRequestClose }: TAdCreateEditErrorModalProps) => {
35+
const AdCreateEditErrorModal = ({
36+
errorCode,
37+
errorMessage,
38+
isModalOpen,
39+
onRequestClose,
40+
}: TAdCreateEditErrorModalProps) => {
3541
const { isMobile } = useDevice();
3642
const textSize = isMobile ? 'md' : 'sm';
3743
return (
@@ -45,13 +51,11 @@ const AdCreateEditErrorModal = ({ errorCode, isModalOpen, onRequestClose }: TAdC
4551
<Text weight='bold'>{(errorCode && errorContent?.[errorCode]?.title) ?? 'Something’s not right'}</Text>
4652
</Modal.Header>
4753
<Modal.Body className='p2p-v2-ad-create-edit-error-modal__body'>
48-
<Text size={textSize}>
49-
{(errorCode && errorContent?.[errorCode]?.description) ?? 'Something’s not right'}
50-
</Text>
54+
<Text size={textSize}>{(errorCode && errorContent?.[errorCode]?.description) ?? errorMessage}</Text>
5155
</Modal.Body>
5256
<Modal.Footer className='p2p-v2-ad-create-edit-error-modal__footer' hideBorder>
5357
<Button onClick={onRequestClose} size='lg' textSize={textSize}>
54-
{errorCode ? 'Update ad' : 'Ok'}
58+
{errorCode && errorContent?.[errorCode]?.title ? 'Update ad' : 'Ok'}
5559
</Button>
5660
</Modal.Footer>
5761
</Modal>

packages/p2p-v2/src/components/Modals/AdCreateEditErrorModal/__tests__/AdCreateEditErrorModal.spec.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ describe('AdCreateEditErrorModal', () => {
2323
expect(screen.getByText('You already have an ad with this rate')).toBeInTheDocument();
2424
});
2525
it('should render the general error message if no error code is provided', () => {
26-
render(<AdCreateEditErrorModal {...mockProps} errorCode={undefined} />);
27-
expect(screen.getAllByText('Something’s not right')).toHaveLength(2);
26+
render(<AdCreateEditErrorModal {...mockProps} errorCode={undefined} errorMessage='error message' />);
27+
expect(screen.getByText('Something’s not right')).toBeInTheDocument();
2828
});
2929
it('should call onRequestClose when the button is clicked', () => {
3030
render(<AdCreateEditErrorModal {...mockProps} />);

packages/p2p-v2/src/components/Modals/PreferredCountriesModal/PreferredCountriesDropdown/PreferredCountriesDropdown.tsx

+9-2
Original file line numberDiff line numberDiff line change
@@ -19,20 +19,27 @@ const PreferredCountriesDropdown = ({
1919
setSelectedCountries,
2020
setShouldDisplayFooter,
2121
}: TPreferredCountriesDropdownProps) => {
22-
const [searchResults, setSearchResults] = useState<TItem[]>(list);
22+
const [searchResults, setSearchResults] = useState<TItem[]>([
23+
...list.filter(item => selectedCountries.includes(item.value)),
24+
...list.filter(item => !selectedCountries.includes(item.value)),
25+
]);
2326
const [searchValue, setSearchValue] = useState('');
2427

2528
const onSearch = (value: string) => {
2629
if (!value) {
2730
setShouldDisplayFooter(true);
2831
setSearchValue('');
29-
setSearchResults(list);
32+
setSearchResults([
33+
...list.filter(item => selectedCountries.includes(item.value)),
34+
...list.filter(item => !selectedCountries.includes(item.value)),
35+
]);
3036
return;
3137
}
3238
setShouldDisplayFooter(false);
3339
setSearchValue(value);
3440
setSearchResults(list.filter(item => item.text.toLowerCase().includes(value.toLowerCase())));
3541
};
42+
3643
return (
3744
<div className='p2p-v2-preferred-countries-dropdown'>
3845
<div className='px-[1.6rem] py-[0.8rem]'>

packages/p2p-v2/src/components/Modals/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
export * from './AdCancelCreateEditModal';
12
export * from './AdConditionsModal';
23
export * from './AdCreateEditErrorModal';
34
export * from './AdCreateEditSuccessModal';

packages/p2p-v2/src/components/PopoverDropdown/PopoverDropdown.tsx

+14-9
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React, { useRef, useState } from 'react';
22
import { useOnClickOutside } from 'usehooks-ts';
33
import { LabelPairedEllipsisVerticalMdRegularIcon } from '@deriv/quill-icons';
4-
import { Button, Text } from '@deriv-com/ui';
4+
import { Button, Text, Tooltip, useDevice } from '@deriv-com/ui';
55
import './PopoverDropdown.scss';
66

77
type TItem = {
@@ -10,23 +10,26 @@ type TItem = {
1010
};
1111

1212
type TPopoverDropdownProps = {
13-
dataTestId?: string;
1413
dropdownList: TItem[];
1514
onClick: (value: string) => void;
15+
tooltipMessage: string;
1616
};
1717

18-
const PopoverDropdown = ({ dataTestId, dropdownList, onClick }: TPopoverDropdownProps) => {
18+
const PopoverDropdown = ({ dropdownList, onClick, tooltipMessage }: TPopoverDropdownProps) => {
1919
const [visible, setVisible] = useState(false);
2020
const ref = useRef(null);
2121
useOnClickOutside(ref, () => setVisible(false));
22+
const { isMobile } = useDevice();
2223

2324
return (
2425
<div className='p2p-v2-popover-dropdown' ref={ref}>
25-
<LabelPairedEllipsisVerticalMdRegularIcon
26-
className='p2p-v2-popover-dropdown__icon'
27-
data-testid={dataTestId}
28-
onClick={() => setVisible(prevState => !prevState)}
29-
/>
26+
<Tooltip message={tooltipMessage} position='bottom' triggerAction='hover'>
27+
<LabelPairedEllipsisVerticalMdRegularIcon
28+
className='p2p-v2-popover-dropdown__icon'
29+
data-testid='dt_p2p_v2_popover_dropdown_icon'
30+
onClick={() => setVisible(prevState => !prevState)}
31+
/>
32+
</Tooltip>
3033
{visible && (
3134
<div className='p2p-v2-popover-dropdown__list'>
3235
{dropdownList.map(item => (
@@ -40,7 +43,9 @@ const PopoverDropdown = ({ dataTestId, dropdownList, onClick }: TPopoverDropdown
4043
}}
4144
variant='ghost'
4245
>
43-
<Text key={item.value}>{item.label}</Text>
46+
<Text key={item.value} size={isMobile ? 'md' : 'sm'}>
47+
{item.label}
48+
</Text>
4449
</Button>
4550
))}
4651
</div>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import React from 'react';
2+
import { render, screen } from '@testing-library/react';
3+
import userEvent from '@testing-library/user-event';
4+
import PopoverDropdown from '../PopoverDropdown';
5+
6+
jest.mock('@deriv-com/ui', () => ({
7+
...jest.requireActual('@deriv-com/ui'),
8+
useDevice: () => ({
9+
isMobile: false,
10+
}),
11+
}));
12+
13+
const mockProps = {
14+
dropdownList: [
15+
{
16+
label: 'label 1',
17+
value: 'value 1',
18+
},
19+
{
20+
label: 'label 2',
21+
value: 'value 2',
22+
},
23+
],
24+
onClick: jest.fn(),
25+
tooltipMessage: 'test tooltip message',
26+
};
27+
28+
describe('PopoverDropdown', () => {
29+
it('should render', () => {
30+
render(<PopoverDropdown {...mockProps} />);
31+
expect(screen.getByTestId('dt_p2p_v2_popover_dropdown_icon')).toBeInTheDocument();
32+
});
33+
it('should render the dropdown list on clicking on the icon', () => {
34+
render(<PopoverDropdown {...mockProps} />);
35+
userEvent.click(screen.getByTestId('dt_p2p_v2_popover_dropdown_icon'));
36+
expect(screen.getByText('label 1')).toBeInTheDocument();
37+
});
38+
it('should call onClick when item is clicked', () => {
39+
render(<PopoverDropdown {...mockProps} />);
40+
userEvent.click(screen.getByTestId('dt_p2p_v2_popover_dropdown_icon'));
41+
userEvent.click(screen.getByText('label 1'));
42+
expect(mockProps.onClick).toHaveBeenCalledWith('value 1');
43+
});
44+
});

packages/p2p-v2/src/pages/my-ads/components/AdConditionsSection/__tests__/AdConditionsSection.spec.tsx

+4
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ jest.mock('@deriv-com/ui', () => ({
1818
useDevice: jest.fn(() => ({ isMobile: false })),
1919
}));
2020

21+
jest.mock('@/hooks', () => ({
22+
useQueryString: jest.fn().mockReturnValue({ queryString: { advertId: '' } }),
23+
}));
24+
2125
const mockSetValue = jest.fn();
2226
jest.mock('react-hook-form', () => ({
2327
...jest.requireActual('react-hook-form'),

packages/p2p-v2/src/pages/my-ads/components/AdFormController/AdFormController.tsx

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import React, { MouseEventHandler } from 'react';
2+
import { useQueryString } from '@/hooks';
23
import { Button, useDevice } from '@deriv-com/ui';
34
import './AdFormController.scss';
45

@@ -21,13 +22,17 @@ const AdFormController = ({
2122
}: TAdFormControllerProps) => {
2223
const { isMobile } = useDevice();
2324
const textSize = isMobile ? 'md' : 'sm';
25+
const { queryString } = useQueryString();
26+
const { advertId = '' } = queryString;
27+
const isEdit = !!advertId;
2428
return (
2529
<div className='p2p-v2-ad-form-controller'>
2630
<Button
2731
color='black'
2832
onClick={() => (onCancel ? onCancel() : goToPreviousStep())}
2933
size='lg'
3034
textSize={textSize}
35+
type='button'
3136
variant='outlined'
3237
>
3338
{onCancel ? 'Cancel' : 'Previous'}
@@ -38,13 +43,14 @@ const AdFormController = ({
3843
onClick={goToNextStep}
3944
size='lg'
4045
textSize={textSize}
46+
type='button'
4147
variant='contained'
4248
>
4349
Next
4450
</Button>
4551
) : (
4652
<Button size='lg' textSize={textSize}>
47-
Post ad
53+
{`${isEdit ? 'Save changes' : 'Post ad'}`}
4854
</Button>
4955
)}
5056
</div>

0 commit comments

Comments
 (0)