* Convert ReturnToCartButton component to TS

* Convert FormStep component to TS

* Remoe proptypes from FormStep and AddressFormn components

* Converted order-summary component to TS

* Convert product-price component to TS

* alias path to type-defs

* Fix errors

* Changes from Thomas feedback

* Change minPrice maxPrice logic to be more robust

* Changed types from inline to interface in ReturnToCartButton component

* Check for string type in order-summary-item

* add missing isString check
This commit is contained in:
Alex Florisca 2021-11-26 17:03:12 +00:00 committed by GitHub
parent c067e990b4
commit d9bf1ba94c
9 changed files with 100 additions and 88 deletions

View File

@ -1,7 +1,6 @@
/**
* External dependencies
*/
import PropTypes from 'prop-types';
import { ValidatedTextInput } from '@woocommerce/base-components/text-input';
import {
BillingCountryInput,
@ -241,14 +240,4 @@ const AddressForm = ( {
);
};
AddressForm.propTypes = {
onChange: PropTypes.func.isRequired,
values: PropTypes.object.isRequired,
fields: PropTypes.arrayOf(
PropTypes.oneOf( Object.keys( defaultAddressFields ) )
),
fieldConfig: PropTypes.object,
type: PropTypes.oneOf( [ 'billing', 'shipping' ] ),
};
export default withInstanceId( AddressForm );

View File

@ -2,7 +2,6 @@
* External dependencies
*/
import classnames from 'classnames';
import PropTypes from 'prop-types';
import Title from '@woocommerce/base-components/title';
/**
@ -10,7 +9,12 @@ import Title from '@woocommerce/base-components/title';
*/
import './style.scss';
const StepHeading = ( { title, stepHeadingContent } ) => (
interface StepHeadingProps {
title: string;
stepHeadingContent?: JSX.Element;
}
const StepHeading = ( { title, stepHeadingContent }: StepHeadingProps ) => (
<div className="wc-block-components-checkout-step__heading">
<Title
aria-hidden="true"
@ -27,6 +31,18 @@ const StepHeading = ( { title, stepHeadingContent } ) => (
</div>
);
interface FormStepProps {
id?: string;
className?: string;
title?: string;
legend?: string;
description?: string;
children?: React.ReactNode;
disabled?: boolean;
showStepNumber?: boolean;
stepHeadingContent?: () => JSX.Element | undefined;
}
const FormStep = ( {
id,
className,
@ -36,8 +52,8 @@ const FormStep = ( {
children,
disabled = false,
showStepNumber = true,
stepHeadingContent = () => {},
} ) => {
stepHeadingContent = () => undefined,
}: FormStepProps ): JSX.Element => {
// If the form step doesn't have a legend or title, render a <div> instead
// of a <fieldset>.
const Element = legend || title ? 'fieldset' : 'div';
@ -80,16 +96,4 @@ const FormStep = ( {
);
};
FormStep.propTypes = {
id: PropTypes.string,
className: PropTypes.string,
title: PropTypes.string,
description: PropTypes.string,
children: PropTypes.node,
showStepNumber: PropTypes.bool,
stepHeadingContent: PropTypes.func,
disabled: PropTypes.bool,
legend: PropTypes.string,
};
export default FormStep;

View File

@ -2,17 +2,23 @@
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import PropTypes from 'prop-types';
import { useContainerWidthContext } from '@woocommerce/base-context';
import { Panel } from '@woocommerce/blocks-checkout';
import type { CartItem } from '@woocommerce/types';
/**
* Internal dependencies
*/
import OrderSummaryItem from './order-summary-item.js';
import OrderSummaryItem from './order-summary-item';
import './style.scss';
const OrderSummary = ( { cartItems = [] } ) => {
interface OrderSummaryProps {
cartItems: CartItem[];
}
const OrderSummary = ( {
cartItems = [],
}: OrderSummaryProps ): null | JSX.Element => {
const { isLarge, hasContainerWidth } = useContainerWidthContext();
if ( ! hasContainerWidth ) {
@ -45,10 +51,4 @@ const OrderSummary = ( { cartItems = [] } ) => {
);
};
OrderSummary.propTypes = {
cartItems: PropTypes.arrayOf(
PropTypes.shape( { key: PropTypes.string.isRequired } )
),
};
export default OrderSummary;

View File

@ -14,11 +14,11 @@ import {
__experimentalApplyCheckoutFilter,
mustContain,
} from '@woocommerce/blocks-checkout';
import PropTypes from 'prop-types';
import Dinero from 'dinero.js';
import { getSetting } from '@woocommerce/settings';
import { useMemo } from '@wordpress/element';
import { useStoreCart } from '@woocommerce/base-context/hooks';
import { CartItem, isString } from '@woocommerce/types';
/**
* Internal dependencies
@ -28,9 +28,14 @@ import ProductImage from '../product-image';
import ProductLowStockBadge from '../product-low-stock-badge';
import ProductMetadata from '../product-metadata';
const productPriceValidation = ( value ) => mustContain( value, '<price/>' );
const productPriceValidation = ( value: string ): true | never =>
mustContain( value, '<price/>' );
const OrderSummaryItem = ( { cartItem } ) => {
interface OrderSummaryProps {
cartItem: CartItem;
}
const OrderSummaryItem = ( { cartItem }: OrderSummaryProps ): JSX.Element => {
const {
images,
low_stock_remaining: lowStockRemaining,
@ -72,13 +77,17 @@ const OrderSummaryItem = ( { cartItem } ) => {
const regularPriceSingle = Dinero( {
amount: parseInt( prices.raw_prices.regular_price, 10 ),
precision: parseInt( prices.raw_prices.precision, 10 ),
precision: isString( prices.raw_prices.precision )
? parseInt( prices.raw_prices.precision, 10 )
: prices.raw_prices.precision,
} )
.convertPrecision( priceCurrency.minorUnit )
.getAmount();
const priceSingle = Dinero( {
amount: parseInt( prices.raw_prices.price, 10 ),
precision: parseInt( prices.raw_prices.precision, 10 ),
precision: isString( prices.raw_prices.precision )
? parseInt( prices.raw_prices.precision, 10 )
: prices.raw_prices.precision,
} )
.convertPrecision( priceCurrency.minorUnit )
.getAmount();
@ -126,7 +135,7 @@ const OrderSummaryItem = ( { cartItem } ) => {
<div className="wc-block-components-order-summary-item__image">
<div className="wc-block-components-order-summary-item__quantity">
<Label
label={ quantity }
label={ quantity.toString() }
screenReaderLabel={ sprintf(
/* translators: %d number of products of the same type in the cart */
_n(
@ -203,20 +212,4 @@ const OrderSummaryItem = ( { cartItem } ) => {
);
};
OrderSummaryItem.propTypes = {
cartItems: PropTypes.shape( {
images: PropTypes.array,
low_stock_remaining: PropTypes.number,
name: PropTypes.string.isRequired,
permalink: PropTypes.string,
prices: PropTypes.shape( {
price: PropTypes.string,
regular_price: PropTypes.string,
} ),
quantity: PropTypes.number,
summary: PropTypes.string,
variation: PropTypes.array,
} ),
};
export default OrderSummaryItem;

View File

@ -10,7 +10,13 @@ import { Icon, arrowBack } from '@woocommerce/icons';
*/
import './style.scss';
const ReturnToCartButton = ( { link } ) => {
interface ReturnToCartButtonProps {
link?: string;
}
const ReturnToCartButton = ( {
link,
}: ReturnToCartButtonProps ): JSX.Element => {
return (
<a
href={ link || CART_URL }

View File

@ -6,8 +6,8 @@ import NumberFormat, {
NumberFormatProps,
} from 'react-number-format';
import classNames from 'classnames';
import type { Currency } from '@woocommerce/price-format';
import type { ReactElement } from 'react';
import type { Currency } from '@woocommerce/types';
/**
* Internal dependencies
@ -20,6 +20,8 @@ interface FormattedMonetaryAmountProps {
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.
style?: React.CSSProperties;
renderText?: ( value: string ) => JSX.Element;
}
/**

View File

@ -4,22 +4,30 @@
import { __, sprintf } from '@wordpress/i18n';
import FormattedMonetaryAmount from '@woocommerce/base-components/formatted-monetary-amount';
import classNames from 'classnames';
import PropTypes from 'prop-types';
import { formatPrice } from '@woocommerce/price-format';
import { createInterpolateElement } from '@wordpress/element';
import type { Currency } from '@woocommerce/types';
/**
* Internal dependencies
*/
import './style.scss';
interface PriceRangeProps {
currency: Currency | Record< string, never >; // Currency configuration object;
maxPrice: string | number;
minPrice: string | number;
priceClassName?: string;
priceStyle?: React.CSSProperties;
}
const PriceRange = ( {
currency,
maxPrice,
minPrice,
priceClassName,
priceStyle,
} ) => {
}: PriceRangeProps ) => {
return (
<>
<span className="screen-reader-text">
@ -58,6 +66,16 @@ const PriceRange = ( {
);
};
interface SalePriceProps {
currency: Currency | Record< string, never >; // Currency configuration object.
regularPriceClassName?: string;
regularPriceStyle?: Record< string, string >;
regularPrice: number | string;
priceClassName?: string;
priceStyle?: Record< string, string >;
price: number | string;
}
const SalePrice = ( {
currency,
regularPriceClassName,
@ -66,7 +84,7 @@ const SalePrice = ( {
priceClassName,
priceStyle,
price,
} ) => {
}: SalePriceProps ) => {
return (
<>
<span className="screen-reader-text">
@ -110,20 +128,35 @@ const SalePrice = ( {
);
};
interface ProductPriceProps {
align?: 'left' | 'center' | 'right';
className?: string;
currency: Currency | Record< string, never >; // Currency configuration object.
format: string;
price: number | string;
priceClassName?: string;
priceStyle?: Record< string, string >;
maxPrice?: number | string;
minPrice?: number | string;
regularPrice?: number | string;
regularPriceClassName?: string;
regularPriceStyle?: Record< string, string >;
}
const ProductPrice = ( {
align,
className,
currency,
format = '<price/>',
maxPrice = null,
minPrice = null,
price = null,
maxPrice,
minPrice,
price,
priceClassName,
priceStyle,
regularPrice,
regularPriceClassName,
regularPriceStyle,
} ) => {
}: ProductPriceProps ): JSX.Element => {
const wrapperClassName = classNames(
className,
'price',
@ -161,7 +194,7 @@ const ProductPrice = ( {
regularPriceStyle={ regularPriceStyle }
/>
);
} else if ( minPrice !== null && maxPrice !== null ) {
} else if ( minPrice !== undefined && maxPrice !== undefined ) {
priceComponent = (
<PriceRange
currency={ currency }
@ -171,7 +204,7 @@ const ProductPrice = ( {
priceStyle={ priceStyle }
/>
);
} else if ( price !== null ) {
} else if ( price ) {
priceComponent = (
<FormattedMonetaryAmount
className={ classNames(
@ -194,21 +227,4 @@ const ProductPrice = ( {
);
};
ProductPrice.propTypes = {
align: PropTypes.oneOf( [ 'left', 'center', 'right' ] ),
className: PropTypes.string,
currency: PropTypes.object,
format: PropTypes.string,
price: PropTypes.oneOfType( [ PropTypes.number, PropTypes.string ] ),
priceClassName: PropTypes.string,
priceStyle: PropTypes.object,
// Range price props
maxPrice: PropTypes.oneOfType( [ PropTypes.number, PropTypes.string ] ),
minPrice: PropTypes.oneOfType( [ PropTypes.number, PropTypes.string ] ),
// On sale price props
regularPrice: PropTypes.oneOfType( [ PropTypes.number, PropTypes.string ] ),
regularPriceClassName: PropTypes.string,
regularPriceStyle: PropTypes.object,
};
export default ProductPrice;

View File

@ -7,6 +7,8 @@ import {
ExtensionsData,
} from './cart-response';
import { ProductResponseItemData } from './product-response';
export interface CurrencyInfo {
currency_code: string;
currency_symbol: string;
@ -131,7 +133,7 @@ export interface CartItem {
prices: CartItemPrices;
totals: CartItemTotals;
extensions: ExtensionsData;
item_data: Record< string, unknown >[];
item_data: ProductResponseItemData[];
}
export interface CartTotalsTaxLineItem {

View File

@ -9,7 +9,7 @@ import { __, sprintf } from '@wordpress/i18n';
export const mustContain = (
value: string,
requiredValue: string
): true | Error => {
): true | never => {
if ( ! value.includes( requiredValue ) ) {
throw Error(
sprintf(