diff --git a/__mocks__/LocalizeMock.js b/__mocks__/LocalizeMock.js
index 1debd1ec..f037d7ad 100644
--- a/__mocks__/LocalizeMock.js
+++ b/__mocks__/LocalizeMock.js
@@ -7,7 +7,9 @@ const Localize = ({ i18n_default_text, values }) => {
// Mock for useTranslations hook
const useTranslations = () => ({
- localize: jest.fn(text => text),
+ localize: jest.fn((text, args) => {
+ return text.replace(/{{(.*?)}}/g, (_, match) => args[match.trim()]);
+ }),
});
const localize = jest.fn(text => text);
diff --git a/package-lock.json b/package-lock.json
index e8fd2255..ed3cdfef 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10,7 +10,7 @@
"dependencies": {
"@babel/preset-env": "^7.24.5",
"@deriv-com/api-hooks": "^0.1.19",
- "@deriv-com/translations": "^1.2.3",
+ "@deriv-com/translations": "^1.2.4",
"@deriv-com/ui": "^1.26.0",
"@deriv-com/utils": "latest",
"@deriv/deriv-api": "^1.0.15",
@@ -2654,9 +2654,9 @@
}
},
"node_modules/@deriv-com/translations": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/@deriv-com/translations/-/translations-1.2.3.tgz",
- "integrity": "sha512-NVZ2Ut8e+rG2eYjQhzI0nESmENx5U6wkaVoTZhAfFwswKJfTwDcOJYtmLPpUgAPUcgUNrWRx1iH0o5zGhLTOxg==",
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@deriv-com/translations/-/translations-1.2.4.tgz",
+ "integrity": "sha512-LA6ZVNRhigN+wFQsfNzCCWdF0u4saud/AtnDDDlUo1pioKNckXoMv8RxvodiiSbnU4BvChaar/wvPeR3c+O3oA==",
"dependencies": {
"@xmldom/xmldom": "^0.8.10",
"commander": "^12.0.0",
@@ -22657,9 +22657,9 @@
"requires": {}
},
"@deriv-com/translations": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/@deriv-com/translations/-/translations-1.2.3.tgz",
- "integrity": "sha512-NVZ2Ut8e+rG2eYjQhzI0nESmENx5U6wkaVoTZhAfFwswKJfTwDcOJYtmLPpUgAPUcgUNrWRx1iH0o5zGhLTOxg==",
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@deriv-com/translations/-/translations-1.2.4.tgz",
+ "integrity": "sha512-LA6ZVNRhigN+wFQsfNzCCWdF0u4saud/AtnDDDlUo1pioKNckXoMv8RxvodiiSbnU4BvChaar/wvPeR3c+O3oA==",
"requires": {
"@rollup/rollup-linux-x64-gnu": "^4.17.1",
"@xmldom/xmldom": "^0.8.10",
diff --git a/package.json b/package.json
index 797d2c34..b49028a3 100644
--- a/package.json
+++ b/package.json
@@ -16,7 +16,7 @@
"dependencies": {
"@babel/preset-env": "^7.24.5",
"@deriv-com/api-hooks": "^0.1.19",
- "@deriv-com/translations": "^1.2.3",
+ "@deriv-com/translations": "^1.2.4",
"@deriv-com/ui": "^1.26.0",
"@deriv-com/utils": "latest",
"@deriv/deriv-api": "^1.0.15",
diff --git a/src/components/AdvertiserName/AdvertiserNameStats.scss b/src/components/AdvertiserName/AdvertiserNameStats.scss
index b0e916ff..e146ddad 100644
--- a/src/components/AdvertiserName/AdvertiserNameStats.scss
+++ b/src/components/AdvertiserName/AdvertiserNameStats.scss
@@ -9,7 +9,7 @@
gap: 2rem;
}
- & div {
+ & div:not(.deriv-tooltip-container):not(.deriv-tooltip-container *) {
display: flex;
align-items: center;
gap: 0.8rem;
diff --git a/src/components/AdvertiserName/AdvertiserNameStats.tsx b/src/components/AdvertiserName/AdvertiserNameStats.tsx
index d7be63e6..36ea5fe7 100644
--- a/src/components/AdvertiserName/AdvertiserNameStats.tsx
+++ b/src/components/AdvertiserName/AdvertiserNameStats.tsx
@@ -2,9 +2,10 @@ import clsx from 'clsx';
import { DeepPartial, TAdvertiserStats } from 'types';
import { OnlineStatusIcon, OnlineStatusLabel, StarRating } from '@/components';
import { getCurrentRoute } from '@/utils';
-import { LabelPairedCircleUserSlashSmRegularIcon, LabelPairedThumbsUpSmRegularIcon } from '@deriv/quill-icons';
+import { LabelPairedThumbsUpSmRegularIcon } from '@deriv/quill-icons';
import { Localize } from '@deriv-com/translations';
import { Text, useDevice } from '@deriv-com/ui';
+import BlockUserCount from './BlockUserCount';
import './AdvertiserNameStats.scss';
/**
@@ -64,11 +65,7 @@ const AdvertiserNameStats = ({ advertiserStats }: { advertiserStats: DeepPartial
<>
- {isMobile && (
-
- ({ratingAverage})
-
- )}
+ ({ratingAverage})
()
@@ -83,14 +80,7 @@ const AdvertiserNameStats = ({ advertiserStats }: { advertiserStats: DeepPartial
>
)}
- {isMyProfile && (
-
-
-
- {blockedByCount || 0}
-
-
- )}
+ {isMyProfile &&
}
);
};
diff --git a/src/components/AdvertiserName/BlockUserCount.scss b/src/components/AdvertiserName/BlockUserCount.scss
new file mode 100644
index 00000000..a2bc46e2
--- /dev/null
+++ b/src/components/AdvertiserName/BlockUserCount.scss
@@ -0,0 +1,19 @@
+.block-user-count {
+ & .deriv-tooltip-container {
+ display: flex;
+ align-items: center;
+ gap: 0.1rem;
+ }
+ &__tooltip {
+ padding: 0.8rem;
+ gap: 0;
+
+ @include mobile {
+ display: none;
+ }
+ }
+
+ &__button {
+ padding: 0;
+ }
+}
diff --git a/src/components/AdvertiserName/BlockUserCount.tsx b/src/components/AdvertiserName/BlockUserCount.tsx
new file mode 100644
index 00000000..c79a364d
--- /dev/null
+++ b/src/components/AdvertiserName/BlockUserCount.tsx
@@ -0,0 +1,60 @@
+import { TLocalize } from 'types';
+import { useModalManager } from '@/hooks';
+import { LabelPairedCircleUserSlashSmRegularIcon } from '@deriv/quill-icons';
+import { useTranslations } from '@deriv-com/translations';
+import { Button, Text, Tooltip, useDevice } from '@deriv-com/ui';
+import { BlockUserCountModal } from '../Modals';
+import './BlockUserCount.scss';
+
+type TBlockUserCount = {
+ count?: number;
+};
+
+const getMessage = (localize: TLocalize, count = 0) => {
+ switch (count) {
+ case 0:
+ return localize('Nobody has blocked you. Yay!');
+ case 1:
+ return localize('{{count}} person has blocked you', {
+ count,
+ });
+ default:
+ return localize('{{count}} people have blocked you', {
+ count,
+ });
+ }
+};
+
+const BlockUserCount = ({ count }: TBlockUserCount) => {
+ const { localize } = useTranslations();
+ const { hideModal, isModalOpenFor, showModal } = useModalManager();
+ const { isMobile } = useDevice();
+ return (
+
+ {getMessage(localize, count)}}
+ >
+
+
+ {count ?? 0}
+
+
+ {!!isModalOpenFor('BlockUserCountModal') && (
+
+ )}
+
+ );
+};
+
+export default BlockUserCount;
diff --git a/src/components/AdvertiserName/__tests__/BlockUserCount.spec.tsx b/src/components/AdvertiserName/__tests__/BlockUserCount.spec.tsx
new file mode 100644
index 00000000..bcead7b5
--- /dev/null
+++ b/src/components/AdvertiserName/__tests__/BlockUserCount.spec.tsx
@@ -0,0 +1,50 @@
+import { useDevice } from '@deriv-com/ui';
+import { render, screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import BlockUserCount from '../BlockUserCount';
+
+const modalManager = {
+ hideModal: jest.fn(),
+ isModalOpenFor: jest.fn(),
+ showModal: jest.fn(),
+};
+modalManager.showModal.mockImplementation(() => {
+ modalManager.isModalOpenFor.mockReturnValue(true);
+});
+
+jest.mock('@/hooks', () => ({
+ ...jest.requireActual('@/hooks'),
+ useModalManager: jest.fn(() => modalManager),
+}));
+
+jest.mock('@deriv-com/ui', () => ({
+ ...jest.requireActual('@deriv-com/ui'),
+ useDevice: jest.fn(() => ({ isMobile: false })),
+}));
+
+describe('BlockUserCount', () => {
+ it('should render the component as expected', () => {
+ render();
+ expect(screen.getByText('0')).toBeInTheDocument();
+ expect(screen.getByTestId('dt_block_user_count_button')).toBeInTheDocument();
+ });
+ it('should display the tooltip message on hovering', async () => {
+ render();
+ const button = screen.getByTestId('dt_block_user_count_button');
+ await userEvent.hover(button);
+ expect(screen.getByText('Nobody has blocked you. Yay!')).toBeInTheDocument();
+ });
+ it('should display the count in the tooltip message', async () => {
+ render();
+ const button = screen.getByTestId('dt_block_user_count_button');
+ await userEvent.hover(button);
+ expect(screen.getByText('1 person has blocked you')).toBeInTheDocument();
+ });
+ it('should open the modal on clicking the button in mobile view', async () => {
+ (useDevice as jest.Mock).mockReturnValue({ isMobile: true });
+ render();
+ await userEvent.click(screen.getByTestId('dt_block_user_count_button'));
+ expect(modalManager.showModal).toHaveBeenCalledWith('BlockUserCountModal');
+ expect(modalManager.isModalOpenFor()).toBe(true);
+ });
+});
diff --git a/src/components/AppFooter/AppFooter.tsx b/src/components/AppFooter/AppFooter.tsx
index 147d7288..df92dfe2 100644
--- a/src/components/AppFooter/AppFooter.tsx
+++ b/src/components/AppFooter/AppFooter.tsx
@@ -17,7 +17,7 @@ import WhatsApp from './WhatsApp';
import './AppFooter.scss';
const AppFooter = () => {
- const { currentLang, localize, switchLanguage } = useTranslations();
+ const { currentLang = 'EN', localize, switchLanguage } = useTranslations();
const { hideModal, isModalOpenFor, showModal } = useModalManager();
const openLanguageSettingModal = () => showModal('DesktopLanguagesModal');
diff --git a/src/components/Modals/BlockUserCountModal/BlockUserCountModal.scss b/src/components/Modals/BlockUserCountModal/BlockUserCountModal.scss
new file mode 100644
index 00000000..99a996fa
--- /dev/null
+++ b/src/components/Modals/BlockUserCountModal/BlockUserCountModal.scss
@@ -0,0 +1,3 @@
+.block-user-count-modal {
+ @include default-modal;
+}
diff --git a/src/components/Modals/BlockUserCountModal/BlockUserCountModal.tsx b/src/components/Modals/BlockUserCountModal/BlockUserCountModal.tsx
new file mode 100644
index 00000000..3056704b
--- /dev/null
+++ b/src/components/Modals/BlockUserCountModal/BlockUserCountModal.tsx
@@ -0,0 +1,32 @@
+import { Localize } from '@deriv-com/translations';
+import { Button, Modal, Text } from '@deriv-com/ui';
+import './BlockUserCountModal.scss';
+
+type TBlockUserCountModalProps = {
+ isModalOpen: boolean;
+ message: string;
+ onRequestClose: () => void;
+};
+
+const BlockUserCountModal = ({ isModalOpen, message, onRequestClose }: TBlockUserCountModalProps) => {
+ return (
+
+
+ {message}
+
+
+
+
+
+ );
+};
+
+export default BlockUserCountModal;
diff --git a/src/components/Modals/BlockUserCountModal/__tests__/BlockUserCountModal.spec.tsx b/src/components/Modals/BlockUserCountModal/__tests__/BlockUserCountModal.spec.tsx
new file mode 100644
index 00000000..56100a4e
--- /dev/null
+++ b/src/components/Modals/BlockUserCountModal/__tests__/BlockUserCountModal.spec.tsx
@@ -0,0 +1,21 @@
+import { render, screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import BlockUserCountModal from '../BlockUserCountModal';
+
+const mockProps = {
+ isModalOpen: true,
+ message: 'test',
+ onRequestClose: jest.fn(),
+};
+
+describe('BlockUserCountModal', () => {
+ it('should render the modal component as expected', () => {
+ render();
+ expect(screen.getByText('test')).toBeInTheDocument();
+ });
+ it('should handle the onclick event for the ok button', async () => {
+ render();
+ await userEvent.click(screen.getByText('Ok'));
+ expect(mockProps.onRequestClose).toHaveBeenCalled();
+ });
+});
diff --git a/src/components/Modals/BlockUserCountModal/index.ts b/src/components/Modals/BlockUserCountModal/index.ts
new file mode 100644
index 00000000..cd216f53
--- /dev/null
+++ b/src/components/Modals/BlockUserCountModal/index.ts
@@ -0,0 +1 @@
+export { default as BlockUserCountModal } from './BlockUserCountModal';
diff --git a/src/components/Modals/index.ts b/src/components/Modals/index.ts
index 1d239b27..3fb2f423 100644
--- a/src/components/Modals/index.ts
+++ b/src/components/Modals/index.ts
@@ -7,6 +7,7 @@ export * from './AdRateSwitchModal';
export * from './AdVisibilityErrorModal';
export * from './AvailableP2PBalanceModal';
export * from './BlockUnblockUserModal';
+export * from './BlockUserCountModal';
export * from './DailyLimitModal';
export * from './EmailLinkBlockedModal';
export * from './EmailLinkExpiredModal';
diff --git a/src/components/PaymentMethodCard/PaymentMethodCardHeader/PaymentMethodCardHeader.scss b/src/components/PaymentMethodCard/PaymentMethodCardHeader/PaymentMethodCardHeader.scss
index dc4ba77a..9f1c5b37 100644
--- a/src/components/PaymentMethodCard/PaymentMethodCardHeader/PaymentMethodCardHeader.scss
+++ b/src/components/PaymentMethodCard/PaymentMethodCardHeader/PaymentMethodCardHeader.scss
@@ -12,6 +12,11 @@
.deriv-dropdown {
width: 3.2rem;
+ & .deriv-dropdown__button {
+ transform: none;
+ transition: none;
+ }
+
&__items {
width: 12.8rem;
border: 0;
diff --git a/src/components/PaymentMethodForm/PaymentMethodForm.scss b/src/components/PaymentMethodForm/PaymentMethodForm.scss
index b84819b2..305e2adf 100644
--- a/src/components/PaymentMethodForm/PaymentMethodForm.scss
+++ b/src/components/PaymentMethodForm/PaymentMethodForm.scss
@@ -78,7 +78,7 @@
@include mobile {
overflow: auto;
- height: calc(100vh - 14rem);
+ height: calc(100vh - 12rem);
padding-bottom: 8rem;
}
}
@@ -112,6 +112,9 @@
}
& .deriv-dropdown {
+ & .deriv-dropdown__items {
+ box-shadow: 0 4px 6px 0 rgba(0, 0, 0, 0.24);
+ }
& .deriv-input {
height: 4rem;
align-items: center;
diff --git a/src/components/PaymentMethodForm/PaymentMethodFormModalRenderer/PaymentMethodFormModalRenderer.tsx b/src/components/PaymentMethodForm/PaymentMethodFormModalRenderer/PaymentMethodFormModalRenderer.tsx
index aa8491dd..45f840ce 100644
--- a/src/components/PaymentMethodForm/PaymentMethodFormModalRenderer/PaymentMethodFormModalRenderer.tsx
+++ b/src/components/PaymentMethodForm/PaymentMethodFormModalRenderer/PaymentMethodFormModalRenderer.tsx
@@ -2,6 +2,7 @@ import { useEffect } from 'react';
import { TFormState, TSocketError } from 'types';
import { PaymentMethodErrorModal, PaymentMethodModal } from '@/components/Modals';
import { useModalManager } from '@/hooks';
+import { useTranslations } from '@deriv-com/translations';
type TPaymentMethodFormModalRendererProps = {
actionType: TFormState['actionType'];
@@ -22,6 +23,7 @@ const PaymentMethodFormModalRenderer = ({
setIsError,
updateError,
}: TPaymentMethodFormModalRendererProps) => {
+ const { localize } = useTranslations();
const { hideModal, isModalOpenFor, showModal } = useModalManager();
useEffect(() => {
@@ -32,10 +34,10 @@ const PaymentMethodFormModalRenderer = ({
showModal('PaymentMethodModal');
}
- // TODO: Remember to translate these strings
if (createError || updateError) {
showModal('PaymentMethodErrorModal');
}
+ // eslint-disable-next-line react-hooks/exhaustive-deps
}, [actionType, createError, isCreateSuccessful, isUpdateSuccessful, updateError]);
return (
@@ -49,15 +51,15 @@ const PaymentMethodFormModalRenderer = ({
setIsError(false);
hideModal({ shouldHideAllModals: true });
}}
- title='Something’s not right'
+ title={localize('Something’s not right')}
/>
)}
{!!isModalOpenFor('PaymentMethodModal') && (
{
@@ -69,9 +71,13 @@ const PaymentMethodFormModalRenderer = ({
hideModal();
setIsError(false);
}}
- primaryButtonLabel={actionType === 'ADD' ? 'Go back' : "Don't cancel"}
+ primaryButtonLabel={actionType === 'ADD' ? localize('Go back') : localize("Don't cancel")}
secondaryButtonLabel='Cancel'
- title={actionType === 'ADD' ? 'Cancel adding this payment method?' : 'Cancel your edits?'}
+ title={
+ actionType === 'ADD'
+ ? localize('Cancel adding this payment method?')
+ : localize('Cancel your edits?')
+ }
/>
)}
>
diff --git a/src/components/Search/Search.scss b/src/components/Search/Search.scss
index 2f140442..c8a4470b 100644
--- a/src/components/Search/Search.scss
+++ b/src/components/Search/Search.scss
@@ -9,10 +9,14 @@
&__container {
width: 100%;
}
-
&__field {
margin-left: 0.8rem;
+ // placeholder is hidden when input is focused, visibility hidden doesn't work with firefox.
+ &::-moz-placeholder {
+ color: transparent;
+ }
+
&:not(:placeholder-shown) ~ label,
&:focus ~ label {
display: none;
diff --git a/src/pages/my-ads/screens/MyAdsEmpty/MyAdsEmpty.tsx b/src/pages/my-ads/screens/MyAdsEmpty/MyAdsEmpty.tsx
index e7e5bbd4..d0958989 100644
--- a/src/pages/my-ads/screens/MyAdsEmpty/MyAdsEmpty.tsx
+++ b/src/pages/my-ads/screens/MyAdsEmpty/MyAdsEmpty.tsx
@@ -3,6 +3,7 @@ import { NicknameModal } from '@/components/Modals';
import { MY_ADS_URL } from '@/constants';
import { useIsAdvertiser, useModalManager } from '@/hooks/custom-hooks';
import { DerivLightIcCashierNoAdsIcon } from '@deriv/quill-icons';
+import { Localize } from '@deriv-com/translations';
import { ActionScreen, Button, Text, useDevice } from '@deriv-com/ui';
const MyAdsEmpty = () => {
@@ -23,18 +24,18 @@ const MyAdsEmpty = () => {
size='lg'
textSize={isMobile ? 'md' : 'sm'}
>
- Create new ad
+
}
description={
- Looking to buy or sell USD? You can post your own ad for others to respond.
+
}
icon={}
title={
- You have no ads 😞
+
}
/>
diff --git a/src/pages/my-profile/screens/MyProfile/MyProfile.scss b/src/pages/my-profile/screens/MyProfile/MyProfile.scss
index 297600ee..ab7d7e70 100644
--- a/src/pages/my-profile/screens/MyProfile/MyProfile.scss
+++ b/src/pages/my-profile/screens/MyProfile/MyProfile.scss
@@ -3,7 +3,7 @@
flex-direction: column;
padding-top: 2.4rem;
overflow-y: auto;
- height: calc(100vh - 12rem);
+ height: calc(100vh - 14rem);
@include mobile {
padding: 0;
diff --git a/src/pages/my-profile/screens/MyProfileAdDetails/MyProfileAdDetails.scss b/src/pages/my-profile/screens/MyProfileAdDetails/MyProfileAdDetails.scss
index 563c38b9..75bf0688 100644
--- a/src/pages/my-profile/screens/MyProfileAdDetails/MyProfileAdDetails.scss
+++ b/src/pages/my-profile/screens/MyProfileAdDetails/MyProfileAdDetails.scss
@@ -16,6 +16,14 @@
&__mobile-wrapper {
position: absolute;
top: 4rem;
- height: calc(100vh - 8rem);
+ height: calc(100vh - 4rem);
+ }
+
+ & .deriv-textarea {
+ &__footer {
+ & .deriv-text {
+ font-size: 1.2rem;
+ }
+ }
}
}
diff --git a/src/pages/my-profile/screens/MyProfileAdDetails/MyProfileAdDetails.tsx b/src/pages/my-profile/screens/MyProfileAdDetails/MyProfileAdDetails.tsx
index da8c1c0c..6a511ba4 100644
--- a/src/pages/my-profile/screens/MyProfileAdDetails/MyProfileAdDetails.tsx
+++ b/src/pages/my-profile/screens/MyProfileAdDetails/MyProfileAdDetails.tsx
@@ -20,6 +20,8 @@ const MyProfileAdDetailsTextArea = ({
setContactInfo,
}: TMYProfileAdDetailsTextAreaProps) => {
const { localize } = useTranslations();
+ const { isMobile } = useDevice();
+ const textSize = isMobile ? 'md' : 'sm';
return (
<>