Skip to content

Commit

Permalink
Add support for decimal and thousand separators in the formatPrice
Browse files Browse the repository at this point in the history
…function (woocommerce#5188)

* Format with decimal and thousands, with tests

* Update packages/prices/utils/price.ts

Co-authored-by: Albert Juhé Lluveras <[email protected]>

Co-authored-by: Albert Juhé Lluveras <[email protected]>
  • Loading branch information
2 people authored and jonny-bull committed Dec 16, 2021
1 parent e940401 commit 7f8ea52
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 15 deletions.
50 changes: 46 additions & 4 deletions packages/prices/utils/price.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ const siteCurrencySettings: Currency = {

/**
* Gets currency information in normalized format from an API response or the server.
*
* If no currency was provided, or currency_code is empty, the default store currency will be used.
*/
export const getCurrencyFromPriceResponse = (
// Currency data object, for example an API response containing currency formatting data.
Expand All @@ -74,7 +76,7 @@ export const getCurrencyFromPriceResponse = (
| Record< string, never >
| CartShippingPackageShippingRate
): Currency => {
if ( ! currencyData || typeof currencyData !== 'object' ) {
if ( ! currencyData?.currency_code ) {
return siteCurrencySettings;
}

Expand Down Expand Up @@ -113,6 +115,28 @@ export const getCurrency = (
};
};

const applyThousandSeparator = (
numberString: string,
thousandSeparator: string
): string => {
return numberString.replace( /\B(?=(\d{3})+(?!\d))/g, thousandSeparator );
};

const splitDecimal = (
numberString: string
): {
beforeDecimal: string;
afterDecimal: string;
} => {
const parts = numberString.split( '.' );
const beforeDecimal = parts[ 0 ];
const afterDecimal = parts[ 1 ] || '';
return {
beforeDecimal,
afterDecimal,
};
};

/**
* Format a price, provided using the smallest unit of the currency, as a
* decimal complete with currency symbols using current store settings.
Expand All @@ -134,9 +158,27 @@ export const formatPrice = (
}

const currency: Currency = getCurrency( currencyData );
const formattedPrice: number = priceInt / 10 ** currency.minorUnit;
const formattedValue: string =
currency.prefix + formattedPrice + currency.suffix;

const {
minorUnit,
prefix,
suffix,
decimalSeparator,
thousandSeparator,
} = currency;

const formattedPrice: number = priceInt / 10 ** minorUnit;

const { beforeDecimal, afterDecimal } = splitDecimal(
formattedPrice.toString()
);

const formattedValue = `${ prefix }${ applyThousandSeparator(
beforeDecimal,
thousandSeparator
) }${
afterDecimal ? `${ decimalSeparator }${ afterDecimal }` : ''
}${ suffix }`;

// This uses a textarea to magically decode HTML currency symbols.
const txt = document.createElement( 'textarea' );
Expand Down
47 changes: 36 additions & 11 deletions packages/prices/utils/test/price.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,20 @@ import { formatPrice, getCurrency } from '../price';

describe( 'formatPrice', () => {
test.each`
value | prefix | suffix | expected
${ 1000 } | ${ '€' } | ${ '' } | ${ '€10' }
${ 1000 } | ${ '' } | ${ '€' } | ${ '10€' }
${ 1000 } | ${ '' } | ${ '$' } | ${ '10$' }
${ '1000' } | ${ '€' } | ${ '' } | ${ '€10' }
${ 0 } | ${ '€' } | ${ '' } | ${ '€0' }
${ '' } | ${ '€' } | ${ '' } | ${ '' }
${ null } | ${ '€' } | ${ '' } | ${ '' }
${ undefined } | ${ '€' } | ${ '' } | ${ '' }
value | prefix | suffix | expected
${ 1000 } | ${ '€' } | ${ '' } | ${ '€10' }
${ 1000 } | ${ '' } | ${ '€' } | ${ '10€' }
${ 1000 } | ${ '' } | ${ '$' } | ${ '10$' }
${ '1000' } | ${ '€' } | ${ '' } | ${ '€10' }
${ 0 } | ${ '€' } | ${ '' } | ${ '€0' }
${ '' } | ${ '€' } | ${ '' } | ${ '' }
${ null } | ${ '€' } | ${ '' } | ${ '' }
${ undefined } | ${ '€' } | ${ '' } | ${ '' }
${ 100000 } | ${ '€' } | ${ '' } | ${ '€1,000' }
${ 1000000 } | ${ '€' } | ${ '' } | ${ '€10,000' }
${ 1000000000 } | ${ '€' } | ${ '' } | ${ '€10,000,000' }
`(
'correctly formats price given "$value", "$prefix" prefix, and "$suffix" suffix',
'correctly formats price given "$value", "$prefix" prefix, and "$suffix" suffix as "$expected"',
( { value, prefix, suffix, expected } ) => {
const formattedPrice = formatPrice(
value,
Expand All @@ -26,6 +29,28 @@ describe( 'formatPrice', () => {
}
);

test.each`
value | prefix | decimalSeparator | thousandSeparator | expected
${ 1000000099 } | ${ '$' } | ${ '.' } | ${ ',' } | ${ '$10,000,000.99' }
${ 1000000099 } | ${ '$' } | ${ ',' } | ${ '.' } | ${ '$10.000.000,99' }
`(
'correctly formats price given "$value", "$prefix" prefix, "$decimalSeparator" decimal separator, "$thousandSeparator" thousand separator as "$expected"',
( {
value,
prefix,
decimalSeparator,
thousandSeparator,
expected,
} ) => {
const formattedPrice = formatPrice(
value,
getCurrency( { prefix, decimalSeparator, thousandSeparator } )
);

expect( formattedPrice ).toEqual( expected );
}
);

test.each`
value | expected
${ 1000 } | ${ '$10' }
Expand All @@ -34,7 +59,7 @@ describe( 'formatPrice', () => {
${ null } | ${ '' }
${ undefined } | ${ '' }
`(
'correctly formats price given "$value" only',
'correctly formats price given "$value" only as "$expected"',
( { value, expected } ) => {
const formattedPrice = formatPrice( value );

Expand Down

0 comments on commit 7f8ea52

Please sign in to comment.