From 4dee3b0cef48892be74ff66470083cf20755f7cc Mon Sep 17 00:00:00 2001 From: OGPoyraz Date: Mon, 22 Jul 2024 14:34:27 +0200 Subject: [PATCH 1/4] Add ellipsis for permit fiat values --- ui/helpers/utils/util.js | 16 ++++-- ui/helpers/utils/util.test.js | 11 ++++ ui/hooks/useFiatFormatter.test.ts | 37 ++++++++++++- ui/hooks/useFiatFormatter.ts | 55 +++++++++++++++++-- .../permit-simulation/permit-simulation.tsx | 4 +- .../simulation-details/fiat-display.tsx | 9 +-- 6 files changed, 117 insertions(+), 15 deletions(-) diff --git a/ui/helpers/utils/util.js b/ui/helpers/utils/util.js index 921643b9e83b..825fcd384e76 100644 --- a/ui/helpers/utils/util.js +++ b/ui/helpers/utils/util.js @@ -247,24 +247,30 @@ export function getRandomFileName() { * @param {number} options.truncatedCharLimit - The maximum length of the string. * @param {number} options.truncatedStartChars - The number of characters to preserve at the beginning. * @param {number} options.truncatedEndChars - The number of characters to preserve at the end. + * @param {boolean} options.skipCharacterInEnd - Skip the character at the end. * @returns {string} The shortened string. */ export function shortenString( stringToShorten = '', - { truncatedCharLimit, truncatedStartChars, truncatedEndChars } = { + { + truncatedCharLimit, + truncatedStartChars, + truncatedEndChars, + skipCharacterInEnd, + } = { truncatedCharLimit: TRUNCATED_NAME_CHAR_LIMIT, truncatedStartChars: TRUNCATED_ADDRESS_START_CHARS, truncatedEndChars: TRUNCATED_ADDRESS_END_CHARS, + skipCharacterInEnd: false, }, ) { if (stringToShorten.length < truncatedCharLimit) { return stringToShorten; } - return `${stringToShorten.slice( - 0, - truncatedStartChars, - )}...${stringToShorten.slice(-truncatedEndChars)}`; + return `${stringToShorten.slice(0, truncatedStartChars)}...${ + skipCharacterInEnd ? '' : stringToShorten.slice(-truncatedEndChars) + }`; } /** diff --git a/ui/helpers/utils/util.test.js b/ui/helpers/utils/util.test.js index 76811f432f81..2ad0db6e50a0 100644 --- a/ui/helpers/utils/util.test.js +++ b/ui/helpers/utils/util.test.js @@ -1081,5 +1081,16 @@ describe('util', () => { }), ).toStrictEqual('0x12...7890'); }); + + it('should shorten the string and remove all characters from the end if skipCharacterInEnd is true', () => { + expect( + util.shortenString('0x1234567890123456789012345678901234567890', { + truncatedCharLimit: 10, + truncatedStartChars: 4, + truncatedEndChars: 4, + skipCharacterInEnd: true, + }), + ).toStrictEqual('0x12...'); + }); }); }); diff --git a/ui/hooks/useFiatFormatter.test.ts b/ui/hooks/useFiatFormatter.test.ts index bf7fc670b5ed..7ab5c4b22a8e 100644 --- a/ui/hooks/useFiatFormatter.test.ts +++ b/ui/hooks/useFiatFormatter.test.ts @@ -35,6 +35,40 @@ describe('useFiatFormatter', () => { expect(formatFiat(0)).toBe('$0.00'); }); + describe('shorten the fiat', () => { + it('when currency symbol on the left for given locale', () => { + mockGetIntlLocale.mockReturnValue('en-US'); + mockGetCurrentCurrency.mockReturnValue('USD'); + + const { result } = renderHook(() => useFiatFormatter(true)); + const formatFiat = result.current; + + expect(formatFiat(100000000000000000)).toBe('$100,000,000,...'); + }); + + it('when currency symbol on the right for given locale', () => { + mockGetIntlLocale.mockReturnValue('es-ES'); + mockGetCurrentCurrency.mockReturnValue('EUR'); + + const { result } = renderHook(() => useFiatFormatter(true)); + const formatFiat = result.current; + + expect(formatFiat(100000000000000000)).toBe('100.000.000....€'); + }); + + it('handle unknown currencies by returning amount followed by currency code', () => { + mockGetCurrentCurrency.mockReturnValue('storj'); + mockGetIntlLocale.mockReturnValue('en-US'); + + const { result } = renderHook(() => useFiatFormatter(true)); + const formatFiat = result.current; + + expect(formatFiat(100000)).toBe('100,000 storj'); + expect(formatFiat(500.5)).toBe('500.5 storj'); + expect(formatFiat(0)).toBe('0 storj'); + }); + }); + it('should use the current locale and currency from the mocked functions', () => { mockGetIntlLocale.mockReturnValue('fr-FR'); mockGetCurrentCurrency.mockReturnValue('EUR'); @@ -47,12 +81,13 @@ describe('useFiatFormatter', () => { it('should gracefully handle unknown currencies by returning amount followed by currency code', () => { mockGetCurrentCurrency.mockReturnValue('storj'); + mockGetIntlLocale.mockReturnValue('en-US'); const { result } = renderHook(() => useFiatFormatter()); const formatFiat = result.current; // Testing the fallback formatting for an unknown currency - expect(formatFiat(1000)).toBe('1000 storj'); + expect(formatFiat(100000)).toBe('100,000 storj'); expect(formatFiat(500.5)).toBe('500.5 storj'); expect(formatFiat(0)).toBe('0 storj'); }); diff --git a/ui/hooks/useFiatFormatter.ts b/ui/hooks/useFiatFormatter.ts index 0f5e37151670..66b01abb8841 100644 --- a/ui/hooks/useFiatFormatter.ts +++ b/ui/hooks/useFiatFormatter.ts @@ -1,6 +1,7 @@ import { useSelector } from 'react-redux'; import { getIntlLocale } from '../ducks/locale/locale'; import { getCurrentCurrency } from '../selectors'; +import { shortenString } from '../helpers/utils/util'; /** * Returns a function that formats a fiat amount as a localized string. @@ -17,19 +18,65 @@ import { getCurrentCurrency } from '../selectors'; type FiatFormatter = (fiatAmount: number) => string; -export const useFiatFormatter = (): FiatFormatter => { +export const useFiatFormatter = (shortenFiatValue?: boolean): FiatFormatter => { const locale = useSelector(getIntlLocale); const fiatCurrency = useSelector(getCurrentCurrency); return (fiatAmount: number) => { try { - return new Intl.NumberFormat(locale, { + const formatter = new Intl.NumberFormat(locale, { style: 'currency', currency: fiatCurrency, - }).format(fiatAmount); + }); + + if (!shortenFiatValue) { + return formatter.format(fiatAmount); + } + + const parts = formatter.formatToParts(fiatAmount); + + let currencySymbol = ''; + let numberString = ''; + + parts.forEach((part) => { + if (part.type === 'currency') { + currencySymbol += part.value; + } else { + numberString += part.value; + } + }); + + // Shorten the number part while preserving commas + const shortenedNumberString = shortenString(numberString, { + truncatedCharLimit: 15, + truncatedStartChars: 12, + truncatedEndChars: 0, + skipCharacterInEnd: true, + }); + + // Determine the position of the currency symbol + const currencyBeforeNumber = + parts.findIndex((part) => part.type === 'currency') < + parts.findIndex((part) => part.type === 'integer'); + + // Reassemble the formatted string + return currencyBeforeNumber + ? `${currencySymbol}${shortenedNumberString}` + : `${shortenedNumberString}${currencySymbol}`; } catch (error) { // Fallback for unknown or unsupported currencies - return `${fiatAmount} ${fiatCurrency}`; + const formattedNumber = new Intl.NumberFormat(locale).format(fiatAmount); + const shortenedNumberString = shortenString(formattedNumber, { + truncatedCharLimit: 15, + truncatedStartChars: 12, + truncatedEndChars: 0, + skipCharacterInEnd: true, + }); + + if (shortenFiatValue) { + return `${shortenedNumberString} ${fiatCurrency}`; + } + return `${formattedNumber} ${fiatCurrency}`; } }; }; diff --git a/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/permit-simulation.tsx b/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/permit-simulation.tsx index e4cbed8c83a3..154cbdb041be 100644 --- a/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/permit-simulation.tsx +++ b/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/permit-simulation.tsx @@ -102,7 +102,9 @@ const PermitSimulation: React.FC<{ - {fiatValue && } + {fiatValue && ( + + )} diff --git a/ui/pages/confirmations/components/simulation-details/fiat-display.tsx b/ui/pages/confirmations/components/simulation-details/fiat-display.tsx index 5143cd5ed06b..142e57a8c2e1 100644 --- a/ui/pages/confirmations/components/simulation-details/fiat-display.tsx +++ b/ui/pages/confirmations/components/simulation-details/fiat-display.tsx @@ -35,11 +35,12 @@ export function calculateTotalFiat(fiatAmounts: FiatAmount[]): number { * @param props * @param props.fiatAmount */ -export const IndividualFiatDisplay: React.FC<{ fiatAmount: FiatAmount }> = ({ - fiatAmount, -}) => { +export const IndividualFiatDisplay: React.FC<{ + fiatAmount: FiatAmount; + shortenFiatValue?: boolean; +}> = ({ fiatAmount, shortenFiatValue = false }) => { const hideFiatForTestnet = useHideFiatForTestnet(); - const fiatFormatter = useFiatFormatter(); + const fiatFormatter = useFiatFormatter(shortenFiatValue); if (hideFiatForTestnet) { return null; From a0b82c71abce052936ebeafec635d1321ac7c08c Mon Sep 17 00:00:00 2001 From: OGPoyraz Date: Mon, 22 Jul 2024 14:40:41 +0200 Subject: [PATCH 2/4] fix lint --- ui/hooks/useFiatFormatter.ts | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/ui/hooks/useFiatFormatter.ts b/ui/hooks/useFiatFormatter.ts index 66b01abb8841..855532e0d187 100644 --- a/ui/hooks/useFiatFormatter.ts +++ b/ui/hooks/useFiatFormatter.ts @@ -5,17 +5,24 @@ import { shortenString } from '../helpers/utils/util'; /** * Returns a function that formats a fiat amount as a localized string. + * The hook takes an optional boolean parameter to shorten the fiat value. * * Example usage: * * ``` * const formatFiat = useFiatFormatter(); * const formattedAmount = formatFiat(1000); + * + * const shorteningFiatFormatter = useFiatFormatter(true); + * const shortenedAmount = shorteningFiatFormatter(100000000000000000); * ``` * * @returns A function that takes a fiat amount as a number and returns a formatted string. */ +const TRUNCATED_CHAR_LIMIT_FOR_SHORTENED_FIAT = 15; +const TRUNCATED_START_CHARS_FOR_SHORTENED_FIAT = 12; + type FiatFormatter = (fiatAmount: number) => string; export const useFiatFormatter = (shortenFiatValue?: boolean): FiatFormatter => { @@ -48,8 +55,8 @@ export const useFiatFormatter = (shortenFiatValue?: boolean): FiatFormatter => { // Shorten the number part while preserving commas const shortenedNumberString = shortenString(numberString, { - truncatedCharLimit: 15, - truncatedStartChars: 12, + truncatedCharLimit: TRUNCATED_CHAR_LIMIT_FOR_SHORTENED_FIAT, + truncatedStartChars: TRUNCATED_START_CHARS_FOR_SHORTENED_FIAT, truncatedEndChars: 0, skipCharacterInEnd: true, }); @@ -67,8 +74,8 @@ export const useFiatFormatter = (shortenFiatValue?: boolean): FiatFormatter => { // Fallback for unknown or unsupported currencies const formattedNumber = new Intl.NumberFormat(locale).format(fiatAmount); const shortenedNumberString = shortenString(formattedNumber, { - truncatedCharLimit: 15, - truncatedStartChars: 12, + truncatedCharLimit: TRUNCATED_CHAR_LIMIT_FOR_SHORTENED_FIAT, + truncatedStartChars: TRUNCATED_START_CHARS_FOR_SHORTENED_FIAT, truncatedEndChars: 0, skipCharacterInEnd: true, }); From 475a0466cd2aa2cf6dc343b13d2d417653ec3695 Mon Sep 17 00:00:00 2001 From: OGPoyraz Date: Tue, 23 Jul 2024 11:41:54 +0200 Subject: [PATCH 3/4] Fix suggestions --- ui/hooks/useFiatFormatter.test.ts | 20 +++++++++------ ui/hooks/useFiatFormatter.ts | 25 +++++++++++++------ .../permit-simulation/permit-simulation.tsx | 2 +- .../simulation-details/fiat-display.tsx | 13 +++++----- 4 files changed, 37 insertions(+), 23 deletions(-) diff --git a/ui/hooks/useFiatFormatter.test.ts b/ui/hooks/useFiatFormatter.test.ts index 7ab5c4b22a8e..7e6423f1df34 100644 --- a/ui/hooks/useFiatFormatter.test.ts +++ b/ui/hooks/useFiatFormatter.test.ts @@ -40,32 +40,36 @@ describe('useFiatFormatter', () => { mockGetIntlLocale.mockReturnValue('en-US'); mockGetCurrentCurrency.mockReturnValue('USD'); - const { result } = renderHook(() => useFiatFormatter(true)); + const { result } = renderHook(() => useFiatFormatter()); const formatFiat = result.current; - expect(formatFiat(100000000000000000)).toBe('$100,000,000,...'); + expect(formatFiat(100000000000000000, { shorten: true })).toBe( + '$100,000,000,...', + ); }); it('when currency symbol on the right for given locale', () => { mockGetIntlLocale.mockReturnValue('es-ES'); mockGetCurrentCurrency.mockReturnValue('EUR'); - const { result } = renderHook(() => useFiatFormatter(true)); + const { result } = renderHook(() => useFiatFormatter()); const formatFiat = result.current; - expect(formatFiat(100000000000000000)).toBe('100.000.000....€'); + expect(formatFiat(100000000000000000, { shorten: true })).toBe( + '100.000.000....€', + ); }); it('handle unknown currencies by returning amount followed by currency code', () => { mockGetCurrentCurrency.mockReturnValue('storj'); mockGetIntlLocale.mockReturnValue('en-US'); - const { result } = renderHook(() => useFiatFormatter(true)); + const { result } = renderHook(() => useFiatFormatter()); const formatFiat = result.current; - expect(formatFiat(100000)).toBe('100,000 storj'); - expect(formatFiat(500.5)).toBe('500.5 storj'); - expect(formatFiat(0)).toBe('0 storj'); + expect(formatFiat(100000, { shorten: true })).toBe('100,000 storj'); + expect(formatFiat(500.5, { shorten: true })).toBe('500.5 storj'); + expect(formatFiat(0, { shorten: true })).toBe('0 storj'); }); }); diff --git a/ui/hooks/useFiatFormatter.ts b/ui/hooks/useFiatFormatter.ts index 855532e0d187..0c9bd33ffee5 100644 --- a/ui/hooks/useFiatFormatter.ts +++ b/ui/hooks/useFiatFormatter.ts @@ -5,7 +5,7 @@ import { shortenString } from '../helpers/utils/util'; /** * Returns a function that formats a fiat amount as a localized string. - * The hook takes an optional boolean parameter to shorten the fiat value. + * The hook takes an optional options object to configure the formatting. * * Example usage: * @@ -13,8 +13,8 @@ import { shortenString } from '../helpers/utils/util'; * const formatFiat = useFiatFormatter(); * const formattedAmount = formatFiat(1000); * - * const shorteningFiatFormatter = useFiatFormatter(true); - * const shortenedAmount = shorteningFiatFormatter(100000000000000000); + * const shorteningFiatFormatter = useFiatFormatter(); + * const shortenedAmount = shorteningFiatFormatter(100000000000000000, { shorten: true }); * ``` * * @returns A function that takes a fiat amount as a number and returns a formatted string. @@ -23,20 +23,29 @@ import { shortenString } from '../helpers/utils/util'; const TRUNCATED_CHAR_LIMIT_FOR_SHORTENED_FIAT = 15; const TRUNCATED_START_CHARS_FOR_SHORTENED_FIAT = 12; -type FiatFormatter = (fiatAmount: number) => string; +type FiatFormatterOptions = { + shorten?: boolean; +}; + +type FiatFormatter = ( + fiatAmount: number, + options?: FiatFormatterOptions, +) => string; -export const useFiatFormatter = (shortenFiatValue?: boolean): FiatFormatter => { +export const useFiatFormatter = (): FiatFormatter => { const locale = useSelector(getIntlLocale); const fiatCurrency = useSelector(getCurrentCurrency); - return (fiatAmount: number) => { + return (fiatAmount: number, options: FiatFormatterOptions = {}) => { + const { shorten } = options; + try { const formatter = new Intl.NumberFormat(locale, { style: 'currency', currency: fiatCurrency, }); - if (!shortenFiatValue) { + if (!shorten) { return formatter.format(fiatAmount); } @@ -80,7 +89,7 @@ export const useFiatFormatter = (shortenFiatValue?: boolean): FiatFormatter => { skipCharacterInEnd: true, }); - if (shortenFiatValue) { + if (shorten) { return `${shortenedNumberString} ${fiatCurrency}`; } return `${formattedNumber} ${fiatCurrency}`; diff --git a/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/permit-simulation.tsx b/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/permit-simulation.tsx index 154cbdb041be..280a79022710 100644 --- a/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/permit-simulation.tsx +++ b/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/permit-simulation.tsx @@ -103,7 +103,7 @@ const PermitSimulation: React.FC<{ {fiatValue && ( - + )} diff --git a/ui/pages/confirmations/components/simulation-details/fiat-display.tsx b/ui/pages/confirmations/components/simulation-details/fiat-display.tsx index 142e57a8c2e1..66e9148c676e 100644 --- a/ui/pages/confirmations/components/simulation-details/fiat-display.tsx +++ b/ui/pages/confirmations/components/simulation-details/fiat-display.tsx @@ -32,15 +32,16 @@ export function calculateTotalFiat(fiatAmounts: FiatAmount[]): number { /** * Displays the fiat value of a single balance change. * - * @param props - * @param props.fiatAmount + * @param props - The props object. + * @param props.fiatAmount - The fiat amount to display. + * @param props.shorten - Whether to shorten the fiat amount. */ export const IndividualFiatDisplay: React.FC<{ fiatAmount: FiatAmount; - shortenFiatValue?: boolean; -}> = ({ fiatAmount, shortenFiatValue = false }) => { + shorten?: boolean; +}> = ({ fiatAmount, shorten = false }) => { const hideFiatForTestnet = useHideFiatForTestnet(); - const fiatFormatter = useFiatFormatter(shortenFiatValue); + const fiatFormatter = useFiatFormatter(); if (hideFiatForTestnet) { return null; @@ -51,7 +52,7 @@ export const IndividualFiatDisplay: React.FC<{ } const absFiat = Math.abs(fiatAmount); - return {fiatFormatter(absFiat)}; + return {fiatFormatter(absFiat, { shorten })}; }; /** From d4d259e5244af84101fde7069349d10acbce11a2 Mon Sep 17 00:00:00 2001 From: OGPoyraz Date: Tue, 23 Jul 2024 12:06:28 +0200 Subject: [PATCH 4/4] Fix lint --- ui/helpers/utils/util.js | 1 + .../confirmations/components/simulation-details/amount-pill.tsx | 1 + 2 files changed, 2 insertions(+) diff --git a/ui/helpers/utils/util.js b/ui/helpers/utils/util.js index 825fcd384e76..2c55f520ad6d 100644 --- a/ui/helpers/utils/util.js +++ b/ui/helpers/utils/util.js @@ -289,6 +289,7 @@ export function shortenAddress(address = '') { truncatedCharLimit: TRUNCATED_NAME_CHAR_LIMIT, truncatedStartChars: TRUNCATED_ADDRESS_START_CHARS, truncatedEndChars: TRUNCATED_ADDRESS_END_CHARS, + skipCharacterInEnd: false, }); } diff --git a/ui/pages/confirmations/components/simulation-details/amount-pill.tsx b/ui/pages/confirmations/components/simulation-details/amount-pill.tsx index 5e86018f5e58..a8ad71cfadc6 100644 --- a/ui/pages/confirmations/components/simulation-details/amount-pill.tsx +++ b/ui/pages/confirmations/components/simulation-details/amount-pill.tsx @@ -59,6 +59,7 @@ export const AmountPill: React.FC<{ truncatedCharLimit: 11, truncatedStartChars: 4, truncatedEndChars: 4, + skipCharacterInEnd: false, }); const shortenedTokenIdPart = `#${shortenedDecimalTokenId}`;