diff --git a/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/totals/coupon/index.js b/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/totals/coupon/index.tsx similarity index 84% rename from plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/totals/coupon/index.js rename to plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/totals/coupon/index.tsx index f8c6d51c19e..2e9b23d5cc8 100644 --- a/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/totals/coupon/index.js +++ b/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/totals/coupon/index.tsx @@ -7,7 +7,6 @@ import Button from '@woocommerce/base-components/button'; import { ValidatedTextInput } from '@woocommerce/base-components/text-input'; import Label from '@woocommerce/base-components/label'; import LoadingMask from '@woocommerce/base-components/loading-mask'; -import PropTypes from 'prop-types'; import { withInstanceId } from '@wordpress/compose'; import { ValidationInputError, @@ -20,12 +19,31 @@ import { Panel } from '@woocommerce/blocks-checkout'; */ import './style.scss'; -const TotalsCoupon = ( { +export interface TotalsCouponProps { + /** + * Instance id of the input + */ + instanceId: string; + /** + * Whether the component is in a loading state + */ + isLoading?: boolean; + /** + * Whether the component's parent panel will begin in an open state + */ + initialOpen?: boolean; + /** + * Submit handler + */ + onSubmit?: ( couponValue: string ) => void; +} + +export const TotalsCoupon = ( { instanceId, isLoading = false, initialOpen = false, - onSubmit = () => {}, -} ) => { + onSubmit = () => void 0, +}: TotalsCouponProps ): JSX.Element => { const [ couponValue, setCouponValue ] = useState( '' ); const currentIsLoading = useRef( false ); const { getValidationError, getValidationErrorId } = useValidationContext(); @@ -94,7 +112,9 @@ const TotalsCoupon = ( { className="wc-block-components-totals-coupon__button" disabled={ isLoading || ! couponValue } showSpinner={ isLoading } - onClick={ ( e ) => { + onClick={ ( + e: React.MouseEvent< HTMLElement, 'click' > + ) => { e.preventDefault(); onSubmit( couponValue ); } } @@ -113,9 +133,4 @@ const TotalsCoupon = ( { ); }; -TotalsCoupon.propTypes = { - onSubmit: PropTypes.func, - isLoading: PropTypes.bool, -}; - export default withInstanceId( TotalsCoupon ); diff --git a/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/totals/coupon/stories/index.js b/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/totals/coupon/stories/index.js deleted file mode 100644 index a747122eeeb..00000000000 --- a/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/totals/coupon/stories/index.js +++ /dev/null @@ -1,47 +0,0 @@ -/** - * External dependencies - */ -import { text, boolean } from '@storybook/addon-knobs'; -import { - useValidationContext, - ValidationContextProvider, -} from '@woocommerce/base-context'; - -/** - * Internal dependencies - */ -import TotalsCoupon from '../'; - -export default { - title: - 'WooCommerce Blocks/@base-components/cart-checkout/totals/TotalsCoupon', - component: TotalsCoupon, -}; - -const StoryComponent = ( { validCoupon, isLoading, invalidCouponText } ) => { - const { setValidationErrors } = useValidationContext(); - const onSubmit = ( coupon ) => { - if ( coupon !== validCoupon ) { - setValidationErrors( { coupon: invalidCouponText } ); - } - }; - return ; -}; - -export const Default = () => { - const validCoupon = text( 'A valid coupon code', 'validcoupon' ); - const invalidCouponText = text( - 'Error message for invalid code', - 'Invalid coupon code.' - ); - const isLoading = boolean( 'Toggle isLoading state', false ); - return ( - - - - ); -}; diff --git a/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/totals/coupon/stories/index.tsx b/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/totals/coupon/stories/index.tsx new file mode 100644 index 00000000000..cc4827e1bb5 --- /dev/null +++ b/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/totals/coupon/stories/index.tsx @@ -0,0 +1,70 @@ +/** + * External dependencies + */ +import { useArgs } from '@storybook/client-api'; +import { Story, Meta } from '@storybook/react'; +import { + useValidationContext, + ValidationContextProvider, +} from '@woocommerce/base-context'; +import { INTERACTION_TIMEOUT } from '@woocommerce/storybook-controls'; + +/** + * Internal dependencies + */ +import { TotalsCoupon, TotalsCouponProps } from '..'; + +export default { + title: 'WooCommerce Blocks/@base-components/cart-checkout/totals/Coupon', + component: TotalsCoupon, + args: { + initialOpen: true, + }, +} as Meta< TotalsCouponProps >; + +const INVALID_COUPON_ERROR = { + hidden: false, + message: 'Invalid coupon code', +}; + +const Template: Story< TotalsCouponProps > = ( args ) => { + const [ {}, setArgs ] = useArgs(); + + const onSubmit = ( code: string ) => { + args.onSubmit?.( code ); + setArgs( { isLoading: true } ); + + setTimeout( + () => setArgs( { isLoading: false } ), + INTERACTION_TIMEOUT + ); + }; + + return ; +}; + +export const Default = Template.bind( {} ); +Default.args = {}; + +export const LoadingState = Template.bind( {} ); +LoadingState.args = { + isLoading: true, +}; + +export const ErrorState: Story< TotalsCouponProps > = ( args ) => { + const { setValidationErrors } = useValidationContext(); + + setValidationErrors( { coupon: INVALID_COUPON_ERROR } ); + + return ; +}; + +ErrorState.decorators = [ + ( StoryComponent ) => { + return ( + + + + ); + }, +]; diff --git a/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/totals/discount/index.js b/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/totals/discount/index.tsx similarity index 83% rename from plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/totals/discount/index.js rename to plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/totals/discount/index.tsx index 969c95352ba..3e87e766462 100644 --- a/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/totals/discount/index.js +++ b/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/totals/discount/index.tsx @@ -4,18 +4,37 @@ import { __, sprintf } from '@wordpress/i18n'; import LoadingMask from '@woocommerce/base-components/loading-mask'; import { RemovableChip } from '@woocommerce/base-components/chip'; -import PropTypes from 'prop-types'; import { __experimentalApplyCheckoutFilter, TotalsItem, } from '@woocommerce/blocks-checkout'; import { getSetting } from '@woocommerce/settings'; +import { + CartResponseCouponItemWithLabel, + CartTotalsItem, + Currency, +} from '@woocommerce/types'; +import { LooselyMustHave } from '@woocommerce/type-defs/utils'; /** * Internal dependencies */ import './style.scss'; +export interface TotalsDiscountProps { + cartCoupons: LooselyMustHave< + CartResponseCouponItemWithLabel, + 'code' | 'label' | 'totals' + >[]; + currency: Currency; + isRemovingCoupon: boolean; + removeCoupon: ( couponCode: string ) => void; + values: LooselyMustHave< + CartTotalsItem, + 'total_discount' | 'total_discount_tax' + >; +} + const filteredCartCouponsFilterArg = { context: 'summary', }; @@ -26,7 +45,7 @@ const TotalsDiscount = ( { isRemovingCoupon, removeCoupon, values, -} ) => { +}: TotalsDiscountProps ): JSX.Element | null => { const { total_discount: totalDiscount, total_discount_tax: totalDiscountTax, @@ -110,19 +129,4 @@ const TotalsDiscount = ( { ); }; -TotalsDiscount.propTypes = { - cartCoupons: PropTypes.arrayOf( - PropTypes.shape( { - code: PropTypes.string.isRequired, - } ) - ), - currency: PropTypes.object.isRequired, - isRemovingCoupon: PropTypes.bool.isRequired, - removeCoupon: PropTypes.func.isRequired, - values: PropTypes.shape( { - total_discount: PropTypes.string, - total_discount_tax: PropTypes.string, - } ).isRequired, -}; - export default TotalsDiscount; diff --git a/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/totals/discount/stories/index.js b/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/totals/discount/stories/index.js deleted file mode 100644 index 0f89696b386..00000000000 --- a/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/totals/discount/stories/index.js +++ /dev/null @@ -1,37 +0,0 @@ -/** - * External dependencies - */ -import { text, boolean } from '@storybook/addon-knobs'; -import { currencyKnob } from '@woocommerce/knobs'; - -/** - * Internal dependencies - */ -import TotalsDiscount from '../'; - -export default { - title: - 'WooCommerce Blocks/@base-components/cart-checkout/totals/TotalsDiscount', - component: TotalsDiscount, -}; - -export const Default = () => { - const cartCoupons = [ { code: 'COUPON' } ]; - const currency = currencyKnob(); - const isRemovingCoupon = boolean( 'Toggle isRemovingCoupon state', false ); - const totalDiscount = text( 'Total discount', '1000' ); - const totalDiscountTax = text( 'Total discount tax', '200' ); - - return ( - void null } - values={ { - total_discount: totalDiscount, - total_discount_tax: totalDiscountTax, - } } - /> - ); -}; diff --git a/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/totals/discount/stories/index.tsx b/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/totals/discount/stories/index.tsx new file mode 100644 index 00000000000..1aea99a30db --- /dev/null +++ b/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/totals/discount/stories/index.tsx @@ -0,0 +1,111 @@ +/** + * External dependencies + */ +import { useArgs } from '@storybook/client-api'; +import { Story, Meta } from '@storybook/react'; +import { + currenciesAPIShape as currencies, + currencyControl, + INTERACTION_TIMEOUT, +} from '@woocommerce/storybook-controls'; +import { LooselyMustHave } from '@woocommerce/type-defs/utils'; +import { + CartResponseCouponItemWithLabel, + CartTotalsItem, +} from '@woocommerce/types'; + +/** + * Internal dependencies + */ +import Discount, { TotalsDiscountProps } from '..'; + +const EXAMPLE_COUPONS: CartResponseCouponItemWithLabel[] = [ + { + code: 'AWSMSB', + discount_type: '', + label: 'Awesome Storybook coupon', + totals: { + ...currencies.EUR, + total_discount: '5000', + total_discount_tax: '250', + }, + }, + { + code: 'STONKS', + discount_type: '', + label: 'Most valuable coupon', + totals: { + ...currencies.EUR, + total_discount: '10000', + total_discount_tax: '1000', + }, + }, +]; + +function extractValuesFromCoupons( + coupons: LooselyMustHave< CartResponseCouponItemWithLabel, 'totals' >[] +) { + return coupons.reduce( + ( acc, curr ) => { + const totalDiscount = + Number( acc.total_discount ) + + Number( curr.totals.total_discount ); + const totalDiscountTax = + Number( acc.total_discount_tax ) + + Number( curr.totals.total_discount_tax ); + + return { + total_discount: String( totalDiscount ), + total_discount_tax: String( totalDiscountTax ), + }; + }, + { total_discount: '0', total_discount_tax: '0' } as LooselyMustHave< + CartTotalsItem, + 'total_discount' | 'total_discount_tax' + > + ); +} + +export default { + title: 'WooCommerce Blocks/@base-components/cart-checkout/totals/Discount', + component: Discount, + argTypes: { + currency: currencyControl, + removeCoupon: { action: 'Removing coupon with code' }, + }, + args: { + cartCoupons: EXAMPLE_COUPONS, + isRemovingCoupon: false, + values: extractValuesFromCoupons( EXAMPLE_COUPONS ), + }, +} as Meta< TotalsDiscountProps >; + +const Template: Story< TotalsDiscountProps > = ( args ) => { + const [ {}, setArgs ] = useArgs(); + + const removeCoupon = ( code: string ) => { + args.removeCoupon( code ); + setArgs( { isRemovingCoupon: true } ); + + const cartCoupons = args.cartCoupons.filter( + ( coupon ) => coupon.code !== code + ); + + const values = extractValuesFromCoupons( cartCoupons ); + + setTimeout( + () => setArgs( { cartCoupons, values, isRemovingCoupon: false } ), + INTERACTION_TIMEOUT + ); + }; + + return ; +}; + +export const Default = Template.bind( {} ); +Default.args = {}; + +export const RemovingCoupon = Template.bind( {} ); +RemovingCoupon.args = { + isRemovingCoupon: true, +}; diff --git a/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/totals/footer-item/index.js b/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/totals/footer-item/index.tsx similarity index 65% rename from plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/totals/footer-item/index.js rename to plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/totals/footer-item/index.tsx index ef5d6f60c2f..26da5945def 100644 --- a/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/totals/footer-item/index.js +++ b/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/totals/footer-item/index.tsx @@ -4,23 +4,48 @@ import { __ } from '@wordpress/i18n'; import { createInterpolateElement } from '@wordpress/element'; import FormattedMonetaryAmount from '@woocommerce/base-components/formatted-monetary-amount'; -import PropTypes from 'prop-types'; import { __experimentalApplyCheckoutFilter, TotalsItem, } from '@woocommerce/blocks-checkout'; import { useStoreCart } from '@woocommerce/base-context/hooks'; import { getSetting } from '@woocommerce/settings'; +import { CartResponseTotals, Currency } from '@woocommerce/types'; +import { LooselyMustHave } from '@woocommerce/type-defs/utils'; /** * Internal dependencies */ import './style.scss'; -const TotalsFooterItem = ( { currency, values } ) => { +export interface TotalsFooterItemProps { + /** + * The currency object with which to display the item + */ + currency: Currency; + /** + * An object containing the total price and the total tax + * + * It accepts the entire `CartResponseTotals` to be passed, for + * convenience, but will use only these two properties. + */ + values: LooselyMustHave< CartResponseTotals, 'total_price' | 'total_tax' >; +} + +/** + * The total at the bottom of the cart + * + * Can show how much of the total is in taxes if the settings + * `taxesEnabled` and `displayCartPricesIncludingTax` are both + * enabled. + */ +const TotalsFooterItem = ( { + currency, + values, +}: TotalsFooterItemProps ): JSX.Element => { const SHOW_TAXES = - getSetting( 'taxesEnabled', true ) && - getSetting( 'displayCartPricesIncludingTax', false ); + getSetting< boolean >( 'taxesEnabled', true ) && + getSetting< boolean >( 'displayCartPricesIncludingTax', false ); const { total_price: totalPrice, total_tax: totalTax } = values; @@ -69,12 +94,4 @@ const TotalsFooterItem = ( { currency, values } ) => { ); }; -TotalsFooterItem.propTypes = { - currency: PropTypes.object.isRequired, - values: PropTypes.shape( { - total_price: PropTypes.string, - total_tax: PropTypes.string, - } ).isRequired, -}; - export default TotalsFooterItem; diff --git a/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/totals/footer-item/stories/index.js b/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/totals/footer-item/stories/index.js deleted file mode 100644 index b6a7cba4fe0..00000000000 --- a/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/totals/footer-item/stories/index.js +++ /dev/null @@ -1,32 +0,0 @@ -/** - * External dependencies - */ -import { text } from '@storybook/addon-knobs'; -import { currencyKnob } from '@woocommerce/knobs'; - -/** - * Internal dependencies - */ -import TotalsFooterItem from '../'; - -export default { - title: - 'WooCommerce Blocks/@base-components/cart-checkout/totals/TotalsFooterItem', - component: TotalsFooterItem, -}; - -export const Default = () => { - const currency = currencyKnob(); - const totalPrice = text( 'Total price', '1200' ); - const totalTax = text( 'Total tax', '200' ); - - return ( - - ); -}; diff --git a/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/totals/footer-item/stories/index.tsx b/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/totals/footer-item/stories/index.tsx new file mode 100644 index 00000000000..53643d57be0 --- /dev/null +++ b/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/totals/footer-item/stories/index.tsx @@ -0,0 +1,53 @@ +/** + * External dependencies + */ +import { Story, Meta } from '@storybook/react'; +import { allSettings } from '@woocommerce/settings'; +import { Currency } from '@woocommerce/types'; + +/** + * Internal dependencies + */ +import FooterItem, { TotalsFooterItemProps } from '..'; + +const NZD: Currency = { + code: 'nzd', + symbol: '$', + thousandSeparator: ' ', + decimalSeparator: '.', + minorUnit: 2, + prefix: '$', + suffix: '', +}; + +export default { + title: + 'WooCommerce Blocks/@base-components/cart-checkout/totals/FooterItem', + component: FooterItem, + args: { + currency: NZD, + values: { total_price: '2500', total_tax: '550' }, + }, +} as Meta< TotalsFooterItemProps >; + +const Template: Story< TotalsFooterItemProps > = ( args ) => ( + +); + +export const Default = Template.bind( {} ); +Default.decorators = [ + ( StoryComponent ) => { + allSettings.displayCartPricesIncludingTax = false; + + return ; + }, +]; + +export const IncludingTaxes = Template.bind( {} ); +IncludingTaxes.decorators = [ + ( StoryComponent ) => { + allSettings.displayCartPricesIncludingTax = true; + + return ; + }, +]; diff --git a/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/totals/footer-item/test/__snapshots__/index.js.snap b/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/totals/footer-item/test/__snapshots__/index.js.snap deleted file mode 100644 index f47692188ef..00000000000 --- a/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/totals/footer-item/test/__snapshots__/index.js.snap +++ /dev/null @@ -1,79 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`TotalsFooterItem Does not show the "including %s of tax" line if tax is 0 1`] = ` -
- -`; - -exports[`TotalsFooterItem Does not show the "including %s of tax" line if tax is disabled 1`] = ` -
- -`; - -exports[`TotalsFooterItem Shows the "including %s of tax" line if tax is greater than 0 1`] = ` -
- -
-`; diff --git a/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/totals/footer-item/test/index.js b/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/totals/footer-item/test/index.tsx similarity index 96% rename from plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/totals/footer-item/test/index.js rename to plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/totals/footer-item/test/index.tsx index e9e9e307bdd..4091cab49cf 100644 --- a/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/totals/footer-item/test/index.js +++ b/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/totals/footer-item/test/index.tsx @@ -2,12 +2,12 @@ * External dependencies */ import { render } from '@testing-library/react'; +import { allSettings } from '@woocommerce/settings'; /** * Internal dependencies */ import TotalsFooterItem from '../index'; -import { allSettings } from '../../../../../../settings/shared/settings-init'; describe( 'TotalsFooterItem', () => { beforeEach( () => { diff --git a/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/totals/index.js b/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/totals/index.ts similarity index 100% rename from plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/totals/index.js rename to plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/totals/index.ts diff --git a/plugins/woocommerce-blocks/assets/js/base/components/product-name/index.tsx b/plugins/woocommerce-blocks/assets/js/base/components/product-name/index.tsx index b77b06af398..7e638cf8ce5 100644 --- a/plugins/woocommerce-blocks/assets/js/base/components/product-name/index.tsx +++ b/plugins/woocommerce-blocks/assets/js/base/components/product-name/index.tsx @@ -10,11 +10,26 @@ import { AnchorHTMLAttributes, HTMLAttributes } from 'react'; */ import './style.scss'; -interface ProductNameProps extends AnchorHTMLAttributes< HTMLAnchorElement > { +export interface ProductNameProps + extends AnchorHTMLAttributes< HTMLAnchorElement > { + /** + * If `true` renders a `span` element instead of a link + */ disabled?: boolean; + /** + * The product name + * + * Note: can be an HTML string + */ name: string; - permalink?: string; + /** + * Click handler + */ onClick?: () => void; + /** + * Link for the product + */ + permalink?: string; } /** @@ -22,7 +37,7 @@ interface ProductNameProps extends AnchorHTMLAttributes< HTMLAnchorElement > { * * The store API runs titles through `wp_kses_post()` which removes dangerous HTML tags, so using it inside `dangerouslySetInnerHTML` is considered safe. */ -export default ( { +export const ProductName = ( { className = '', disabled = false, name, @@ -59,3 +74,5 @@ export default ( { /> ); }; + +export default ProductName; diff --git a/plugins/woocommerce-blocks/assets/js/base/components/product-name/stories/index.js b/plugins/woocommerce-blocks/assets/js/base/components/product-name/stories/index.js deleted file mode 100644 index 5486e5cc052..00000000000 --- a/plugins/woocommerce-blocks/assets/js/base/components/product-name/stories/index.js +++ /dev/null @@ -1,26 +0,0 @@ -/** - * External dependencies - */ -import { boolean } from '@storybook/addon-knobs'; - -/** - * Internal dependencies - */ -import ProductName from '../'; - -export default { - title: 'WooCommerce Blocks/@base-components/cart-checkout/ProductName', - component: ProductName, -}; - -export const Default = () => { - const disabled = boolean( 'disabled', false ); - - return ( - - ); -}; diff --git a/plugins/woocommerce-blocks/assets/js/base/components/product-name/stories/index.tsx b/plugins/woocommerce-blocks/assets/js/base/components/product-name/stories/index.tsx new file mode 100644 index 00000000000..9806e93f4dd --- /dev/null +++ b/plugins/woocommerce-blocks/assets/js/base/components/product-name/stories/index.tsx @@ -0,0 +1,32 @@ +/** + * External dependencies + */ +import { Story, Meta } from '@storybook/react'; + +/** + * Internal dependencies + */ +import ProductName, { ProductNameProps } from '..'; + +export default { + title: 'WooCommerce Blocks/@base-components/cart-checkout/ProductName', + component: ProductName, + args: { + name: 'Test product', + permalink: '#', + }, +} as Meta< ProductNameProps >; + +const Template: Story< ProductNameProps > = ( args ) => ( + +); + +export const Default = Template.bind( {} ); +Default.args = { + disabled: false, +}; + +export const DisabledProduct = Template.bind( {} ); +DisabledProduct.args = { + disabled: true, +}; diff --git a/plugins/woocommerce-blocks/assets/js/base/components/tsconfig.json b/plugins/woocommerce-blocks/assets/js/base/components/tsconfig.json index e8dae2570f0..12127dc8836 100644 --- a/plugins/woocommerce-blocks/assets/js/base/components/tsconfig.json +++ b/plugins/woocommerce-blocks/assets/js/base/components/tsconfig.json @@ -2,13 +2,14 @@ "extends": "../../../../tsconfig.base.json", "compilerOptions": {}, "include": [ - ".", - "../../types", - "../../../../packages/prices", - "../../../../packages/checkout", - "../context", - "../../type-defs", - "../hocs" + ".", + "../../types", + "../../../../storybook", + "../../../../packages/prices", + "../../../../packages/checkout", + "../context", + "../../type-defs", + "../hocs" ], "exclude": [ "**/test/**" ] } diff --git a/plugins/woocommerce-blocks/assets/js/editor-components/error-placeholder/stories/error-placeholder.tsx b/plugins/woocommerce-blocks/assets/js/editor-components/error-placeholder/stories/error-placeholder.tsx index fb5483eb88a..ad9fe2c5f10 100644 --- a/plugins/woocommerce-blocks/assets/js/editor-components/error-placeholder/stories/error-placeholder.tsx +++ b/plugins/woocommerce-blocks/assets/js/editor-components/error-placeholder/stories/error-placeholder.tsx @@ -3,6 +3,7 @@ */ import { Story, Meta } from '@storybook/react'; import { useArgs } from '@storybook/client-api'; +import { INTERACTION_TIMEOUT } from '@woocommerce/storybook-controls'; /** * Internal dependencies @@ -21,7 +22,10 @@ const Template: Story< ErrorPlaceholderProps > = ( args ) => { ? () => { setArgs( { isLoading: true } ); - setTimeout( () => setArgs( { isLoading: false } ), 3500 ); + setTimeout( + () => setArgs( { isLoading: false } ), + INTERACTION_TIMEOUT + ); } : undefined; diff --git a/plugins/woocommerce-blocks/assets/js/settings/shared/index.ts b/plugins/woocommerce-blocks/assets/js/settings/shared/index.ts index 62b730ea410..174a13c8cb1 100644 --- a/plugins/woocommerce-blocks/assets/js/settings/shared/index.ts +++ b/plugins/woocommerce-blocks/assets/js/settings/shared/index.ts @@ -6,3 +6,4 @@ import '../../filters/exclude-draft-status-from-analytics'; export * from './default-constants'; export * from './default-address-fields'; export * from './utils'; +export { allSettings } from './settings-init'; diff --git a/plugins/woocommerce-blocks/assets/js/types/type-defs/utils.ts b/plugins/woocommerce-blocks/assets/js/types/type-defs/utils.ts new file mode 100644 index 00000000000..38440979eec --- /dev/null +++ b/plugins/woocommerce-blocks/assets/js/types/type-defs/utils.ts @@ -0,0 +1,6 @@ +/** + * Allow for the entire object to be passed, with only some properties + * required. + */ +export type LooselyMustHave< T, K extends keyof T > = Partial< T > & + Pick< T, K >; diff --git a/plugins/woocommerce-blocks/package-lock.json b/plugins/woocommerce-blocks/package-lock.json index 3de76c9914d..1dfa8a3be6b 100644 --- a/plugins/woocommerce-blocks/package-lock.json +++ b/plugins/woocommerce-blocks/package-lock.json @@ -12928,7 +12928,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", - "dev": true, "requires": { "no-case": "^3.0.4", "tslib": "^2.0.3" @@ -21164,7 +21163,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", - "dev": true, "requires": { "tslib": "^2.0.3" } @@ -21244,8 +21242,7 @@ "map-obj": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", - "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", - "dev": true + "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==" }, "map-or-similar": { "version": "1.5.0", @@ -22502,7 +22499,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", - "dev": true, "requires": { "lower-case": "^2.0.2", "tslib": "^2.0.3" @@ -28179,6 +28175,32 @@ "sentence-case": "^1.1.2" } }, + "snakecase-keys": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/snakecase-keys/-/snakecase-keys-5.1.2.tgz", + "integrity": "sha512-fvtDQZqPBqYb0dEY97TGuOMbN2NJ05Tj4MaoKwjTKkmjcG6mrd58JYGr23UWZRi6Aqv49Fk4HtjTIStOQenaug==", + "requires": { + "map-obj": "^4.1.0", + "snake-case": "^3.0.4", + "type-fest": "^2.5.2" + }, + "dependencies": { + "snake-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", + "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", + "requires": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "type-fest": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.8.0.tgz", + "integrity": "sha512-O+V9pAshf9C6loGaH0idwsmugI2LxVNR7DtS40gVo2EXZVYFgz9OuNtOhgHLdHdapOEWNdvz9Ob/eeuaWwwlxA==" + } + } + }, "snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", diff --git a/plugins/woocommerce-blocks/package.json b/plugins/woocommerce-blocks/package.json index e658169dcd1..715c9071ab1 100644 --- a/plugins/woocommerce-blocks/package.json +++ b/plugins/woocommerce-blocks/package.json @@ -212,6 +212,7 @@ "html-react-parser": "0.14.3", "react-number-format": "4.4.3", "reakit": "1.3.11", + "snakecase-keys": "^5.1.2", "trim-html": "0.1.9", "use-debounce": "7.0.1", "wordpress-components": "npm:@wordpress/components@14.2.0" diff --git a/plugins/woocommerce-blocks/storybook/custom-controls/currency.ts b/plugins/woocommerce-blocks/storybook/custom-controls/currency.ts new file mode 100644 index 00000000000..c50da63224c --- /dev/null +++ b/plugins/woocommerce-blocks/storybook/custom-controls/currency.ts @@ -0,0 +1,43 @@ +/** + * External dependencies + */ +import { Currency, CurrencyResponse } from '@woocommerce/types'; +import snakecaseKeys from 'snakecase-keys'; + +export const currencies: Record< string, Currency > = { + EUR: { + code: 'EUR', + symbol: '€', + thousandSeparator: '.', + decimalSeparator: ',', + minorUnit: 2, + prefix: '', + suffix: '€', + }, + USD: { + code: 'USD', + symbol: '$', + thousandSeparator: ',', + decimalSeparator: '.', + minorUnit: 2, + prefix: '$', + suffix: '', + }, +} as const; + +export const currenciesAPIShape: Record< + string, + CurrencyResponse +> = Object.fromEntries( + Object.entries( currencies ).map( ( [ key, value ] ) => [ + key, + snakecaseKeys( value ), + ] ) +); + +export const currencyControl = { + control: 'select', + defaultValue: currencies.USD, + mapping: currencies, + options: Object.keys( currencies ), +}; diff --git a/plugins/woocommerce-blocks/storybook/custom-controls/index.ts b/plugins/woocommerce-blocks/storybook/custom-controls/index.ts new file mode 100644 index 00000000000..c4a0d5d8578 --- /dev/null +++ b/plugins/woocommerce-blocks/storybook/custom-controls/index.ts @@ -0,0 +1,3 @@ +export * from './currency'; + +export const INTERACTION_TIMEOUT = 1500; diff --git a/plugins/woocommerce-blocks/tsconfig.base.json b/plugins/woocommerce-blocks/tsconfig.base.json index b819937e0dd..fdb587eeccf 100644 --- a/plugins/woocommerce-blocks/tsconfig.base.json +++ b/plugins/woocommerce-blocks/tsconfig.base.json @@ -55,7 +55,8 @@ "@woocommerce/shared-context": [ "assets/js/shared/context" ], "@woocommerce/shared-hocs": [ "assets/js/shared/hocs" ], "@woocommerce/type-defs/*": [ "assets/js/types/type-defs/*" ], - "@woocommerce/types": [ "assets/js/types" ] + "@woocommerce/types": [ "assets/js/types" ], + "@woocommerce/storybook-controls": [ "storybook/custom-controls" ] } } } diff --git a/plugins/woocommerce-blocks/tsconfig.json b/plugins/woocommerce-blocks/tsconfig.json index 1643f8a5d9f..cef4cd07024 100644 --- a/plugins/woocommerce-blocks/tsconfig.json +++ b/plugins/woocommerce-blocks/tsconfig.json @@ -3,7 +3,8 @@ "include": [ "./assets/js/**/*", "./assets/js/blocks/cart-checkout/checkout/inner-blocks/**/block.json", - "./assets/js/blocks/cart-checkout/mini-cart-contents/inner-blocks/**/block.json" + "./assets/js/blocks/cart-checkout/mini-cart-contents/inner-blocks/**/block.json", + "./storybook/**/*" ], "exclude": [ "./assets/js/data" ], "references": [