* Remove i2 from build

* Remove i2 php registration

* Rename old checkout dir

* Rename i2 dir

* Migrate i1 to i2 code

* Register block metadata to fix frontend rendering when migrating to i2

* Register in correct order

* Missing styles

* add register-components to side effectful list

* wrong block class in e2e test

* wp prefix missing on selector

* Fix top level block test

* Reselect labels to work around rerendering

* missing empty cart styles

* Consolodate duplicate styles

* remove init code

* update selectBlockByName

Co-authored-by: Nadir Seghir <nadir.seghir@gmail.com>
This commit is contained in:
Mike Jolley 2021-09-16 13:16:21 +01:00 committed by GitHub
parent c967f72507
commit 5c08c75612
165 changed files with 1206 additions and 3404 deletions

View File

@ -138,16 +138,13 @@ const renderInnerBlocks = ( {
const parsedElement = parse( element.outerHTML );
if ( isValidElement( parsedElement ) ) {
const elementChildren =
element.children && element.children.length
? renderInnerBlocks( {
const elementChildren = renderInnerBlocks( {
block,
blockMap,
children: element.children,
children: element.children || [],
depth: depth + 1,
blockWrapper,
} )
: null;
} );
return elementChildren
? cloneElement(
parsedElement,
@ -243,15 +240,12 @@ export const renderParentBlock = ( {
* In addition to getProps, we need to render and return the children. This adds children to props.
*/
const getPropsWithChildren = ( element: Element, i: number ) => {
const children =
element.children && element.children.length
? renderInnerBlocks( {
const children = renderInnerBlocks( {
block: blockName,
blockMap,
children: element.children,
children: element.children || [],
blockWrapper,
} )
: null;
} );
return { ...getProps( element, i ), children };
};
/**

View File

@ -119,3 +119,11 @@
}
}
}
.editor-styles-wrapper {
.wp-block h4.wc-block-components-checkout-step__title {
@include font-size(regular);
line-height: 24px;
margin: 0 $gap-small 0 0;
}
}

View File

@ -3,6 +3,7 @@
align-items: flex-start;
display: flex;
position: relative;
margin-top: em($gap-large);
.wc-block-components-checkbox__input[type="checkbox"] {
font-size: 1em;

View File

@ -1,8 +0,0 @@
export const PRODUCT_OUT_OF_STOCK = 'woocommerce_product_out_of_stock';
export const PRODUCT_NOT_PURCHASABLE =
'woocommerce_rest_cart_product_is_not_purchasable';
export const PRODUCT_NOT_ENOUGH_STOCK =
'woocommerce_rest_cart_product_no_stock';
export const PRODUCT_SOLD_INDIVIDUALLY =
'woocommerce_rest_cart_product_sold_individually';
export const GENERIC_CART_ITEM_ERROR = 'woocommerce_rest_cart_item_error';

View File

@ -1,136 +0,0 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { CART_URL } from '@woocommerce/block-settings';
import { Icon, removeCart } from '@woocommerce/icons';
import { getSetting } from '@woocommerce/settings';
import { decodeEntities } from '@wordpress/html-entities';
/**
* Internal dependencies
*/
import {
PRODUCT_OUT_OF_STOCK,
PRODUCT_NOT_PURCHASABLE,
PRODUCT_NOT_ENOUGH_STOCK,
PRODUCT_SOLD_INDIVIDUALLY,
GENERIC_CART_ITEM_ERROR,
} from './constants';
const cartItemErrorCodes = [
PRODUCT_OUT_OF_STOCK,
PRODUCT_NOT_PURCHASABLE,
PRODUCT_NOT_ENOUGH_STOCK,
PRODUCT_SOLD_INDIVIDUALLY,
GENERIC_CART_ITEM_ERROR,
];
/**
* When an order was not created for the checkout, for example, when an item
* was out of stock, this component will be shown instead of the checkout form.
*
* The error message is derived by the hydrated API request passed to the
* checkout block.
*/
const CheckoutOrderError = () => {
const preloadedApiRequests = getSetting( 'preloadedApiRequests', {} );
const checkoutData = {
code: '',
message: '',
...( preloadedApiRequests[ '/wc/store/checkout' ]?.body || {} ),
};
const errorData = {
code: checkoutData.code || 'unknown',
message:
decodeEntities( checkoutData.message ) ||
__(
'There was a problem checking out. Please try again. If the problem persists, please get in touch with us so we can assist.',
'woo-gutenberg-products-block'
),
};
return (
<div className="wc-block-checkout-error">
<Icon
className="wc-block-checkout-error__image"
alt=""
srcElement={ removeCart }
size={ 100 }
/>
<ErrorTitle errorData={ errorData } />
<ErrorMessage errorData={ errorData } />
<ErrorButton errorData={ errorData } />
</div>
);
};
/**
* Get the error message to display.
*
* @param {Object} props Incoming props for the component.
* @param {Object} props.errorData Object containing code and message.
*/
const ErrorTitle = ( { errorData } ) => {
let heading = __( 'Checkout error', 'woo-gutenberg-products-block' );
if ( cartItemErrorCodes.includes( errorData.code ) ) {
heading = __(
'There is a problem with your cart',
'woo-gutenberg-products-block'
);
}
return (
<strong className="wc-block-checkout-error_title">{ heading }</strong>
);
};
/**
* Get the error message to display.
*
* @param {Object} props Incoming props for the component.
* @param {Object} props.errorData Object containing code and message.
*/
const ErrorMessage = ( { errorData } ) => {
let message = errorData.message;
if ( cartItemErrorCodes.includes( errorData.code ) ) {
message =
message +
' ' +
__(
'Please edit your cart and try again.',
'woo-gutenberg-products-block'
);
}
return <p className="wc-block-checkout-error__description">{ message }</p>;
};
/**
* Get the CTA button to display.
*
* @param {Object} props Incoming props for the component.
* @param {Object} props.errorData Object containing code and message.
*/
const ErrorButton = ( { errorData } ) => {
let buttonText = __( 'Retry', 'woo-gutenberg-products-block' );
let buttonUrl = 'javascript:window.location.reload(true)';
if ( cartItemErrorCodes.includes( errorData.code ) ) {
buttonText = __( 'Edit your cart', 'woo-gutenberg-products-block' );
buttonUrl = CART_URL;
}
return (
<span className="wp-block-button">
<a href={ buttonUrl } className="wp-block-button__link">
{ buttonText }
</a>
</span>
);
};
export default CheckoutOrderError;

View File

@ -1,37 +0,0 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { SHOP_URL } from '@woocommerce/block-settings';
import { Icon, cart } from '@woocommerce/icons';
const EmptyCart = () => {
return (
<div className="wc-block-checkout-empty">
<Icon
className="wc-block-checkout-empty__image"
alt=""
srcElement={ cart }
size={ 100 }
/>
<strong className="wc-block-checkout-empty__title">
{ __( 'Your cart is empty!', 'woo-gutenberg-products-block' ) }
</strong>
<p className="wc-block-checkout-empty__description">
{ __(
"Checkout is not available whilst your cart is empty—please take a look through our store and come back when you're ready to place an order.",
'woo-gutenberg-products-block'
) }
</p>
{ SHOP_URL && (
<span className="wp-block-button">
<a href={ SHOP_URL } className="wp-block-button__link">
{ __( 'Browse store', 'woo-gutenberg-products-block' ) }
</a>
</span>
) }
</div>
);
};
export default EmptyCart;

View File

@ -1,37 +0,0 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { Icon, button } from '@wordpress/icons';
import { registerFeaturePluginBlockType } from '@woocommerce/block-settings';
/**
* Internal dependencies
*/
import attributes from './attributes';
import { Edit, Save } from './edit';
registerFeaturePluginBlockType( 'woocommerce/checkout-actions-block', {
title: __( 'Actions', 'woo-gutenberg-products-block' ),
category: 'woocommerce',
description: __(
'Allow customers to place their order.',
'woo-gutenberg-products-block'
),
icon: {
src: <Icon icon={ button } />,
foreground: '#874FB9',
},
supports: {
align: false,
html: false,
multiple: false,
reusable: false,
inserter: false,
},
parent: [ 'woocommerce/checkout-fields-block' ],
attributes,
apiVersion: 2,
edit: Edit,
save: Save,
} );

View File

@ -1,37 +0,0 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { Icon, address } from '@woocommerce/icons';
import { registerFeaturePluginBlockType } from '@woocommerce/block-settings';
/**
* Internal dependencies
*/
import { Edit, Save } from './edit';
import attributes from './attributes';
registerFeaturePluginBlockType( 'woocommerce/checkout-billing-address-block', {
title: __( 'Billing Address', 'woo-gutenberg-products-block' ),
category: 'woocommerce',
description: __(
"Collect your customer's billing address.",
'woo-gutenberg-products-block'
),
icon: {
src: <Icon srcElement={ address } />,
foreground: '#874FB9',
},
supports: {
align: false,
html: false,
multiple: false,
reusable: false,
inserter: false,
},
parent: [ 'woocommerce/checkout-fields-block' ],
attributes,
apiVersion: 2,
edit: Edit,
save: Save,
} );

View File

@ -1,40 +0,0 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { Icon, contact } from '@woocommerce/icons';
import { registerFeaturePluginBlockType } from '@woocommerce/block-settings';
/**
* Internal dependencies
*/
import { Edit, Save } from './edit';
import attributes from './attributes';
registerFeaturePluginBlockType(
'woocommerce/checkout-contact-information-block',
{
title: __( 'Contact Information', 'woo-gutenberg-products-block' ),
category: 'woocommerce',
description: __(
"Collect your customer's contact information.",
'woo-gutenberg-products-block'
),
icon: {
src: <Icon srcElement={ contact } />,
foreground: '#874FB9',
},
supports: {
align: false,
html: false,
multiple: false,
reusable: false,
inserter: false,
},
parent: [ 'woocommerce/checkout-fields-block' ],
attributes,
apiVersion: 2,
edit: Edit,
save: Save,
}
);

View File

@ -1,44 +0,0 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { Icon, card } from '@woocommerce/icons';
import { registerFeaturePluginBlockType } from '@woocommerce/block-settings';
/**
* Internal dependencies
*/
import { Edit, Save } from './edit';
registerFeaturePluginBlockType( 'woocommerce/checkout-express-payment-block', {
title: __( 'Express Checkout', 'woo-gutenberg-products-block' ),
category: 'woocommerce',
description: __(
'Provide an express payment option for your customers.',
'woo-gutenberg-products-block'
),
icon: {
src: <Icon srcElement={ card } />,
foreground: '#874FB9',
},
supports: {
align: false,
html: false,
multiple: false,
reusable: false,
inserter: false,
},
parent: [ 'woocommerce/checkout-fields-block' ],
attributes: {
lock: {
type: 'object',
default: {
remove: true,
move: true,
},
},
},
apiVersion: 2,
edit: Edit,
save: Save,
} );

View File

@ -1,35 +0,0 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { Icon, column } from '@wordpress/icons';
import { registerFeaturePluginBlockType } from '@woocommerce/block-settings';
/**
* Internal dependencies
*/
import { Edit, Save } from './edit';
registerFeaturePluginBlockType( 'woocommerce/checkout-fields-block', {
title: __( 'Checkout Fields Block', 'woo-gutenberg-products-block' ),
category: 'woocommerce',
description: __(
'Column containing checkout address fields.',
'woo-gutenberg-products-block'
),
icon: {
src: <Icon icon={ column } />,
foreground: '#874FB9',
},
supports: {
align: false,
html: false,
multiple: false,
reusable: false,
inserter: false,
},
parent: [ 'woocommerce/checkout-i2' ],
apiVersion: 2,
edit: Edit,
save: Save,
} );

View File

@ -1,43 +0,0 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { Icon, notes } from '@woocommerce/icons';
import { registerFeaturePluginBlockType } from '@woocommerce/block-settings';
/**
* Internal dependencies
*/
import { Edit, Save } from './edit';
registerFeaturePluginBlockType( 'woocommerce/checkout-order-note-block', {
title: __( 'Order Note', 'woo-gutenberg-products-block' ),
category: 'woocommerce',
description: __(
'Allow customers to add a note to their order.',
'woo-gutenberg-products-block'
),
icon: {
src: <Icon srcElement={ notes } />,
foreground: '#874FB9',
},
supports: {
align: false,
html: false,
multiple: false,
reusable: false,
},
parent: [ 'woocommerce/checkout-fields-block' ],
attributes: {
lock: {
type: 'object',
default: {
move: true,
remove: true,
},
},
},
apiVersion: 2,
edit: Edit,
save: Save,
} );

View File

@ -1,37 +0,0 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { Icon, totals } from '@woocommerce/icons';
import { registerFeaturePluginBlockType } from '@woocommerce/block-settings';
/**
* Internal dependencies
*/
import { Edit, Save } from './edit';
import attributes from './attributes';
registerFeaturePluginBlockType( 'woocommerce/checkout-order-summary-block', {
title: __( 'Order Summary', 'woo-gutenberg-products-block' ),
category: 'woocommerce',
description: __(
'Show customers a summary of their order.',
'woo-gutenberg-products-block'
),
icon: {
src: <Icon srcElement={ totals } />,
foreground: '#874FB9',
},
supports: {
align: false,
html: false,
multiple: false,
reusable: false,
inserter: false,
},
parent: [ 'woocommerce/checkout-totals-block' ],
attributes,
apiVersion: 2,
edit: Edit,
save: Save,
} );

View File

@ -1,37 +0,0 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { Icon, card } from '@woocommerce/icons';
import { registerFeaturePluginBlockType } from '@woocommerce/block-settings';
/**
* Internal dependencies
*/
import { Edit, Save } from './edit';
import attributes from './attributes';
registerFeaturePluginBlockType( 'woocommerce/checkout-payment-block', {
title: __( 'Payment Options', 'woo-gutenberg-products-block' ),
category: 'woocommerce',
description: __(
'Payment options for your store.',
'woo-gutenberg-products-block'
),
icon: {
src: <Icon srcElement={ card } />,
foreground: '#874FB9',
},
supports: {
align: false,
html: false,
multiple: false,
reusable: false,
inserter: false,
},
parent: [ 'woocommerce/checkout-fields-block' ],
attributes,
apiVersion: 2,
edit: Edit,
save: Save,
} );

View File

@ -1,37 +0,0 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { Icon, address } from '@woocommerce/icons';
import { registerFeaturePluginBlockType } from '@woocommerce/block-settings';
/**
* Internal dependencies
*/
import { Edit, Save } from './edit';
import attributes from './attributes';
registerFeaturePluginBlockType( 'woocommerce/checkout-shipping-address-block', {
title: __( 'Shipping Address', 'woo-gutenberg-products-block' ),
category: 'woocommerce',
description: __(
"Collect your customer's shipping address.",
'woo-gutenberg-products-block'
),
icon: {
src: <Icon srcElement={ address } />,
foreground: '#874FB9',
},
supports: {
align: false,
html: false,
multiple: false,
reusable: false,
inserter: false,
},
parent: [ 'woocommerce/checkout-fields-block' ],
attributes,
apiVersion: 2,
edit: Edit,
save: Save,
} );

View File

@ -1,37 +0,0 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { Icon, truck } from '@woocommerce/icons';
import { registerFeaturePluginBlockType } from '@woocommerce/block-settings';
/**
* Internal dependencies
*/
import { Edit, Save } from './edit';
import attributes from './attributes';
registerFeaturePluginBlockType( 'woocommerce/checkout-shipping-methods-block', {
title: __( 'Shipping Options', 'woo-gutenberg-products-block' ),
category: 'woocommerce',
description: __(
'Shipping options for your store.',
'woo-gutenberg-products-block'
),
icon: {
src: <Icon srcElement={ truck } />,
foreground: '#874FB9',
},
supports: {
align: false,
html: false,
multiple: false,
reusable: false,
inserter: false,
},
parent: [ 'woocommerce/checkout-fields-block' ],
attributes,
apiVersion: 2,
edit: Edit,
save: Save,
} );

View File

@ -1,42 +0,0 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { Icon, asterisk } from '@woocommerce/icons';
import { registerFeaturePluginBlockType } from '@woocommerce/block-settings';
/**
* Internal dependencies
*/
import { Edit, Save } from './edit';
registerFeaturePluginBlockType( 'woocommerce/checkout-terms-block', {
title: __( 'Terms and Conditions', 'woo-gutenberg-products-block' ),
category: 'woocommerce',
description: __(
'Ensure customers agree to your terms and conditions and privacy policy.',
'woo-gutenberg-products-block'
),
icon: {
src: <Icon srcElement={ asterisk } />,
foreground: '#874FB9',
},
supports: {
align: false,
html: false,
multiple: false,
reusable: false,
},
parent: [ 'woocommerce/checkout-fields-block' ],
attributes: {
checkbox: {
type: 'boolean',
default: false,
},
text: {
type: 'string',
required: false,
},
},
edit: Edit,
save: Save,
} );

View File

@ -1,36 +0,0 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { Icon, column } from '@wordpress/icons';
import { registerFeaturePluginBlockType } from '@woocommerce/block-settings';
/**
* Internal dependencies
*/
import { Edit, Save } from './edit';
registerFeaturePluginBlockType( 'woocommerce/checkout-totals-block', {
title: __( 'Checkout Totals Block', 'woo-gutenberg-products-block' ),
category: 'woocommerce',
description: __(
'Column containing the checkout totals.',
'woo-gutenberg-products-block'
),
icon: {
src: <Icon icon={ column } />,
foreground: '#874FB9',
},
supports: {
align: false,
html: false,
multiple: false,
reusable: false,
inserter: false,
},
parent: [ 'woocommerce/checkout-i2' ],
attributes: {},
apiVersion: 2,
edit: Edit,
save: Save,
} );

View File

@ -1,98 +0,0 @@
// Loading skeleton.
.is-loading.wp-block-woocommerce-checkout-i2 {
.wp-block-woocommerce-checkout-totals-block,
.wp-block-woocommerce-checkout-fields-block {
> div {
@include placeholder();
margin: 0 0 1.5em 0;
display: none;
}
.wp-block-woocommerce-checkout-contact-information-block,
.wp-block-woocommerce-checkout-payment-block {
min-height: 10em;
display: block;
}
.wp-block-woocommerce-checkout-shipping-address-block {
min-height: 24em;
display: block;
}
.wp-block-woocommerce-checkout-actions-block {
width: 50%;
min-height: 4em;
margin-left: 50%;
display: block;
}
.wp-block-woocommerce-checkout-order-summary-block {
min-height: 47em;
display: block;
}
}
// @todo these styles replace the need for SidebarLayout styles. We define styles here so placeholder elements (loading state) for the checkout has the same sidebar type layout before JS loads.
&.wp-block-woocommerce-checkout-i2 {
display: flex;
flex-wrap: wrap;
margin: 0 auto $gap;
position: relative;
.wp-block-woocommerce-checkout-fields-block {
box-sizing: border-box;
margin: 0;
padding-right: percentage(math.div($gap-largest, 1060px)); // ~1060px is the default width of the content area in Storefront.
width: 65%;
}
.wp-block-woocommerce-checkout-totals-block {
box-sizing: border-box;
margin: 0;
padding-left: percentage(math.div($gap-large, 1060px));
width: 35%;
.wc-block-components-panel > h2 {
@include font-size(regular);
@include reset-box();
@include reset-typography();
.wc-block-components-panel__button {
font-weight: 400;
}
}
}
}
.is-medium,
.is-small,
.is-mobile {
&.wp-block-woocommerce-checkout-i2 {
flex-direction: column;
margin: 0 auto $gap;
.wp-block-woocommerce-checkout-fields-block {
padding: 0;
width: 100%;
}
.wp-block-woocommerce-checkout-totals-block {
padding: 0;
width: 100%;
}
}
}
.is-large {
.wp-block-woocommerce-checkout-totals-block {
.wc-block-components-totals-item,
.wc-block-components-panel {
padding-left: $gap;
padding-right: $gap;
}
}
}
// For Twenty Twenty we need to increase specificity a bit more.
.theme-twentytwenty {
.wp-block-woocommerce-checkout-totals-block .wc-block-components-panel > h2 {
@include font-size(large);
@include reset-box();
}
}
}

View File

@ -1,62 +0,0 @@
/**
* External dependencies
*/
import { getSetting } from '@woocommerce/settings';
const blockAttributes = {
isPreview: {
type: 'boolean',
default: false,
save: false,
},
showCompanyField: {
type: 'boolean',
default: false,
},
requireCompanyField: {
type: 'boolean',
default: false,
},
allowCreateAccount: {
type: 'boolean',
default: false,
},
showApartmentField: {
type: 'boolean',
default: true,
},
showPhoneField: {
type: 'boolean',
default: true,
},
requirePhoneField: {
type: 'boolean',
default: false,
},
showOrderNotes: {
type: 'boolean',
default: true,
},
showPolicyLinks: {
type: 'boolean',
default: true,
},
showReturnToCart: {
type: 'boolean',
default: true,
},
cartPageId: {
type: 'number',
default: 0,
},
hasDarkControls: {
type: 'boolean',
default: getSetting( 'hasDarkEditorStyleSupport', false ),
},
showRateAfterTaxName: {
type: 'boolean',
default: getSetting( 'displayCartPricesIncludingTax', false ),
},
};
export default blockAttributes;

View File

@ -3,7 +3,7 @@
*/
import { getSetting } from '@woocommerce/settings';
export const blockName = 'woocommerce/checkout-i2';
export const blockName = 'woocommerce/checkout';
export const blockAttributes = {
isPreview: {
type: 'boolean',

View File

@ -1,151 +0,0 @@
/**
* External dependencies
*/
import classnames from 'classnames';
import { useEffect } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import {
PlaceOrderButton,
Policies,
ReturnToCartButton,
} from '@woocommerce/base-components/cart-checkout';
import {
useCheckoutContext,
useEditorContext,
useValidationContext,
} from '@woocommerce/base-context';
import { useStoreCart, useStoreNotices } from '@woocommerce/base-context/hooks';
import {
Sidebar,
SidebarLayout,
Main,
} from '@woocommerce/base-components/sidebar-layout';
import withScrollToTop from '@woocommerce/base-hocs/with-scroll-to-top';
import { isWcVersion, getSetting } from '@woocommerce/settings';
/**
* Internal dependencies
*/
import CheckoutForm from './form';
import CheckoutSidebar from './sidebar';
import CheckoutOrderError from './checkout-order-error';
import { CheckoutExpressPayment } from '../payment-methods';
import { LOGIN_TO_CHECKOUT_URL } from './utils';
import './style.scss';
/**
* Main Checkout Component.
*
* @param {Object} props Component props.
* @param {Object} props.attributes Incoming block attributes.
* @param {function(any):any} props.scrollToTop Function for scrolling to top.
*/
const Checkout = ( { attributes, scrollToTop } ) => {
const { isEditor } = useEditorContext();
const {
cartItems,
cartTotals,
cartCoupons,
cartFees,
cartNeedsPayment,
} = useStoreCart();
const {
hasOrder,
hasError: checkoutHasError,
isIdle: checkoutIsIdle,
customerId,
} = useCheckoutContext();
const {
hasValidationErrors,
showAllValidationErrors,
} = useValidationContext();
const { hasNoticesOfType } = useStoreNotices();
const hasErrorsToDisplay =
checkoutIsIdle &&
checkoutHasError &&
( hasValidationErrors || hasNoticesOfType( 'default' ) );
// Checkout signup is feature gated to WooCommerce 4.7 and newer;
// uses updated my-account/lost-password screen from 4.7+ for
// setting initial password.
const allowCreateAccount =
attributes.allowCreateAccount && isWcVersion( '4.7.0', '>=' );
useEffect( () => {
if ( hasErrorsToDisplay ) {
showAllValidationErrors();
scrollToTop( { focusableSelector: 'input:invalid' } );
}
}, [ hasErrorsToDisplay, scrollToTop, showAllValidationErrors ] );
if ( ! isEditor && ! hasOrder ) {
return <CheckoutOrderError />;
}
if (
! isEditor &&
! customerId &&
! getSetting( 'checkoutAllowsGuest', false ) &&
! ( allowCreateAccount && getSetting( 'checkoutAllowsSignup', false ) )
) {
return (
<>
{ __(
'You must be logged in to checkout. ',
'woo-gutenberg-products-block'
) }
<a href={ LOGIN_TO_CHECKOUT_URL }>
{ __(
'Click here to log in.',
'woo-gutenberg-products-block'
) }
</a>
</>
);
}
const checkoutClassName = classnames( 'wc-block-checkout', {
'has-dark-controls': attributes.hasDarkControls,
} );
return (
<>
<SidebarLayout className={ checkoutClassName }>
<Main className="wc-block-checkout__main">
{ cartNeedsPayment && <CheckoutExpressPayment /> }
<CheckoutForm
showApartmentField={ attributes.showApartmentField }
showCompanyField={ attributes.showCompanyField }
showOrderNotes={ attributes.showOrderNotes }
showPhoneField={ attributes.showPhoneField }
requireCompanyField={ attributes.requireCompanyField }
requirePhoneField={ attributes.requirePhoneField }
allowCreateAccount={ allowCreateAccount }
/>
<div className="wc-block-checkout__actions">
{ attributes.showReturnToCart && (
<ReturnToCartButton
link={ getSetting(
'page-' + attributes?.cartPageId,
false
) }
/>
) }
<PlaceOrderButton />
</div>
{ attributes.showPolicyLinks && <Policies /> }
</Main>
<Sidebar className="wc-block-checkout__sidebar">
<CheckoutSidebar
cartCoupons={ cartCoupons }
cartItems={ cartItems }
cartTotals={ cartTotals }
cartFees={ cartFees }
showRateAfterTaxName={ attributes.showRateAfterTaxName }
/>
</Sidebar>
</SidebarLayout>
</>
);
};
export default withScrollToTop( Checkout );

View File

@ -10,6 +10,7 @@ import { decodeEntities } from '@wordpress/html-entities';
/**
* Internal dependencies
*/
import './style.scss';
import {
PRODUCT_OUT_OF_STOCK,
PRODUCT_NOT_PURCHASABLE,

View File

@ -0,0 +1,21 @@
.wc-block-checkout-error {
padding: $gap-largest;
text-align: center;
width: 100%;
.wc-block-checkout-error__image {
max-width: 150px;
margin: 0 auto 1em;
display: block;
color: inherit;
}
.wc-block-checkout-error__title {
display: block;
margin: 0;
font-weight: bold;
}
.wc-block-checkout-error__description {
display: block;
margin: 0.25em 0 1em 0;
}
}

View File

@ -1,399 +0,0 @@
/**
* External dependencies
*/
import classnames from 'classnames';
import { __ } from '@wordpress/i18n';
import { InspectorControls } from '@wordpress/block-editor';
import {
PanelBody,
ToggleControl,
CheckboxControl,
Notice,
Disabled,
} from '@wordpress/components';
import BlockErrorBoundary from '@woocommerce/base-components/block-error-boundary';
import {
PRIVACY_URL,
TERMS_URL,
CHECKOUT_PAGE_ID,
} from '@woocommerce/block-settings';
import { isWcVersion, getAdminLink, getSetting } from '@woocommerce/settings';
import { createInterpolateElement, useRef } from '@wordpress/element';
import {
EditorProvider,
useEditorContext,
StoreNoticesProvider,
CheckoutProvider,
} from '@woocommerce/base-context';
import { CartCheckoutFeedbackPrompt } from '@woocommerce/editor-components/feedback-prompt';
import PageSelector from '@woocommerce/editor-components/page-selector';
import { CartCheckoutCompatibilityNotice } from '@woocommerce/editor-components/compatibility-notices';
import {
previewCart,
previewSavedPaymentMethods,
} from '@woocommerce/resource-previews';
/**
* Internal dependencies
*/
import Block from './block.js';
import './editor.scss';
const BlockSettings = ( { attributes, setAttributes } ) => {
const {
showCompanyField,
showApartmentField,
showPhoneField,
requireCompanyField,
requirePhoneField,
allowCreateAccount,
showOrderNotes,
showPolicyLinks,
showReturnToCart,
cartPageId,
hasDarkControls,
showRateAfterTaxName,
} = attributes;
const { currentPostId } = useEditorContext();
const { current: savedCartPageId } = useRef( cartPageId );
// Checkout signup is feature gated to WooCommerce 4.7 and newer;
// uses updated my-account/lost-password screen from 4.7+ for
// setting initial password.
// Also implicitly gated to feature plugin, because Checkout
// block is gated to plugin
const showCreateAccountOption =
getSetting( 'checkoutAllowsSignup', false ) &&
isWcVersion( '4.7.0', '>=' );
return (
<InspectorControls>
{ currentPostId !== CHECKOUT_PAGE_ID && (
<Notice
className="wc-block-checkout__page-notice"
isDismissible={ false }
status="warning"
>
{ createInterpolateElement(
__(
'If you would like to use this block as your default checkout you must update your <a>page settings in WooCommerce</a>.',
'woo-gutenberg-products-block'
),
{
a: (
// eslint-disable-next-line jsx-a11y/anchor-has-content
<a
href={ getAdminLink(
'admin.php?page=wc-settings&tab=advanced'
) }
target="_blank"
rel="noopener noreferrer"
/>
),
}
) }
</Notice>
) }
<PanelBody
title={ __(
'Address options',
'woo-gutenberg-products-block'
) }
>
<p className="wc-block-checkout__controls-text">
{ __(
'Include additional address fields in the checkout form.',
'woo-gutenberg-products-block'
) }
</p>
<ToggleControl
label={ __( 'Company', 'woo-gutenberg-products-block' ) }
checked={ showCompanyField }
onChange={ () =>
setAttributes( {
showCompanyField: ! showCompanyField,
} )
}
/>
{ showCompanyField && (
<CheckboxControl
label={ __(
'Require company name?',
'woo-gutenberg-products-block'
) }
checked={ requireCompanyField }
onChange={ () =>
setAttributes( {
requireCompanyField: ! requireCompanyField,
} )
}
className="components-base-control--nested"
/>
) }
<ToggleControl
label={ __(
'Apartment, suite, etc.',
'woo-gutenberg-products-block'
) }
checked={ showApartmentField }
onChange={ () =>
setAttributes( {
showApartmentField: ! showApartmentField,
} )
}
/>
<ToggleControl
label={ __( 'Phone', 'woo-gutenberg-products-block' ) }
checked={ showPhoneField }
onChange={ () =>
setAttributes( {
showPhoneField: ! showPhoneField,
} )
}
/>
{ showPhoneField && (
<CheckboxControl
label={ __(
'Require phone number?',
'woo-gutenberg-products-block'
) }
checked={ requirePhoneField }
onChange={ () =>
setAttributes( {
requirePhoneField: ! requirePhoneField,
} )
}
className="components-base-control--nested"
/>
) }
</PanelBody>
{ showCreateAccountOption && (
<PanelBody
title={ __(
'Account options',
'woo-gutenberg-products-block'
) }
>
<ToggleControl
label={ __(
'Allow shoppers to sign up for a user account during checkout',
'woo-gutenberg-products-block'
) }
checked={ allowCreateAccount }
onChange={ () =>
setAttributes( {
allowCreateAccount: ! allowCreateAccount,
} )
}
/>
</PanelBody>
) }
<PanelBody
title={ __( 'Order notes', 'woo-gutenberg-products-block' ) }
>
<p className="wc-block-checkout__controls-text">
{ __(
'Reduce the number of fields to checkout.',
'woo-gutenberg-products-block'
) }
</p>
<ToggleControl
label={ __(
'Allow shoppers to optionally add order notes',
'woo-gutenberg-products-block'
) }
checked={ showOrderNotes }
onChange={ () =>
setAttributes( {
showOrderNotes: ! showOrderNotes,
} )
}
/>
</PanelBody>
<PanelBody
title={ __(
'Navigation options',
'woo-gutenberg-products-block'
) }
>
<ToggleControl
label={ __(
'Show links to policies',
'woo-gutenberg-products-block'
) }
help={ __(
'Shows links to your "terms and conditions" and "privacy policy" pages.',
'woo-gutenberg-products-block'
) }
checked={ showPolicyLinks }
onChange={ () =>
setAttributes( {
showPolicyLinks: ! showPolicyLinks,
} )
}
/>
{ showPolicyLinks && ( ! PRIVACY_URL || ! TERMS_URL ) && (
<Notice
className="wc-block-base-control-notice"
isDismissible={ false }
>
{ createInterpolateElement(
__(
'Pages must be first setup in store settings: <a1>Privacy policy</a1>, <a2>Terms and conditions</a2>.',
'woo-gutenberg-products-block'
),
{
a1: (
// eslint-disable-next-line jsx-a11y/anchor-has-content
<a
href={ getAdminLink(
'admin.php?page=wc-settings&tab=account'
) }
target="_blank"
rel="noopener noreferrer"
/>
),
a2: (
// eslint-disable-next-line jsx-a11y/anchor-has-content
<a
href={ getAdminLink(
'admin.php?page=wc-settings&tab=advanced'
) }
target="_blank"
rel="noopener noreferrer"
/>
),
}
) }
</Notice>
) }
<ToggleControl
label={ __(
'Show a "Return to Cart" link',
'woo-gutenberg-products-block'
) }
checked={ showReturnToCart }
onChange={ () =>
setAttributes( {
showReturnToCart: ! showReturnToCart,
} )
}
/>
</PanelBody>
{ showReturnToCart &&
! (
currentPostId === CHECKOUT_PAGE_ID && savedCartPageId === 0
) && (
<PageSelector
pageId={ cartPageId }
setPageId={ ( id ) =>
setAttributes( { cartPageId: id } )
}
labels={ {
title: __(
'Return to Cart button',
'woo-gutenberg-products-block'
),
default: __(
'WooCommerce Cart Page',
'woo-gutenberg-products-block'
),
} }
/>
) }
{ getSetting( 'taxesEnabled' ) &&
getSetting( 'displayItemizedTaxes', false ) &&
! getSetting( 'displayCartPricesIncludingTax', false ) && (
<PanelBody
title={ __( 'Taxes', 'woo-gutenberg-products-block' ) }
>
<ToggleControl
label={ __(
'Show rate after tax name',
'woo-gutenberg-products-block'
) }
help={ __(
'Show the percentage rate alongside each tax line in the summary.',
'woo-gutenberg-products-block'
) }
checked={ showRateAfterTaxName }
onChange={ () =>
setAttributes( {
showRateAfterTaxName: ! showRateAfterTaxName,
} )
}
/>
</PanelBody>
) }
<PanelBody title={ __( 'Style', 'woo-gutenberg-products-block' ) }>
<ToggleControl
label={ __(
'Dark mode inputs',
'woo-gutenberg-products-block'
) }
help={ __(
'Inputs styled specifically for use on dark background colors.',
'woo-gutenberg-products-block'
) }
checked={ hasDarkControls }
onChange={ () =>
setAttributes( {
hasDarkControls: ! hasDarkControls,
} )
}
/>
</PanelBody>
<CartCheckoutFeedbackPrompt />
</InspectorControls>
);
};
const CheckoutEditor = ( { attributes, setAttributes } ) => {
const { className, isPreview } = attributes;
return (
<>
<EditorProvider
previewData={ { previewCart, previewSavedPaymentMethods } }
>
<div
className={ classnames(
className,
'wp-block-woocommerce-checkout',
{
'is-editor-preview': isPreview,
}
) }
>
<BlockSettings
attributes={ attributes }
setAttributes={ setAttributes }
/>
<BlockErrorBoundary
header={ __(
'Checkout Block Error',
'woo-gutenberg-products-block'
) }
text={ __(
'There was an error whilst rendering the checkout block. If this problem continues, try re-creating the block.',
'woo-gutenberg-products-block'
) }
showErrorMessage={ true }
errorMessagePrefix={ __(
'Error message:',
'woo-gutenberg-products-block'
) }
>
<StoreNoticesProvider context="wc/checkout">
<Disabled>
<CheckoutProvider>
<Block attributes={ attributes } />
</CheckoutProvider>
</Disabled>
</StoreNoticesProvider>
</BlockErrorBoundary>
</div>
</EditorProvider>
<CartCheckoutCompatibilityNotice blockName="checkout" />
</>
);
};
export default CheckoutEditor;

View File

@ -28,6 +28,7 @@ import { CartCheckoutFeedbackPrompt } from '@woocommerce/editor-components/feedb
import { CHECKOUT_PAGE_ID } from '@woocommerce/block-settings';
import { createInterpolateElement } from '@wordpress/element';
import { getAdminLink } from '@woocommerce/settings';
import { CartCheckoutCompatibilityNotice } from '@woocommerce/editor-components/compatibility-notices';
/**
* Internal dependencies
@ -261,6 +262,7 @@ export const Edit = ( {
);
return (
<>
<EditorProvider
previewData={ { previewCart, previewSavedPaymentMethods } }
>
@ -302,6 +304,8 @@ export const Edit = ( {
</Columns>
</CheckoutProvider>
</EditorProvider>
<CartCheckoutCompatibilityNotice blockName="checkout" />
</>
);
};

View File

@ -1,29 +0,0 @@
.editor-styles-wrapper {
.wp-block h4.wc-block-components-checkout-step__title {
@include font-size(regular);
line-height: 24px;
margin: 0 $gap-small 0 0;
}
}
.wc-block-checkout__controls-text {
color: #999;
font-style: italic;
}
.components-base-control--nested {
padding-left: 52px;
margin-top: -12px;
}
.wc-block-checkout__page-notice {
margin: 0;
}
.components-panel__body-title .components-button {
opacity: 1;
}
.wp-block-woocommerce-checkout.is-editor-preview {
max-height: 1000px;
overflow: hidden;
}

View File

@ -5,6 +5,11 @@ import { __ } from '@wordpress/i18n';
import { SHOP_URL } from '@woocommerce/block-settings';
import { Icon, cart } from '@woocommerce/icons';
/**
* Internal dependencies
*/
import './style.scss';
const EmptyCart = () => {
return (
<div className="wc-block-checkout-empty">

View File

@ -0,0 +1,21 @@
.wc-block-checkout-empty {
padding: $gap-largest;
text-align: center;
width: 100%;
.wc-block-checkout-empty__image {
max-width: 150px;
margin: 0 auto 1em;
display: block;
color: inherit;
}
.wc-block-checkout-empty__title {
display: block;
margin: 0;
font-weight: bold;
}
.wc-block-checkout-empty__description {
display: block;
margin: 0.25em 0 1em 0;
}
}

View File

@ -1,31 +0,0 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { FormStep } from '@woocommerce/base-components/cart-checkout';
import { useCheckoutSubmit } from '@woocommerce/base-context/hooks';
import PropTypes from 'prop-types';
const BillingFieldsStep = ( { children } ) => {
const { isDisabled } = useCheckoutSubmit();
return (
<FormStep
id="billing-fields"
disabled={ isDisabled }
className="wc-block-checkout__billing-fields"
title={ __( 'Billing address', 'woo-gutenberg-products-block' ) }
description={ __(
'Enter the address that matches your card or payment method.',
'woo-gutenberg-products-block'
) }
>
{ children }
</FormStep>
);
};
BillingFieldsStep.propTypes = {
children: PropTypes.node.isRequired,
};
export default BillingFieldsStep;

View File

@ -1,72 +0,0 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { FormStep } from '@woocommerce/base-components/cart-checkout';
import { ValidatedTextInput } from '@woocommerce/base-components/text-input';
import { useCheckoutContext } from '@woocommerce/base-context';
import { useCheckoutSubmit } from '@woocommerce/base-context/hooks';
import { getSetting } from '@woocommerce/settings';
import CheckboxControl from '@woocommerce/base-components/checkbox-control';
/**
* Internal dependencies
*/
import LoginPrompt from './login-prompt';
const ContactFieldsStep = ( {
emailValue,
onChangeEmail,
allowCreateAccount,
} ) => {
const {
customerId,
shouldCreateAccount,
setShouldCreateAccount,
} = useCheckoutContext();
const { isDisabled } = useCheckoutSubmit();
const createAccountUI = ! customerId &&
allowCreateAccount &&
getSetting( 'checkoutAllowsGuest', false ) &&
getSetting( 'checkoutAllowsSignup', false ) && (
<CheckboxControl
className="wc-block-checkout__create-account"
label={ __(
'Create an account?',
'woo-gutenberg-products-block'
) }
checked={ shouldCreateAccount }
onChange={ ( value ) => setShouldCreateAccount( value ) }
/>
);
return (
<FormStep
id="contact-fields"
disabled={ isDisabled }
className="wc-block-checkout__contact-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'
) }
stepHeadingContent={ () => <LoginPrompt /> }
>
<ValidatedTextInput
id="email"
type="email"
label={ __( 'Email address', 'woo-gutenberg-products-block' ) }
value={ emailValue }
autoComplete="email"
onChange={ onChangeEmail }
required={ true }
/>
{ createAccountUI }
</FormStep>
);
};
export default ContactFieldsStep;

View File

@ -1,156 +0,0 @@
/**
* External dependencies
*/
import PropTypes from 'prop-types';
import { useEffect, useMemo } from '@wordpress/element';
import {
useCheckoutContext,
useShippingDataContext,
} from '@woocommerce/base-context';
import {
useStoreEvents,
useCheckoutAddress,
} from '@woocommerce/base-context/hooks';
import { AddressForm } from '@woocommerce/base-components/cart-checkout';
import Form from '@woocommerce/base-components/form';
/**
* Internal dependencies
*/
import BillingFieldsStep from './billing-fields-step';
import ContactFieldsStep from './contact-fields-step';
import ShippingFieldsStep from './shipping-fields-step';
import PhoneNumber from './phone-number';
import OrderNotesStep from './order-notes-step';
import PaymentMethodStep from './payment-method-step';
import ShippingOptionsStep from './shipping-options-step';
import './style.scss';
const CheckoutForm = ( {
requireCompanyField,
requirePhoneField,
showApartmentField,
showCompanyField,
showOrderNotes,
showPhoneField,
allowCreateAccount,
} ) => {
const { onSubmit } = useCheckoutContext();
const {
defaultAddressFields,
billingFields,
setBillingFields,
setEmail,
setPhone,
setShippingAsBilling,
setShippingFields,
shippingAsBilling,
shippingFields,
showBillingFields,
} = useCheckoutAddress();
const { needsShipping } = useShippingDataContext();
const { dispatchCheckoutEvent } = useStoreEvents();
const addressFieldsConfig = useMemo( () => {
return {
company: {
hidden: ! showCompanyField,
required: requireCompanyField,
},
address_2: {
hidden: ! showApartmentField,
},
};
}, [ showCompanyField, requireCompanyField, showApartmentField ] );
// Ignore changes to dispatchCheckoutEvent callback so this is ran on first mount only.
useEffect( () => {
dispatchCheckoutEvent( 'render-checkout-form' );
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [] );
return (
<Form className="wc-block-checkout__form" onSubmit={ onSubmit }>
<ContactFieldsStep
emailValue={ billingFields.email }
onChangeEmail={ ( value ) => {
setEmail( value );
dispatchCheckoutEvent( 'set-email-address' );
} }
allowCreateAccount={ allowCreateAccount }
/>
{ needsShipping && (
<ShippingFieldsStep
shippingAsBilling={ shippingAsBilling }
setShippingAsBilling={ setShippingAsBilling }
>
<AddressForm
id="shipping"
type="shipping"
onChange={ ( values ) => {
setShippingFields( values );
dispatchCheckoutEvent( 'set-shipping-address' );
} }
values={ shippingFields }
fields={ Object.keys( defaultAddressFields ) }
fieldConfig={ addressFieldsConfig }
/>
{ showPhoneField && (
<PhoneNumber
isRequired={ requirePhoneField }
value={ billingFields.phone }
onChange={ ( value ) => {
setPhone( value );
dispatchCheckoutEvent( 'set-phone-number', {
step: 'shipping',
} );
} }
/>
) }
</ShippingFieldsStep>
) }
{ showBillingFields && (
<BillingFieldsStep>
<AddressForm
id="billing"
type="billing"
onChange={ ( values ) => {
setBillingFields( values );
dispatchCheckoutEvent( 'set-billing-address' );
} }
values={ billingFields }
fields={ Object.keys( defaultAddressFields ) }
fieldConfig={ addressFieldsConfig }
/>
{ showPhoneField && ! needsShipping && (
<PhoneNumber
isRequired={ requirePhoneField }
value={ billingFields.phone }
onChange={ ( value ) => {
setPhone( value );
dispatchCheckoutEvent( 'set-phone-number', {
step: 'billing',
} );
} }
/>
) }
</BillingFieldsStep>
) }
<ShippingOptionsStep />
<PaymentMethodStep />
{ showOrderNotes && <OrderNotesStep /> }
</Form>
);
};
CheckoutForm.propTypes = {
requireCompanyField: PropTypes.bool.isRequired,
requirePhoneField: PropTypes.bool.isRequired,
showApartmentField: PropTypes.bool.isRequired,
showCompanyField: PropTypes.bool.isRequired,
showOrderNotes: PropTypes.bool.isRequired,
showPhoneField: PropTypes.bool.isRequired,
allowCreateAccount: PropTypes.bool.isRequired,
};
export default CheckoutForm;

View File

@ -1,33 +0,0 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { getSetting } from '@woocommerce/settings';
import { useCheckoutContext } from '@woocommerce/base-context';
/**
* Internal dependencies
*/
import { LOGIN_TO_CHECKOUT_URL } from '../utils';
const LoginPrompt = () => {
const { customerId } = useCheckoutContext();
if ( ! getSetting( 'checkoutShowLoginReminder', true ) || customerId ) {
return null;
}
return (
<>
{ __(
'Already have an account? ',
'woo-gutenberg-products-block'
) }
<a href={ LOGIN_TO_CHECKOUT_URL }>
{ __( 'Log in.', 'woo-gutenberg-products-block' ) }
</a>
</>
);
};
export default LoginPrompt;

View File

@ -1,42 +0,0 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { Placeholder, Button } from 'wordpress-components';
import { Icon, truck } from '@woocommerce/icons';
import { ADMIN_URL } from '@woocommerce/settings';
/**
* Internal dependencies
*/
import './style.scss';
const NoShippingPlaceholder = () => {
return (
<Placeholder
icon={ <Icon srcElement={ truck } /> }
label={ __( 'Shipping options', 'woo-gutenberg-products-block' ) }
className="wc-block-checkout__no-shipping-placeholder"
>
<span className="wc-block-checkout__no-shipping-placeholder-description">
{ __(
'Your store does not have any Shipping Options configured. Once you have added your Shipping Options they will appear here.',
'woo-gutenberg-products-block'
) }
</span>
<Button
isSecondary
href={ `${ ADMIN_URL }admin.php?page=wc-settings&tab=shipping` }
target="_blank"
rel="noopener noreferrer"
>
{ __(
'Configure Shipping Options',
'woo-gutenberg-products-block'
) }
</Button>
</Placeholder>
);
};
export default NoShippingPlaceholder;

View File

@ -1,21 +0,0 @@
.components-placeholder.wc-block-checkout__no-shipping-placeholder {
margin-bottom: $gap;
* {
pointer-events: all; // Overrides parent disabled component in editor context
}
.components-placeholder__fieldset {
display: block;
.components-button {
background-color: $gray-900;
color: $white;
}
.wc-block-checkout__no-shipping-placeholder-description {
display: block;
margin: 0.25em 0 1em 0;
}
}
}

View File

@ -1,50 +0,0 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { FormStep } from '@woocommerce/base-components/cart-checkout';
import {
useCheckoutContext,
useShippingDataContext,
} from '@woocommerce/base-context';
import { useCheckoutSubmit } from '@woocommerce/base-context/hooks';
/**
* Internal dependencies
*/
import CheckoutOrderNotes from './order-notes';
const OrderNotesStep = () => {
const { needsShipping } = useShippingDataContext();
const { orderNotes, dispatchActions } = useCheckoutContext();
const { isDisabled } = useCheckoutSubmit();
const { setOrderNotes } = dispatchActions;
return (
<FormStep
id="order-notes"
showStepNumber={ false }
className="wc-block-checkout__order-notes"
disabled={ isDisabled }
>
<CheckoutOrderNotes
onChange={ setOrderNotes }
disabled={ isDisabled }
placeholder={
needsShipping
? __(
'Notes about your order, e.g. special notes for delivery.',
'woo-gutenberg-products-block'
)
: __(
'Notes about your order.',
'woo-gutenberg-products-block'
)
}
value={ orderNotes }
/>
</FormStep>
);
};
export default OrderNotesStep;

View File

@ -1,51 +0,0 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { FormStep } from '@woocommerce/base-components/cart-checkout';
import { StoreNoticesProvider } from '@woocommerce/base-context';
import {
useStoreCart,
useEmitResponse,
usePaymentMethods,
useCheckoutSubmit,
} from '@woocommerce/base-context/hooks';
/**
* Internal dependencies
*/
import { PaymentMethods } from '../../payment-methods';
const PaymentMethodStep = () => {
const { isDisabled } = useCheckoutSubmit();
const { cartNeedsPayment } = useStoreCart();
const { paymentMethods } = usePaymentMethods();
const { noticeContexts } = useEmitResponse();
if ( ! cartNeedsPayment ) {
return null;
}
return (
<FormStep
id="payment-method"
disabled={ isDisabled }
className="wc-block-checkout__payment-method"
title={ __( 'Payment method', 'woo-gutenberg-products-block' ) }
description={
Object.keys( paymentMethods ).length > 1
? __(
'Select a payment method below.',
'woo-gutenberg-products-block'
)
: ''
}
>
<StoreNoticesProvider context={ noticeContexts.PAYMENTS }>
<PaymentMethods />
</StoreNoticesProvider>
</FormStep>
);
};
export default PaymentMethodStep;

View File

@ -1,34 +0,0 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { ValidatedTextInput } from '@woocommerce/base-components/text-input';
/**
* Renders a phone number input.
*
* @param {Object} props Component props.
* @param {boolean} props.isRequired Is the phone number required or optional.
* @param {Function} props.onChange Event fired when the input changes.
* @param {string} props.value Value of the input.
* @return {*} The component.
*/
const PhoneNumber = ( { isRequired = false, value = '', onChange } ) => {
return (
<ValidatedTextInput
id="phone"
type="tel"
autoComplete="tel"
required={ isRequired }
label={
isRequired
? __( 'Phone', 'woo-gutenberg-products-block' )
: __( 'Phone (optional)', 'woo-gutenberg-products-block' )
}
value={ value }
onChange={ onChange }
/>
);
};
export default PhoneNumber;

View File

@ -1,48 +0,0 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { FormStep } from '@woocommerce/base-components/cart-checkout';
import CheckboxControl from '@woocommerce/base-components/checkbox-control';
import { useCheckoutSubmit } from '@woocommerce/base-context/hooks';
import PropTypes from 'prop-types';
const ShippingFieldsStep = ( {
shippingAsBilling,
setShippingAsBilling,
children,
} ) => {
const { isDisabled } = useCheckoutSubmit();
return (
<FormStep
id="shipping-fields"
disabled={ isDisabled }
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'
) }
>
{ children }
<CheckboxControl
className="wc-block-checkout__use-address-for-billing"
label={ __(
'Use same address for billing',
'woo-gutenberg-products-block'
) }
checked={ shippingAsBilling }
onChange={ ( isChecked ) => setShippingAsBilling( isChecked ) }
/>
</FormStep>
);
};
ShippingFieldsStep.propTypes = {
shippingAsBilling: PropTypes.bool.isRequired,
setShippingAsBilling: PropTypes.func.isRequired,
children: PropTypes.node.isRequired,
};
export default ShippingFieldsStep;

View File

@ -1,120 +0,0 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import {
FormStep,
ShippingRatesControl,
} from '@woocommerce/base-components/cart-checkout';
import {
getShippingRatesPackageCount,
getShippingRatesRateCount,
} from '@woocommerce/base-utils';
import { getCurrencyFromPriceResponse } from '@woocommerce/price-format';
import FormattedMonetaryAmount from '@woocommerce/base-components/formatted-monetary-amount';
import {
useEditorContext,
useShippingDataContext,
} from '@woocommerce/base-context';
import { useCheckoutSubmit } from '@woocommerce/base-context/hooks';
import { decodeEntities } from '@wordpress/html-entities';
import { Notice } from 'wordpress-components';
import classnames from 'classnames';
import { getSetting } from '@woocommerce/settings';
import type { PackageRateOption } from '@woocommerce/type-defs/shipping';
import type { CartShippingPackageShippingRate } from '@woocommerce/type-defs/cart';
/**
* Internal dependencies
*/
import NoShippingPlaceholder from './no-shipping-placeholder';
/**
* Renders a shipping rate control option.
*
* @param {Object} option Shipping Rate.
*/
const renderShippingRatesControlOption = (
option: CartShippingPackageShippingRate
): PackageRateOption => {
const priceWithTaxes = getSetting( 'displayCartPricesIncludingTax', false )
? parseInt( option.price, 10 ) + parseInt( option.taxes, 10 )
: parseInt( option.price, 10 );
return {
label: decodeEntities( option.name ),
value: option.rate_id,
description: decodeEntities( option.description ),
secondaryLabel: (
<FormattedMonetaryAmount
currency={ getCurrencyFromPriceResponse( option ) }
value={ priceWithTaxes }
/>
),
secondaryDescription: decodeEntities( option.delivery_time ),
};
};
const ShippingOptionsStep = (): JSX.Element | null => {
const { isDisabled } = useCheckoutSubmit();
const { isEditor } = useEditorContext();
const {
shippingRates,
shippingRatesLoading,
needsShipping,
hasCalculatedShipping,
} = useShippingDataContext();
if ( ! needsShipping ) {
return null;
}
return (
<FormStep
id="shipping-option"
disabled={ isDisabled }
className="wc-block-checkout__shipping-option"
title={ __( 'Shipping options', 'woo-gutenberg-products-block' ) }
description={
getShippingRatesRateCount( shippingRates ) > 1
? __(
'Select shipping options below.',
'woo-gutenberg-products-block'
)
: ''
}
>
{ isEditor && ! getShippingRatesPackageCount( shippingRates ) ? (
<NoShippingPlaceholder />
) : (
<ShippingRatesControl
noResultsMessage={
hasCalculatedShipping ? (
<Notice
isDismissible={ false }
className={ classnames(
'wc-block-components-shipping-rates-control__no-results-notice',
'woocommerce-error'
) }
>
{ __(
'There are no shipping options available. Please ensure that your address has been entered correctly, or contact us if you need any help.',
'woo-gutenberg-products-block'
) }
</Notice>
) : (
__(
'Shipping options will appear here after entering your full shipping address.',
'woo-gutenberg-products-block'
)
)
}
renderOption={ renderShippingRatesControlOption }
shippingRates={ shippingRates }
shippingRatesLoading={ shippingRatesLoading }
/>
) }
</FormStep>
);
};
export default ShippingOptionsStep;

View File

@ -1,64 +0,0 @@
.wc-block-checkout__form {
margin: 0;
max-width: 100%;
}
.wc-block-checkout__create-account,
.wc-block-checkout__use-address-for-billing {
margin-top: em($gap-large);
}
.wc-block-checkout__shipping-option {
.wc-block-components-radio-control__option {
@include with-translucent-border( 0 0 1px );
margin: 0;
padding: em($gap-small) 0 em($gap-small) em($gap-largest);
}
.wc-block-components-shipping-rates-control__no-results-notice {
margin: em($gap-small) 0;
}
}
.is-small,
.is-medium,
.is-large {
.wc-block-checkout__billing-fields,
.wc-block-checkout__shipping-fields {
.wc-block-components-address-form {
margin-left: #{-$gap-small * 0.5};
margin-right: #{-$gap-small * 0.5};
&::after {
content: "";
clear: both;
display: block;
}
.wc-block-components-text-input,
.wc-block-components-country-input,
.wc-block-components-state-input {
float: left;
margin-left: #{$gap-small * 0.5};
margin-right: #{$gap-small * 0.5};
position: relative;
width: calc(50% - #{$gap-small});
&:nth-of-type(2),
&:first-of-type {
margin-top: 0;
}
}
.wc-block-components-address-form__company,
.wc-block-components-address-form__address_1,
.wc-block-components-address-form__address_2 {
width: calc(100% - #{$gap-small});
}
.wc-block-components-checkbox {
clear: both;
}
}
}
}

View File

@ -1,95 +0,0 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import {
withStoreCartApiHydration,
withRestApiHydration,
} from '@woocommerce/block-hocs';
import { useStoreCart } from '@woocommerce/base-context/hooks';
import {
CheckoutProvider,
StoreNoticesProvider,
ValidationContextProvider,
} from '@woocommerce/base-context';
import BlockErrorBoundary from '@woocommerce/base-components/block-error-boundary';
import { CURRENT_USER_IS_ADMIN } from '@woocommerce/settings';
import {
renderFrontend,
getValidBlockAttributes,
} from '@woocommerce/base-utils';
import { StoreSnackbarNoticesProvider } from '@woocommerce/base-context/providers';
import { SlotFillProvider } from '@woocommerce/blocks-checkout';
/**
* Internal dependencies
*/
import Block from './block.js';
import blockAttributes from './attributes';
import EmptyCart from './empty-cart/index.js';
const reloadPage = () => void window.location.reload( true );
const errorBoundaryProps = {
header: __( 'Something went wrong…', 'woo-gutenberg-products-block' ),
text: __(
'The checkout has encountered an unexpected error. If the error persists, please get in touch with us for help.',
'woo-gutenberg-products-block'
),
showErrorMessage: CURRENT_USER_IS_ADMIN,
button: (
<button className="wc-block-button" onClick={ reloadPage }>
{ __( 'Reload the page', 'woo-gutenberg-products-block' ) }
</button>
),
};
/**
* Wrapper component for the checkout block.
*
* @param {Object} props Props for the block.
*/
const CheckoutFrontend = ( props ) => {
const { cartItems, cartIsLoading } = useStoreCart();
return (
<>
{ ! cartIsLoading && cartItems.length === 0 ? (
<EmptyCart />
) : (
<BlockErrorBoundary { ...errorBoundaryProps }>
<StoreSnackbarNoticesProvider context="wc/checkout">
<StoreNoticesProvider context="wc/checkout">
<ValidationContextProvider>
<SlotFillProvider>
<CheckoutProvider>
<Block { ...props } />
</CheckoutProvider>
</SlotFillProvider>
</ValidationContextProvider>
</StoreNoticesProvider>
</StoreSnackbarNoticesProvider>
</BlockErrorBoundary>
) }
</>
);
};
const getProps = ( el ) => {
return {
attributes: getValidBlockAttributes( blockAttributes, el.dataset ),
};
};
const getErrorBoundaryProps = () => {
return errorBoundaryProps;
};
renderFrontend( {
selector: '.wp-block-woocommerce-checkout',
Block: withStoreCartApiHydration(
withRestApiHydration( CheckoutFrontend )
),
getProps,
getErrorBoundaryProps,
} );

View File

@ -59,7 +59,7 @@ const Wrapper = ( {
renderParentBlock( {
Block: withStoreCartApiHydration( withRestApiHydration( Block ) ),
blockName,
selector: '.wp-block-woocommerce-checkout-i2',
selector: '.wp-block-woocommerce-checkout',
getProps,
blockMap: getRegisteredBlockComponents( blockName ) as Record<
string,

View File

@ -1,86 +0,0 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { Icon, card } from '@woocommerce/icons';
import classnames from 'classnames';
import {
registerFeaturePluginBlockType,
isExperimentalBuild,
} from '@woocommerce/block-settings';
import { createBlock } from '@wordpress/blocks';
/**
* Internal dependencies
*/
import edit from './edit';
import blockAttributes from './attributes';
import './editor.scss';
const transforms = isExperimentalBuild()
? {
transforms: {
from: [
{
type: 'block',
blocks: [ 'woocommerce/checkout' ],
transform: ( attributes ) => {
return createBlock( 'woocommerce/checkout', {
attributes,
} );
},
},
],
to: [
{
type: 'block',
blocks: [ 'woocommerce/checkout-i2' ],
transform: ( attributes ) => {
return createBlock(
'woocommerce/checkout-i2',
attributes
);
},
},
],
},
}
: {};
const settings = {
title: __( 'Checkout', 'woo-gutenberg-products-block' ),
icon: {
src: <Icon srcElement={ card } />,
foreground: '#96588a',
},
category: 'woocommerce',
keywords: [ __( 'WooCommerce', 'woo-gutenberg-products-block' ) ],
description: __(
'Display a checkout form so your customers can submit orders.',
'woo-gutenberg-products-block'
),
supports: {
align: [ 'wide', 'full' ],
html: false,
multiple: false,
},
example: {
attributes: {
isPreview: true,
},
},
attributes: blockAttributes,
edit,
//Save the props to post content.
save( { attributes } ) {
return (
<div
className={ classnames( 'is-loading', attributes.className ) }
/>
);
},
...transforms,
};
registerFeaturePluginBlockType( 'woocommerce/checkout', settings );

View File

@ -2,9 +2,9 @@
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import classnames from 'classnames';
import { Icon, fields } from '@woocommerce/icons';
import { registerFeaturePluginBlockType } from '@woocommerce/block-settings';
import { createBlock } from '@wordpress/blocks';
/**
* Internal dependencies
@ -14,7 +14,7 @@ import { blockName, blockAttributes } from './attributes';
import './inner-blocks';
const settings = {
title: __( 'Checkout i2', 'woo-gutenberg-products-block' ),
title: __( 'Checkout', 'woo-gutenberg-products-block' ),
icon: {
src: <Icon srcElement={ fields } />,
foreground: '#874FB9',
@ -34,28 +34,22 @@ const settings = {
apiVersion: 2,
edit: Edit,
save: Save,
transforms: {
to: [
// Migrates v1 to v2 checkout.
deprecated: [
{
type: 'block',
blocks: [ 'woocommerce/checkout' ],
transform: ( attributes ) => {
return createBlock( 'woocommerce/checkout', {
attributes,
} );
attributes: blockAttributes,
save( { attributes }: { attributes: { className: string } } ) {
return (
<div
className={ classnames(
'is-loading',
attributes.className
) }
/>
);
},
},
],
from: [
{
type: 'block',
blocks: [ 'woocommerce/checkout-i2' ],
transform: ( attributes ) => {
return createBlock( 'woocommerce/checkout-i2', attributes );
},
},
],
},
};
registerFeaturePluginBlockType( blockName, settings );

View File

@ -0,0 +1,25 @@
{
"name": "woocommerce/checkout-actions-block",
"version": "1.0.0",
"title": "Actions",
"description": "Allow customers to place their order.",
"category": "woocommerce",
"supports": {
"align": false,
"html": false,
"multiple": false,
"reusable": false,
"inserter": false
},
"attributes": {
"lock": {
"default": {
"remove": true,
"move": true
}
}
},
"parent": [ "woocommerce/checkout-fields-block" ],
"textdomain": "woo-gutenberg-products-block",
"apiVersion": 2
}

View File

@ -0,0 +1,22 @@
/**
* External dependencies
*/
import { Icon, button } from '@wordpress/icons';
import { registerFeaturePluginBlockType } from '@woocommerce/block-settings';
/**
* Internal dependencies
*/
import attributes from './attributes';
import { Edit, Save } from './edit';
import metadata from './block.json';
registerFeaturePluginBlockType( metadata, {
icon: {
src: <Icon icon={ button } />,
foreground: '#874FB9',
},
attributes,
edit: Edit,
save: Save,
} );

View File

@ -0,0 +1,25 @@
{
"name": "woocommerce/checkout-billing-address-block",
"version": "1.0.0",
"title": "Billing Address",
"description": "Collect your customer's billing address.",
"category": "woocommerce",
"supports": {
"align": false,
"html": false,
"multiple": false,
"reusable": false,
"inserter": false
},
"attributes": {
"lock": {
"default": {
"remove": true,
"move": true
}
}
},
"parent": [ "woocommerce/checkout-fields-block" ],
"textdomain": "woo-gutenberg-products-block",
"apiVersion": 2
}

View File

@ -0,0 +1,22 @@
/**
* External dependencies
*/
import { Icon, address } from '@woocommerce/icons';
import { registerFeaturePluginBlockType } from '@woocommerce/block-settings';
/**
* Internal dependencies
*/
import { Edit, Save } from './edit';
import attributes from './attributes';
import metadata from './block.json';
registerFeaturePluginBlockType( metadata, {
icon: {
src: <Icon srcElement={ address } />,
foreground: '#874FB9',
},
attributes,
edit: Edit,
save: Save,
} );

View File

@ -0,0 +1,25 @@
{
"name": "woocommerce/checkout-contact-information-block",
"version": "1.0.0",
"title": "Contact Information",
"description": "Collect your customer's contact information.",
"category": "woocommerce",
"supports": {
"align": false,
"html": false,
"multiple": false,
"reusable": false,
"inserter": false
},
"attributes": {
"lock": {
"default": {
"remove": true,
"move": true
}
}
},
"parent": [ "woocommerce/checkout-fields-block" ],
"textdomain": "woo-gutenberg-products-block",
"apiVersion": 2
}

View File

@ -0,0 +1,22 @@
/**
* External dependencies
*/
import { Icon, contact } from '@woocommerce/icons';
import { registerFeaturePluginBlockType } from '@woocommerce/block-settings';
/**
* Internal dependencies
*/
import { Edit, Save } from './edit';
import attributes from './attributes';
import metadata from './block.json';
registerFeaturePluginBlockType( metadata, {
icon: {
src: <Icon srcElement={ contact } />,
foreground: '#874FB9',
},
attributes,
edit: Edit,
save: Save,
} );

View File

@ -0,0 +1,25 @@
{
"name": "woocommerce/checkout-express-payment-block",
"version": "1.0.0",
"title": "Express Checkout",
"description": "Provide an express payment option for your customers.",
"category": "woocommerce",
"supports": {
"align": false,
"html": false,
"multiple": false,
"reusable": false,
"inserter": false
},
"attributes": {
"lock": {
"default": {
"remove": true,
"move": true
}
}
},
"parent": [ "woocommerce/checkout-fields-block" ],
"textdomain": "woo-gutenberg-products-block",
"apiVersion": 2
}

View File

@ -0,0 +1,20 @@
/**
* External dependencies
*/
import { Icon, card } from '@woocommerce/icons';
import { registerFeaturePluginBlockType } from '@woocommerce/block-settings';
/**
* Internal dependencies
*/
import { Edit, Save } from './edit';
import metadata from './block.json';
registerFeaturePluginBlockType( metadata, {
icon: {
src: <Icon srcElement={ card } />,
foreground: '#874FB9',
},
edit: Edit,
save: Save,
} );

View File

@ -0,0 +1,25 @@
{
"name": "woocommerce/checkout-fields-block",
"version": "1.0.0",
"title": "Checkout Fields",
"description": "Column containing checkout address fields.",
"category": "woocommerce",
"supports": {
"align": false,
"html": false,
"multiple": false,
"reusable": false,
"inserter": false
},
"attributes": {
"lock": {
"default": {
"remove": true,
"move": true
}
}
},
"parent": [ "woocommerce/checkout" ],
"textdomain": "woo-gutenberg-products-block",
"apiVersion": 2
}

View File

@ -11,6 +11,7 @@ import { innerBlockAreas } from '@woocommerce/blocks-checkout';
import { useCheckoutBlockControlsContext } from '../../context';
import { useForcedLayout } from '../../use-forced-layout';
import { getAllowedBlocks } from '../../editor-utils';
import './style.scss';
export const Edit = ( { clientId }: { clientId: string } ): JSX.Element => {
const blockProps = useBlockProps();

View File

@ -3,6 +3,11 @@
*/
import { Main } from '@woocommerce/base-components/sidebar-layout';
/**
* Internal dependencies
*/
import './style.scss';
const FrontendBlock = ( {
children,
}: {

View File

@ -0,0 +1,20 @@
/**
* External dependencies
*/
import { Icon, column } from '@wordpress/icons';
import { registerFeaturePluginBlockType } from '@woocommerce/block-settings';
/**
* Internal dependencies
*/
import { Edit, Save } from './edit';
import metadata from './block.json';
registerFeaturePluginBlockType( metadata, {
icon: {
src: <Icon icon={ column } />,
foreground: '#874FB9',
},
edit: Edit,
save: Save,
} );

View File

@ -0,0 +1,50 @@
.wc-block-checkout__form {
margin: 0;
max-width: 100%;
}
.is-mobile,
.is-small,
.is-medium {
.wc-block-checkout__main {
order: 1;
}
}
.is-small,
.is-medium,
.is-large {
.wc-block-components-address-form {
margin-left: #{-$gap-small * 0.5};
margin-right: #{-$gap-small * 0.5};
&::after {
content: "";
clear: both;
display: block;
}
.wc-block-components-text-input,
.wc-block-components-country-input,
.wc-block-components-state-input {
float: left;
margin-left: #{$gap-small * 0.5};
margin-right: #{$gap-small * 0.5};
position: relative;
width: calc(50% - #{$gap-small});
&:nth-of-type(2),
&:first-of-type {
margin-top: 0;
}
}
.wc-block-components-address-form__company,
.wc-block-components-address-form__address_1,
.wc-block-components-address-form__address_2 {
width: calc(100% - #{$gap-small});
}
.wc-block-components-checkbox {
clear: both;
}
}
}

View File

@ -0,0 +1,25 @@
{
"name": "woocommerce/checkout-order-note-block",
"version": "1.0.0",
"title": "Order Note",
"description": "Allow customers to add a note to their order.",
"category": "woocommerce",
"supports": {
"align": false,
"html": false,
"multiple": false,
"reusable": false,
"inserter": false
},
"attributes": {
"lock": {
"default": {
"remove": true,
"move": true
}
}
},
"parent": [ "woocommerce/checkout-fields-block" ],
"textdomain": "woo-gutenberg-products-block",
"apiVersion": 2
}

View File

@ -11,7 +11,7 @@ import {
/**
* Internal dependencies
*/
import CheckoutOrderNotes from '../../../checkout/form/order-notes';
import CheckoutOrderNotes from '../../order-notes';
const Block = (): JSX.Element => {
const { needsShipping } = useShippingDataContext();

View File

@ -0,0 +1,20 @@
/**
* External dependencies
*/
import { Icon, notes } from '@woocommerce/icons';
import { registerFeaturePluginBlockType } from '@woocommerce/block-settings';
/**
* Internal dependencies
*/
import { Edit, Save } from './edit';
import metadata from './block.json';
registerFeaturePluginBlockType( metadata, {
icon: {
src: <Icon srcElement={ notes } />,
foreground: '#874FB9',
},
edit: Edit,
save: Save,
} );

View File

@ -0,0 +1,24 @@
{
"name": "woocommerce/checkout-order-summary-block",
"version": "1.0.0",
"title": "Order Summary",
"description": "Show customers a summary of their order.",
"category": "woocommerce",
"supports": {
"align": false,
"html": false,
"multiple": false,
"reusable": false,
"inserter": false
},
"attributes": {
"lock": {
"default": {
"remove": true
}
}
},
"parent": [ "woocommerce/checkout-totals-block" ],
"textdomain": "woo-gutenberg-products-block",
"apiVersion": 2
}

View File

@ -0,0 +1,22 @@
/**
* External dependencies
*/
import { Icon, totals } from '@woocommerce/icons';
import { registerFeaturePluginBlockType } from '@woocommerce/block-settings';
/**
* Internal dependencies
*/
import { Edit, Save } from './edit';
import attributes from './attributes';
import metadata from './block.json';
registerFeaturePluginBlockType( metadata, {
icon: {
src: <Icon srcElement={ totals } />,
foreground: '#874FB9',
},
attributes,
edit: Edit,
save: Save,
} );

View File

@ -0,0 +1,25 @@
{
"name": "woocommerce/checkout-payment-block",
"version": "1.0.0",
"title": "Payment Options",
"description": "Payment options for your store.",
"category": "woocommerce",
"supports": {
"align": false,
"html": false,
"multiple": false,
"reusable": false,
"inserter": false
},
"attributes": {
"lock": {
"default": {
"remove": true,
"move": true
}
}
},
"parent": [ "woocommerce/checkout-fields-block" ],
"textdomain": "woo-gutenberg-products-block",
"apiVersion": 2
}

Some files were not shown because too many files have changed in this diff Show More