Add types to packages directory (https://github.com/woocommerce/woocommerce-blocks/pull/3914)
* Add Typescript to Panel and Icon * Fix Icon component import * Convert packages/checkout/utils/validation/index to TypeScript * Convert checkout registry to TypeScript * Add return type to mustContain * Add TypeScript to Totals components from @woocommerce/blocks-checkout * Add TypeScript to @woocommerce/price-format * Use types from @woocommerce/type-defs when possible * Allow empty objects when loading * Fix formatting in payment-method-data-context.js * Add missing return types * Fix up price warnings * Fix more warnings in FormattedMonetaryAmount Co-authored-by: Raluca Stan <ralucastn@gmail.com> Co-authored-by: Thomas Roberts <thomas.roberts@automattic.com> Co-authored-by: Mike Jolley <mike.jolley@me.com>
This commit is contained in:
parent
ba16cf9b74
commit
0e1b1e3579
|
@ -33,20 +33,22 @@ const currencyToNumberFormat = ( currency ) => {
|
|||
* Takes a price and returns a formatted price using the NumberFormat component.
|
||||
*
|
||||
* @param {Object} props Component props.
|
||||
* @param {string} props.className CSS class used.
|
||||
* @param {string=} props.className CSS class used.
|
||||
* @param {number} props.value Value of money amount.
|
||||
* @param {Object} props.currency Currency configuration object.
|
||||
* @param {function():any} props.onValueChange Function to call when value changes.
|
||||
* @param {Object} props.props Rest of props passed into component.
|
||||
* @param {function():any=} props.onValueChange Function to call when value changes.
|
||||
* @param {string=} props.displayType Display type.
|
||||
* @param {Object=} props.props Rest of props passed into component.
|
||||
*/
|
||||
const FormattedMonetaryAmount = ( {
|
||||
className,
|
||||
className = '',
|
||||
value,
|
||||
currency,
|
||||
onValueChange,
|
||||
onValueChange = () => {},
|
||||
displayType = 'text',
|
||||
...props
|
||||
} ) => {
|
||||
if ( value === '-' ) {
|
||||
if ( ! Number.isFinite( value ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -62,7 +64,7 @@ const FormattedMonetaryAmount = ( {
|
|||
className
|
||||
);
|
||||
const numberFormatProps = {
|
||||
displayType: 'text',
|
||||
displayType,
|
||||
...props,
|
||||
...currencyToNumberFormat( currency ),
|
||||
value: undefined,
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*/
|
||||
import { Fragment } from '@wordpress/element';
|
||||
import classNames from 'classnames';
|
||||
import type { HTMLAttributes } from 'react';
|
||||
import type { ReactElement, HTMLAttributes } from 'react';
|
||||
|
||||
interface LabelProps {
|
||||
label?: string;
|
||||
|
@ -23,7 +23,7 @@ const Label = ( {
|
|||
screenReaderLabel,
|
||||
wrapperElement,
|
||||
wrapperProps = {},
|
||||
}: LabelProps ): JSX.Element => {
|
||||
}: LabelProps ): ReactElement => {
|
||||
let Wrapper;
|
||||
|
||||
const hasLabel = typeof label !== 'undefined' && label !== null;
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { cloneElement, isValidElement } from 'wordpress-element';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
function Icon( { srcElement, size = 24, ...props } ) {
|
||||
return (
|
||||
isValidElement( srcElement ) &&
|
||||
cloneElement( srcElement, {
|
||||
width: size,
|
||||
height: size,
|
||||
...props,
|
||||
} )
|
||||
);
|
||||
}
|
||||
|
||||
Icon.propTypes = {
|
||||
srcElement: PropTypes.element,
|
||||
size: PropTypes.number,
|
||||
};
|
||||
|
||||
export default Icon;
|
|
@ -0,0 +1,28 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { cloneElement, isValidElement } from 'wordpress-element';
|
||||
import type { ReactElement } from 'react';
|
||||
|
||||
interface IconProps {
|
||||
srcElement?: ReactElement;
|
||||
size?: number;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
function Icon( {
|
||||
srcElement,
|
||||
size = 24,
|
||||
...props
|
||||
}: IconProps ): ReactElement | null {
|
||||
if ( ! isValidElement( srcElement ) ) {
|
||||
return null;
|
||||
}
|
||||
return cloneElement( srcElement, {
|
||||
width: size,
|
||||
height: size,
|
||||
...props,
|
||||
} );
|
||||
}
|
||||
|
||||
export default Icon;
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"extends": "../../../tsconfig.json",
|
||||
"compilerOptions": {},
|
||||
"include": [ "." ],
|
||||
"exclude": [ "**/test/**" ]
|
||||
}
|
|
@ -2,8 +2,8 @@
|
|||
* External dependencies
|
||||
*/
|
||||
import { useState } from '@wordpress/element';
|
||||
import type { ReactChildren, ReactNode, ReactElement } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Icon, chevronUp, chevronDown } from '@woocommerce/icons';
|
||||
|
||||
/**
|
||||
|
@ -11,6 +11,15 @@ import { Icon, chevronUp, chevronDown } from '@woocommerce/icons';
|
|||
*/
|
||||
import './style.scss';
|
||||
|
||||
interface PanelProps {
|
||||
children?: ReactChildren;
|
||||
className?: string;
|
||||
initialOpen?: boolean;
|
||||
hasBorder?: boolean;
|
||||
title?: ReactNode;
|
||||
titleTag?: keyof JSX.IntrinsicElements;
|
||||
}
|
||||
|
||||
const Panel = ( {
|
||||
children,
|
||||
className,
|
||||
|
@ -18,8 +27,8 @@ const Panel = ( {
|
|||
hasBorder = false,
|
||||
title,
|
||||
titleTag: TitleTag = 'div',
|
||||
} ) => {
|
||||
const [ isOpen, setIsOpen ] = useState( initialOpen );
|
||||
}: PanelProps ): ReactElement => {
|
||||
const [ isOpen, setIsOpen ] = useState< boolean >( initialOpen );
|
||||
|
||||
return (
|
||||
<div
|
||||
|
@ -51,12 +60,4 @@ const Panel = ( {
|
|||
);
|
||||
};
|
||||
|
||||
Panel.propTypes = {
|
||||
className: PropTypes.string,
|
||||
hasBorder: PropTypes.bool,
|
||||
initialOpen: PropTypes.bool,
|
||||
title: PropTypes.node,
|
||||
titleTag: PropTypes.string,
|
||||
};
|
||||
|
||||
export default Panel;
|
|
@ -9,16 +9,30 @@ import { CURRENT_USER_IS_ADMIN } from '@woocommerce/block-settings';
|
|||
*/
|
||||
import { returnTrue } from '../';
|
||||
|
||||
let checkoutFilters = {};
|
||||
type CheckoutFilterFunction = < T >(
|
||||
label: T,
|
||||
extensions: Record< string, unknown >,
|
||||
args?: CheckoutFilterArguments
|
||||
) => T;
|
||||
|
||||
type CheckoutFilterArguments =
|
||||
| ( Record< string, unknown > & {
|
||||
context?: string;
|
||||
} )
|
||||
| null;
|
||||
|
||||
let checkoutFilters: Record<
|
||||
string,
|
||||
Record< string, CheckoutFilterFunction >
|
||||
> = {};
|
||||
|
||||
/**
|
||||
* Register filters for a specific extension.
|
||||
*
|
||||
* @param {string} namespace Name of the extension namespace.
|
||||
* @param {Object} filters Object of filters for that namespace. Each key of
|
||||
* the object is the name of a filter.
|
||||
*/
|
||||
export const __experimentalRegisterCheckoutFilters = ( namespace, filters ) => {
|
||||
export const __experimentalRegisterCheckoutFilters = (
|
||||
namespace: string,
|
||||
filters: Record< string, CheckoutFilterFunction >
|
||||
): void => {
|
||||
checkoutFilters = {
|
||||
...checkoutFilters,
|
||||
[ namespace ]: filters,
|
||||
|
@ -32,7 +46,7 @@ export const __experimentalRegisterCheckoutFilters = ( namespace, filters ) => {
|
|||
* @return {Function[]} Array of functions that are registered for that filter
|
||||
* name.
|
||||
*/
|
||||
const getCheckoutFilters = ( filterName ) => {
|
||||
const getCheckoutFilters = ( filterName: string ): CheckoutFilterFunction[] => {
|
||||
const namespaces = Object.keys( checkoutFilters );
|
||||
const filters = namespaces
|
||||
.map( ( namespace ) => checkoutFilters[ namespace ][ filterName ] )
|
||||
|
@ -42,18 +56,6 @@ const getCheckoutFilters = ( filterName ) => {
|
|||
|
||||
/**
|
||||
* Apply a filter.
|
||||
*
|
||||
* @param {Object} o Object of arguments.
|
||||
* @param {string} o.filterName Name of the filter to apply.
|
||||
* @param {any} o.defaultValue Default value to filter.
|
||||
* @param {Object} [o.extensions] Values extend to REST API response.
|
||||
* @param {any} [o.arg] Argument to pass to registered functions.
|
||||
* If several arguments need to be passed, use
|
||||
* an object.
|
||||
* @param {Function} [o.validation] Function that needs to return true when
|
||||
* the filtered value is passed in order for
|
||||
* the filter to be applied.
|
||||
* @return {any} Filtered value.
|
||||
*/
|
||||
export const __experimentalApplyCheckoutFilter = ( {
|
||||
filterName,
|
||||
|
@ -61,7 +63,18 @@ export const __experimentalApplyCheckoutFilter = ( {
|
|||
extensions,
|
||||
arg = null,
|
||||
validation = returnTrue,
|
||||
} ) => {
|
||||
}: {
|
||||
/** Name of the filter to apply. */
|
||||
filterName: string;
|
||||
/** Default value to filter. */
|
||||
defaultValue: unknown;
|
||||
/** Values extend to REST API response. */
|
||||
extensions: Record< string, unknown >;
|
||||
/** Object containing arguments for the filter function. */
|
||||
arg: CheckoutFilterArguments;
|
||||
/** Function that needs to return true when the filtered value is passed in order for the filter to be applied. */
|
||||
validation: ( value: unknown ) => boolean;
|
||||
} ): unknown => {
|
||||
return useMemo( () => {
|
||||
const filters = getCheckoutFilters( filterName );
|
||||
|
|
@ -4,14 +4,26 @@
|
|||
import classnames from 'classnames';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { DISPLAY_CART_PRICES_INCLUDING_TAX } from '@woocommerce/block-settings';
|
||||
import PropTypes from 'prop-types';
|
||||
import type { Currency } from '@woocommerce/price-format';
|
||||
import type { CartFeeItem } from '@woocommerce/type-defs/cart';
|
||||
import type { ReactElement } from 'react';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import TotalsItem from '../item';
|
||||
|
||||
const TotalsFees = ( { currency, cartFees, className } ) => {
|
||||
interface TotalsFeesProps {
|
||||
currency: Currency;
|
||||
cartFees: CartFeeItem[];
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const TotalsFees = ( {
|
||||
currency,
|
||||
cartFees,
|
||||
className,
|
||||
}: TotalsFeesProps ): ReactElement | null => {
|
||||
return (
|
||||
<>
|
||||
{ cartFees.map( ( { id, name, totals } ) => {
|
||||
|
@ -46,10 +58,4 @@ const TotalsFees = ( { currency, cartFees, className } ) => {
|
|||
);
|
||||
};
|
||||
|
||||
TotalsFees.propTypes = {
|
||||
currency: PropTypes.object.isRequired,
|
||||
cartFees: PropTypes.array.isRequired,
|
||||
className: PropTypes.string,
|
||||
};
|
||||
|
||||
export default TotalsFees;
|
|
@ -1,52 +0,0 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import PropTypes from 'prop-types';
|
||||
import classnames from 'classnames';
|
||||
import { isValidElement } from '@wordpress/element';
|
||||
import FormattedMonetaryAmount from '@woocommerce/base-components/formatted-monetary-amount';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import './style.scss';
|
||||
|
||||
const TotalsItem = ( { className, currency, label, value, description } ) => {
|
||||
return (
|
||||
<div
|
||||
className={ classnames(
|
||||
'wc-block-components-totals-item',
|
||||
className
|
||||
) }
|
||||
>
|
||||
<span className="wc-block-components-totals-item__label">
|
||||
{ label }
|
||||
</span>
|
||||
{ isValidElement( value ) ? (
|
||||
<div className="wc-block-components-totals-item__value">
|
||||
{ value }
|
||||
</div>
|
||||
) : (
|
||||
<FormattedMonetaryAmount
|
||||
className="wc-block-components-totals-item__value"
|
||||
currency={ currency }
|
||||
displayType="text"
|
||||
value={ value }
|
||||
/>
|
||||
) }
|
||||
<div className="wc-block-components-totals-item__description">
|
||||
{ description }
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
TotalsItem.propTypes = {
|
||||
currency: PropTypes.object,
|
||||
label: PropTypes.string.isRequired,
|
||||
value: PropTypes.oneOfType( [ PropTypes.number, PropTypes.node ] ),
|
||||
className: PropTypes.string,
|
||||
description: PropTypes.node,
|
||||
};
|
||||
|
||||
export default TotalsItem;
|
|
@ -0,0 +1,71 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import classnames from 'classnames';
|
||||
import { isValidElement } from '@wordpress/element';
|
||||
import FormattedMonetaryAmount from '@woocommerce/base-components/formatted-monetary-amount';
|
||||
import type { ReactElement, ReactNode } from 'react';
|
||||
import type { Currency } from '@woocommerce/price-format';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import './style.scss';
|
||||
|
||||
interface TotalsItemProps {
|
||||
className?: string;
|
||||
currency: Currency;
|
||||
label: string;
|
||||
// Value may be a number, or react node. Numbers are passed to FormattedMonetaryAmount.
|
||||
value: number | ReactNode;
|
||||
description?: ReactNode;
|
||||
}
|
||||
|
||||
const TotalsItemValue = ( {
|
||||
value,
|
||||
currency,
|
||||
}: Partial< TotalsItemProps > ): ReactElement | null => {
|
||||
if ( isValidElement( value ) ) {
|
||||
return (
|
||||
<div className="wc-block-components-totals-item__value">
|
||||
{ value }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return Number.isFinite( value ) ? (
|
||||
<FormattedMonetaryAmount
|
||||
className="wc-block-components-totals-item__value"
|
||||
currency={ currency || {} }
|
||||
displayType="text"
|
||||
value={ value as number }
|
||||
/>
|
||||
) : null;
|
||||
};
|
||||
|
||||
const TotalsItem = ( {
|
||||
className,
|
||||
currency,
|
||||
label,
|
||||
value,
|
||||
description,
|
||||
}: TotalsItemProps ): ReactElement => {
|
||||
return (
|
||||
<div
|
||||
className={ classnames(
|
||||
'wc-block-components-totals-item',
|
||||
className
|
||||
) }
|
||||
>
|
||||
<span className="wc-block-components-totals-item__label">
|
||||
{ label }
|
||||
</span>
|
||||
<TotalsItemValue value={ value } currency={ currency } />
|
||||
<div className="wc-block-components-totals-item__description">
|
||||
{ description }
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default TotalsItem;
|
|
@ -3,14 +3,32 @@
|
|||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { DISPLAY_CART_PRICES_INCLUDING_TAX } from '@woocommerce/block-settings';
|
||||
import PropTypes from 'prop-types';
|
||||
import type { Currency } from '@woocommerce/price-format';
|
||||
import type { ReactElement } from 'react';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import TotalsItem from '../item';
|
||||
|
||||
const Subtotal = ( { currency, values, className } ) => {
|
||||
interface Values {
|
||||
// eslint-disable-next-line camelcase
|
||||
total_items: string;
|
||||
// eslint-disable-next-line camelcase
|
||||
total_items_tax: string;
|
||||
}
|
||||
|
||||
interface SubtotalProps {
|
||||
className?: string;
|
||||
currency: Currency;
|
||||
values: Values | Record< string, never >;
|
||||
}
|
||||
|
||||
const Subtotal = ( {
|
||||
currency,
|
||||
values,
|
||||
className,
|
||||
}: SubtotalProps ): ReactElement => {
|
||||
const { total_items: totalItems, total_items_tax: totalItemsTax } = values;
|
||||
const itemsValue = parseInt( totalItems, 10 );
|
||||
const itemsTaxValue = parseInt( totalItemsTax, 10 );
|
||||
|
@ -29,13 +47,4 @@ const Subtotal = ( { currency, values, className } ) => {
|
|||
);
|
||||
};
|
||||
|
||||
Subtotal.propTypes = {
|
||||
currency: PropTypes.object.isRequired,
|
||||
values: PropTypes.shape( {
|
||||
total_items: PropTypes.string,
|
||||
total_items_tax: PropTypes.string,
|
||||
} ).isRequired,
|
||||
className: PropTypes.string,
|
||||
};
|
||||
|
||||
export default Subtotal;
|
|
@ -3,18 +3,37 @@
|
|||
*/
|
||||
import classnames from 'classnames';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import PropTypes from 'prop-types';
|
||||
import {
|
||||
TAXES_ENABLED,
|
||||
DISPLAY_ITEMIZED_TAXES,
|
||||
} from '@woocommerce/block-settings';
|
||||
import type { Currency } from '@woocommerce/price-format';
|
||||
import type { CartTotalsTaxLineItem } from '@woocommerce/type-defs/cart';
|
||||
import { ReactElement } from 'react';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import TotalsItem from '../item';
|
||||
|
||||
const TotalsTaxes = ( { currency, values, className } ) => {
|
||||
interface Values {
|
||||
// eslint-disable-next-line camelcase
|
||||
tax_lines: CartTotalsTaxLineItem[];
|
||||
// eslint-disable-next-line camelcase
|
||||
total_tax: string;
|
||||
}
|
||||
|
||||
interface TotalsTaxesProps {
|
||||
className?: string;
|
||||
currency: Currency;
|
||||
values: Values | Record< string, never >;
|
||||
}
|
||||
|
||||
const TotalsTaxes = ( {
|
||||
currency,
|
||||
values,
|
||||
className,
|
||||
}: TotalsTaxesProps ): ReactElement | null => {
|
||||
const { total_tax: totalTax, tax_lines: taxLines } = values;
|
||||
|
||||
if ( ! TAXES_ENABLED ) {
|
||||
|
@ -50,12 +69,4 @@ const TotalsTaxes = ( { currency, values, className } ) => {
|
|||
);
|
||||
};
|
||||
|
||||
TotalsTaxes.propTypes = {
|
||||
currency: PropTypes.object.isRequired,
|
||||
values: PropTypes.shape( {
|
||||
total_tax: PropTypes.string,
|
||||
} ).isRequired,
|
||||
className: PropTypes.string,
|
||||
};
|
||||
|
||||
export default TotalsTaxes;
|
|
@ -5,12 +5,8 @@ import { __, sprintf } from '@wordpress/i18n';
|
|||
|
||||
/**
|
||||
* Checks if value passed is a string, throws an error if not.
|
||||
*
|
||||
* @param {string} value Value to be validated.
|
||||
*
|
||||
* @return {Error|true} Error if value is not string, true otherwise.
|
||||
*/
|
||||
export const mustBeString = ( value ) => {
|
||||
export const mustBeString = ( value: unknown ): true | Error => {
|
||||
if ( typeof value !== 'string' ) {
|
||||
throw Error(
|
||||
sprintf(
|
||||
|
@ -27,14 +23,9 @@ export const mustBeString = ( value ) => {
|
|||
};
|
||||
|
||||
/**
|
||||
* Checks if value passed contain passed label
|
||||
*
|
||||
* @param {string} value Value to be validated.
|
||||
* @param {string} label Label to be searched for.
|
||||
*
|
||||
* @return {Error|true} Error if value contains label, true otherwise.
|
||||
* Checks if value passed contain passed label.
|
||||
*/
|
||||
export const mustContain = ( value, label ) => {
|
||||
export const mustContain = ( value: string, label: string ): true | Error => {
|
||||
if ( ! value.includes( label ) ) {
|
||||
throw Error(
|
||||
sprintf(
|
||||
|
@ -55,8 +46,5 @@ export const mustContain = ( value, label ) => {
|
|||
* A function that always return true.
|
||||
* We need to have a single instance of this function so it doesn't
|
||||
* invalidate our memo comparison.
|
||||
*
|
||||
*
|
||||
* @return {true} Returns true.
|
||||
*/
|
||||
export const returnTrue = () => true;
|
||||
export const returnTrue = (): true => true;
|
|
@ -1 +1,2 @@
|
|||
export * from './utils';
|
||||
export * from './types';
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
export interface Currency {
|
||||
code: string;
|
||||
decimalSeparator: string;
|
||||
minorUnit: number;
|
||||
prefix: string;
|
||||
suffix: string;
|
||||
symbol: string;
|
||||
thousandSeparator: string;
|
||||
}
|
|
@ -2,14 +2,24 @@
|
|||
* External dependencies
|
||||
*/
|
||||
import { CURRENCY } from '@woocommerce/settings';
|
||||
import type { CurrencyResponseInfo } from '@woocommerce/type-defs/cart-response';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import type { Currency } from '../types';
|
||||
|
||||
type SymbolPosition = 'left' | 'left_space' | 'right' | 'right_space';
|
||||
|
||||
/**
|
||||
* Get currency prefix.
|
||||
*
|
||||
* @param {string} symbol Currency symbol.
|
||||
* @param {string} symbolPosition Position of currency symbol from settings.
|
||||
*/
|
||||
const getPrefix = ( symbol, symbolPosition ) => {
|
||||
const getPrefix = (
|
||||
// Currency symbol.
|
||||
symbol: string,
|
||||
// Position of currency symbol from settings.
|
||||
symbolPosition: SymbolPosition
|
||||
): string => {
|
||||
const prefixes = {
|
||||
left: symbol,
|
||||
left_space: ' ' + symbol,
|
||||
|
@ -21,11 +31,13 @@ const getPrefix = ( symbol, symbolPosition ) => {
|
|||
|
||||
/**
|
||||
* Get currency suffix.
|
||||
*
|
||||
* @param {string} symbol Currency symbol.
|
||||
* @param {string} symbolPosition Position of currency symbol from settings.
|
||||
*/
|
||||
const getSuffix = ( symbol, symbolPosition ) => {
|
||||
const getSuffix = (
|
||||
// Currency symbol.
|
||||
symbol: string,
|
||||
// Position of currency symbol from settings.
|
||||
symbolPosition: SymbolPosition
|
||||
): string => {
|
||||
const suffixes = {
|
||||
left: '',
|
||||
left_space: '',
|
||||
|
@ -38,23 +50,29 @@ const getSuffix = ( symbol, symbolPosition ) => {
|
|||
/**
|
||||
* Currency information in normalized format from server settings.
|
||||
*/
|
||||
const siteCurrencySettings = {
|
||||
const siteCurrencySettings: Currency = {
|
||||
code: CURRENCY.code,
|
||||
symbol: CURRENCY.symbol,
|
||||
thousandSeparator: CURRENCY.thousandSeparator,
|
||||
decimalSeparator: CURRENCY.decimalSeparator,
|
||||
minorUnit: CURRENCY.precision,
|
||||
prefix: getPrefix( CURRENCY.symbol, CURRENCY.symbolPosition ),
|
||||
suffix: getSuffix( CURRENCY.symbol, CURRENCY.symbolPosition ),
|
||||
prefix: getPrefix(
|
||||
CURRENCY.symbol,
|
||||
CURRENCY.symbolPosition as SymbolPosition
|
||||
),
|
||||
suffix: getSuffix(
|
||||
CURRENCY.symbol,
|
||||
CURRENCY.symbolPosition as SymbolPosition
|
||||
),
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets currency information in normalized format from an API response or the server.
|
||||
*
|
||||
* @param {Object} currencyData Currency data object, for example an API response containing currency formatting data.
|
||||
* @return {Object} Normalized currency info.
|
||||
*/
|
||||
export const getCurrencyFromPriceResponse = ( currencyData ) => {
|
||||
export const getCurrencyFromPriceResponse = (
|
||||
// Currency data object, for example an API response containing currency formatting data.
|
||||
currencyData: CurrencyResponseInfo | Record< string, never >
|
||||
): Currency => {
|
||||
if ( ! currencyData || typeof currencyData !== 'object' ) {
|
||||
return siteCurrencySettings;
|
||||
}
|
||||
|
@ -84,11 +102,10 @@ export const getCurrencyFromPriceResponse = ( currencyData ) => {
|
|||
|
||||
/**
|
||||
* Gets currency information in normalized format, allowing overrides.
|
||||
*
|
||||
* @param {Object} currencyData Currency data object.
|
||||
* @return {Object} Normalized currency info.
|
||||
*/
|
||||
export const getCurrency = ( currencyData = {} ) => {
|
||||
export const getCurrency = (
|
||||
currencyData: Partial< Currency > = {}
|
||||
): Currency => {
|
||||
return {
|
||||
...siteCurrencySettings,
|
||||
...currencyData,
|
||||
|
@ -98,24 +115,27 @@ export const getCurrency = ( currencyData = {} ) => {
|
|||
/**
|
||||
* Format a price, provided using the smallest unit of the currency, as a
|
||||
* decimal complete with currency symbols using current store settings.
|
||||
*
|
||||
* @param {number|string} price Price in minor unit, e.g. cents.
|
||||
* @param {Object} currencyData Currency data object.
|
||||
*/
|
||||
export const formatPrice = ( price, currencyData ) => {
|
||||
export const formatPrice = (
|
||||
// Price in minor unit, e.g. cents.
|
||||
price: number | string,
|
||||
currencyData: Currency
|
||||
): string => {
|
||||
if ( price === '' || price === undefined ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const priceInt = parseInt( price, 10 );
|
||||
const priceInt: number =
|
||||
typeof price === 'number' ? price : parseInt( price, 10 );
|
||||
|
||||
if ( ! Number.isFinite( priceInt ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const currency = getCurrency( currencyData );
|
||||
const formattedPrice = priceInt / 10 ** currency.minorUnit;
|
||||
const formattedValue = currency.prefix + formattedPrice + currency.suffix;
|
||||
const currency: Currency = getCurrency( currencyData );
|
||||
const formattedPrice: number = priceInt / 10 ** currency.minorUnit;
|
||||
const formattedValue: string =
|
||||
currency.prefix + formattedPrice + currency.suffix;
|
||||
|
||||
// This uses a textarea to magically decode HTML currency symbols.
|
||||
const txt = document.createElement( 'textarea' );
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"extends": "../tsconfig.base.json",
|
||||
"compilerOptions": {},
|
||||
"include": [
|
||||
".",
|
||||
"../assets/js/icons",
|
||||
"../assets/js/settings",
|
||||
"../assets/js/type-defs",
|
||||
"../assets/js/base/components"
|
||||
],
|
||||
"exclude": [ "**/test/**" ]
|
||||
}
|
|
@ -46,6 +46,7 @@
|
|||
"@woocommerce/icons": [ "assets/js/icons" ],
|
||||
"@woocommerce/resource-previews": [ "assets/js/previews" ],
|
||||
"@woocommerce/knobs": [ "storybook/knobs" ],
|
||||
"@woocommerce/price-format": [ "packages/prices" ],
|
||||
"@woocommerce/settings": [ "assets/js/settings/shared" ],
|
||||
"@woocommerce/shared-context": [ "assets/js/shared/context" ],
|
||||
"@woocommerce/type-defs/*": [ "assets/js/type-defs/*" ],
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
{
|
||||
"extends": "./tsconfig.base.json",
|
||||
"include": [ "./assets/js/**/*" ],
|
||||
"exclude": [ "./assets/js/data" ],
|
||||
"references": [
|
||||
{ "path": "./assets/js/data" },
|
||||
{ "path": "/assets/js/base/components" }
|
||||
]
|
||||
"extends": "./tsconfig.base.json",
|
||||
"include": [ "./assets/js/**/*" ],
|
||||
"exclude": [ "./assets/js/data" ],
|
||||
"references": [
|
||||
{ "path": "./assets/js/data" },
|
||||
{ "path": "./assets/js/icons" },
|
||||
{ "path": "./assets/js/base/components" },
|
||||
{ "path": "./packages" }
|
||||
]
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue