initial commit
This commit is contained in:
@ -0,0 +1,17 @@
|
||||
export default {
|
||||
cartPageId: {
|
||||
type: 'number',
|
||||
default: 0,
|
||||
},
|
||||
showReturnToCart: {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
},
|
||||
lock: {
|
||||
type: 'object',
|
||||
default: {
|
||||
move: true,
|
||||
remove: true,
|
||||
},
|
||||
},
|
||||
};
|
@ -0,0 +1,26 @@
|
||||
{
|
||||
"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": {
|
||||
"type": "object",
|
||||
"default": {
|
||||
"remove": true,
|
||||
"move": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"parent": [ "woocommerce/checkout-fields-block" ],
|
||||
"textdomain": "woo-gutenberg-products-block",
|
||||
"apiVersion": 2
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { getSetting } from '@woocommerce/settings';
|
||||
import {
|
||||
PlaceOrderButton,
|
||||
ReturnToCartButton,
|
||||
} from '@woocommerce/base-components/cart-checkout';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import './style.scss';
|
||||
|
||||
const Block = ( {
|
||||
cartPageId,
|
||||
showReturnToCart,
|
||||
}: {
|
||||
cartPageId: number;
|
||||
showReturnToCart: boolean;
|
||||
} ): JSX.Element => {
|
||||
return (
|
||||
<div className="wc-block-checkout__actions">
|
||||
{ showReturnToCart && (
|
||||
<ReturnToCartButton
|
||||
link={ getSetting( 'page-' + cartPageId, false ) }
|
||||
/>
|
||||
) }
|
||||
<PlaceOrderButton />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Block;
|
@ -0,0 +1,97 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { useRef } from '@wordpress/element';
|
||||
import { useSelect } from '@wordpress/data';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { InspectorControls, useBlockProps } from '@wordpress/block-editor';
|
||||
import PageSelector from '@woocommerce/editor-components/page-selector';
|
||||
import { PanelBody, ToggleControl, Disabled } from '@wordpress/components';
|
||||
import { CHECKOUT_PAGE_ID } from '@woocommerce/block-settings';
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import Block from './block';
|
||||
|
||||
export const Edit = ( {
|
||||
attributes,
|
||||
setAttributes,
|
||||
}: {
|
||||
attributes: {
|
||||
showReturnToCart: boolean;
|
||||
cartPageId: number;
|
||||
};
|
||||
setAttributes: ( attributes: Record< string, unknown > ) => void;
|
||||
} ): JSX.Element => {
|
||||
const blockProps = useBlockProps();
|
||||
const { cartPageId = 0, showReturnToCart = true } = attributes;
|
||||
const { current: savedCartPageId } = useRef( cartPageId );
|
||||
const currentPostId = useSelect(
|
||||
( select ) => {
|
||||
if ( ! savedCartPageId ) {
|
||||
const store = select( 'core/editor' );
|
||||
return store.getCurrentPostId();
|
||||
}
|
||||
return savedCartPageId;
|
||||
},
|
||||
[ savedCartPageId ]
|
||||
);
|
||||
|
||||
return (
|
||||
<div { ...blockProps }>
|
||||
<InspectorControls>
|
||||
<PanelBody
|
||||
title={ __(
|
||||
'Account options',
|
||||
'woo-gutenberg-products-block'
|
||||
) }
|
||||
>
|
||||
<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: number ) =>
|
||||
setAttributes( { cartPageId: id } )
|
||||
}
|
||||
labels={ {
|
||||
title: __(
|
||||
'Return to Cart button',
|
||||
'woo-gutenberg-products-block'
|
||||
),
|
||||
default: __(
|
||||
'WooCommerce Cart Page',
|
||||
'woo-gutenberg-products-block'
|
||||
),
|
||||
} }
|
||||
/>
|
||||
) }
|
||||
</InspectorControls>
|
||||
<Disabled>
|
||||
<Block
|
||||
showReturnToCart={ showReturnToCart }
|
||||
cartPageId={ cartPageId }
|
||||
/>
|
||||
</Disabled>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const Save = (): JSX.Element => {
|
||||
return <div { ...useBlockProps.save() } />;
|
||||
};
|
@ -0,0 +1,12 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import withFilteredAttributes from '@woocommerce/base-hocs/with-filtered-attributes';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import Block from './block';
|
||||
import attributes from './attributes';
|
||||
|
||||
export default withFilteredAttributes( attributes )( Block );
|
@ -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,
|
||||
} );
|
@ -0,0 +1,39 @@
|
||||
.wc-block-checkout__actions {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.wc-block-components-checkout-place-order-button {
|
||||
width: 50%;
|
||||
padding: 1em;
|
||||
height: auto;
|
||||
|
||||
.wc-block-components-button__text {
|
||||
line-height: 24px;
|
||||
|
||||
> svg {
|
||||
fill: $white;
|
||||
vertical-align: top;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.is-mobile {
|
||||
.wc-block-checkout__actions {
|
||||
.wc-block-components-checkout-return-to-cart-button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.wc-block-components-checkout-place-order-button {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.is-large {
|
||||
.wc-block-checkout__actions {
|
||||
@include with-translucent-border(1px 0 0);
|
||||
padding: em($gap-large) 0;
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import formStepAttributes from '../../form-step/attributes';
|
||||
|
||||
export default {
|
||||
...formStepAttributes( {
|
||||
defaultTitle: __( 'Billing address', 'woo-gutenberg-products-block' ),
|
||||
defaultDescription: __(
|
||||
'Enter the address that matches your card or payment method.',
|
||||
'woo-gutenberg-products-block'
|
||||
),
|
||||
} ),
|
||||
lock: {
|
||||
type: 'object',
|
||||
default: {
|
||||
move: true,
|
||||
remove: true,
|
||||
},
|
||||
},
|
||||
};
|
@ -0,0 +1,26 @@
|
||||
{
|
||||
"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": {
|
||||
"type": "object",
|
||||
"default": {
|
||||
"remove": true,
|
||||
"move": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"parent": [ "woocommerce/checkout-fields-block" ],
|
||||
"textdomain": "woo-gutenberg-products-block",
|
||||
"apiVersion": 2
|
||||
}
|
@ -0,0 +1,90 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { useMemo, useEffect, Fragment } from '@wordpress/element';
|
||||
import { Disabled } from 'wordpress-components';
|
||||
import {
|
||||
useCheckoutAddress,
|
||||
useStoreEvents,
|
||||
useEditorContext,
|
||||
} from '@woocommerce/base-context';
|
||||
import { AddressForm } from '@woocommerce/base-components/cart-checkout';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import PhoneNumber from '../../phone-number';
|
||||
|
||||
const Block = ( {
|
||||
showCompanyField = false,
|
||||
showApartmentField = false,
|
||||
showPhoneField = false,
|
||||
requireCompanyField = false,
|
||||
requirePhoneField = false,
|
||||
}: {
|
||||
showCompanyField: boolean;
|
||||
showApartmentField: boolean;
|
||||
showPhoneField: boolean;
|
||||
requireCompanyField: boolean;
|
||||
requirePhoneField: boolean;
|
||||
} ): JSX.Element => {
|
||||
const {
|
||||
defaultAddressFields,
|
||||
billingFields,
|
||||
setBillingFields,
|
||||
setPhone,
|
||||
} = useCheckoutAddress();
|
||||
const { dispatchCheckoutEvent } = useStoreEvents();
|
||||
const { isEditor } = useEditorContext();
|
||||
|
||||
// Clears data if fields are hidden.
|
||||
useEffect( () => {
|
||||
if ( ! showPhoneField ) {
|
||||
setPhone( '' );
|
||||
}
|
||||
}, [ showPhoneField, setPhone ] );
|
||||
|
||||
const addressFieldsConfig = useMemo( () => {
|
||||
return {
|
||||
company: {
|
||||
hidden: ! showCompanyField,
|
||||
required: requireCompanyField,
|
||||
},
|
||||
address_2: {
|
||||
hidden: ! showApartmentField,
|
||||
},
|
||||
};
|
||||
}, [ showCompanyField, requireCompanyField, showApartmentField ] );
|
||||
|
||||
const AddressFormWrapperComponent = isEditor ? Disabled : Fragment;
|
||||
|
||||
return (
|
||||
<AddressFormWrapperComponent>
|
||||
<AddressForm
|
||||
id="billing"
|
||||
type="billing"
|
||||
onChange={ ( values: Record< string, unknown > ) => {
|
||||
setBillingFields( values );
|
||||
dispatchCheckoutEvent( 'set-billing-address' );
|
||||
} }
|
||||
values={ billingFields }
|
||||
fields={ Object.keys( defaultAddressFields ) }
|
||||
fieldConfig={ addressFieldsConfig }
|
||||
/>
|
||||
{ showPhoneField && (
|
||||
<PhoneNumber
|
||||
isRequired={ requirePhoneField }
|
||||
value={ billingFields.phone }
|
||||
onChange={ ( value ) => {
|
||||
setPhone( value );
|
||||
dispatchCheckoutEvent( 'set-phone-number', {
|
||||
step: 'billing',
|
||||
} );
|
||||
} }
|
||||
/>
|
||||
) }
|
||||
</AddressFormWrapperComponent>
|
||||
);
|
||||
};
|
||||
|
||||
export default Block;
|
@ -0,0 +1,74 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { useBlockProps } from '@wordpress/block-editor';
|
||||
import { useCheckoutAddress } from '@woocommerce/base-context/hooks';
|
||||
import { innerBlockAreas } from '@woocommerce/blocks-checkout';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import {
|
||||
FormStepBlock,
|
||||
AdditionalFields,
|
||||
AdditionalFieldsContent,
|
||||
} from '../../form-step';
|
||||
import {
|
||||
useCheckoutBlockContext,
|
||||
useCheckoutBlockControlsContext,
|
||||
} from '../../context';
|
||||
import Block from './block';
|
||||
|
||||
export const Edit = ( {
|
||||
attributes,
|
||||
setAttributes,
|
||||
}: {
|
||||
attributes: {
|
||||
title: string;
|
||||
description: string;
|
||||
showStepNumber: boolean;
|
||||
};
|
||||
setAttributes: ( attributes: Record< string, unknown > ) => void;
|
||||
} ): JSX.Element | null => {
|
||||
const {
|
||||
showCompanyField,
|
||||
showApartmentField,
|
||||
requireCompanyField,
|
||||
showPhoneField,
|
||||
requirePhoneField,
|
||||
} = useCheckoutBlockContext();
|
||||
const {
|
||||
addressFieldControls: Controls,
|
||||
} = useCheckoutBlockControlsContext();
|
||||
const { showBillingFields } = useCheckoutAddress();
|
||||
|
||||
if ( ! showBillingFields ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<FormStepBlock
|
||||
setAttributes={ setAttributes }
|
||||
attributes={ attributes }
|
||||
className="wc-block-checkout__billing-fields"
|
||||
>
|
||||
<Controls />
|
||||
<Block
|
||||
showCompanyField={ showCompanyField }
|
||||
showApartmentField={ showApartmentField }
|
||||
requireCompanyField={ requireCompanyField }
|
||||
showPhoneField={ showPhoneField }
|
||||
requirePhoneField={ requirePhoneField }
|
||||
/>
|
||||
<AdditionalFields block={ innerBlockAreas.BILLING_ADDRESS } />
|
||||
</FormStepBlock>
|
||||
);
|
||||
};
|
||||
|
||||
export const Save = (): JSX.Element => {
|
||||
return (
|
||||
<div { ...useBlockProps.save() }>
|
||||
<AdditionalFieldsContent />
|
||||
</div>
|
||||
);
|
||||
};
|
@ -0,0 +1,62 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import withFilteredAttributes from '@woocommerce/base-hocs/with-filtered-attributes';
|
||||
import { FormStep } from '@woocommerce/base-components/cart-checkout';
|
||||
import { useCheckoutContext } from '@woocommerce/base-context';
|
||||
import { useCheckoutAddress } from '@woocommerce/base-context/hooks';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import Block from './block';
|
||||
import attributes from './attributes';
|
||||
import { useCheckoutBlockContext } from '../../context';
|
||||
|
||||
const FrontendBlock = ( {
|
||||
title,
|
||||
description,
|
||||
showStepNumber,
|
||||
children,
|
||||
}: {
|
||||
title: string;
|
||||
description: string;
|
||||
showStepNumber: boolean;
|
||||
children: JSX.Element;
|
||||
} ): JSX.Element | null => {
|
||||
const { isProcessing: checkoutIsProcessing } = useCheckoutContext();
|
||||
const { showBillingFields } = useCheckoutAddress();
|
||||
const {
|
||||
requireCompanyField,
|
||||
requirePhoneField,
|
||||
showApartmentField,
|
||||
showCompanyField,
|
||||
showPhoneField,
|
||||
} = useCheckoutBlockContext();
|
||||
|
||||
if ( ! showBillingFields ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<FormStep
|
||||
id="billing-fields"
|
||||
disabled={ checkoutIsProcessing }
|
||||
className="wc-block-checkout__billing-fields"
|
||||
title={ title }
|
||||
description={ description }
|
||||
showStepNumber={ showStepNumber }
|
||||
>
|
||||
<Block
|
||||
requireCompanyField={ requireCompanyField }
|
||||
showApartmentField={ showApartmentField }
|
||||
showCompanyField={ showCompanyField }
|
||||
showPhoneField={ showPhoneField }
|
||||
requirePhoneField={ requirePhoneField }
|
||||
/>
|
||||
{ children }
|
||||
</FormStep>
|
||||
);
|
||||
};
|
||||
|
||||
export default withFilteredAttributes( attributes )( FrontendBlock );
|
@ -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,
|
||||
} );
|
@ -0,0 +1,29 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import formStepAttributes from '../../form-step/attributes';
|
||||
|
||||
export default {
|
||||
...formStepAttributes( {
|
||||
defaultTitle: __(
|
||||
'Contact information',
|
||||
'woo-gutenberg-products-block'
|
||||
),
|
||||
defaultDescription: __(
|
||||
"We'll use this email to send you details and updates about your order.",
|
||||
'woo-gutenberg-products-block'
|
||||
),
|
||||
} ),
|
||||
lock: {
|
||||
type: 'object',
|
||||
default: {
|
||||
remove: true,
|
||||
move: true,
|
||||
},
|
||||
},
|
||||
};
|
@ -0,0 +1,26 @@
|
||||
{
|
||||
"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": {
|
||||
"type": "object",
|
||||
"default": {
|
||||
"remove": true,
|
||||
"move": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"parent": [ "woocommerce/checkout-fields-block" ],
|
||||
"textdomain": "woo-gutenberg-products-block",
|
||||
"apiVersion": 2
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { ValidatedTextInput } from '@woocommerce/base-components/text-input';
|
||||
import {
|
||||
useCheckoutContext,
|
||||
useCheckoutAddress,
|
||||
useStoreEvents,
|
||||
} from '@woocommerce/base-context';
|
||||
import { getSetting } from '@woocommerce/settings';
|
||||
import CheckboxControl from '@woocommerce/base-components/checkbox-control';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
|
||||
const Block = ( {
|
||||
allowCreateAccount,
|
||||
}: {
|
||||
allowCreateAccount: boolean;
|
||||
} ): JSX.Element => {
|
||||
const {
|
||||
customerId,
|
||||
shouldCreateAccount,
|
||||
setShouldCreateAccount,
|
||||
} = useCheckoutContext();
|
||||
const { billingFields, setEmail } = useCheckoutAddress();
|
||||
const { dispatchCheckoutEvent } = useStoreEvents();
|
||||
|
||||
const onChangeEmail = ( value ) => {
|
||||
setEmail( value );
|
||||
dispatchCheckoutEvent( 'set-email-address' );
|
||||
};
|
||||
|
||||
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 (
|
||||
<>
|
||||
<ValidatedTextInput
|
||||
id="email"
|
||||
type="email"
|
||||
label={ __( 'Email address', 'woo-gutenberg-products-block' ) }
|
||||
value={ billingFields.email }
|
||||
autoComplete="email"
|
||||
onChange={ onChangeEmail }
|
||||
required={ true }
|
||||
/>
|
||||
{ createAccountUI }
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Block;
|
@ -0,0 +1,55 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { useBlockProps } from '@wordpress/block-editor';
|
||||
import { Disabled } from '@wordpress/components';
|
||||
import { innerBlockAreas } from '@woocommerce/blocks-checkout';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import {
|
||||
FormStepBlock,
|
||||
AdditionalFields,
|
||||
AdditionalFieldsContent,
|
||||
} from '../../form-step';
|
||||
import Block from './block';
|
||||
import {
|
||||
useCheckoutBlockContext,
|
||||
useCheckoutBlockControlsContext,
|
||||
} from '../../context';
|
||||
|
||||
export const Edit = ( {
|
||||
attributes,
|
||||
setAttributes,
|
||||
}: {
|
||||
attributes: {
|
||||
title: string;
|
||||
description: string;
|
||||
showStepNumber: boolean;
|
||||
};
|
||||
setAttributes: ( attributes: Record< string, unknown > ) => void;
|
||||
} ): JSX.Element => {
|
||||
const { allowCreateAccount } = useCheckoutBlockContext();
|
||||
const { accountControls: Controls } = useCheckoutBlockControlsContext();
|
||||
return (
|
||||
<FormStepBlock
|
||||
attributes={ attributes }
|
||||
setAttributes={ setAttributes }
|
||||
>
|
||||
<Controls />
|
||||
<Disabled>
|
||||
<Block allowCreateAccount={ allowCreateAccount } />
|
||||
</Disabled>
|
||||
<AdditionalFields block={ innerBlockAreas.CONTACT_INFORMATION } />
|
||||
</FormStepBlock>
|
||||
);
|
||||
};
|
||||
|
||||
export const Save = (): JSX.Element => {
|
||||
return (
|
||||
<div { ...useBlockProps.save() }>
|
||||
<AdditionalFieldsContent />
|
||||
</div>
|
||||
);
|
||||
};
|
@ -0,0 +1,47 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import withFilteredAttributes from '@woocommerce/base-hocs/with-filtered-attributes';
|
||||
import { FormStep } from '@woocommerce/base-components/cart-checkout';
|
||||
import { useCheckoutContext } from '@woocommerce/base-context';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import Block from './block';
|
||||
import attributes from './attributes';
|
||||
import LoginPrompt from './login-prompt';
|
||||
import { useCheckoutBlockContext } from '../../context';
|
||||
|
||||
const FrontendBlock = ( {
|
||||
title,
|
||||
description,
|
||||
showStepNumber,
|
||||
children,
|
||||
}: {
|
||||
title: string;
|
||||
description: string;
|
||||
allowCreateAccount: boolean;
|
||||
showStepNumber: boolean;
|
||||
children: JSX.Element;
|
||||
} ) => {
|
||||
const { isProcessing: checkoutIsProcessing } = useCheckoutContext();
|
||||
const { allowCreateAccount } = useCheckoutBlockContext();
|
||||
|
||||
return (
|
||||
<FormStep
|
||||
id="contact-fields"
|
||||
disabled={ checkoutIsProcessing }
|
||||
className="wc-block-checkout__contact-fields"
|
||||
title={ title }
|
||||
description={ description }
|
||||
showStepNumber={ showStepNumber }
|
||||
stepHeadingContent={ () => <LoginPrompt /> }
|
||||
>
|
||||
<Block allowCreateAccount={ allowCreateAccount } />
|
||||
{ children }
|
||||
</FormStep>
|
||||
);
|
||||
};
|
||||
|
||||
export default withFilteredAttributes( attributes )( FrontendBlock );
|
@ -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,
|
||||
} );
|
@ -0,0 +1,33 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { getSetting } from '@woocommerce/settings';
|
||||
import { useCheckoutContext } from '@woocommerce/base-context';
|
||||
import { LOGIN_URL } from '@woocommerce/block-settings';
|
||||
|
||||
const LOGIN_TO_CHECKOUT_URL = `${ LOGIN_URL }?redirect_to=${ encodeURIComponent(
|
||||
window.location.href
|
||||
) }`;
|
||||
|
||||
const LoginPrompt = () => {
|
||||
const { customerId } = useCheckoutContext();
|
||||
|
||||
if ( ! getSetting( 'checkoutShowLoginReminder', true ) || customerId ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{ __(
|
||||
'Already have an account? ',
|
||||
'woocommerce'
|
||||
) }
|
||||
<a href={ LOGIN_TO_CHECKOUT_URL }>
|
||||
{ __( 'Log in.', 'woocommerce' ) }
|
||||
</a>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default LoginPrompt;
|
@ -0,0 +1,26 @@
|
||||
{
|
||||
"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": {
|
||||
"type": "object",
|
||||
"default": {
|
||||
"remove": true,
|
||||
"move": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"parent": [ "woocommerce/checkout-fields-block" ],
|
||||
"textdomain": "woo-gutenberg-products-block",
|
||||
"apiVersion": 2
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { useStoreCart } from '@woocommerce/base-context/hooks';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { CheckoutExpressPayment } from '../../../payment-methods';
|
||||
|
||||
const Block = (): JSX.Element | null => {
|
||||
const { cartNeedsPayment } = useStoreCart();
|
||||
|
||||
if ( ! cartNeedsPayment ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <CheckoutExpressPayment />;
|
||||
};
|
||||
|
||||
export default Block;
|
@ -0,0 +1,86 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { useBlockProps } from '@wordpress/block-editor';
|
||||
import { Placeholder, Button } from 'wordpress-components';
|
||||
import { useExpressPaymentMethods } from '@woocommerce/base-context/hooks';
|
||||
import { Icon, card } from '@woocommerce/icons';
|
||||
import { ADMIN_URL } from '@woocommerce/settings';
|
||||
import classnames from 'classnames';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import Block from './block';
|
||||
import './editor.scss';
|
||||
|
||||
/**
|
||||
* Renders a placeholder in the editor.
|
||||
*/
|
||||
const NoExpressPaymentMethodsPlaceholder = () => {
|
||||
return (
|
||||
<Placeholder
|
||||
icon={ <Icon srcElement={ card } /> }
|
||||
label={ __( 'Express Checkout', 'woo-gutenberg-products-block' ) }
|
||||
className="wp-block-woocommerce-checkout-express-payment-block-placeholder"
|
||||
>
|
||||
<span className="wp-block-woocommerce-checkout-express-payment-block-placeholder__description">
|
||||
{ __(
|
||||
"Your store doesn't have any Payment Methods that support the Express Checkout Block. If they are added, they will be shown here.",
|
||||
'woo-gutenberg-products-block'
|
||||
) }
|
||||
</span>
|
||||
<Button
|
||||
isPrimary
|
||||
href={ `${ ADMIN_URL }admin.php?page=wc-settings&tab=checkout` }
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="wp-block-woocommerce-checkout-express-payment-block-placeholder__button"
|
||||
>
|
||||
{ __(
|
||||
'Configure Payment Methods',
|
||||
'woo-gutenberg-products-block'
|
||||
) }
|
||||
</Button>
|
||||
</Placeholder>
|
||||
);
|
||||
};
|
||||
|
||||
export const Edit = ( {
|
||||
attributes,
|
||||
}: {
|
||||
attributes: {
|
||||
lock: {
|
||||
move: boolean;
|
||||
remove: boolean;
|
||||
};
|
||||
};
|
||||
} ): JSX.Element | null => {
|
||||
const { paymentMethods, isInitialized } = useExpressPaymentMethods();
|
||||
const hasExpressPaymentMethods = Object.keys( paymentMethods ).length > 0;
|
||||
const blockProps = useBlockProps( {
|
||||
className: classnames( {
|
||||
'wp-block-woocommerce-checkout-express-payment-block--has-express-payment-methods': hasExpressPaymentMethods,
|
||||
} ),
|
||||
attributes,
|
||||
} );
|
||||
|
||||
if ( ! isInitialized ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div { ...blockProps }>
|
||||
{ hasExpressPaymentMethods ? (
|
||||
<Block />
|
||||
) : (
|
||||
<NoExpressPaymentMethodsPlaceholder />
|
||||
) }
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const Save = (): JSX.Element => {
|
||||
return <div { ...useBlockProps.save() } />;
|
||||
};
|
@ -0,0 +1,29 @@
|
||||
// Adjust padding and margins in the editor to improve selected block outlines.
|
||||
.wp-block-woocommerce-checkout-express-payment-block {
|
||||
margin: 14px 0 28px;
|
||||
|
||||
.components-placeholder__label svg {
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
.wc-block-components-express-payment-continue-rule--checkout {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
&.wp-block-woocommerce-checkout-express-payment-block--has-express-payment-methods {
|
||||
padding: 14px 0;
|
||||
margin: -14px 0 14px 0 !important;
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
|
||||
.wp-block-woocommerce-checkout-express-payment-block-placeholder {
|
||||
* {
|
||||
pointer-events: all; // Overrides parent disabled component in editor context
|
||||
}
|
||||
|
||||
.wp-block-woocommerce-checkout-express-payment-block-placeholder__description {
|
||||
display: block;
|
||||
margin: 0 0 1em;
|
||||
}
|
||||
}
|
@ -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,
|
||||
} );
|
@ -0,0 +1,26 @@
|
||||
{
|
||||
"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": {
|
||||
"type": "object",
|
||||
"default": {
|
||||
"remove": true,
|
||||
"move": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"parent": [ "woocommerce/checkout" ],
|
||||
"textdomain": "woo-gutenberg-products-block",
|
||||
"apiVersion": 2
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { useBlockProps, InnerBlocks } from '@wordpress/block-editor';
|
||||
import { Main } from '@woocommerce/base-components/sidebar-layout';
|
||||
import { innerBlockAreas } from '@woocommerce/blocks-checkout';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
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();
|
||||
const allowedBlocks = getAllowedBlocks( innerBlockAreas.CHECKOUT_FIELDS );
|
||||
|
||||
const {
|
||||
addressFieldControls: Controls,
|
||||
} = useCheckoutBlockControlsContext();
|
||||
|
||||
useForcedLayout( {
|
||||
clientId,
|
||||
template: allowedBlocks,
|
||||
} );
|
||||
return (
|
||||
<Main className="wc-block-checkout__main">
|
||||
<div { ...blockProps }>
|
||||
<Controls />
|
||||
<form className="wc-block-components-form wc-block-checkout__form">
|
||||
<InnerBlocks
|
||||
allowedBlocks={ allowedBlocks }
|
||||
templateLock={ false }
|
||||
renderAppender={ InnerBlocks.ButtonBlockAppender }
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
</Main>
|
||||
);
|
||||
};
|
||||
|
||||
export const Save = (): JSX.Element => {
|
||||
return (
|
||||
<div { ...useBlockProps.save() }>
|
||||
<InnerBlocks.Content />
|
||||
</div>
|
||||
);
|
||||
};
|
@ -0,0 +1,25 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { Main } from '@woocommerce/base-components/sidebar-layout';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import './style.scss';
|
||||
|
||||
const FrontendBlock = ( {
|
||||
children,
|
||||
}: {
|
||||
children: JSX.Element;
|
||||
} ): JSX.Element => {
|
||||
return (
|
||||
<Main className="wc-block-checkout__main">
|
||||
<form className="wc-block-components-form wc-block-checkout__form">
|
||||
{ children }
|
||||
</form>
|
||||
</Main>
|
||||
);
|
||||
};
|
||||
|
||||
export default FrontendBlock;
|
@ -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,
|
||||
} );
|
@ -0,0 +1,53 @@
|
||||
.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-checkout__shipping-fields,
|
||||
.wc-block-checkout__billing-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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
{
|
||||
"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": {
|
||||
"type": "object",
|
||||
"default": {
|
||||
"remove": true,
|
||||
"move": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"parent": [ "woocommerce/checkout-fields-block" ],
|
||||
"textdomain": "woo-gutenberg-products-block",
|
||||
"apiVersion": 2
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { FormStep } from '@woocommerce/base-components/cart-checkout';
|
||||
import {
|
||||
useCheckoutContext,
|
||||
useShippingDataContext,
|
||||
} from '@woocommerce/base-context';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import CheckoutOrderNotes from '../../order-notes';
|
||||
|
||||
const Block = (): JSX.Element => {
|
||||
const { needsShipping } = useShippingDataContext();
|
||||
const {
|
||||
isProcessing: checkoutIsProcessing,
|
||||
orderNotes,
|
||||
dispatchActions,
|
||||
} = useCheckoutContext();
|
||||
const { setOrderNotes } = dispatchActions;
|
||||
|
||||
return (
|
||||
<FormStep
|
||||
id="order-notes"
|
||||
showStepNumber={ false }
|
||||
className="wc-block-checkout__order-notes"
|
||||
disabled={ checkoutIsProcessing }
|
||||
>
|
||||
<CheckoutOrderNotes
|
||||
disabled={ checkoutIsProcessing }
|
||||
onChange={ setOrderNotes }
|
||||
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 Block;
|
@ -0,0 +1,26 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { useBlockProps } from '@wordpress/block-editor';
|
||||
import { Disabled } from '@wordpress/components';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import Block from './block';
|
||||
import './editor.scss';
|
||||
|
||||
export const Edit = (): JSX.Element => {
|
||||
const blockProps = useBlockProps();
|
||||
return (
|
||||
<div { ...blockProps }>
|
||||
<Disabled>
|
||||
<Block />
|
||||
</Disabled>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const Save = (): JSX.Element => {
|
||||
return <div { ...useBlockProps.save() } />;
|
||||
};
|
@ -0,0 +1,12 @@
|
||||
// Adjust padding and margins in the editor to improve selected block outlines.
|
||||
.wp-block-woocommerce-checkout-order-note-block {
|
||||
margin-top: 20px;
|
||||
margin-bottom: 20px;
|
||||
padding-top: 4px;
|
||||
padding-bottom: 4px;
|
||||
|
||||
.wc-block-checkout__add-note {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
@ -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,
|
||||
} );
|
@ -0,0 +1,18 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { getSetting } from '@woocommerce/settings';
|
||||
|
||||
export default {
|
||||
showRateAfterTaxName: {
|
||||
type: 'boolean',
|
||||
default: getSetting( 'displayCartPricesIncludingTax', false ),
|
||||
},
|
||||
lock: {
|
||||
type: 'object',
|
||||
default: {
|
||||
move: true,
|
||||
remove: true,
|
||||
},
|
||||
},
|
||||
};
|
@ -0,0 +1,25 @@
|
||||
{
|
||||
"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": {
|
||||
"type": "object",
|
||||
"default": {
|
||||
"remove": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"parent": [ "woocommerce/checkout-totals-block" ],
|
||||
"textdomain": "woo-gutenberg-products-block",
|
||||
"apiVersion": 2
|
||||
}
|
@ -0,0 +1,112 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import {
|
||||
OrderSummary,
|
||||
TotalsCoupon,
|
||||
TotalsDiscount,
|
||||
TotalsFooterItem,
|
||||
TotalsShipping,
|
||||
} from '@woocommerce/base-components/cart-checkout';
|
||||
import {
|
||||
Subtotal,
|
||||
TotalsFees,
|
||||
TotalsTaxes,
|
||||
ExperimentalOrderMeta,
|
||||
TotalsWrapper,
|
||||
} from '@woocommerce/blocks-checkout';
|
||||
|
||||
import { getCurrencyFromPriceResponse } from '@woocommerce/price-format';
|
||||
import { useShippingDataContext } from '@woocommerce/base-context';
|
||||
import {
|
||||
useStoreCartCoupons,
|
||||
useStoreCart,
|
||||
} from '@woocommerce/base-context/hooks';
|
||||
import { getSetting } from '@woocommerce/settings';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
|
||||
const Block = ( {
|
||||
showRateAfterTaxName = false,
|
||||
}: {
|
||||
showRateAfterTaxName: boolean;
|
||||
} ): JSX.Element => {
|
||||
const { cartItems, cartTotals, cartCoupons, cartFees } = useStoreCart();
|
||||
const {
|
||||
applyCoupon,
|
||||
removeCoupon,
|
||||
isApplyingCoupon,
|
||||
isRemovingCoupon,
|
||||
} = useStoreCartCoupons();
|
||||
|
||||
const { needsShipping } = useShippingDataContext();
|
||||
const totalsCurrency = getCurrencyFromPriceResponse( cartTotals );
|
||||
|
||||
// Prepare props to pass to the ExperimentalOrderMeta slot fill.
|
||||
// We need to pluck out receiveCart.
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const { extensions, receiveCart, ...cart } = useStoreCart();
|
||||
const slotFillProps = {
|
||||
extensions,
|
||||
cart,
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<TotalsWrapper>
|
||||
<OrderSummary cartItems={ cartItems } />
|
||||
</TotalsWrapper>
|
||||
<TotalsWrapper>
|
||||
<Subtotal currency={ totalsCurrency } values={ cartTotals } />
|
||||
<TotalsFees currency={ totalsCurrency } cartFees={ cartFees } />
|
||||
<TotalsDiscount
|
||||
cartCoupons={ cartCoupons }
|
||||
currency={ totalsCurrency }
|
||||
isRemovingCoupon={ isRemovingCoupon }
|
||||
removeCoupon={ removeCoupon }
|
||||
values={ cartTotals }
|
||||
/>
|
||||
</TotalsWrapper>
|
||||
{ getSetting( 'couponsEnabled', true ) && (
|
||||
<TotalsWrapper>
|
||||
<TotalsCoupon
|
||||
onSubmit={ applyCoupon }
|
||||
initialOpen={ false }
|
||||
isLoading={ isApplyingCoupon }
|
||||
/>
|
||||
</TotalsWrapper>
|
||||
) }
|
||||
{ needsShipping && (
|
||||
<TotalsWrapper>
|
||||
<TotalsShipping
|
||||
showCalculator={ false }
|
||||
showRateSelector={ false }
|
||||
values={ cartTotals }
|
||||
currency={ totalsCurrency }
|
||||
/>
|
||||
</TotalsWrapper>
|
||||
) }
|
||||
{ ! getSetting( 'displayCartPricesIncludingTax', false ) &&
|
||||
parseInt( cartTotals.total_tax, 10 ) > 0 && (
|
||||
<TotalsWrapper>
|
||||
<TotalsTaxes
|
||||
currency={ totalsCurrency }
|
||||
showRateAfterTaxName={ showRateAfterTaxName }
|
||||
values={ cartTotals }
|
||||
/>
|
||||
</TotalsWrapper>
|
||||
) }
|
||||
<TotalsWrapper>
|
||||
<TotalsFooterItem
|
||||
currency={ totalsCurrency }
|
||||
values={ cartTotals }
|
||||
/>
|
||||
</TotalsWrapper>
|
||||
<ExperimentalOrderMeta.Slot { ...slotFillProps } />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Block;
|
@ -0,0 +1,79 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { useBlockProps, InspectorControls } from '@wordpress/block-editor';
|
||||
import { Disabled, PanelBody, ToggleControl } from '@wordpress/components';
|
||||
import { getSetting } from '@woocommerce/settings';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import Block from './block';
|
||||
|
||||
export const Edit = ( {
|
||||
attributes,
|
||||
setAttributes,
|
||||
}: {
|
||||
attributes: {
|
||||
showRateAfterTaxName: boolean;
|
||||
lock: {
|
||||
move: boolean;
|
||||
remove: boolean;
|
||||
};
|
||||
};
|
||||
setAttributes: ( attributes: Record< string, unknown > ) => void;
|
||||
} ): JSX.Element => {
|
||||
const blockProps = useBlockProps();
|
||||
const taxesEnabled = getSetting( 'taxesEnabled' ) as boolean;
|
||||
const displayItemizedTaxes = getSetting(
|
||||
'displayItemizedTaxes',
|
||||
false
|
||||
) as boolean;
|
||||
const displayCartPricesIncludingTax = getSetting(
|
||||
'displayCartPricesIncludingTax',
|
||||
false
|
||||
) as boolean;
|
||||
return (
|
||||
<div { ...blockProps }>
|
||||
<InspectorControls>
|
||||
{ taxesEnabled &&
|
||||
displayItemizedTaxes &&
|
||||
! displayCartPricesIncludingTax && (
|
||||
<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={ attributes.showRateAfterTaxName }
|
||||
onChange={ () =>
|
||||
setAttributes( {
|
||||
showRateAfterTaxName: ! attributes.showRateAfterTaxName,
|
||||
} )
|
||||
}
|
||||
/>
|
||||
</PanelBody>
|
||||
) }
|
||||
</InspectorControls>
|
||||
<Disabled>
|
||||
<Block
|
||||
showRateAfterTaxName={ attributes.showRateAfterTaxName }
|
||||
/>
|
||||
</Disabled>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const Save = (): JSX.Element => {
|
||||
return <div { ...useBlockProps.save() } />;
|
||||
};
|
@ -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,
|
||||
} );
|
@ -0,0 +1,23 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import formStepAttributes from '../../form-step/attributes';
|
||||
|
||||
export default {
|
||||
...formStepAttributes( {
|
||||
defaultTitle: __( 'Payment options', 'woo-gutenberg-products-block' ),
|
||||
defaultDescription: '',
|
||||
} ),
|
||||
lock: {
|
||||
type: 'object',
|
||||
default: {
|
||||
move: true,
|
||||
remove: true,
|
||||
},
|
||||
},
|
||||
};
|
@ -0,0 +1,26 @@
|
||||
{
|
||||
"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": {
|
||||
"type": "object",
|
||||
"default": {
|
||||
"remove": true,
|
||||
"move": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"parent": [ "woocommerce/checkout-fields-block" ],
|
||||
"textdomain": "woo-gutenberg-products-block",
|
||||
"apiVersion": 2
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { StoreNoticesProvider } from '@woocommerce/base-context';
|
||||
import { useEmitResponse } from '@woocommerce/base-context/hooks';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { PaymentMethods } from '../../../payment-methods';
|
||||
|
||||
const Block = (): JSX.Element | null => {
|
||||
const { noticeContexts } = useEmitResponse();
|
||||
|
||||
return (
|
||||
<StoreNoticesProvider context={ noticeContexts.PAYMENTS }>
|
||||
<PaymentMethods />
|
||||
</StoreNoticesProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export default Block;
|
@ -0,0 +1,87 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { InspectorControls, useBlockProps } from '@wordpress/block-editor';
|
||||
import { PanelBody, Disabled, ExternalLink } from '@wordpress/components';
|
||||
import { ADMIN_URL, getSetting } from '@woocommerce/settings';
|
||||
import ExternalLinkCard from '@woocommerce/editor-components/external-link-card';
|
||||
import { innerBlockAreas } from '@woocommerce/blocks-checkout';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import {
|
||||
FormStepBlock,
|
||||
FormStepBlockProps,
|
||||
AdditionalFields,
|
||||
AdditionalFieldsContent,
|
||||
} from '../../form-step';
|
||||
import Block from './block';
|
||||
|
||||
type paymentAdminLink = {
|
||||
id: number;
|
||||
title: string;
|
||||
description: string;
|
||||
};
|
||||
|
||||
export const Edit = ( props: FormStepBlockProps ): JSX.Element => {
|
||||
const globalPaymentMethods = getSetting(
|
||||
'globalPaymentMethods'
|
||||
) as paymentAdminLink[];
|
||||
|
||||
return (
|
||||
<FormStepBlock
|
||||
{ ...props }
|
||||
className="wc-block-checkout__payment-method"
|
||||
>
|
||||
<InspectorControls>
|
||||
{ globalPaymentMethods.length > 0 && (
|
||||
<PanelBody
|
||||
title={ __(
|
||||
'Methods',
|
||||
'woo-gutenberg-products-block'
|
||||
) }
|
||||
>
|
||||
<p className="wc-block-checkout__controls-text">
|
||||
{ __(
|
||||
'You currently have the following payment integrations active.',
|
||||
'woo-gutenberg-products-block'
|
||||
) }
|
||||
</p>
|
||||
{ globalPaymentMethods.map( ( method ) => {
|
||||
return (
|
||||
<ExternalLinkCard
|
||||
key={ method.id }
|
||||
href={ `${ ADMIN_URL }admin.php?page=wc-settings&tab=checkout§ion=${ method.id }` }
|
||||
title={ method.title }
|
||||
description={ method.description }
|
||||
/>
|
||||
);
|
||||
} ) }
|
||||
<ExternalLink
|
||||
href={ `${ ADMIN_URL }admin.php?page=wc-settings&tab=checkout` }
|
||||
>
|
||||
{ __(
|
||||
'Manage payment methods',
|
||||
'woo-gutenberg-products-block'
|
||||
) }
|
||||
</ExternalLink>
|
||||
</PanelBody>
|
||||
) }
|
||||
</InspectorControls>
|
||||
<Disabled>
|
||||
<Block />
|
||||
</Disabled>
|
||||
<AdditionalFields block={ innerBlockAreas.PAYMENT_METHODS } />
|
||||
</FormStepBlock>
|
||||
);
|
||||
};
|
||||
|
||||
export const Save = (): JSX.Element => {
|
||||
return (
|
||||
<div { ...useBlockProps.save() }>
|
||||
<AdditionalFieldsContent />
|
||||
</div>
|
||||
);
|
||||
};
|
@ -0,0 +1,53 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { useStoreCart, useEmitResponse } from '@woocommerce/base-context/hooks';
|
||||
import withFilteredAttributes from '@woocommerce/base-hocs/with-filtered-attributes';
|
||||
import { FormStep } from '@woocommerce/base-components/cart-checkout';
|
||||
import {
|
||||
useCheckoutContext,
|
||||
StoreNoticesProvider,
|
||||
} from '@woocommerce/base-context';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import Block from './block';
|
||||
import attributes from './attributes';
|
||||
|
||||
const FrontendBlock = ( {
|
||||
title,
|
||||
description,
|
||||
showStepNumber,
|
||||
children,
|
||||
}: {
|
||||
title: string;
|
||||
description: string;
|
||||
showStepNumber: boolean;
|
||||
children: JSX.Element;
|
||||
} ) => {
|
||||
const { isProcessing: checkoutIsProcessing } = useCheckoutContext();
|
||||
const { cartNeedsPayment } = useStoreCart();
|
||||
const { noticeContexts } = useEmitResponse();
|
||||
|
||||
if ( ! cartNeedsPayment ) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<FormStep
|
||||
id="payment-method"
|
||||
disabled={ checkoutIsProcessing }
|
||||
className="wc-block-checkout__payment-method"
|
||||
title={ title }
|
||||
description={ description }
|
||||
showStepNumber={ showStepNumber }
|
||||
>
|
||||
<StoreNoticesProvider context={ noticeContexts.PAYMENTS }>
|
||||
<Block />
|
||||
</StoreNoticesProvider>
|
||||
{ children }
|
||||
</FormStep>
|
||||
);
|
||||
};
|
||||
|
||||
export default withFilteredAttributes( attributes )( FrontendBlock );
|
@ -0,0 +1,22 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { Icon, card } 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={ card } />,
|
||||
foreground: '#874FB9',
|
||||
},
|
||||
attributes,
|
||||
edit: Edit,
|
||||
save: Save,
|
||||
} );
|
@ -0,0 +1,26 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import formStepAttributes from '../../form-step/attributes';
|
||||
|
||||
export default {
|
||||
...formStepAttributes( {
|
||||
defaultTitle: __( 'Shipping address', 'woo-gutenberg-products-block' ),
|
||||
defaultDescription: __(
|
||||
'Enter the address where you want your order delivered.',
|
||||
'woo-gutenberg-products-block'
|
||||
),
|
||||
} ),
|
||||
lock: {
|
||||
type: 'object',
|
||||
default: {
|
||||
move: true,
|
||||
remove: true,
|
||||
},
|
||||
},
|
||||
};
|
@ -0,0 +1,26 @@
|
||||
{
|
||||
"name": "woocommerce/checkout-shipping-address-block",
|
||||
"version": "1.0.0",
|
||||
"title": "Shipping Address",
|
||||
"description": "Collect your customer's shipping address.",
|
||||
"category": "woocommerce",
|
||||
"supports": {
|
||||
"align": false,
|
||||
"html": false,
|
||||
"multiple": false,
|
||||
"reusable": false,
|
||||
"inserter": false
|
||||
},
|
||||
"attributes": {
|
||||
"lock": {
|
||||
"type": "object",
|
||||
"default": {
|
||||
"remove": true,
|
||||
"move": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"parent": [ "woocommerce/checkout-fields-block" ],
|
||||
"textdomain": "woo-gutenberg-products-block",
|
||||
"apiVersion": 2
|
||||
}
|
@ -0,0 +1,108 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { useMemo, useEffect, Fragment } from '@wordpress/element';
|
||||
import { Disabled } from 'wordpress-components';
|
||||
import { AddressForm } from '@woocommerce/base-components/cart-checkout';
|
||||
import {
|
||||
useCheckoutAddress,
|
||||
useStoreEvents,
|
||||
useEditorContext,
|
||||
} from '@woocommerce/base-context';
|
||||
import CheckboxControl from '@woocommerce/base-components/checkbox-control';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import PhoneNumber from '../../phone-number';
|
||||
|
||||
const Block = ( {
|
||||
showCompanyField = false,
|
||||
showApartmentField = false,
|
||||
showPhoneField = false,
|
||||
requireCompanyField = false,
|
||||
requirePhoneField = false,
|
||||
}: {
|
||||
showCompanyField: boolean;
|
||||
showApartmentField: boolean;
|
||||
showPhoneField: boolean;
|
||||
requireCompanyField: boolean;
|
||||
requirePhoneField: boolean;
|
||||
} ): JSX.Element => {
|
||||
const {
|
||||
defaultAddressFields,
|
||||
setShippingFields,
|
||||
shippingFields,
|
||||
setShippingAsBilling,
|
||||
shippingAsBilling,
|
||||
setShippingPhone,
|
||||
} = useCheckoutAddress();
|
||||
const { dispatchCheckoutEvent } = useStoreEvents();
|
||||
const { isEditor } = useEditorContext();
|
||||
|
||||
// Clears data if fields are hidden.
|
||||
useEffect( () => {
|
||||
if ( ! showPhoneField ) {
|
||||
setShippingPhone( '' );
|
||||
}
|
||||
}, [ showPhoneField, setShippingPhone ] );
|
||||
|
||||
const addressFieldsConfig = useMemo( () => {
|
||||
return {
|
||||
company: {
|
||||
hidden: ! showCompanyField,
|
||||
required: requireCompanyField,
|
||||
},
|
||||
address_2: {
|
||||
hidden: ! showApartmentField,
|
||||
},
|
||||
};
|
||||
}, [ showCompanyField, requireCompanyField, showApartmentField ] );
|
||||
|
||||
const AddressFormWrapperComponent = isEditor ? Disabled : Fragment;
|
||||
|
||||
return (
|
||||
<>
|
||||
<AddressFormWrapperComponent>
|
||||
<AddressForm
|
||||
id="shipping"
|
||||
type="shipping"
|
||||
onChange={ ( values: Record< string, unknown > ) => {
|
||||
setShippingFields( values );
|
||||
dispatchCheckoutEvent( 'set-shipping-address' );
|
||||
} }
|
||||
values={ shippingFields }
|
||||
fields={ Object.keys( defaultAddressFields ) }
|
||||
fieldConfig={ addressFieldsConfig }
|
||||
/>
|
||||
{ showPhoneField && (
|
||||
<PhoneNumber
|
||||
id="shipping-phone"
|
||||
isRequired={ requirePhoneField }
|
||||
value={ shippingFields.phone }
|
||||
onChange={ ( value ) => {
|
||||
setShippingPhone( value );
|
||||
dispatchCheckoutEvent( 'set-phone-number', {
|
||||
step: 'shipping',
|
||||
} );
|
||||
} }
|
||||
/>
|
||||
) }
|
||||
</AddressFormWrapperComponent>
|
||||
<CheckboxControl
|
||||
className="wc-block-checkout__use-address-for-billing"
|
||||
label={ __(
|
||||
'Use same address for billing',
|
||||
'woo-gutenberg-products-block'
|
||||
) }
|
||||
checked={ shippingAsBilling }
|
||||
onChange={ ( checked: boolean ) =>
|
||||
setShippingAsBilling( checked )
|
||||
}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Block;
|
@ -0,0 +1,67 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { useBlockProps } from '@wordpress/block-editor';
|
||||
import { innerBlockAreas } from '@woocommerce/blocks-checkout';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import {
|
||||
FormStepBlock,
|
||||
AdditionalFields,
|
||||
AdditionalFieldsContent,
|
||||
} from '../../form-step';
|
||||
import {
|
||||
useCheckoutBlockContext,
|
||||
useCheckoutBlockControlsContext,
|
||||
} from '../../context';
|
||||
import Block from './block';
|
||||
|
||||
export const Edit = ( {
|
||||
attributes,
|
||||
setAttributes,
|
||||
}: {
|
||||
attributes: {
|
||||
title: string;
|
||||
description: string;
|
||||
showStepNumber: boolean;
|
||||
};
|
||||
setAttributes: ( attributes: Record< string, unknown > ) => void;
|
||||
} ): JSX.Element => {
|
||||
const {
|
||||
showCompanyField,
|
||||
showApartmentField,
|
||||
requireCompanyField,
|
||||
showPhoneField,
|
||||
requirePhoneField,
|
||||
} = useCheckoutBlockContext();
|
||||
const {
|
||||
addressFieldControls: Controls,
|
||||
} = useCheckoutBlockControlsContext();
|
||||
return (
|
||||
<FormStepBlock
|
||||
setAttributes={ setAttributes }
|
||||
attributes={ attributes }
|
||||
className="wc-block-checkout__shipping-fields"
|
||||
>
|
||||
<Controls />
|
||||
<Block
|
||||
showCompanyField={ showCompanyField }
|
||||
showApartmentField={ showApartmentField }
|
||||
requireCompanyField={ requireCompanyField }
|
||||
showPhoneField={ showPhoneField }
|
||||
requirePhoneField={ requirePhoneField }
|
||||
/>
|
||||
<AdditionalFields block={ innerBlockAreas.SHIPPING_ADDRESS } />
|
||||
</FormStepBlock>
|
||||
);
|
||||
};
|
||||
|
||||
export const Save = (): JSX.Element => {
|
||||
return (
|
||||
<div { ...useBlockProps.save() }>
|
||||
<AdditionalFieldsContent />
|
||||
</div>
|
||||
);
|
||||
};
|
@ -0,0 +1,62 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import withFilteredAttributes from '@woocommerce/base-hocs/with-filtered-attributes';
|
||||
import { FormStep } from '@woocommerce/base-components/cart-checkout';
|
||||
import { useCheckoutContext } from '@woocommerce/base-context';
|
||||
import { useCheckoutAddress } from '@woocommerce/base-context/hooks';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import Block from './block';
|
||||
import attributes from './attributes';
|
||||
import { useCheckoutBlockContext } from '../../context';
|
||||
|
||||
const FrontendBlock = ( {
|
||||
title,
|
||||
description,
|
||||
showStepNumber,
|
||||
children,
|
||||
}: {
|
||||
title: string;
|
||||
description: string;
|
||||
showStepNumber: boolean;
|
||||
children: JSX.Element;
|
||||
} ) => {
|
||||
const { isProcessing: checkoutIsProcessing } = useCheckoutContext();
|
||||
const { showShippingFields } = useCheckoutAddress();
|
||||
const {
|
||||
requireCompanyField,
|
||||
requirePhoneField,
|
||||
showApartmentField,
|
||||
showCompanyField,
|
||||
showPhoneField,
|
||||
} = useCheckoutBlockContext();
|
||||
|
||||
if ( ! showShippingFields ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<FormStep
|
||||
id="shipping-fields"
|
||||
disabled={ checkoutIsProcessing }
|
||||
className="wc-block-checkout__shipping-fields"
|
||||
title={ title }
|
||||
description={ description }
|
||||
showStepNumber={ showStepNumber }
|
||||
>
|
||||
<Block
|
||||
requireCompanyField={ requireCompanyField }
|
||||
requirePhoneField={ requirePhoneField }
|
||||
showApartmentField={ showApartmentField }
|
||||
showCompanyField={ showCompanyField }
|
||||
showPhoneField={ showPhoneField }
|
||||
/>
|
||||
{ children }
|
||||
</FormStep>
|
||||
);
|
||||
};
|
||||
|
||||
export default withFilteredAttributes( attributes )( FrontendBlock );
|
@ -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,
|
||||
} );
|
@ -0,0 +1,27 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import formStepAttributes from '../../form-step/attributes';
|
||||
|
||||
export default {
|
||||
...formStepAttributes( {
|
||||
defaultTitle: __( 'Shipping options', 'woo-gutenberg-products-block' ),
|
||||
defaultDescription: '',
|
||||
} ),
|
||||
allowCreateAccount: {
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
},
|
||||
lock: {
|
||||
type: 'object',
|
||||
default: {
|
||||
move: true,
|
||||
remove: true,
|
||||
},
|
||||
},
|
||||
};
|
@ -0,0 +1,26 @@
|
||||
{
|
||||
"name": "woocommerce/checkout-shipping-methods-block",
|
||||
"version": "1.0.0",
|
||||
"title": "Shipping Options",
|
||||
"description": "Shipping options for your store.",
|
||||
"category": "woocommerce",
|
||||
"supports": {
|
||||
"align": false,
|
||||
"html": false,
|
||||
"multiple": false,
|
||||
"reusable": false,
|
||||
"inserter": false
|
||||
},
|
||||
"attributes": {
|
||||
"lock": {
|
||||
"type": "object",
|
||||
"default": {
|
||||
"remove": true,
|
||||
"move": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"parent": [ "woocommerce/checkout-fields-block" ],
|
||||
"textdomain": "woo-gutenberg-products-block",
|
||||
"apiVersion": 2
|
||||
}
|
@ -0,0 +1,112 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { ShippingRatesControl } from '@woocommerce/base-components/cart-checkout';
|
||||
import { getShippingRatesPackageCount } 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 { 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';
|
||||
import './style.scss';
|
||||
|
||||
/**
|
||||
* 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 Block = (): JSX.Element | null => {
|
||||
const { isEditor } = useEditorContext();
|
||||
const {
|
||||
shippingRates,
|
||||
shippingRatesLoading,
|
||||
needsShipping,
|
||||
hasCalculatedShipping,
|
||||
} = useShippingDataContext();
|
||||
|
||||
if ( ! needsShipping ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const shippingRatesPackageCount = getShippingRatesPackageCount(
|
||||
shippingRates
|
||||
);
|
||||
|
||||
if (
|
||||
! isEditor &&
|
||||
! hasCalculatedShipping &&
|
||||
! shippingRatesPackageCount
|
||||
) {
|
||||
return (
|
||||
<p>
|
||||
{ __(
|
||||
'Shipping options will be displayed here after entering your full shipping address.',
|
||||
'woo-gutenberg-products-block'
|
||||
) }
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{ isEditor && ! shippingRatesPackageCount ? (
|
||||
<NoShippingPlaceholder />
|
||||
) : (
|
||||
<ShippingRatesControl
|
||||
noResultsMessage={
|
||||
<Notice
|
||||
isDismissible={ false }
|
||||
className={ classnames(
|
||||
'wc-block-components-shipping-rates-control__no-results-notice',
|
||||
'woocommerce-error'
|
||||
) }
|
||||
>
|
||||
{ __(
|
||||
'There are no shipping options available. Please check your shipping address.',
|
||||
'woo-gutenberg-products-block'
|
||||
) }
|
||||
</Notice>
|
||||
}
|
||||
renderOption={ renderShippingRatesControlOption }
|
||||
shippingRates={ shippingRates }
|
||||
shippingRatesLoading={ shippingRatesLoading }
|
||||
/>
|
||||
) }
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Block;
|
@ -0,0 +1,130 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { InspectorControls, useBlockProps } from '@wordpress/block-editor';
|
||||
import { PanelBody, Disabled, ExternalLink } from '@wordpress/components';
|
||||
import { ADMIN_URL, getSetting } from '@woocommerce/settings';
|
||||
import ExternalLinkCard from '@woocommerce/editor-components/external-link-card';
|
||||
import { innerBlockAreas } from '@woocommerce/blocks-checkout';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import {
|
||||
FormStepBlock,
|
||||
AdditionalFields,
|
||||
AdditionalFieldsContent,
|
||||
} from '../../form-step';
|
||||
import Block from './block';
|
||||
|
||||
type shippingAdminLink = {
|
||||
id: number;
|
||||
title: string;
|
||||
description: string;
|
||||
};
|
||||
|
||||
export const Edit = ( {
|
||||
attributes,
|
||||
setAttributes,
|
||||
}: {
|
||||
attributes: {
|
||||
title: string;
|
||||
description: string;
|
||||
showStepNumber: boolean;
|
||||
allowCreateAccount: boolean;
|
||||
};
|
||||
setAttributes: ( attributes: Record< string, unknown > ) => void;
|
||||
} ): JSX.Element => {
|
||||
const globalShippingMethods = getSetting(
|
||||
'globalShippingMethods'
|
||||
) as shippingAdminLink[];
|
||||
const activeShippingZones = getSetting(
|
||||
'activeShippingZones'
|
||||
) as shippingAdminLink[];
|
||||
|
||||
return (
|
||||
<FormStepBlock
|
||||
attributes={ attributes }
|
||||
setAttributes={ setAttributes }
|
||||
>
|
||||
<InspectorControls>
|
||||
{ globalShippingMethods.length > 0 && (
|
||||
<PanelBody
|
||||
title={ __(
|
||||
'Methods',
|
||||
'woo-gutenberg-products-block'
|
||||
) }
|
||||
>
|
||||
<p className="wc-block-checkout__controls-text">
|
||||
{ __(
|
||||
'You currently have the following shipping integrations active.',
|
||||
'woo-gutenberg-products-block'
|
||||
) }
|
||||
</p>
|
||||
{ globalShippingMethods.map( ( method ) => {
|
||||
return (
|
||||
<ExternalLinkCard
|
||||
key={ method.id }
|
||||
href={ `${ ADMIN_URL }admin.php?page=wc-settings&tab=shipping§ion=${ method.id }` }
|
||||
title={ method.title }
|
||||
description={ method.description }
|
||||
/>
|
||||
);
|
||||
} ) }
|
||||
<ExternalLink
|
||||
href={ `${ ADMIN_URL }admin.php?page=wc-settings&tab=shipping` }
|
||||
>
|
||||
{ __(
|
||||
'Manage shipping methods',
|
||||
'woo-gutenberg-products-block'
|
||||
) }
|
||||
</ExternalLink>
|
||||
</PanelBody>
|
||||
) }
|
||||
{ activeShippingZones.length && (
|
||||
<PanelBody
|
||||
title={ __( 'Zones', 'woo-gutenberg-products-block' ) }
|
||||
>
|
||||
<p className="wc-block-checkout__controls-text">
|
||||
{ __(
|
||||
'You currently have the following shipping zones active.',
|
||||
'woo-gutenberg-products-block'
|
||||
) }
|
||||
</p>
|
||||
{ activeShippingZones.map( ( zone ) => {
|
||||
return (
|
||||
<ExternalLinkCard
|
||||
key={ zone.id }
|
||||
href={ `${ ADMIN_URL }admin.php?page=wc-settings&tab=shipping&zone_id=${ zone.id }` }
|
||||
title={ zone.title }
|
||||
description={ zone.description }
|
||||
/>
|
||||
);
|
||||
} ) }
|
||||
<ExternalLink
|
||||
href={ `${ ADMIN_URL }admin.php?page=wc-settings&tab=shipping` }
|
||||
>
|
||||
{ __(
|
||||
'Manage shipping zones',
|
||||
'woo-gutenberg-products-block'
|
||||
) }
|
||||
</ExternalLink>
|
||||
</PanelBody>
|
||||
) }
|
||||
</InspectorControls>
|
||||
<Disabled>
|
||||
<Block />
|
||||
</Disabled>
|
||||
<AdditionalFields block={ innerBlockAreas.SHIPPING_METHODS } />
|
||||
</FormStepBlock>
|
||||
);
|
||||
};
|
||||
|
||||
export const Save = (): JSX.Element => {
|
||||
return (
|
||||
<div { ...useBlockProps.save() }>
|
||||
<AdditionalFieldsContent />
|
||||
</div>
|
||||
);
|
||||
};
|
@ -0,0 +1,53 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import withFilteredAttributes from '@woocommerce/base-hocs/with-filtered-attributes';
|
||||
import { FormStep } from '@woocommerce/base-components/cart-checkout';
|
||||
import { useCheckoutContext } from '@woocommerce/base-context';
|
||||
import { useCheckoutAddress } from '@woocommerce/base-context/hooks';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import Block from './block';
|
||||
import attributes from './attributes';
|
||||
|
||||
const FrontendBlock = ( {
|
||||
title,
|
||||
description,
|
||||
showStepNumber,
|
||||
children,
|
||||
}: {
|
||||
title: string;
|
||||
description: string;
|
||||
requireCompanyField: boolean;
|
||||
requirePhoneField: boolean;
|
||||
showApartmentField: boolean;
|
||||
showCompanyField: boolean;
|
||||
showPhoneField: boolean;
|
||||
showStepNumber: boolean;
|
||||
children: JSX.Element;
|
||||
} ) => {
|
||||
const { isProcessing: checkoutIsProcessing } = useCheckoutContext();
|
||||
const { showShippingFields } = useCheckoutAddress();
|
||||
|
||||
if ( ! showShippingFields ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<FormStep
|
||||
id="shipping-option"
|
||||
disabled={ checkoutIsProcessing }
|
||||
className="wc-block-checkout__shipping-option"
|
||||
title={ title }
|
||||
description={ description }
|
||||
showStepNumber={ showStepNumber }
|
||||
>
|
||||
<Block />
|
||||
{ children }
|
||||
</FormStep>
|
||||
);
|
||||
};
|
||||
|
||||
export default withFilteredAttributes( attributes )( FrontendBlock );
|
@ -0,0 +1,22 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { Icon, truck } 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={ truck } />,
|
||||
foreground: '#874FB9',
|
||||
},
|
||||
attributes,
|
||||
edit: Edit,
|
||||
save: Save,
|
||||
} );
|
@ -0,0 +1,42 @@
|
||||
/**
|
||||
* 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', 'woocommerce' ) }
|
||||
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.',
|
||||
'woocommerce'
|
||||
) }
|
||||
</span>
|
||||
<Button
|
||||
isSecondary
|
||||
href={ `${ ADMIN_URL }admin.php?page=wc-settings&tab=shipping` }
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{ __(
|
||||
'Configure Shipping Options',
|
||||
'woocommerce'
|
||||
) }
|
||||
</Button>
|
||||
</Placeholder>
|
||||
);
|
||||
};
|
||||
|
||||
export default NoShippingPlaceholder;
|
@ -0,0 +1,21 @@
|
||||
.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;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
.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;
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
{
|
||||
"name": "woocommerce/checkout-terms-block",
|
||||
"version": "1.0.0",
|
||||
"title": "Terms and Conditions",
|
||||
"description": "Ensure customers agree to your terms and conditions and privacy policy.",
|
||||
"category": "woocommerce",
|
||||
"supports": {
|
||||
"align": false,
|
||||
"html": false,
|
||||
"multiple": false,
|
||||
"reusable": false
|
||||
},
|
||||
"attributes": {
|
||||
"checkbox": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"text": {
|
||||
"type": "string",
|
||||
"required": false
|
||||
}
|
||||
},
|
||||
"parent": [ "woocommerce/checkout-fields-block" ],
|
||||
"textdomain": "woo-gutenberg-products-block",
|
||||
"apiVersion": 2
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __, sprintf } from '@wordpress/i18n';
|
||||
import { PRIVACY_URL, TERMS_URL } from '@woocommerce/block-settings';
|
||||
|
||||
const termsPageLink = TERMS_URL
|
||||
? `<a href="${ TERMS_URL }">${ __(
|
||||
'Terms and Conditions',
|
||||
'woo-gutenberg-product-blocks'
|
||||
) }</a>`
|
||||
: __( 'Terms and Conditions', 'woo-gutenberg-product-blocks' );
|
||||
|
||||
const privacyPageLink = PRIVACY_URL
|
||||
? `<a href="${ PRIVACY_URL }">${ __(
|
||||
'Privacy Policy',
|
||||
'woo-gutenberg-product-blocks'
|
||||
) }</a>`
|
||||
: __( 'Privacy Policy', 'woo-gutenberg-product-blocks' );
|
||||
|
||||
export const termsConsentDefaultText = sprintf(
|
||||
/* translators: %1$s terms page link, %2$s privacy page link. */
|
||||
__(
|
||||
'By proceeding with your purchase you agree to our %1$s and %2$s',
|
||||
'woo-gutenberg-product-blocks'
|
||||
),
|
||||
termsPageLink,
|
||||
privacyPageLink
|
||||
);
|
||||
|
||||
export const termsCheckboxDefaultText = sprintf(
|
||||
/* translators: %1$s terms page link, %2$s privacy page link. */
|
||||
__(
|
||||
'You must accept our %1$s and %2$s to continue with your purchase.',
|
||||
'woo-gutenberg-product-blocks'
|
||||
),
|
||||
termsPageLink,
|
||||
privacyPageLink
|
||||
);
|
@ -0,0 +1,183 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import {
|
||||
useBlockProps,
|
||||
RichText,
|
||||
InspectorControls,
|
||||
} from '@wordpress/block-editor';
|
||||
import CheckboxControl from '@woocommerce/base-components/checkbox-control';
|
||||
import { PanelBody, ToggleControl, Notice } from '@wordpress/components';
|
||||
import { PRIVACY_URL, TERMS_URL } from '@woocommerce/block-settings';
|
||||
import { ADMIN_URL } from '@woocommerce/settings';
|
||||
import { Icon, external } from '@wordpress/icons';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import './editor.scss';
|
||||
import { termsConsentDefaultText, termsCheckboxDefaultText } from './constants';
|
||||
|
||||
export const Edit = ( {
|
||||
attributes: { checkbox, text },
|
||||
setAttributes,
|
||||
}: {
|
||||
attributes: { text: string; checkbox: boolean };
|
||||
setAttributes: ( attributes: Record< string, unknown > ) => void;
|
||||
} ): JSX.Element => {
|
||||
const blockProps = useBlockProps();
|
||||
|
||||
const defaultText = checkbox
|
||||
? termsCheckboxDefaultText
|
||||
: termsConsentDefaultText;
|
||||
|
||||
const currentText = text || defaultText;
|
||||
return (
|
||||
<div { ...blockProps }>
|
||||
<InspectorControls>
|
||||
<PanelBody
|
||||
title={ __(
|
||||
'Display options',
|
||||
'woo-gutenberg-products-block'
|
||||
) }
|
||||
>
|
||||
<ToggleControl
|
||||
label={ __(
|
||||
'Require checkbox',
|
||||
'woo-gutenberg-products-block'
|
||||
) }
|
||||
checked={ checkbox }
|
||||
onChange={ () =>
|
||||
setAttributes( {
|
||||
checkbox: ! checkbox,
|
||||
} )
|
||||
}
|
||||
/>
|
||||
</PanelBody>
|
||||
</InspectorControls>
|
||||
<div className="wc-block-checkout__terms">
|
||||
{ checkbox ? (
|
||||
<>
|
||||
<CheckboxControl
|
||||
id="terms-condition"
|
||||
checked={ false }
|
||||
/>
|
||||
<RichText
|
||||
value={ currentText }
|
||||
onChange={ ( value ) =>
|
||||
setAttributes( { text: value } )
|
||||
}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<RichText
|
||||
tagName="span"
|
||||
value={ currentText }
|
||||
onChange={ ( value ) =>
|
||||
setAttributes( { text: value } )
|
||||
}
|
||||
/>
|
||||
) }
|
||||
</div>
|
||||
{ /* Show this notice if a terms page or a privacy page is not setup. */ }
|
||||
{ ( ! TERMS_URL || ! PRIVACY_URL ) && (
|
||||
<Notice
|
||||
className="wc-block-checkout__terms_notice"
|
||||
status="warning"
|
||||
isDismissible={ false }
|
||||
actions={ [
|
||||
! TERMS_URL && {
|
||||
className: 'wc-block-checkout__terms_notice-button',
|
||||
label: (
|
||||
<>
|
||||
{ __(
|
||||
'Setup a Terms and Conditions page',
|
||||
'woo-gutenberg-products-block'
|
||||
) }
|
||||
<Icon
|
||||
icon={ external }
|
||||
size={ 16 }
|
||||
className="wc-block-checkout__terms_notice-button__icon"
|
||||
/>
|
||||
</>
|
||||
),
|
||||
|
||||
onClick: () =>
|
||||
window.open(
|
||||
`${ ADMIN_URL }admin.php?page=wc-settings&tab=advanced`,
|
||||
'_blank'
|
||||
),
|
||||
},
|
||||
! PRIVACY_URL && {
|
||||
className: 'wc-block-checkout__terms_notice-button',
|
||||
label: (
|
||||
<>
|
||||
{ __(
|
||||
'Setup a Privacy Policy page',
|
||||
'woo-gutenberg-products-block'
|
||||
) }
|
||||
<Icon
|
||||
size={ 16 }
|
||||
icon={ external }
|
||||
className="wc-block-checkout__terms_notice-button__icon"
|
||||
/>
|
||||
</>
|
||||
),
|
||||
onClick: () =>
|
||||
window.open(
|
||||
`${ ADMIN_URL }options-privacy.php`,
|
||||
'_blank'
|
||||
),
|
||||
},
|
||||
].filter( Boolean ) }
|
||||
>
|
||||
<p>
|
||||
{ __(
|
||||
"You don't seem to have a Terms and Conditions and/or a Privacy Policy pages setup.",
|
||||
'woo-gutenberg-products-block'
|
||||
) }
|
||||
</p>
|
||||
</Notice>
|
||||
) }
|
||||
{ /* Show this notice if we have both a terms and privacy pages, but they're not present in the text. */ }
|
||||
{ TERMS_URL &&
|
||||
PRIVACY_URL &&
|
||||
! (
|
||||
currentText.includes( TERMS_URL ) &&
|
||||
currentText.includes( PRIVACY_URL )
|
||||
) && (
|
||||
<Notice
|
||||
className="wc-block-checkout__terms_notice"
|
||||
status="warning"
|
||||
isDismissible={ false }
|
||||
actions={
|
||||
termsConsentDefaultText !== text
|
||||
? [
|
||||
{
|
||||
label: __(
|
||||
'Restore default text',
|
||||
'woo-gutenberg-products-block'
|
||||
),
|
||||
onClick: () =>
|
||||
setAttributes( { text: '' } ),
|
||||
},
|
||||
]
|
||||
: []
|
||||
}
|
||||
>
|
||||
<p>
|
||||
{ __(
|
||||
'Ensure you add links to your policy pages in this section.',
|
||||
'woo-gutenberg-products-block'
|
||||
) }
|
||||
</p>
|
||||
</Notice>
|
||||
) }
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const Save = (): JSX.Element => {
|
||||
return <div { ...useBlockProps.save() } />;
|
||||
};
|
@ -0,0 +1,32 @@
|
||||
// Adjust padding and margins in the editor to improve selected block outlines.
|
||||
.wc-block-checkout__terms {
|
||||
margin: 20px 0;
|
||||
padding-top: 4px;
|
||||
padding-bottom: 4px;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
|
||||
.block-editor-rich-text__editable {
|
||||
vertical-align: middle;
|
||||
line-height: em(24px);
|
||||
}
|
||||
}
|
||||
|
||||
.wc-block-components-checkbox {
|
||||
margin-right: $gap;
|
||||
}
|
||||
|
||||
.wc-block-checkout__terms_notice .components-notice__action {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.wc-block-checkout__terms_notice-button {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
|
||||
.wc-block-checkout__terms_notice-button__icon {
|
||||
margin-left: $gap-smallest;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,104 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import classnames from 'classnames';
|
||||
import { useState, useEffect } from '@wordpress/element';
|
||||
import CheckboxControl from '@woocommerce/base-components/checkbox-control';
|
||||
import { useCheckoutSubmit } from '@woocommerce/base-context/hooks';
|
||||
import { withInstanceId } from '@wordpress/compose';
|
||||
import type { ValidationData } from '@woocommerce/type-defs/contexts';
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { termsConsentDefaultText, termsCheckboxDefaultText } from './constants';
|
||||
import './style.scss';
|
||||
|
||||
const FrontendBlock = ( {
|
||||
text,
|
||||
checkbox,
|
||||
instanceId,
|
||||
validation,
|
||||
}: {
|
||||
text: string;
|
||||
checkbox: boolean;
|
||||
instanceId: string;
|
||||
validation: ValidationData;
|
||||
} ): JSX.Element => {
|
||||
const [ checked, setChecked ] = useState( false );
|
||||
|
||||
const { isDisabled } = useCheckoutSubmit();
|
||||
|
||||
const validationErrorId = 'terms-and-conditions-' + instanceId;
|
||||
const {
|
||||
getValidationError,
|
||||
setValidationErrors,
|
||||
clearValidationError,
|
||||
} = validation;
|
||||
|
||||
const error = getValidationError( validationErrorId ) || {};
|
||||
const hasError = error.message && ! error.hidden;
|
||||
|
||||
// Track validation errors for this input.
|
||||
useEffect( () => {
|
||||
if ( ! checkbox ) {
|
||||
return;
|
||||
}
|
||||
if ( checked ) {
|
||||
clearValidationError( validationErrorId );
|
||||
} else {
|
||||
setValidationErrors( {
|
||||
[ validationErrorId ]: {
|
||||
message: __(
|
||||
'Please read and accept the terms and conditions.',
|
||||
'woo-gutenberg-products-block'
|
||||
),
|
||||
hidden: true,
|
||||
},
|
||||
} );
|
||||
}
|
||||
return () => {
|
||||
clearValidationError( validationErrorId );
|
||||
};
|
||||
}, [
|
||||
checkbox,
|
||||
checked,
|
||||
validationErrorId,
|
||||
clearValidationError,
|
||||
setValidationErrors,
|
||||
] );
|
||||
|
||||
return (
|
||||
<div
|
||||
className={ classnames( 'wc-block-checkout__terms', {
|
||||
'wc-block-checkout__terms--disabled': isDisabled,
|
||||
} ) }
|
||||
>
|
||||
{ checkbox ? (
|
||||
<>
|
||||
<CheckboxControl
|
||||
id="terms-and-conditions"
|
||||
checked={ checked }
|
||||
onChange={ () => setChecked( ( value ) => ! value ) }
|
||||
hasError={ hasError }
|
||||
disabled={ isDisabled }
|
||||
>
|
||||
<span
|
||||
dangerouslySetInnerHTML={ {
|
||||
__html: text || termsCheckboxDefaultText,
|
||||
} }
|
||||
/>
|
||||
</CheckboxControl>
|
||||
</>
|
||||
) : (
|
||||
<span
|
||||
dangerouslySetInnerHTML={ {
|
||||
__html: text || termsConsentDefaultText,
|
||||
} }
|
||||
/>
|
||||
) }
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default withInstanceId( FrontendBlock );
|
@ -0,0 +1,19 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { Icon, asterisk } 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={ asterisk } />,
|
||||
foreground: '#874FB9',
|
||||
},
|
||||
edit: Edit,
|
||||
save: Save,
|
||||
} );
|
@ -0,0 +1,13 @@
|
||||
.wc-block-checkout__terms {
|
||||
margin: 1.5em 0;
|
||||
text-align: justify;
|
||||
|
||||
textarea {
|
||||
top: -5px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
&.wc-block-checkout__terms--disabled {
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
{
|
||||
"name": "woocommerce/checkout-totals-block",
|
||||
"version": "1.0.0",
|
||||
"title": "Checkout Totals",
|
||||
"description": "Column containing the checkout totals.",
|
||||
"category": "woocommerce",
|
||||
"supports": {
|
||||
"align": false,
|
||||
"html": false,
|
||||
"multiple": false,
|
||||
"reusable": false,
|
||||
"inserter": false
|
||||
},
|
||||
"attributes": {
|
||||
"checkbox": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"text": {
|
||||
"type": "string",
|
||||
"required": false
|
||||
}
|
||||
},
|
||||
"parent": [ "woocommerce/checkout" ],
|
||||
"textdomain": "woo-gutenberg-products-block",
|
||||
"apiVersion": 2
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { useBlockProps, InnerBlocks } from '@wordpress/block-editor';
|
||||
import { Sidebar } from '@woocommerce/base-components/sidebar-layout';
|
||||
import { innerBlockAreas } from '@woocommerce/blocks-checkout';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import './style.scss';
|
||||
import { useForcedLayout } from '../../use-forced-layout';
|
||||
import { getAllowedBlocks } from '../../editor-utils';
|
||||
|
||||
export const Edit = ( { clientId }: { clientId: string } ): JSX.Element => {
|
||||
const blockProps = useBlockProps();
|
||||
const allowedBlocks = getAllowedBlocks( innerBlockAreas.CHECKOUT_TOTALS );
|
||||
|
||||
useForcedLayout( {
|
||||
clientId,
|
||||
template: allowedBlocks,
|
||||
} );
|
||||
|
||||
return (
|
||||
<Sidebar className="wc-block-checkout__sidebar">
|
||||
<div { ...blockProps }>
|
||||
<InnerBlocks
|
||||
allowedBlocks={ allowedBlocks }
|
||||
templateLock={ false }
|
||||
/>
|
||||
</div>
|
||||
</Sidebar>
|
||||
);
|
||||
};
|
||||
|
||||
export const Save = (): JSX.Element => {
|
||||
return (
|
||||
<div { ...useBlockProps.save() }>
|
||||
<InnerBlocks.Content />
|
||||
</div>
|
||||
);
|
||||
};
|
@ -0,0 +1,21 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { Sidebar } from '@woocommerce/base-components/sidebar-layout';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import './style.scss';
|
||||
|
||||
const FrontendBlock = ( {
|
||||
children,
|
||||
}: {
|
||||
children: JSX.Element;
|
||||
} ): JSX.Element => {
|
||||
return (
|
||||
<Sidebar className="wc-block-checkout__sidebar">{ children }</Sidebar>
|
||||
);
|
||||
};
|
||||
|
||||
export default FrontendBlock;
|
@ -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,
|
||||
} );
|
@ -0,0 +1,23 @@
|
||||
.wc-block-checkout__sidebar {
|
||||
.wc-block-components-product-name {
|
||||
display: block;
|
||||
color: inherit;
|
||||
flex-grow: 1;
|
||||
// Required by IE11.
|
||||
flex-basis: 0;
|
||||
}
|
||||
.wc-block-components-totals-taxes,
|
||||
.wc-block-components-totals-footer-item {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.is-mobile,
|
||||
.is-small,
|
||||
.is-medium {
|
||||
.wc-block-checkout__sidebar {
|
||||
margin-bottom: $gap-large;
|
||||
order: 0;
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import './checkout-fields-block';
|
||||
import './checkout-totals-block';
|
||||
import './checkout-shipping-address-block';
|
||||
import './checkout-terms-block';
|
||||
import './checkout-contact-information-block';
|
||||
import './checkout-billing-address-block';
|
||||
import './checkout-actions-block';
|
||||
import './checkout-order-note-block';
|
||||
import './checkout-order-summary-block';
|
||||
import './checkout-payment-block';
|
||||
import './checkout-express-payment-block';
|
||||
import './checkout-shipping-methods-block';
|
@ -0,0 +1,135 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { lazy } from '@wordpress/element';
|
||||
import { WC_BLOCKS_BUILD_URL } from '@woocommerce/block-settings';
|
||||
import { registerCheckoutBlock } from '@woocommerce/blocks-checkout';
|
||||
|
||||
// Modify webpack publicPath at runtime based on location of WordPress Plugin.
|
||||
// eslint-disable-next-line no-undef,camelcase
|
||||
__webpack_public_path__ = WC_BLOCKS_BUILD_URL;
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import checkoutActionsMetadata from './checkout-actions-block/block.json';
|
||||
import checkoutBillingAddressMetadata from './checkout-billing-address-block/block.json';
|
||||
import checkoutContactInformationMetadata from './checkout-contact-information-block/block.json';
|
||||
import checkoutExpressPaymentMetadata from './checkout-express-payment-block/block.json';
|
||||
import checkoutFieldsMetadata from './checkout-fields-block/block.json';
|
||||
import checkoutOrderNoteMetadata from './checkout-order-note-block/block.json';
|
||||
import checkoutOrderSummaryMetadata from './checkout-order-summary-block/block.json';
|
||||
import checkoutPaymentMetadata from './checkout-payment-block/block.json';
|
||||
import checkoutShippingAddressMetadata from './checkout-shipping-address-block/block.json';
|
||||
import checkoutShippingMethodsMetadata from './checkout-shipping-methods-block/block.json';
|
||||
import checkoutTermsMetadata from './checkout-terms-block/block.json';
|
||||
import checkoutTotalsMetadata from './checkout-totals-block/block.json';
|
||||
|
||||
// @todo When forcing all blocks at once, they will append based on the order they are registered. Introduce formal sorting param.
|
||||
registerCheckoutBlock( {
|
||||
metadata: checkoutFieldsMetadata,
|
||||
component: lazy( () =>
|
||||
import(
|
||||
/* webpackChunkName: "checkout-blocks/fields" */ './checkout-fields-block/frontend'
|
||||
)
|
||||
),
|
||||
} );
|
||||
|
||||
registerCheckoutBlock( {
|
||||
metadata: checkoutExpressPaymentMetadata,
|
||||
component: lazy( () =>
|
||||
import(
|
||||
/* webpackChunkName: "checkout-blocks/express-payment" */ './checkout-express-payment-block/block'
|
||||
)
|
||||
),
|
||||
} );
|
||||
|
||||
registerCheckoutBlock( {
|
||||
metadata: checkoutContactInformationMetadata,
|
||||
component: lazy( () =>
|
||||
import(
|
||||
/* webpackChunkName: "checkout-blocks/contact-information" */ './checkout-contact-information-block/frontend'
|
||||
)
|
||||
),
|
||||
} );
|
||||
|
||||
registerCheckoutBlock( {
|
||||
metadata: checkoutShippingAddressMetadata,
|
||||
component: lazy( () =>
|
||||
import(
|
||||
/* webpackChunkName: "checkout-blocks/shipping-address" */ './checkout-shipping-address-block/frontend'
|
||||
)
|
||||
),
|
||||
} );
|
||||
|
||||
registerCheckoutBlock( {
|
||||
metadata: checkoutBillingAddressMetadata,
|
||||
component: lazy( () =>
|
||||
import(
|
||||
/* webpackChunkName: "checkout-blocks/billing-address" */ './checkout-billing-address-block/frontend'
|
||||
)
|
||||
),
|
||||
} );
|
||||
|
||||
registerCheckoutBlock( {
|
||||
metadata: checkoutShippingMethodsMetadata,
|
||||
component: lazy( () =>
|
||||
import(
|
||||
/* webpackChunkName: "checkout-blocks/shipping-methods" */ './checkout-shipping-methods-block/frontend'
|
||||
)
|
||||
),
|
||||
} );
|
||||
|
||||
registerCheckoutBlock( {
|
||||
metadata: checkoutPaymentMetadata,
|
||||
component: lazy( () =>
|
||||
import(
|
||||
/* webpackChunkName: "checkout-blocks/payment" */ './checkout-payment-block/frontend'
|
||||
)
|
||||
),
|
||||
} );
|
||||
|
||||
registerCheckoutBlock( {
|
||||
metadata: checkoutOrderNoteMetadata,
|
||||
component: lazy( () =>
|
||||
import(
|
||||
/* webpackChunkName: "checkout-blocks/order-note" */ './checkout-order-note-block/block'
|
||||
)
|
||||
),
|
||||
} );
|
||||
|
||||
registerCheckoutBlock( {
|
||||
metadata: checkoutTermsMetadata,
|
||||
component: lazy( () =>
|
||||
import(
|
||||
/* webpackChunkName: "checkout-blocks/terms" */ './checkout-terms-block/frontend'
|
||||
)
|
||||
),
|
||||
} );
|
||||
|
||||
registerCheckoutBlock( {
|
||||
metadata: checkoutActionsMetadata,
|
||||
component: lazy( () =>
|
||||
import(
|
||||
/* webpackChunkName: "checkout-blocks/actions" */ './checkout-actions-block/frontend'
|
||||
)
|
||||
),
|
||||
} );
|
||||
|
||||
registerCheckoutBlock( {
|
||||
metadata: checkoutTotalsMetadata,
|
||||
component: lazy( () =>
|
||||
import(
|
||||
/* webpackChunkName: "checkout-blocks/totals" */ './checkout-totals-block/frontend'
|
||||
)
|
||||
),
|
||||
} );
|
||||
|
||||
registerCheckoutBlock( {
|
||||
metadata: checkoutOrderSummaryMetadata,
|
||||
component: lazy( () =>
|
||||
import(
|
||||
/* webpackChunkName: "checkout-blocks/order-summary" */ './checkout-order-summary-block/block'
|
||||
)
|
||||
),
|
||||
} );
|
Reference in New Issue
Block a user