Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

farrah/feat: awareness banner #438

Merged
merged 7 commits into from
Feb 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions src/components/AwarenessBanner/AwarenessBanner.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.awareness-banner {
margin-top: 16px;

@include mobile {
margin-left: 16px;
margin-right: 16px;
width: calc(100% - 32px);
}
}
20 changes: 20 additions & 0 deletions src/components/AwarenessBanner/AwarenessBanner.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Localize } from '@deriv-com/translations';
import { InlineMessage, Text, useDevice } from '@deriv-com/ui';
import './AwarenessBanner.scss';

const AwarenessBanner = () => {
const { isDesktop } = useDevice();

return (
<InlineMessage className='awareness-banner' iconPosition={isDesktop ? 'center' : 'top'} variant='warning'>
<Text size='xs'>
<Localize
components={[<strong key={0} />]}
i18n_default_text='<0>Stay safe:</0> Never share login details or verification codes. Check URLs and contact us only via live chat.'
/>
</Text>
</InlineMessage>
);
};

export default AwarenessBanner;
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { render, screen } from '@testing-library/react';
import AwarenessBanner from '../AwarenessBanner';

jest.mock('@deriv-com/ui', () => ({
...jest.requireActual('@deriv-com/ui'),
useDevice: jest.fn(() => ({
isMobile: false,
})),
}));

describe('PNVBanner', () => {
it('should render the proper message', async () => {
render(<AwarenessBanner />);

expect(
screen.getByText(
/Never share login details or verification codes. Check URLs and contact us only via live chat./i
)
).toBeInTheDocument();
});
});
1 change: 1 addition & 0 deletions src/components/AwarenessBanner/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as AwarenessBanner } from './AwarenessBanner';
2 changes: 1 addition & 1 deletion src/components/BuySellForm/BuySellForm.scss
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
z-index: 1;

&--is-buy {
top: -4rem;
top: -10rem;
}

& .payment-method-form {
Expand Down
4 changes: 3 additions & 1 deletion src/components/Modals/FilterModal/FilterModal.scss
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

@include mobile-or-tablet-screen {
position: absolute;
top: -4rem;
top: -10rem;
z-index: 1;
height: calc(100vh - 8rem);
width: 100%;
Expand All @@ -16,9 +16,11 @@
transform: rotate(180deg);
}
}

& .mobile-wrapper {
&__header {
gap: 0;

span {
margin: 0 auto;
}
Expand Down
1 change: 1 addition & 0 deletions src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export * from './AdvertiserName';
export * from './AdvertsTableRow';
export * from './AppFooter';
export * from './AppHeader';
export * from './AwarenessBanner';
export * from './Badge';
export * from './BuySellForm';
export * from './Checklist';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@

&__full-page-modal {
position: absolute;
top: -4rem;
top: -10rem;
left: 0;
z-index: 1;
height: calc(100vh - 5rem);
Expand Down
10 changes: 9 additions & 1 deletion src/pages/buy-sell/screens/BuySell/BuySell.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
import clsx from 'clsx';
import { useHistory, useLocation } from 'react-router-dom';
import { OutsideBusinessHoursHint, PageReturn, PNVBanner, TemporarilyBarredHint, Verification } from '@/components';
import {
AwarenessBanner,
OutsideBusinessHoursHint,
PageReturn,
PNVBanner,
TemporarilyBarredHint,
Verification,
} from '@/components';
import { BUY_SELL_URL } from '@/constants';
import {
useGetBusinessHours,
Expand Down Expand Up @@ -50,6 +57,7 @@ const BuySell = () => {
{isAdvertiserBarred && <TemporarilyBarredHint />}
{!isScheduleAvailable && !isAdvertiserBarred && <OutsideBusinessHoursHint />}
{isAdvertiser && shouldShowVerification && <PNVBanner />}
<AwarenessBanner />
<BuySellTable />
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ jest.mock('@/hooks/custom-hooks', () => ({
useIsAdvertiserBarred: jest.fn().mockReturnValue(false),
}));

jest.mock('@deriv-com/ui', () => ({
...jest.requireActual('@deriv-com/ui'),
useDevice: jest.fn(() => ({ isDesktop: true })),
}));

jest.mock('../../BuySellTable/BuySellTable', () => jest.fn(() => <div>BuySellTable</div>));

const mockUseLocation = useLocation as jest.MockedFunction<typeof useLocation>;
Expand Down
24 changes: 21 additions & 3 deletions src/pages/my-ads/components/AdFormTextArea/AdFormTextArea.tsx
Original file line number Diff line number Diff line change
@@ -1,33 +1,51 @@
import { ChangeEvent } from 'react';
import clsx from 'clsx';
import { Controller, useFormContext } from 'react-hook-form';
import { VALID_SYMBOLS_PATTERN } from '@/constants';
import { getTextFieldError } from '@/utils';
import { useTranslations } from '@deriv-com/translations';
import { TextArea } from '@deriv-com/ui';
import './AdFormTextArea.scss';

export type TChangeEvent = (e: ChangeEvent<HTMLTextAreaElement>) => void;

type TAdFormTextAreaProps = {
className?: string;
field: string;
hint?: string;
label: string;
name: string;
onFieldChange?: TChangeEvent;
required?: boolean;
};
const AdFormTextArea = ({ field, hint = '', label, name, required = false }: TAdFormTextAreaProps) => {
const AdFormTextArea = ({
className,
field,
hint = '',
label,
name,
onFieldChange,
required = false,
}: TAdFormTextAreaProps) => {
const { control } = useFormContext();
const { localize } = useTranslations();
return (
<Controller
control={control}
name={name}
render={({ field: { onBlur, onChange, value }, fieldState: { error } }) => (
<div className='mb-[2.4rem] ad-form-textarea'>
<div className={clsx('mb-[2.4rem] ad-form-textarea', className)}>
<TextArea
className={className}
hint={error ? error.message : hint}
isInvalid={!!error}
label={label}
maxLength={300}
onBlur={onBlur}
onChange={onChange}
onChange={e => {
onChange(e);
onFieldChange?.(e);
}}
shouldShowCounter
textSize='sm'
value={value}
Expand Down
19 changes: 19 additions & 0 deletions src/pages/my-ads/components/AdTypeSection/AdTypeSection.scss
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,23 @@
&--edit {
padding-top: 2.4rem;
}

&__textarea {
.deriv-textarea__footer p {
padding-left: 16px;
}
}

&__instructions {
textarea {
border-color: #ffad3a !important;
}

textarea:focus + label span,
label,
p,
span {
color: #ffad3a;
}
}
}
39 changes: 35 additions & 4 deletions src/pages/my-ads/components/AdTypeSection/AdTypeSection.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { MouseEventHandler } from 'react';
import { MouseEventHandler, useEffect, useState } from 'react';
import clsx from 'clsx';
import { Controller, useFormContext } from 'react-hook-form';
import { TCurrency } from 'types';
import { FloatingRate, RadioGroup } from '@/components';
import { BUY_SELL, RATE_TYPE } from '@/constants';
import { BUY_SELL, RATE_TYPE, VALID_SYMBOLS_PATTERN } from '@/constants';
import { api } from '@/hooks';
import { useQueryString } from '@/hooks/custom-hooks';
import { getValidationRules, restrictDecimalPlace } from '@/utils';
Expand All @@ -27,6 +27,7 @@
};

const AdTypeSection = ({ currency, localCurrency, onCancel, rateType, ...props }: TAdTypeSectionProps) => {
const [isInstructionsWarningVisible, setIsInstructionsWarningVisible] = useState(false);
const { queryString } = useQueryString();
const { data: advertiserInfo } = api.advertiser.useGetInfo();
const { balance_available: balanceAvailable } = advertiserInfo || {};
Expand Down Expand Up @@ -69,6 +70,23 @@
});
};

const validateInstructions = (value: string) => {
const regExp = /.*(\+?\d{1,4}[-.\s]?)?((\(\d{1,4}\))|\d{1,4})[-.\s]?\d{1,4}[-.\s]?\d{1,9}.*/;
const strings = typeof value === 'string' ? value.split(' ') : [];
const hasStringOfNumbers = strings.some(str => regExp.test(str));
const isValid = VALID_SYMBOLS_PATTERN.test(value);

if (isValid) {
setIsInstructionsWarningVisible(hasStringOfNumbers);
} else {
setIsInstructionsWarningVisible(false);
}
};

useEffect(() => {
validateInstructions(getValues('instructions'));
}, []);

Check warning on line 88 in src/pages/my-ads/components/AdTypeSection/AdTypeSection.tsx

View workflow job for this annotation

GitHub Actions / build_to_cloudflare_pages

React Hook useEffect has a missing dependency: 'getValues'. Either include it or remove the dependency array

return (
<div className={clsx('ad-type-section', { 'ad-type-section--edit': isEdit })}>
{!isEdit && (
Expand Down Expand Up @@ -186,10 +204,23 @@
/>
)}
<AdFormTextArea
className={clsx('ad-type-section__textarea', {
'ad-type-section__instructions': isInstructionsWarningVisible,
})}
field={localize('Instructions')}
hint={localize('This information will be visible to everyone')}
label={localize('Instructions(optional)')}
hint={
isInstructionsWarningVisible
? localize('Make sure you’re not sharing your personal details.')
: localize(
'This information will be visible to everyone. Don’t share your phone number or personal details.'
)
}
label={localize('Instructions (optional)')}
name='instructions'
onFieldChange={e => {
const { value } = e.target;
validateInstructions(value);
}}
/>
<AdFormController {...props} isNextButtonDisabled={!isValid} onCancel={onCancel} />
</div>
Expand Down
6 changes: 6 additions & 0 deletions src/pages/my-ads/screens/MyAds/MyAds.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.my-ads {
@include mobile-or-tablet-screen {
max-height: calc(100% - 8rem);
overflow: auto;
}
}
13 changes: 11 additions & 2 deletions src/pages/my-ads/screens/MyAds/MyAds.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { OutsideBusinessHoursHint, PNVBanner, TemporarilyBarredHint, Verification } from '@/components';
import {
AwarenessBanner,
OutsideBusinessHoursHint,
PNVBanner,
TemporarilyBarredHint,
Verification,
} from '@/components';
import {
useGetBusinessHours,
useGetPhoneNumberVerification,
Expand All @@ -8,6 +14,7 @@ import {
} from '@/hooks/custom-hooks';
import { Loader } from '@deriv-com/ui';
import { MyAdsTable } from './MyAdsTable';
import './MyAds.scss';

const MyAds = () => {
const isAdvertiserBarred = useIsAdvertiserBarred();
Expand All @@ -23,15 +30,17 @@ const MyAds = () => {
if (isAdvertiserNotVerified)
return (
<div className='overflow-y-auto h-[calc(100%-11rem)]'>
<AwarenessBanner />
<Verification />;
</div>
);

return (
<div className='flex flex-col h-full'>
<div className='flex flex-col h-full my-ads'>
{isAdvertiserBarred && <TemporarilyBarredHint />}
{!isScheduleAvailable && !isAdvertiserBarred && <OutsideBusinessHoursHint />}
{isAdvertiser && shouldShowVerification && <PNVBanner />}
<AwarenessBanner />
<MyAdsTable />
</div>
);
Expand Down
3 changes: 2 additions & 1 deletion src/pages/my-ads/screens/MyAdsEmpty/MyAdsEmpty.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@ const MyAdsEmpty = () => {
const history = useHistory();
const textSize = isMobile ? 'lg' : 'md';
return (
<div className='mt-[11.8rem] mx-[1.6rem]'>
<div className='mt-[3.8rem] mx-[1.6rem]'>
<ActionScreen
actionButtons={
<Button
className='mb-[8rem]'
disabled={isAdvertiserBarred}
onClick={() => {
if (isAdvertiser) history.push(`${MY_ADS_URL}/adForm?formAction=create`);
Expand Down
4 changes: 3 additions & 1 deletion src/pages/my-profile/screens/MyProfile/MyProfile.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useEffect } from 'react';
import { PNVBanner, ProfileContent, Verification } from '@/components';
import { AwarenessBanner, PNVBanner, ProfileContent, Verification } from '@/components';
import { NicknameModal } from '@/components/Modals';
import {
useAdvertiserStats,
Expand Down Expand Up @@ -55,6 +55,7 @@ const MyProfile = () => {
if (isAdvertiserNotVerified) {
return (
<div className='overflow-y-auto h-[calc(100%-11rem)]'>
<AwarenessBanner />
<Verification />
</div>
);
Expand All @@ -73,6 +74,7 @@ const MyProfile = () => {
return (
<div className='h-full'>
{isAdvertiser && shouldShowVerification && <PNVBanner />}
<AwarenessBanner />
<div className='my-profile'>
<ProfileContent data={advertiserStats} />
<Tabs
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useHistory } from 'react-router-dom';
import { TAdvertiserStats } from 'types';
import { MobileTabs, ProfileContent } from '@/components';
import { AwarenessBanner, MobileTabs, ProfileContent } from '@/components';
import { GUIDE_URL } from '@/constants';
import { useQueryString } from '@/hooks/custom-hooks';
import { MyProfileAdDetails } from '../MyProfileAdDetails';
Expand Down Expand Up @@ -32,6 +32,7 @@ const MyProfileMobile = ({ data }: TMyProfileMobileProps) => {

return (
<>
<AwarenessBanner />
<ProfileContent data={data} />
<MobileTabs
onChangeTab={clickedTab => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ jest.mock('@/hooks/custom-hooks', () => ({
})),
}));

jest.mock('@deriv-com/ui', () => ({
...jest.requireActual('@deriv-com/ui'),
useDevice: jest.fn(() => ({ isDesktop: true })),
}));

const mockProps = {
data: {} as TAdvertiserStats,
};
Expand Down
Loading
Loading