woocommerce/packages/woocommerce-blocks/assets/js/base/components/formatted-monetary-amount/index.tsx
2021-12-10 12:03:04 +00:00

105 lines
2.5 KiB
TypeScript

/**
* External dependencies
*/
import NumberFormat, {
NumberFormatValues,
NumberFormatProps,
} from 'react-number-format';
import classNames from 'classnames';
import type { Currency } from '@woocommerce/price-format';
import type { ReactElement } from 'react';
/**
* Internal dependencies
*/
import './style.scss';
interface FormattedMonetaryAmountProps {
className?: string;
displayType?: NumberFormatProps[ 'displayType' ];
value: number | string; // Value of money amount.
currency: Currency | Record< string, never >; // Currency configuration object.
onValueChange?: ( unit: number ) => void; // Function to call when value changes.
}
/**
* Formats currency data into the expected format for NumberFormat.
*/
const currencyToNumberFormat = (
currency: FormattedMonetaryAmountProps[ 'currency' ]
) => {
return {
thousandSeparator: currency.thousandSeparator,
decimalSeparator: currency.decimalSeparator,
decimalScale: currency.minorUnit,
fixedDecimalScale: true,
prefix: currency.prefix,
suffix: currency.suffix,
isNumericString: true,
};
};
/**
* FormattedMonetaryAmount component.
*
* Takes a price and returns a formatted price using the NumberFormat component.
*/
const FormattedMonetaryAmount = ( {
className,
value: rawValue,
currency,
onValueChange,
displayType = 'text',
...props
}: FormattedMonetaryAmountProps ): ReactElement | null => {
const value =
typeof rawValue === 'string' ? parseInt( rawValue, 10 ) : rawValue;
if ( ! Number.isFinite( value ) ) {
return null;
}
const priceValue = value / 10 ** currency.minorUnit;
if ( ! Number.isFinite( priceValue ) ) {
return null;
}
const classes = classNames(
'wc-block-formatted-money-amount',
'wc-block-components-formatted-money-amount',
className
);
const numberFormatProps = {
...props,
...currencyToNumberFormat( currency ),
value: undefined,
currency: undefined,
onValueChange: undefined,
};
// Wrapper for NumberFormat onValueChange which handles subunit conversion.
const onValueChangeWrapper = onValueChange
? ( values: NumberFormatValues ) => {
const minorUnitValue =
( ( values.value as unknown ) as number ) *
10 ** currency.minorUnit;
onValueChange( minorUnitValue );
}
: () => {
/* not used */
};
return (
<NumberFormat
className={ classes }
displayType={ displayType }
{ ...numberFormatProps }
value={ priceValue }
onValueChange={ onValueChangeWrapper }
/>
);
};
export default FormattedMonetaryAmount;