Add payment method api and components to checkout steps (https://github.com/woocommerce/woocommerce-blocks/pull/1349)

This adds a payment methods api to the checkout block that allows for extension to register payment methods with the block.
This commit is contained in:
Darren Ethier 2020-01-06 17:28:09 -05:00 committed by GitHub
parent 44f70d8438
commit 8627b81c16
37 changed files with 1360 additions and 323 deletions

View File

@ -0,0 +1,52 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { useExpressPaymentMethods } from '@woocommerce/base-hooks';
/**
* Internal dependencies
*/
import ExpressPaymentMethods from './express-payment-methods';
import './style.scss';
const ExpressCheckoutContainer = ( { children } ) => {
return (
<div className="wc-component__checkout-container wc-component__container-with-border">
<div className="wc-component__text-overlay-on-border">
<strong>
{ __( 'Express checkout', 'woo-gutenberg-products-block' ) }
</strong>
</div>
<div className="wc-component__container-content">{ children }</div>
</div>
);
};
const ExpressCheckoutFormControl = () => {
const { paymentMethods, isInitialized } = useExpressPaymentMethods();
// determine whether we even show this
// @todo if in the editor we probably would want to show a placeholder maybe?
if (
! isInitialized ||
( isInitialized && Object.keys( paymentMethods ).length === 0 )
) {
return null;
}
return (
<ExpressCheckoutContainer>
<p>
{ __(
'In a hurry? Use one of our express checkout options below:',
'woo-gutenberg-products-block'
) }
</p>
<ExpressPaymentMethods />
</ExpressCheckoutContainer>
);
};
export default ExpressCheckoutFormControl;

View File

@ -0,0 +1,44 @@
/**
* External dependencies
*/
import {
useCheckoutData,
usePaymentEvents,
useExpressPaymentMethods,
} from '@woocommerce/base-hooks';
import { cloneElement } from '@wordpress/element';
const ExpressPaymentMethods = () => {
const [ checkoutData ] = useCheckoutData();
const { dispatch, select } = usePaymentEvents();
// not implementing isInitialized here because it's utilized further
// up in the tree for express payment methods. We won't even get here if
// there's no payment methods after initialization.
const { paymentMethods } = useExpressPaymentMethods();
const paymentMethodSlugs = Object.keys( paymentMethods );
const content =
paymentMethodSlugs.length > 0 ? (
paymentMethodSlugs.map( ( slug ) => {
const expressPaymentMethod =
paymentMethods[ slug ].activeContent;
const paymentEvents = { dispatch, select };
return (
<li key={ slug } id={ `express-payment-method-${ slug }` }>
{ cloneElement( expressPaymentMethod, {
checkoutData,
paymentEvents,
} ) }
</li>
);
} )
) : (
<li key="noneRegistered">No registered Payment Methods</li>
);
return (
<ul className="wc-component__express-payment-event-buttons">
{ content }
</ul>
);
};
export default ExpressPaymentMethods;

View File

@ -0,0 +1,3 @@
export { default as PaymentMethods } from './payment-methods';
export { default as ExpressPaymentMethods } from './express-payment-methods';
export { default as ExpressCheckoutFormControl } from './express-checkout';

View File

@ -0,0 +1,90 @@
/**
* External dependencies
*/
import {
useCheckoutData,
usePaymentEvents,
useActivePaymentMethod,
usePaymentMethods,
} from '@woocommerce/base-hooks';
import { useCallback, cloneElement } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
/**
* Internal dependencies
*/
import Tabs from '../tabs';
const noPaymentMethodTab = () => {
const label = __( 'Not Existing', 'woo-gutenberg-products-block' );
return {
name: label,
label,
title: () => label,
};
};
const createTabs = ( paymentMethods ) => {
const paymentMethodsKeys = Object.keys( paymentMethods );
return paymentMethodsKeys.length > 0
? paymentMethodsKeys.map( ( key ) => {
const { label, ariaLabel } = paymentMethods[ key ];
return {
name: key,
title: () => label,
ariaLabel,
};
} )
: [ noPaymentMethodTab() ];
};
const PaymentMethods = () => {
const [ checkoutData ] = useCheckoutData();
const { dispatch, select } = usePaymentEvents();
const { isInitialized, paymentMethods } = usePaymentMethods();
const {
activePaymentMethod,
setActivePaymentMethod,
} = useActivePaymentMethod();
const getRenderedTab = useCallback(
() => ( selectedTab ) => {
const paymentMethod =
( paymentMethods[ selectedTab ] &&
paymentMethods[ selectedTab ].activeContent ) ||
null;
const paymentEvents = { dispatch, select };
return paymentMethod
? cloneElement( paymentMethod, {
isActive: true,
checkoutData,
paymentEvents,
} )
: null;
},
[ checkoutData, dispatch, select ]
);
if (
! isInitialized ||
( Object.keys( paymentMethods ).length === 0 && isInitialized )
) {
// @todo this can be a placeholder informing the user there are no
// payment methods setup?
return <div>No Payment Methods Initialized</div>;
}
return (
<Tabs
className="wc-component__payment-method-options"
onSelect={ ( tabName ) => setActivePaymentMethod( tabName ) }
tabs={ createTabs( paymentMethods ) }
initialTabName={ activePaymentMethod }
ariaLabel={ __(
'Payment Methods',
'woo-gutenberg-products-block'
) }
>
{ getRenderedTab() }
</Tabs>
);
};
export default PaymentMethods;

View File

@ -0,0 +1,38 @@
.wc-component__container-with-border {
margin: auto;
border: 2px solid $black;
border-radius: 5px;
padding: 10px;
margin-bottom: $gap-large;
.wc-component__text-overlay-on-border {
position: relative;
top: -24px;
background-color: $white;
padding-left: $gap-small;
padding-right: $gap-small;
width: fit-content;
color: $black;
}
.wc-component__container-content {
margin-top: -15px;
padding-left: $gap-large;
padding-right: $gap-large;
padding-bottom: $gap;
}
.wc-component__express-payment-event-buttons {
list-style: none;
display: flex;
flex-direction: row;
flex-wrap: wrap;
width: 100%;
padding: 0;
margin: 0;
> li {
display: inline-block;
width: 50%;
}
}
}

View File

@ -0,0 +1,105 @@
/**
* External dependencies
*/
import { useState } from '@wordpress/element';
import { withInstanceId } from '@wordpress/compose';
import classnames from 'classnames';
import { __ } from '@wordpress/i18n';
/**
* Internal dependencies
*/
import './style.scss';
const TabButton = ( {
tabId,
onClick,
children,
selected,
ariaLabel,
...rest
} ) => {
return (
<button
role="tab"
type="button"
tabIndex={ selected ? null : -1 }
aria-selected={ selected }
aria-label={ ariaLabel }
id={ tabId }
onClick={ onClick }
{ ...rest }
>
<span className="wc-component__tab-item-content">{ children }</span>
</button>
);
};
const Tabs = ( {
className,
onSelect = () => null,
tabs,
activeClass = 'is-active',
initialTabName,
instanceId,
ariaLabel = __( 'Tabbed Content', 'woo-gutenberg-products-block' ),
children,
} ) => {
const [ selected, setSelected ] = useState(
initialTabName || ( tabs.length > 0 ? tabs[ 0 ].name : '' )
);
if ( ! selected ) {
return null;
}
const handleClick = ( tabKey ) => {
setSelected( tabKey );
onSelect( tabKey );
};
const selectedTab = tabs.find( ( tab ) => tab.name === selected );
if ( ! selectedTab ) {
throw new Error( 'There is no available tab for the selected item' );
}
const selectedId = `${ instanceId }-${ selectedTab.name }`;
return (
<div className={ classnames( 'wc-component__tabs', className ) }>
<div
role="tablist"
aria-label={ ariaLabel }
className="wc-component__tab-list"
>
{ tabs.map( ( tab ) => (
<TabButton
className={ classnames(
'wc-component__tab-item',
tab.className,
{
[ activeClass ]: tab.name === selected,
}
) }
tabId={ `${ instanceId }-${ tab.name }` }
aria-controls={ `${ instanceId }-${ tab.name }-view` }
selected={ tab.name === selected }
key={ tab.name }
ariaLabel={ tab.ariaLabel || null }
onClick={ () => handleClick( tab.name ) }
>
{ tab.title() }
</TabButton>
) ) }
</div>
{ selectedTab && (
<div
aria-labelledby={ selectedId }
role="tabpanel"
id={ `${ selectedId }-view` }
className="wc-component__tab-content"
tabIndex="0"
>
{ children( selected ) }
</div>
) }
</div>
);
};
export default withInstanceId( Tabs );

View File

@ -0,0 +1,35 @@
.wc-component__tabs {
.wc-component__tab-list {
display: flex;
flex-direction: row;
flex-wrap: wrap;
width: 100%;
> .wc-component__tab-item {
border: none;
flex: auto;
background: transparent;
padding: $gap-small $gap;
color: $black;
outline-offset: -1px;
transition: box-shadow 0.1s linear;
&.is-active {
box-shadow: inset 0 -3px $black;
font-weight: 600;
position: relative;
}
&:focus {
color: $black;
outline-offset: -1px;
outline: 1px dotted $gray-60;
}
.wc-component__tab-item-content {
width: fit-content;
display: block;
margin: auto;
}
}
}
.wc-component__tab-content {
padding: $gap;
}
}

View File

@ -0,0 +1,67 @@
/**
* External dependencies
*/
import {
createContext,
useContext,
useState,
useMemo,
} from '@wordpress/element';
import { __ } from '@wordpress/i18n';
const CheckoutContext = createContext( {} );
export const useCheckoutContext = () => {
return useContext( CheckoutContext );
};
const CheckoutProvider = ( {
children,
initialActivePaymentMethod,
placeOrderLabel = __( 'Place Order', 'woo-gutenberg-product-block' ),
} ) => {
const [ successRedirectUrl, setSuccessRedirectUrl ] = useState( '' );
const [ failureRedirectUrl, setFailureRedirectUrl ] = useState( '' );
const [ isCheckoutComplete, setIsCheckoutComplete ] = useState( false );
const [ checkoutHasError, setCheckoutHasError ] = useState( false );
const [ notices, updateNotices ] = useState( [] );
const [ isCalculating, setIsCalculating ] = useState( false );
const [ activePaymentMethod, setActivePaymentMethod ] = useState(
initialActivePaymentMethod
);
const contextValue = useMemo( () => {
return {
successRedirectUrl,
setSuccessRedirectUrl,
failureRedirectUrl,
setFailureRedirectUrl,
isCheckoutComplete,
setIsCheckoutComplete,
checkoutHasError,
setCheckoutHasError,
isCalculating,
setIsCalculating,
notices,
updateNotices,
activePaymentMethod,
setActivePaymentMethod,
placeOrderLabel,
};
}, [
successRedirectUrl,
failureRedirectUrl,
isCheckoutComplete,
isCalculating,
checkoutHasError,
activePaymentMethod,
placeOrderLabel,
notices,
] );
return (
<CheckoutContext.Provider value={ contextValue }>
{ children }
</CheckoutContext.Provider>
);
};
export default CheckoutProvider;

View File

@ -0,0 +1,4 @@
export { default as useCheckoutEvents } from './use-checkout-events';
export { default as useCheckoutNotices } from './use-checkout-notices';
export { default as useCheckoutRedirectUrls } from './use-checkout-redirect-urls';
export { default as useCheckoutData } from './use-checkout-data';

View File

@ -0,0 +1,29 @@
// @todo this should be a value object. Provided via wc-settings?
const currencyObject = {
code: 'USD',
precision: 2,
symbol: '$',
symbolPosition: 'left',
decimalSeparator: '.',
priceFormat: '%1$s%2$s',
thousandSeparator: ',',
};
const useCheckoutData = () => {
// @todo this will likely be a global wp.data store state so that things
// like shipping selection, quantity changes, etc that affect totals etc
// will automatically update the payment data. For POC this is hardcoded
const checkoutData = {
// this likely should be a float.
total: 10.123,
currency: currencyObject,
// @todo, should this be a standard format of items in the checkout/cart
// provided to ALL payment methods? Line items includes taxes/shipping
// costs? Coupons?
lineItems: [],
};
const updateCheckoutData = () => {};
return [ checkoutData, updateCheckoutData ];
};
export default useCheckoutData;

View File

@ -0,0 +1,41 @@
/**
* External dependencies
*/
import useCheckoutContext from '@woocommerce/base-context/checkout-context';
export const useCheckoutEvents = () => {
const {
isCheckoutComplete,
setIsCheckoutComplete,
checkoutHasError,
setCheckoutHasError,
isCalculating,
setIsCalculating,
} = useCheckoutContext();
const setHasError = () => {
setCheckoutHasError( true );
};
const cancelCheckoutError = () => {
setCheckoutHasError( false );
};
const setComplete = () => {
cancelCheckoutError();
setIsCheckoutComplete( true );
};
const setCalculating = () => {
setIsCalculating( true );
};
const cancelCalculating = () => {
setIsCalculating( false );
};
return {
setIsCheckoutComplete: setComplete,
setCheckoutHasError: setHasError,
cancelCheckoutError,
setIsCalculating: setCalculating,
cancelCalculating,
isCalculating,
isCheckoutComplete,
checkoutHasError,
};
};

View File

@ -0,0 +1,24 @@
/**
* External dependencies
*/
import useCheckoutContext from '@woocommerce/base-context/checkout-context';
const useCheckoutNotices = () => {
const { notices, updateNotices } = useCheckoutContext();
const addNotice = ( notice ) => {
updateNotices( ( originalNotices ) => [ ...originalNotices, notice ] );
};
const clearAllNotices = () => {
// @todo...figure out how notices are saved - might need unique ids?
// Do we have a special notice creator that takes care of that?
// Use wp notice api?
updateNotices( [] );
};
return {
notices,
addNotice,
clearAllNotices,
};
};
export default useCheckoutNotices;

View File

@ -0,0 +1,9 @@
/**
* External dependencies
*/
import useCheckoutContext from '@woocommerce/base-context/checkout-context';
export const useCheckoutPlaceOrderLabel = () => {
const { placeOrderLabel } = useCheckoutContext();
return placeOrderLabel;
};

View File

@ -0,0 +1,22 @@
/**
* External dependencies
*/
import useCheckoutContext from '@woocommerce/base-context/checkout-context';
const useCheckoutRedirectUrls = () => {
const {
successRedirectUrl,
setSuccessRedirectUrl,
failureRedirectUrl,
setFailureRedirectUrl,
} = useCheckoutContext();
return {
successRedirectUrl,
setSuccessRedirectUrl,
failureRedirectUrl,
setFailureRedirectUrl,
};
};
export default useCheckoutRedirectUrls;

View File

@ -5,3 +5,5 @@ export * from './use-collection';
export * from './use-collection-header';
export * from './use-collection-data';
export * from './use-previous';
export * from './checkout';
export * from './payment-methods';

View File

@ -0,0 +1,7 @@
export const STATUS = {
PRISTINE: 'pristine',
STARTED: 'started',
ERROR: 'has_error',
FAILED: 'failed',
SUCCESS: 'success',
};

View File

@ -0,0 +1,3 @@
export { default as useActivePaymentMethod } from './use-active-payment-method';
export { default as usePaymentEvents } from './use-payment-events';
export * from './use-payment-methods';

View File

@ -0,0 +1,32 @@
/**
* External dependencies
*/
import { useCheckoutContext } from '@woocommerce/base-context/checkout-context';
import { usePaymentMethods } from '@woocommerce/base-hooks';
import { useEffect } from '@wordpress/element';
const useActivePaymentMethod = () => {
const {
activePaymentMethod,
setActivePaymentMethod,
} = useCheckoutContext();
const { paymentMethods, isInitialized } = usePaymentMethods();
// if payment method has not been set yet, let's set it.
useEffect( () => {
// if not initialized yet bail
if ( ! isInitialized ) {
return;
}
if ( ! activePaymentMethod && activePaymentMethod !== null ) {
const paymentMethodIds = Object.keys( paymentMethods );
setActivePaymentMethod(
paymentMethodIds.length > 0
? paymentMethods[ paymentMethodIds[ 0 ] ].name
: null
);
}
}, [ activePaymentMethod, setActivePaymentMethod, isInitialized ] );
return { activePaymentMethod, setActivePaymentMethod };
};
export default useActivePaymentMethod;

View File

@ -0,0 +1,38 @@
/**
* External dependencies
*/
import { useState, useMemo } from '@wordpress/element';
/**
* Internal dependencies
*/
import { STATUS } from './constants';
const { STARTED, ERROR, FAILED, SUCCESS, PRISTINE } = STATUS;
const usePaymentEvents = () => {
const { paymentStatus, setPaymentStatus } = useState( PRISTINE );
const dispatch = useMemo(
() => ( {
started: () => setPaymentStatus( STARTED ),
error: () => setPaymentStatus( ERROR ),
failed: () => setPaymentStatus( FAILED ),
success: () => setPaymentStatus( SUCCESS ),
} ),
[ setPaymentStatus ]
);
const select = useMemo(
() => ( {
isPristine: () => paymentStatus === PRISTINE,
isStarted: () => paymentStatus === STARTED,
isFinished: () =>
[ ERROR, FAILED, SUCCESS ].includes( paymentStatus ),
hasError: () => paymentStatus === ERROR,
hasFailed: () => paymentStatus === FAILED,
isSuccessful: () => paymentStatus === SUCCESS,
} ),
[ paymentStatus ]
);
return { dispatch, select };
};
export default usePaymentEvents;

View File

@ -0,0 +1,59 @@
/**
* External dependencies
*/
import {
getPaymentMethods,
getExpressPaymentMethods,
} from '@woocommerce/blocks-registry';
import { useState, useEffect, useRef } from '@wordpress/element';
const usePaymentMethodState = ( registeredPaymentMethods ) => {
const [ paymentMethods, setPaymentMethods ] = useState( [] );
const [ isInitialized, setIsInitialized ] = useState( false );
const countPaymentMethodsInitializing = useRef(
Object.keys( registeredPaymentMethods ).length
);
useEffect( () => {
// if all payment methods are initialized then bail.
if ( isInitialized ) {
return;
}
// loop through payment methods and see what the state is
for ( const paymentMethodId in registeredPaymentMethods ) {
const current = registeredPaymentMethods[ paymentMethodId ];
current.canMakePayment
.then( ( canPay ) => {
if ( canPay ) {
setPaymentMethods( ( previousPaymentMethods ) => {
return {
...previousPaymentMethods,
[ current.id ]: current,
};
} );
}
// update the initialized count
countPaymentMethodsInitializing.current--;
// if count remaining less than 1, then set initialized.
if ( countPaymentMethodsInitializing.current < 1 ) {
setIsInitialized( true );
}
} )
.catch( ( error ) => {
// @todo, would be a good place to use the checkout error
// hooks here? Or maybe throw and catch by error boundary?
throw new Error(
'Problem with payment method initialization' +
( error.message || '' )
);
} );
}
}, [ isInitialized ] );
return { paymentMethods, isInitialized };
};
export const usePaymentMethods = () =>
usePaymentMethodState( getPaymentMethods() );
export const useExpressPaymentMethods = () =>
usePaymentMethodState( getExpressPaymentMethods() );

View File

@ -1,2 +1,3 @@
export { getRegisteredInnerBlocks } from './get-registered-inner-blocks';
export { registerInnerBlock } from './register-inner-block';
export * from './payment-methods';

View File

@ -0,0 +1,49 @@
/**
* External dependencies
*/
import { isValidElement } from '@wordpress/element';
export const assertValidPaymentMethodComponent = (
component,
componentName
) => {
// @todo detect if functional component (not render prop)
if ( typeof component !== 'function' ) {
throw new TypeError(
`The ${ componentName } for the payment method must be a functional component`
);
}
};
export const assertValidElement = ( element, elementName ) => {
if ( element !== null && ! isValidElement( element ) ) {
throw new TypeError(
`The ${ elementName } for the payment method must be a React element or null.`
);
}
};
export const assertConfigHasProperties = (
config,
expectedProperties = []
) => {
const missingProperties = expectedProperties.reduce( ( acc, property ) => {
if ( ! config.hasOwnProperty( property ) ) {
acc.push( property );
}
return acc;
}, [] );
if ( missingProperties.length > 0 ) {
const message =
'The payment method configuration object is missing the following properties:';
throw new TypeError( message + missingProperties.join( ', ' ) );
}
};
export const assertValidPaymentMethodCreator = ( creator, configName ) => {
if ( typeof creator !== 'function' ) {
throw new TypeError(
`A payment method must be registered with a function that creates and returns a ${ configName } instance`
);
}
};

View File

@ -0,0 +1,29 @@
/**
* Internal dependencies
*/
import { assertConfigHasProperties, assertValidElement } from './assertions';
export default class ExpressPaymentMethodConfig {
constructor( config ) {
// validate config
ExpressPaymentMethodConfig.assertValidConfig( config );
this.id = config.id;
this.activeContent = config.activeContent;
this.canMakePayment = config.canMakePayment;
}
static assertValidConfig = ( config ) => {
assertConfigHasProperties( config, [ 'id', 'activeContent' ] );
if ( typeof config.id !== 'string' ) {
throw new TypeError(
'The id for the express payment method must be a string'
);
}
assertValidElement( config.activeContent, 'activeContent' );
if ( ! ( config.canMakePayment instanceof Promise ) ) {
throw new TypeError(
'The canMakePayment property for the express payment method must be a promise.'
);
}
};
}

View File

@ -0,0 +1 @@
export * from './registry';

View File

@ -0,0 +1,44 @@
/**
* Internal dependencies
*/
import { assertConfigHasProperties, assertValidElement } from './assertions';
export default class PaymentMethodConfig {
constructor( config ) {
// validate config
PaymentMethodConfig.assertValidConfig( config );
this.id = config.id;
this.label = config.label;
this.stepContent = config.stepContent;
this.ariaLabel = config.ariaLabel;
this.activeContent = config.activeContent;
this.canMakePayment = config.canMakePayment;
}
static assertValidConfig = ( config ) => {
assertConfigHasProperties( config, [
'id',
'label',
'stepContent',
'ariaLabel',
'activeContent',
'canMakePayment',
] );
if ( typeof config.id !== 'string' ) {
throw new Error( 'The id for the payment method must be a string' );
}
assertValidElement( config.label, 'label' );
assertValidElement( config.stepContent, 'stepContent' );
assertValidElement( config.activeContent, 'activeContent' );
if ( typeof config.ariaLabel !== 'string' ) {
throw new TypeError(
'The ariaLabel for the payment method must be a string'
);
}
if ( ! ( config.canMakePayment instanceof Promise ) ) {
throw new TypeError(
'The canMakePayment property for the payment method must be a promise.'
);
}
};
}

View File

@ -0,0 +1,46 @@
/**
* Internal dependencies
*/
import { assertValidPaymentMethodCreator } from './assertions';
import { default as PaymentMethodConfig } from './payment-method-config';
import { default as ExpressPaymentMethodConfig } from './express-payment-method-config';
// currently much leeway is given to the payment method for the shape of their components. We should investigate payment methods
// using a component creator that is fed a configuration object so that the built component for the payment method is tightly
// controlled (fitting the ui/ux requirements of the checkout/cart). Once we know the pattern most payment methods will follow
// (i.e. fields, event callbacks, validation, etc) this is likely more feasible.
const paymentMethods = {};
const expressPaymentMethods = {};
export const registerPaymentMethod = ( paymentMethodCreator ) => {
assertValidPaymentMethodCreator(
paymentMethodCreator,
'PaymentMethodConfig'
);
const paymentMethodConfig = paymentMethodCreator( PaymentMethodConfig );
if ( paymentMethodConfig instanceof PaymentMethodConfig ) {
paymentMethods[ paymentMethodConfig.id ] = paymentMethodConfig;
}
};
export const registerExpressPaymentMethod = ( expressPaymentMethodCreator ) => {
assertValidPaymentMethodCreator(
expressPaymentMethodCreator,
'ExpressPaymentMethodConfig'
);
const paymentMethodConfig = expressPaymentMethodCreator(
ExpressPaymentMethodConfig
);
if ( paymentMethodConfig instanceof ExpressPaymentMethodConfig ) {
expressPaymentMethods[ paymentMethodConfig.id ] = paymentMethodConfig;
}
};
export const getPaymentMethods = () => {
return paymentMethods;
};
export const getExpressPaymentMethods = () => {
return expressPaymentMethods;
};

View File

@ -9,11 +9,18 @@ import NoShipping from '@woocommerce/base-components/checkout/no-shipping';
import TextInput from '@woocommerce/base-components/text-input';
import RadioControl from '@woocommerce/base-components/radio-control';
import InputRow from '@woocommerce/base-components/input-row';
import { CheckboxControl, Placeholder } from '@wordpress/components';
import { CheckboxControl } from '@wordpress/components';
import CheckoutProvider from '@woocommerce/base-context/checkout-context';
import {
ExpressCheckoutFormControl,
PaymentMethods,
} from '@woocommerce/base-components/payment-methods';
/**
* Internal dependencies
*/
import './style.scss';
import '../../../payment-methods-demo';
/**
* Component displaying an attribute filter.
@ -24,80 +31,67 @@ const Block = ( { shippingMethods = [], isEditor = false } ) => {
const [ shouldSavePayment, setShouldSavePayment ] = useState( true );
const [ shippingFields, setShippingFields ] = useState( {} );
return (
<CheckoutForm>
<FormStep
id="billing-fields"
className="wc-block-checkout__billing-fields"
title={ __(
'Contact information',
'woo-gutenberg-products-block'
) }
description={ __(
"We'll use this email to send you details and updates about your order.",
'woo-gutenberg-products-block'
) }
stepNumber={ 1 }
stepHeadingContent={ () => (
<Fragment>
{ __(
'Already have an account? ',
'woo-gutenberg-products-block'
) }
<a href="/wp-login.php">
{ __( 'Log in.', 'woo-gutenberg-products-block' ) }
</a>
</Fragment>
) }
>
<TextInput
id="email-field"
type="email"
label={ __(
'Email address',
'woo-gutenberg-products-block'
) }
value={ contactFields.email }
onChange={ ( newValue ) =>
setContactFields( {
...contactFields,
email: newValue,
} )
}
/>
<CheckboxControl
className="wc-block-checkout__keep-updated"
label={ __(
'Keep me up to date on news and exclusive offers',
'woo-gutenberg-products-block'
) }
checked={ contactFields.keepUpdated }
onChange={ () =>
setContactFields( {
...contactFields,
keepUpdated: ! contactFields.keepUpdated,
} )
}
/>
</FormStep>
{ shippingMethods.length === 0 && (
<CheckoutProvider>
<ExpressCheckoutFormControl />
<CheckoutForm>
<FormStep
id="shipping-fields"
className="wc-block-checkout__shipping-fields"
id="billing-fields"
className="wc-block-checkout__billing-fields"
title={ __(
'Shipping address',
'Contact information',
'woo-gutenberg-products-block'
) }
description={ __(
'Enter the physical address where you want us to deliver your order.',
"We'll use this email to send you details and updates about your order.",
'woo-gutenberg-products-block'
) }
stepNumber={ 2 }
stepNumber={ 1 }
stepHeadingContent={ () => (
<Fragment>
{ __(
'Already have an account? ',
'woo-gutenberg-products-block'
) }
<a href="/wp-login.php">
{ __(
'Log in.',
'woo-gutenberg-products-block'
) }
</a>
</Fragment>
) }
>
{ isEditor && <NoShipping /> }
<TextInput
id="email-field"
type="email"
label={ __(
'Email address',
'woo-gutenberg-products-block'
) }
value={ contactFields.email }
onChange={ ( newValue ) =>
setContactFields( {
...contactFields,
email: newValue,
} )
}
/>
<CheckboxControl
className="wc-block-checkout__keep-updated"
label={ __(
'Keep me up to date on news and exclusive offers',
'woo-gutenberg-products-block'
) }
checked={ contactFields.keepUpdated }
onChange={ () =>
setContactFields( {
...contactFields,
keepUpdated: ! contactFields.keepUpdated,
} )
}
/>
</FormStep>
) }
{ shippingMethods.length > 0 && (
<Fragment>
{ shippingMethods.length === 0 && (
<FormStep
id="shipping-fields"
className="wc-block-checkout__shipping-fields"
@ -111,238 +105,261 @@ const Block = ( { shippingMethods = [], isEditor = false } ) => {
) }
stepNumber={ 2 }
>
<InputRow>
<TextInput
id="shipping-first-name"
label={ __(
'First name',
'woo-gutenberg-products-block'
) }
value={ shippingFields.firstName }
onChange={ ( newValue ) =>
setShippingFields( {
...shippingFields,
firstName: newValue,
} )
}
/>
<TextInput
id="shipping-last-name"
label={ __(
'Surname',
'woo-gutenberg-products-block'
) }
value={ shippingFields.lastName }
onChange={ ( newValue ) =>
setShippingFields( {
...shippingFields,
lastName: newValue,
} )
}
/>
</InputRow>
<TextInput
id="shipping-street-address"
label={ __(
'Street address',
'woo-gutenberg-products-block'
) }
value={ shippingFields.streetAddress }
onChange={ ( newValue ) =>
setShippingFields( {
...shippingFields,
streetAddress: newValue,
} )
}
/>
<TextInput
id="shipping-apartment"
label={ __(
'Apartment, suite, etc.',
'woo-gutenberg-products-block'
) }
value={ shippingFields.apartment }
onChange={ ( newValue ) =>
setShippingFields( {
...shippingFields,
apartment: newValue,
} )
}
/>
<InputRow>
<TextInput
id="shipping-country"
label={ __(
'Country',
'woo-gutenberg-products-block'
) }
value={ shippingFields.country }
onChange={ ( newValue ) =>
setShippingFields( {
...shippingFields,
country: newValue,
} )
}
/>
<TextInput
id="shipping-city"
label={ __(
'City',
'woo-gutenberg-products-block'
) }
value={ shippingFields.city }
onChange={ ( newValue ) =>
setShippingFields( {
...shippingFields,
city: newValue,
} )
}
/>
</InputRow>
<InputRow>
<TextInput
id="shipping-county"
label={ __(
'County',
'woo-gutenberg-products-block'
) }
value={ shippingFields.county }
onChange={ ( newValue ) =>
setShippingFields( {
...shippingFields,
county: newValue,
} )
}
/>
<TextInput
id="shipping-postal-code"
label={ __(
'Postal code',
'woo-gutenberg-products-block'
) }
value={ shippingFields.postalCode }
onChange={ ( newValue ) =>
setShippingFields( {
...shippingFields,
postalCode: newValue,
} )
}
/>
</InputRow>
<TextInput
id="shipping-phone"
type="tel"
label={ __(
'Phone',
'woo-gutenberg-products-block'
) }
value={ shippingFields.phone }
onChange={ ( newValue ) =>
setShippingFields( {
...shippingFields,
phone: newValue,
} )
}
/>
<CheckboxControl
className="wc-block-checkout__use-address-for-billing"
label={ __(
'Use same address for billing',
'woo-gutenberg-products-block'
) }
checked={ shippingFields.useSameForBilling }
onChange={ () =>
setShippingFields( {
...shippingFields,
useSameForBilling: ! shippingFields.useSameForBilling,
} )
}
/>
{ isEditor && <NoShipping /> }
</FormStep>
<FormStep
id="shipping-option"
className="wc-block-checkout__shipping-option"
title={ __(
'Shipping options',
'woo-gutenberg-products-block'
) }
description={ __(
'Select your shipping method below.',
'woo-gutenberg-products-block'
) }
stepNumber={ 3 }
>
<RadioControl
selected={ shippingMethod.method || 'collect' }
id="shipping-method"
onChange={ ( option ) =>
setShippingMethod( {
...shippingMethod,
method: option,
} )
}
options={ [
{
label: 'Click & Collect',
value: 'collect',
description:
'Pickup between 12:00 - 16:00 (Mon-Fri)',
secondaryLabel: 'FREE',
},
{
label: 'Regular shipping',
value: 'usps-normal',
description: 'Dispatched via USPS',
secondaryLabel: '€10.00',
secondaryDescription: '5 business days',
},
{
label: 'Express shipping',
value: 'ups-express',
description: 'Dispatched via USPS',
secondaryLabel: '€50.00',
secondaryDescription: '2 business days',
},
] }
/>
<CheckboxControl
className="wc-block-checkout__add-note"
label="Add order notes?"
checked={ shippingMethod.orderNote }
onChange={ () =>
setShippingMethod( {
...shippingMethod,
orderNote: ! shippingMethod.orderNote,
} )
}
/>
</FormStep>
</Fragment>
) }
<FormStep
id="payment-method"
className="wc-block-checkout__payment-method"
title={ __( 'Payment method', 'woo-gutenberg-products-block' ) }
description={ __(
'Select a payment method below.',
'woo-gutenberg-products-block'
) }
stepNumber={ 4 }
>
<Placeholder>Payment methods, coming soon</Placeholder>
<CheckboxControl
className="wc-block-checkout__save-card-info"
label={ __(
'Save payment information to my account for future purchases.',
{ shippingMethods.length > 0 && (
<Fragment>
<FormStep
id="shipping-fields"
className="wc-block-checkout__shipping-fields"
title={ __(
'Shipping address',
'woo-gutenberg-products-block'
) }
description={ __(
'Enter the physical address where you want us to deliver your order.',
'woo-gutenberg-products-block'
) }
stepNumber={ 2 }
>
<InputRow>
<TextInput
id="shipping-first-name"
label={ __(
'First name',
'woo-gutenberg-products-block'
) }
value={ shippingFields.firstName }
onChange={ ( newValue ) =>
setShippingFields( {
...shippingFields,
firstName: newValue,
} )
}
/>
<TextInput
id="shipping-last-name"
label={ __(
'Surname',
'woo-gutenberg-products-block'
) }
value={ shippingFields.lastName }
onChange={ ( newValue ) =>
setShippingFields( {
...shippingFields,
lastName: newValue,
} )
}
/>
</InputRow>
<TextInput
id="shipping-street-address"
label={ __(
'Street address',
'woo-gutenberg-products-block'
) }
value={ shippingFields.streetAddress }
onChange={ ( newValue ) =>
setShippingFields( {
...shippingFields,
streetAddress: newValue,
} )
}
/>
<TextInput
id="shipping-apartment"
label={ __(
'Apartment, suite, etc.',
'woo-gutenberg-products-block'
) }
value={ shippingFields.apartment }
onChange={ ( newValue ) =>
setShippingFields( {
...shippingFields,
apartment: newValue,
} )
}
/>
<InputRow>
<TextInput
id="shipping-country"
label={ __(
'Country',
'woo-gutenberg-products-block'
) }
value={ shippingFields.country }
onChange={ ( newValue ) =>
setShippingFields( {
...shippingFields,
country: newValue,
} )
}
/>
<TextInput
id="shipping-city"
label={ __(
'City',
'woo-gutenberg-products-block'
) }
value={ shippingFields.country }
onChange={ ( newValue ) =>
setShippingFields( {
...shippingFields,
country: newValue,
} )
}
/>
</InputRow>
<InputRow>
<TextInput
id="shipping-county"
label={ __(
'County',
'woo-gutenberg-products-block'
) }
value={ shippingFields.county }
onChange={ ( newValue ) =>
setShippingFields( {
...shippingFields,
county: newValue,
} )
}
/>
<TextInput
id="shipping-postal-code"
label={ __(
'Postal code',
'woo-gutenberg-products-block'
) }
value={ shippingFields.postalCode }
onChange={ ( newValue ) =>
setShippingFields( {
...shippingFields,
postalCode: newValue,
} )
}
/>
</InputRow>
<TextInput
id="shipping-phone"
type="tel"
label={ __(
'Phone',
'woo-gutenberg-products-block'
) }
value={ shippingFields.phone }
onChange={ ( newValue ) =>
setShippingFields( {
...shippingFields,
phone: newValue,
} )
}
/>
<CheckboxControl
className="wc-block-checkout__use-address-for-billing"
label={ __(
'Use same address for billing',
'woo-gutenberg-products-block'
) }
checked={ shippingFields.useSameForBilling }
onChange={ () =>
setShippingFields( {
...shippingFields,
useSameForBilling: ! shippingFields.useSameForBilling,
} )
}
/>
</FormStep>
<FormStep
id="shipping-option"
className="wc-block-checkout__shipping-option"
title={ __(
'Shipping options',
'woo-gutenberg-products-block'
) }
description={ __(
'Select your shipping method below.',
'woo-gutenberg-products-block'
) }
stepNumber={ 3 }
>
<RadioControl
selected={ shippingMethod.method || 'collect' }
id="shipping-method"
onChange={ ( option ) =>
setShippingMethod( {
...shippingMethod,
method: option,
} )
}
options={ [
{
label: 'Click & Collect',
value: 'collect',
description:
'Pickup between 12:00 - 16:00 (Mon-Fri)',
secondaryLabel: 'FREE',
},
{
label: 'Regular shipping',
value: 'usps-normal',
description: 'Dispatched via USPS',
secondaryLabel: '€10.00',
secondaryDescription: '5 business days',
},
{
label: 'Express shipping',
value: 'ups-express',
description: 'Dispatched via USPS',
secondaryLabel: '€50.00',
secondaryDescription: '2 business days',
},
] }
/>
<CheckboxControl
className="wc-block-checkout__add-note"
label="Add order notes?"
checked={ shippingMethod.orderNote }
onChange={ () =>
setShippingMethod( {
...shippingMethod,
orderNote: ! shippingMethod.orderNote,
} )
}
/>
</FormStep>
</Fragment>
) }
<FormStep
id="payment-method"
className="wc-block-checkout__payment-method"
title={ __(
'Payment method',
'woo-gutenberg-products-block'
) }
checked={ shouldSavePayment }
onChange={ () =>
setShouldSavePayment( ! shouldSavePayment )
}
/>
</FormStep>
</CheckoutForm>
description={ __(
'Select a payment method below.',
'woo-gutenberg-products-block'
) }
stepNumber={ 4 }
>
<PaymentMethods />
{ /*@todo this should be something the payment method controls*/ }
<CheckboxControl
className="wc-blocks-checkout__save-card-info"
label={ __(
'Save payment information to my account for future purchases.',
'woo-gutenberg-products-block'
) }
checked={ shouldSavePayment }
onChange={ () =>
setShouldSavePayment( ! shouldSavePayment )
}
/>
</FormStep>
</CheckoutForm>
</CheckoutProvider>
);
};

View File

@ -0,0 +1,2 @@
export const applePayImage =
"data:image/svg+xml,%3Csvg width='264' height='48' viewBox='0 0 264 48' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Crect width='264' height='48' rx='3' fill='black'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M125.114 16.6407C125.682 15.93 126.067 14.9756 125.966 14C125.135 14.0415 124.121 14.549 123.533 15.2602C123.006 15.8693 122.539 16.8641 122.661 17.7983C123.594 17.8797 124.526 17.3317 125.114 16.6407Z' fill='white'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M125.955 17.982C124.601 17.9011 123.448 18.7518 122.801 18.7518C122.154 18.7518 121.163 18.0224 120.092 18.0421C118.696 18.0629 117.402 18.8524 116.694 20.1079C115.238 22.6196 116.31 26.3453 117.726 28.3909C118.414 29.4028 119.242 30.5174 120.334 30.4769C121.366 30.4365 121.77 29.8087 123.024 29.8087C124.277 29.8087 124.641 30.4769 125.733 30.4567C126.865 30.4365 127.573 29.4443 128.261 28.4313C129.049 27.2779 129.373 26.1639 129.393 26.1027C129.373 26.0825 127.209 25.2515 127.189 22.7606C127.169 20.6751 128.888 19.6834 128.969 19.6217C127.998 18.1847 126.481 18.0224 125.955 17.982Z' fill='white'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M136.131 23.1804H138.834C140.886 23.1804 142.053 22.0752 142.053 20.1592C142.053 18.2432 140.886 17.1478 138.845 17.1478H136.131V23.1804ZM139.466 15.1582C142.411 15.1582 144.461 17.1903 144.461 20.1483C144.461 23.1172 142.369 25.1596 139.392 25.1596H136.131V30.3498H133.775V15.1582H139.466Z' fill='white'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M152.198 26.224V25.3712L149.579 25.5397C148.106 25.6341 147.339 26.182 147.339 27.14C147.339 28.0664 148.138 28.6667 149.39 28.6667C150.988 28.6667 152.198 27.6449 152.198 26.224ZM145.046 27.2032C145.046 25.2551 146.529 24.1395 149.263 23.971L152.198 23.7922V22.9498C152.198 21.7181 151.388 21.0442 149.947 21.0442C148.758 21.0442 147.896 21.6548 147.717 22.5916H145.592C145.656 20.6232 147.507 19.1914 150.01 19.1914C152.703 19.1914 154.459 20.602 154.459 22.7917V30.351H152.282V28.5298H152.229C151.609 29.719 150.241 30.4666 148.758 30.4666C146.571 30.4666 145.046 29.1612 145.046 27.2032Z' fill='white'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M156.461 34.4145V32.5934C156.608 32.6141 156.965 32.6354 157.155 32.6354C158.196 32.6354 158.785 32.1932 159.142 31.0564L159.353 30.3824L155.366 19.3281H157.827L160.604 28.298H160.657L163.434 19.3281H165.832L161.698 30.9402C160.752 33.6038 159.668 34.4778 157.376 34.4778C157.197 34.4778 156.618 34.4565 156.461 34.4145Z' fill='white'/%3E%3C/svg%3E%0A";

View File

@ -0,0 +1,9 @@
/**
* Internal dependencies
*/
import { applePayImage } from './apple-pay';
import { paypalImage } from './paypal';
export const expressApplePay = <img src={ applePayImage } alt="" />;
export const expressPaypal = <img src={ paypalImage } alt="" />;

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,32 @@
/**
* External dependencies
*/
import {
registerExpressPaymentMethod,
registerPaymentMethod,
} from '@woocommerce/blocks-registry';
/**
* Internal dependencies
*/
import { expressApplePay, expressPaypal } from './express-payment';
import { paypalPaymentMethod, ccPaymentMethod } from './payment-methods';
registerExpressPaymentMethod(
( Config ) =>
new Config( {
id: 'applepay',
activeContent: expressApplePay,
canMakePayment: Promise.resolve( true ),
} )
);
registerExpressPaymentMethod(
( Config ) =>
new Config( {
id: 'paypal',
activeContent: expressPaypal,
canMakePayment: Promise.resolve( true ),
} )
);
registerPaymentMethod( ( Config ) => new Config( paypalPaymentMethod ) );
registerPaymentMethod( ( Config ) => new Config( ccPaymentMethod ) );

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,31 @@
/**
* Internal dependencies
*/
import { paypalSvg } from './paypal';
import { ccSvg } from './cc';
export const paypalPaymentMethod = {
id: 'paypal',
label: <img src={ paypalSvg } alt="" />,
stepContent: <div>Billing steps</div>,
activeContent: (
<div>
<p>This is where paypal payment method stuff would be.</p>
</div>
),
canMakePayment: Promise.resolve( true ),
ariaLabel: 'paypal payment method',
};
export const ccPaymentMethod = {
id: 'cc',
label: <img src={ ccSvg } alt="" />,
stepContent: null,
activeContent: (
<div>
<p>This is where cc payment method stuff would be.</p>
</div>
),
canMakePayment: Promise.resolve( true ),
ariaLabel: 'credit-card-payment-method',
};

File diff suppressed because one or more lines are too long

View File

@ -2086,6 +2086,7 @@
"@woocommerce/currency": "2.0.0",
"@woocommerce/date": "2.0.0",
"@woocommerce/navigation": "4.0.0",
"@wordpress/components": "8.4.0",
"@wordpress/compose": "3.7.1",
"@wordpress/date": "3.5.0",
"@wordpress/element": "2.8.1",
@ -2115,6 +2116,85 @@
"react-transition-group": "2.9.0"
},
"dependencies": {
"@wordpress/components": {
"version": "8.4.0",
"resolved": "https://registry.npmjs.org/@wordpress/components/-/components-8.4.0.tgz",
"integrity": "sha512-Fr2L8b8RxUC6hPiN2pMVKGaeB25RA+g4ENpA32LzmuEGd0ITBXtZfFUv3+5FMcUe4y9KFQZbXxBRElBf4xA+SQ==",
"requires": {
"@babel/runtime": "^7.4.4",
"@wordpress/a11y": "^2.5.1",
"@wordpress/compose": "^3.8.0",
"@wordpress/deprecated": "^2.6.1",
"@wordpress/dom": "^2.6.0",
"@wordpress/element": "^2.9.0",
"@wordpress/hooks": "^2.6.0",
"@wordpress/i18n": "^3.7.0",
"@wordpress/is-shallow-equal": "^1.6.1",
"@wordpress/keycodes": "^2.7.0",
"@wordpress/rich-text": "^3.8.0",
"classnames": "^2.2.5",
"clipboard": "^2.0.1",
"dom-scroll-into-view": "^1.2.1",
"lodash": "^4.17.15",
"memize": "^1.0.5",
"moment": "^2.22.1",
"mousetrap": "^1.6.2",
"re-resizable": "^6.0.0",
"react-dates": "^17.1.1",
"react-spring": "^8.0.20",
"rememo": "^3.0.0",
"tinycolor2": "^1.4.1",
"uuid": "^3.3.2"
},
"dependencies": {
"@wordpress/compose": {
"version": "3.9.0",
"resolved": "https://registry.npmjs.org/@wordpress/compose/-/compose-3.9.0.tgz",
"integrity": "sha512-UhuHCQANb0ob5TBZSQl1y6i7FBTKXXI0jwzPKkWhLmMByjM8pIQte53nQu9RbwQ1I5ItHqhzKoy5wyaXdjgWkQ==",
"requires": {
"@babel/runtime": "^7.4.4",
"@wordpress/element": "^2.10.0",
"@wordpress/is-shallow-equal": "^1.6.1",
"lodash": "^4.17.15"
}
},
"@wordpress/element": {
"version": "2.10.0",
"resolved": "https://registry.npmjs.org/@wordpress/element/-/element-2.10.0.tgz",
"integrity": "sha512-CedhhFfcubM/lsluY7JPC49VG0/Ee0chOBJ1vyDEvIJmQk0bM3sLfgt5qVK773yivVOqD9blQMO+JihnaMXF8w==",
"requires": {
"@babel/runtime": "^7.4.4",
"@wordpress/escape-html": "^1.6.0",
"lodash": "^4.17.15",
"react": "^16.9.0",
"react-dom": "^16.9.0"
}
},
"@wordpress/i18n": {
"version": "3.7.0",
"resolved": "https://registry.npmjs.org/@wordpress/i18n/-/i18n-3.7.0.tgz",
"integrity": "sha512-yavu3yAKbSkEosQvEd0lCa064SdFFb8i6f7RfZGDq/TQfJHBaJQvRA4Hd/CtrOXqS6DLjw2rLNrVG4XcJFss1A==",
"requires": {
"@babel/runtime": "^7.4.4",
"gettext-parser": "^1.3.1",
"lodash": "^4.17.15",
"memize": "^1.0.5",
"sprintf-js": "^1.1.1",
"tannin": "^1.1.0"
}
},
"@wordpress/keycodes": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/@wordpress/keycodes/-/keycodes-2.7.0.tgz",
"integrity": "sha512-FPOFKSPY5WrvQuNr1l/WYn/ey+NoRO+RKQTlGR2EgpfWonqVGpV+CfEVyvgPVj8BBVcQHVDJYGkNEKDsoZ5l+g==",
"requires": {
"@babel/runtime": "^7.4.4",
"@wordpress/i18n": "^3.7.0",
"lodash": "^4.17.15"
}
}
}
},
"@wordpress/date": {
"version": "3.5.0",
"resolved": "https://registry.npmjs.org/@wordpress/date/-/date-3.5.0.tgz",
@ -2249,7 +2329,6 @@
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@wordpress/a11y/-/a11y-2.5.1.tgz",
"integrity": "sha512-aZidVNquAYrGopHLUr96vhX47kteAw41EC3zAsP2wxMkmTd2q2olCAaBo12a4rbj0JwNyw4Pq2uqis5hZVuMXw==",
"dev": true,
"requires": {
"@babel/runtime": "^7.4.4",
"@wordpress/dom-ready": "^2.5.1"
@ -2671,7 +2750,6 @@
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/@wordpress/dom/-/dom-2.6.0.tgz",
"integrity": "sha512-ERti77Y7Y0Oix7jfvIvBRX6Jx7hTvg6k6ke6LmeuMo+V7g5abmNEHLU4tL/dGSLNw9/SShStTIPu9Vg2IL44WA==",
"dev": true,
"requires": {
"@babel/runtime": "^7.4.4",
"lodash": "^4.17.15"
@ -2681,7 +2759,6 @@
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@wordpress/dom-ready/-/dom-ready-2.5.1.tgz",
"integrity": "sha512-HPEPWdShm/xJjQJVOOWpehvt4wtK0W5D/X0wpLfWAQjPOsTMTKnlRsRL3vTjIlgTrVZan1rv+3ZJv7AwH/1U5g==",
"dev": true,
"requires": {
"@babel/runtime": "^7.4.4"
}
@ -2770,7 +2847,6 @@
"version": "2.10.0",
"resolved": "https://registry.npmjs.org/@wordpress/element/-/element-2.10.0.tgz",
"integrity": "sha512-CedhhFfcubM/lsluY7JPC49VG0/Ee0chOBJ1vyDEvIJmQk0bM3sLfgt5qVK773yivVOqD9blQMO+JihnaMXF8w==",
"dev": true,
"requires": {
"@babel/runtime": "^7.4.4",
"@wordpress/escape-html": "^1.6.0",
@ -3007,7 +3083,6 @@
"version": "3.9.0",
"resolved": "https://registry.npmjs.org/@wordpress/rich-text/-/rich-text-3.9.0.tgz",
"integrity": "sha512-i+EqcigVIGPhjKyLI8VlX9EX8Cf1B7h8Hz+2S5aO47PrSO8W5Q0FCqMjONCqGCHdXXxnWHdz6OUrKl0otbqYxw==",
"dev": true,
"requires": {
"@babel/runtime": "^7.4.4",
"@wordpress/compose": "^3.9.0",
@ -3028,7 +3103,6 @@
"version": "3.9.0",
"resolved": "https://registry.npmjs.org/@wordpress/compose/-/compose-3.9.0.tgz",
"integrity": "sha512-UhuHCQANb0ob5TBZSQl1y6i7FBTKXXI0jwzPKkWhLmMByjM8pIQte53nQu9RbwQ1I5ItHqhzKoy5wyaXdjgWkQ==",
"dev": true,
"requires": {
"@babel/runtime": "^7.4.4",
"@wordpress/element": "^2.10.0",
@ -3040,7 +3114,6 @@
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/@wordpress/keycodes/-/keycodes-2.7.0.tgz",
"integrity": "sha512-FPOFKSPY5WrvQuNr1l/WYn/ey+NoRO+RKQTlGR2EgpfWonqVGpV+CfEVyvgPVj8BBVcQHVDJYGkNEKDsoZ5l+g==",
"dev": true,
"requires": {
"@babel/runtime": "^7.4.4",
"@wordpress/i18n": "^3.7.0",
@ -6228,7 +6301,6 @@
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.4.tgz",
"integrity": "sha512-Vw26VSLRpJfBofiVaFb/I8PVfdI1OxKcYShe6fm0sP/DtmiWQNCjhM/okTvdCo0G+lMMm1rMYbk4IK4x1X+kgQ==",
"dev": true,
"requires": {
"good-listener": "^1.2.2",
"select": "^1.1.2",
@ -7410,8 +7482,7 @@
"delegate": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz",
"integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==",
"dev": true
"integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw=="
},
"delegates": {
"version": "1.0.0",
@ -7530,8 +7601,7 @@
"dom-scroll-into-view": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/dom-scroll-into-view/-/dom-scroll-into-view-1.2.1.tgz",
"integrity": "sha1-6PNnMt0ImwIBqI14Fdw/iObWbH4=",
"dev": true
"integrity": "sha1-6PNnMt0ImwIBqI14Fdw/iObWbH4="
},
"dom-serializer": {
"version": "0.1.1",
@ -8710,8 +8780,7 @@
"fast-memoize": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/fast-memoize/-/fast-memoize-2.5.1.tgz",
"integrity": "sha512-xdmw296PCL01tMOXx9mdJSmWY29jQgxyuZdq0rEHMu+Tpe1eOEtCycoG6chzlcrWsNgpZP7oL8RiQr7+G6Bl6g==",
"dev": true
"integrity": "sha512-xdmw296PCL01tMOXx9mdJSmWY29jQgxyuZdq0rEHMu+Tpe1eOEtCycoG6chzlcrWsNgpZP7oL8RiQr7+G6Bl6g=="
},
"fastq": {
"version": "1.6.0",
@ -10060,7 +10129,6 @@
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz",
"integrity": "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=",
"dev": true,
"requires": {
"delegate": "^3.1.2"
}
@ -13711,8 +13779,7 @@
"mousetrap": {
"version": "1.6.3",
"resolved": "https://registry.npmjs.org/mousetrap/-/mousetrap-1.6.3.tgz",
"integrity": "sha512-bd+nzwhhs9ifsUrC2tWaSgm24/oo2c83zaRyZQF06hYA6sANfsXHtnZ19AbbbDXCDzeH5nZBSQ4NvCjgD62tJA==",
"dev": true
"integrity": "sha512-bd+nzwhhs9ifsUrC2tWaSgm24/oo2c83zaRyZQF06hYA6sANfsXHtnZ19AbbbDXCDzeH5nZBSQ4NvCjgD62tJA=="
},
"move-concurrently": {
"version": "1.0.1",
@ -16034,7 +16101,6 @@
"version": "6.1.1",
"resolved": "https://registry.npmjs.org/re-resizable/-/re-resizable-6.1.1.tgz",
"integrity": "sha512-ngzX5xbXi9LlIghJUYZaBDkJUIMLYqO3tQ2cJZoNprCRGhfHnbyufKm51MZRIOBlLigLzPPFKBxQE8ZLezKGfA==",
"dev": true,
"requires": {
"fast-memoize": "^2.5.1"
}
@ -16191,7 +16257,6 @@
"version": "8.0.27",
"resolved": "https://registry.npmjs.org/react-spring/-/react-spring-8.0.27.tgz",
"integrity": "sha512-nDpWBe3ZVezukNRandTeLSPcwwTMjNVu1IDq9qA/AMiUqHuRN4BeSWvKr3eIxxg1vtiYiOLy4FqdfCP5IoP77g==",
"dev": true,
"requires": {
"@babel/runtime": "^7.3.1",
"prop-types": "^15.5.8"
@ -16564,8 +16629,7 @@
"rememo": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/rememo/-/rememo-3.0.0.tgz",
"integrity": "sha512-eWtut/7pqMRnSccbexb647iPjN7ir6Tmf4RG92ZVlykFEkHqGYy9tWnpHH3I+FS+WQ6lQ1i1iDgarYzGKgTcRQ==",
"dev": true
"integrity": "sha512-eWtut/7pqMRnSccbexb647iPjN7ir6Tmf4RG92ZVlykFEkHqGYy9tWnpHH3I+FS+WQ6lQ1i1iDgarYzGKgTcRQ=="
},
"remove-trailing-separator": {
"version": "1.1.0",
@ -17322,8 +17386,7 @@
"select": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz",
"integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0=",
"dev": true
"integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0="
},
"semver": {
"version": "5.7.1",
@ -19019,8 +19082,7 @@
"tiny-emitter": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
"integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==",
"dev": true
"integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q=="
},
"tiny-invariant": {
"version": "1.0.6",
@ -19060,8 +19122,7 @@
"tinycolor2": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.1.tgz",
"integrity": "sha1-9PrTM0R7wLB9TcjpIJ2POaisd+g=",
"dev": true
"integrity": "sha1-9PrTM0R7wLB9TcjpIJ2POaisd+g="
},
"tmp": {
"version": "0.0.33",
@ -19659,8 +19720,7 @@
"uuid": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz",
"integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==",
"dev": true
"integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ=="
},
"v8-compile-cache": {
"version": "2.1.0",

View File

@ -11,6 +11,7 @@
"@woocommerce/settings": "assets/js/settings/shared",
"@woocommerce/block-settings": "assets/js/settings/blocks",
"@woocommerce/block-components(.*)$": "assets/js/components/$1",
"@woocommerce/blocks-registry(.*)$": "assets/js/blocks-registry/$1",
"@woocommerce/block-hocs(.*)$": "assets/js/hocs/$1",
"@woocommerce/base-components(.*)$": "assets/js/base/components/$1",
"@woocommerce/base-context(.*)$": "assets/js/base/context/$1",

View File

@ -63,6 +63,11 @@ const CoreConfig = {
loader: 'babel-loader?cacheDirectory',
options: {
presets: [ '@wordpress/babel-preset-default' ],
plugins: [
require.resolve(
'@babel/plugin-proposal-class-properties'
),
].filter( Boolean ),
},
},
},