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": [