Option to 'use shipping address for billing': add attribute and make it work in the frontend (https://github.com/woocommerce/woocommerce-blocks/pull/1857)

* Create useShippingAsBilling attribute

* Fix missing prop

* Refactor FormStep so stepNumber is generated by CSS instead of being passed as a prop

* Add billing address form

* Add text before controls

* Remove old @todo comment
This commit is contained in:
Albert Juhé Lluveras 2020-03-04 16:13:38 +01:00 committed by GitHub
parent e2f769eedf
commit 2544ffd7d1
10 changed files with 155 additions and 72 deletions

View File

@ -4,13 +4,31 @@
import { __ } from '@wordpress/i18n';
import PropTypes from 'prop-types';
import TextInput from '@woocommerce/base-components/text-input';
import { ShippingCountryInput } from '@woocommerce/base-components/country-input';
import { ShippingStateInput } from '@woocommerce/base-components/state-input';
import {
BillingCountryInput,
ShippingCountryInput,
} from '@woocommerce/base-components/country-input';
import {
BillingStateInput,
ShippingStateInput,
} from '@woocommerce/base-components/state-input';
import {
COUNTRY_LOCALE,
DEFAULT_ADDRESS_FIELDS,
} from '@woocommerce/block-settings';
const defaultFieldNames = [
'first_name',
'last_name',
'company',
'address_1',
'address_2',
'country',
'city',
'postcode',
'state',
];
const defaultFieldValues = {
first_name: {
autocomplete: 'given-name',
@ -43,18 +61,9 @@ const defaultFieldValues = {
};
const AddressForm = ( {
fields = [
'first_name',
'last_name',
'company',
'address_1',
'address_2',
'country',
'city',
'postcode',
'state',
],
fields = defaultFieldNames,
onChange,
type = 'shipping',
values,
} ) => {
const countryLocale = COUNTRY_LOCALE[ values.country ] || {};
@ -75,8 +84,12 @@ const AddressForm = ( {
return null;
}
if ( addressField.key === 'country' ) {
const Tag =
type === 'shipping'
? ShippingCountryInput
: BillingCountryInput;
return (
<ShippingCountryInput
<Tag
key={ addressField.key }
label={ __(
'Country / Region',
@ -96,8 +109,12 @@ const AddressForm = ( {
);
}
if ( addressField.key === 'state' ) {
const Tag =
type === 'shipping'
? ShippingStateInput
: BillingStateInput;
return (
<ShippingStateInput
<Tag
key={ addressField.key }
country={ values.country }
label={ addressField.label }
@ -139,7 +156,8 @@ const AddressForm = ( {
AddressForm.propTypes = {
onChange: PropTypes.func.isRequired,
values: PropTypes.object.isRequired,
fields: PropTypes.arrayOf( PropTypes.string ),
fields: PropTypes.arrayOf( PropTypes.oneOf( defaultFieldNames ) ),
type: PropTypes.oneOf( [ 'billing', 'shipping' ] ),
};
export default AddressForm;

View File

@ -3,32 +3,12 @@
*/
import classnames from 'classnames';
import PropTypes from 'prop-types';
import { __, sprintf } from '@wordpress/i18n';
/**
* Internal dependencies
*/
import Label from '../../label';
import './style.scss';
const StepNumber = ( { stepNumber } ) => {
return (
<div className="wc-block-checkout-step__number">
<Label
label={ stepNumber }
screenReaderLabel={ sprintf(
__(
// translators: %s is a step number (1, 2, 3...)
'Step %d',
'woo-gutenberg-products-block'
),
stepNumber
) }
/>
</div>
);
};
const StepHeading = ( { title, stepHeadingContent } ) => (
<div className="wc-block-checkout-step__heading">
<h4 aria-hidden="true" className="wc-block-checkout-step__title">
@ -43,7 +23,6 @@ const StepHeading = ( { title, stepHeadingContent } ) => (
const FormStep = ( {
id,
className,
stepNumber,
title,
legend,
description,
@ -56,7 +35,6 @@ const FormStep = ( {
id={ id }
>
<legend className="screen-reader-text">{ legend || title }</legend>
<StepNumber stepNumber={ stepNumber } />
<StepHeading
title={ title }
stepHeadingContent={ stepHeadingContent() }
@ -72,11 +50,11 @@ const FormStep = ( {
FormStep.propTypes = {
id: PropTypes.string,
className: PropTypes.string,
stepNumber: PropTypes.number,
title: PropTypes.string,
description: PropTypes.string,
children: PropTypes.node,
stepHeadingContent: PropTypes.func,
legend: PropTypes.string,
};
export default FormStep;

View File

@ -1,6 +1,10 @@
$circle-size: 24px;
$line-offset-from-circle-size: 8px;
.wc-block-checkout-form {
counter-reset: checkout-step;
}
.wc-block-checkout-form fieldset.wc-block-checkout-step {
position: relative;
border: none;
@ -50,7 +54,22 @@ $line-offset-from-circle-size: 8px;
margin-bottom: $gap-large;
}
.wc-block-checkout-step__number {
// because themes can register different background colors, we can't always
// relay on using white border to offest the step left line,
.wc-block-checkout-step::before {
content: "";
// 1 Circle size + offset of the first circle and next circle.
height: calc(100% - #{$circle-size + $line-offset-from-circle-size * 2});
width: 1px;
background-color: $gray-10;
position: absolute;
left: 0;
top: $circle-size + $line-offset-from-circle-size;
}
.wc-block-checkout-step::after {
counter-increment: checkout-step;
content: counter(checkout-step);
position: absolute;
width: $circle-size;
height: $circle-size;
@ -64,16 +83,3 @@ $line-offset-from-circle-size: 8px;
border-radius: $circle-size / 2;
box-sizing: content-box;
}
// because themes can register different background colors, we can't always
// relay on using white border to offest the step left line,
.wc-block-checkout-step::before {
content: "";
// 1 Circle size + offset of the first circle and next circle.
height: calc(100% - #{$circle-size + $line-offset-from-circle-size * 2});
width: 1px;
background-color: $gray-10;
position: absolute;
left: 0;
top: $circle-size + $line-offset-from-circle-size;
}

View File

@ -28,11 +28,15 @@ import { decodeEntities } from '@wordpress/html-entities';
import './style.scss';
import '../../../payment-methods-demo';
const Block = ( { shippingRates = [], isEditor = false } ) => {
const Block = ( { attributes, isEditor = false, shippingRates = [] } ) => {
const [ selectedShippingRate, setSelectedShippingRate ] = useState( {} );
const [ contactFields, setContactFields ] = useState( {} );
const [ shouldSavePayment, setShouldSavePayment ] = useState( true );
const [ shippingFields, setShippingFields ] = useState( {} );
const [ billingFields, setBillingFields ] = useState( {} );
const [ useShippingAsBilling, setUseShippingAsBilling ] = useState(
attributes.useShippingAsBilling
);
const renderShippingRatesControlOption = ( option ) => ( {
label: decodeEntities( option.name ),
@ -47,13 +51,19 @@ const Block = ( { shippingRates = [], isEditor = false } ) => {
secondaryDescription: decodeEntities( option.delivery_time ),
} );
const useShippingAddressAsBilling = isEditor
? attributes.useShippingAsBilling
: useShippingAsBilling;
const showBillingFields =
! SHIPPING_ENABLED || ! useShippingAddressAsBilling;
return (
<CheckoutProvider>
<ExpressCheckoutFormControl />
<CheckoutForm>
<FormStep
id="billing-fields"
className="wc-block-checkout__billing-fields"
id="contact-fields"
className="wc-block-checkout__contact-fields"
title={ __(
'Contact information',
'woo-gutenberg-products-block'
@ -62,7 +72,6 @@ const Block = ( { shippingRates = [], isEditor = false } ) => {
"We'll use this email to send you details and updates about your order.",
'woo-gutenberg-products-block'
) }
stepNumber={ 1 }
stepHeadingContent={ () => (
<Fragment>
{ __(
@ -121,7 +130,6 @@ const Block = ( { shippingRates = [], isEditor = false } ) => {
'Enter the physical address where you want us to deliver your order.',
'woo-gutenberg-products-block'
) }
stepNumber={ 2 }
>
<AddressForm
onChange={ setShippingFields }
@ -149,13 +157,46 @@ const Block = ( { shippingRates = [], isEditor = false } ) => {
'Use same address for billing',
'woo-gutenberg-products-block'
) }
checked={ shippingFields.useSameForBilling }
onChange={ () =>
setShippingFields( {
...shippingFields,
useSameForBilling: ! shippingFields.useSameForBilling,
checked={ useShippingAddressAsBilling }
onChange={ ( isChecked ) =>
setUseShippingAsBilling( isChecked )
}
/>
</FormStep>
) }
{ showBillingFields && (
<FormStep
id="billing-fields"
className="wc-block-checkout__billing-fields"
title={ __(
'Billing address',
'woo-gutenberg-products-block'
) }
description={ __(
'Enter the address that matches your card or payment method.',
'woo-gutenberg-products-block'
) }
>
<AddressForm
onChange={ setBillingFields }
type="billing"
values={ billingFields }
/>
<TextInput
type="tel"
label={ __(
'Phone',
'woo-gutenberg-products-block'
) }
value={ billingFields.phone }
autoComplete="tel"
onChange={ ( newValue ) =>
setBillingFields( {
...billingFields,
phone: newValue,
} )
}
required={ true }
/>
</FormStep>
) }
@ -174,7 +215,6 @@ const Block = ( { shippingRates = [], isEditor = false } ) => {
'Select your shipping method below.',
'woo-gutenberg-products-block'
) }
stepNumber={ 3 }
>
{ shippingRates.length > 0 ? (
<Packages
@ -249,7 +289,6 @@ const Block = ( { shippingRates = [], isEditor = false } ) => {
'Select a payment method below.',
'woo-gutenberg-products-block'
) }
stepNumber={ 4 }
>
<PaymentMethods />
{ /*@todo this should be something the payment method controls*/ }

View File

@ -5,6 +5,8 @@ import { __ } from '@wordpress/i18n';
import { withFeedbackPrompt } from '@woocommerce/block-hocs';
import { previewShippingRates } from '@woocommerce/resource-previews';
import { SHIPPING_METHODS_EXIST } from '@woocommerce/block-settings';
import { InspectorControls } from '@wordpress/block-editor';
import { PanelBody, ToggleControl } from '@wordpress/components';
/**
* Internal dependencies
@ -12,11 +14,38 @@ import { SHIPPING_METHODS_EXIST } from '@woocommerce/block-settings';
import Block from './block.js';
import './editor.scss';
const CheckoutEditor = ( { attributes } ) => {
const { className } = attributes;
const CheckoutEditor = ( { attributes, setAttributes } ) => {
const { className, useShippingAsBilling } = attributes;
// @todo: wrap Block with Disabled once you finish building the form
return (
<div className={ className }>
<InspectorControls>
<PanelBody
title={ __(
'Billing address',
'woo-gutenberg-products-block'
) }
>
<p className="wc-block-checkout__controls-text">
{ __(
'Reduce the number of fields required to checkout.',
'woo-gutenberg-products-block'
) }
</p>
<ToggleControl
label={ __(
'Use the shipping address as the billing address',
'woo-gutenberg-products-block'
) }
checked={ useShippingAsBilling }
onChange={ () =>
setAttributes( {
useShippingAsBilling: ! useShippingAsBilling,
} )
}
/>
</PanelBody>
</InspectorControls>
<Block
attributes={ attributes }
isEditor={ true }

View File

@ -3,3 +3,8 @@
line-height: 24px;
margin: 0 $gap-small 0 0;
}
.wc-block-checkout__controls-text {
color: #999;
font-style: italic;
}

View File

@ -9,10 +9,10 @@ import { withRestApiHydration } from '@woocommerce/block-hocs';
import Block from './block.js';
import renderFrontend from '../../../utils/render-frontend.js';
const getProps = () => {
const getProps = ( el ) => {
return {
attributes: {
isEditor: false,
useShippingAsBilling: el.dataset.useShippingAsBilling !== 'false',
},
};
};

View File

@ -38,15 +38,22 @@ const settings = {
type: 'boolean',
default: false,
},
useShippingAsBilling: {
type: 'boolean',
default: true,
},
},
edit,
/**
* Save the props to post content.
*/
save( { attributes } ) {
const { className } = attributes;
const { className, useShippingAsBilling } = attributes;
const data = {
'data-use-shipping-as-billing': useShippingAsBilling,
};
return (
<div className={ className }>
<div className={ className } { ...data }>
Checkout block coming soon to store near you
</div>
);

View File

@ -5,6 +5,7 @@
}
@include breakpoint( ">480px" ) {
.wc-block-checkout__billing-fields,
.wc-block-checkout__shipping-fields {
.wc-block-address-form {
display: grid;

View File

@ -12,7 +12,7 @@ import BlockErrorBoundary from '@woocommerce/base-components/block-error-boundar
* @param {Function} [getProps] Function to generate the props object for the
* block.
*/
export default ( selector, Block, getProps = () => {} ) => {
export default ( selector, Block, getProps = () => ( {} ) ) => {
const containers = document.querySelectorAll( selector );
if ( containers.length ) {