woocommerce/plugins/woocommerce-blocks/assets/js/blocks/cart-checkout/checkout/block.js

372 lines
10 KiB
JavaScript
Raw Normal View History

/**
* External dependencies
*/
import { Fragment, useState, useCallback, useEffect } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import AddressForm from '@woocommerce/base-components/address-form';
import defaultAddressFields from '@woocommerce/base-components/address-form/default-address-fields';
import {
FormStep,
CheckoutForm,
NoShipping,
Policies,
} from '@woocommerce/base-components/checkout';
import {
PlaceOrderButton,
ReturnToCartButton,
} from '@woocommerce/base-components/cart-checkout';
import { ValidatedTextInput } from '@woocommerce/base-components/text-input';
Update and select shipping rates dynamically (https://github.com/woocommerce/woocommerce-blocks/pull/1794) * add select shipping endpoint to router * add select shipping method * add selected rates to cart * better select rates * move schema function to seperate function * move validation to Cart Controller * fix wrong session key * Update shipping/cart endpoints (https://github.com/woocommerce/woocommerce-blocks/pull/1833) * Items should not have keys in API response * Include package ID in response (this is just a basic index) * /cart/select-shipping-rate/package_id * Add package_id to package array * Update responses and add shipping-rates to main cart endpoint * update-shipping endpoint * Add querying selected shipping rate to the store (https://github.com/woocommerce/woocommerce-blocks/pull/1829) * add selecting shipping to store * directly call useSelectShippingRate * refactor cart keys transformation to reducer * remove selecting first result and accept selecting * move update shipping to new endpoint * pass selected rates down * select shipping right directly and fix editor issues * fix some broken prop types * key -> package id * Update and fix cart/shipping-rate tests * fix case for when rates are set * Update useShippingRates test * add args to rest endpoint * move selecting shipping rate logic to hook * fix some naming issues * update propTypes * update action call * fully watch cart state * address review issues * fix prop type issues * fix issue with rates not loading in checkout * remove extra package for shipping * move ShippingCalculatorOptions to outside Co-authored-by: Mike Jolley <mike.jolley@me.com> Co-authored-by: Albert Juhé Lluveras <aljullu@gmail.com>
2020-03-05 19:54:05 +00:00
import ShippingRatesControl from '@woocommerce/base-components/shipping-rates-control';
import CheckboxControl from '@woocommerce/base-components/checkbox-control';
import { getCurrencyFromPriceResponse } from '@woocommerce/base-utils';
import FormattedMonetaryAmount from '@woocommerce/base-components/formatted-monetary-amount';
Add validation context provider and implement validation for shipping country and coupons. (https://github.com/woocommerce/woocommerce-blocks/pull/1972) * add errormessage handling to countryinput (along with storybook) * add types for react * Add validation context and implement * implement validation context for country field validation * tweak ValidationInputError so that it can receive property name for getting error from * improve storybook webpack config to pull from tsconfig.json * update storybook story to cover changes with context * Wrap Checkout Provider with Validation Context Provider * add screen-reader-text style to storybook * add styles for input error validation to text input * improve styling for ValidationInputError component * add validation error handling to TotalsCouponCode component And story * make sure errors are cleared on successful receive/remove item * dispatch loading cancellation on catching errors This is needed because loading would be cancelled before the error is thrown so any error handling after the thrown error will not be able to rely on loading. * implement validation setting for coupon errors * add error color to labels on inputs too * fix borders back and force border color * remove extra structure and improve validation error with alignment for coupon code * add aria-describedby for text inputs * add back in validation context provider to fix rebase issue * rework validation so it works for both checkout and cart * Some styling tweaks * more style fixes * remove unnecessary method * make sure new function is included in context defaults * package.lock update? seems harmless so rolling with it.
2020-03-17 11:45:33 +00:00
import {
CheckoutProvider,
useValidationContext,
useCheckoutContext,
useShippingDataContext,
useBillingDataContext,
Add validation context provider and implement validation for shipping country and coupons. (https://github.com/woocommerce/woocommerce-blocks/pull/1972) * add errormessage handling to countryinput (along with storybook) * add types for react * Add validation context and implement * implement validation context for country field validation * tweak ValidationInputError so that it can receive property name for getting error from * improve storybook webpack config to pull from tsconfig.json * update storybook story to cover changes with context * Wrap Checkout Provider with Validation Context Provider * add screen-reader-text style to storybook * add styles for input error validation to text input * improve styling for ValidationInputError component * add validation error handling to TotalsCouponCode component And story * make sure errors are cleared on successful receive/remove item * dispatch loading cancellation on catching errors This is needed because loading would be cancelled before the error is thrown so any error handling after the thrown error will not be able to rely on loading. * implement validation setting for coupon errors * add error color to labels on inputs too * fix borders back and force border color * remove extra structure and improve validation error with alignment for coupon code * add aria-describedby for text inputs * add back in validation context provider to fix rebase issue * rework validation so it works for both checkout and cart * Some styling tweaks * more style fixes * remove unnecessary method * make sure new function is included in context defaults * package.lock update? seems harmless so rolling with it.
2020-03-17 11:45:33 +00:00
} from '@woocommerce/base-context';
import {
ExpressCheckoutFormControl,
PaymentMethods,
} from '@woocommerce/base-components/payment-methods';
import { SHIPPING_ENABLED } from '@woocommerce/block-settings';
import { decodeEntities } from '@wordpress/html-entities';
import {
Sidebar,
SidebarLayout,
Main,
} from '@woocommerce/base-components/sidebar-layout';
import { getSetting } from '@woocommerce/settings';
import withScrollToTop from '@woocommerce/base-hocs/with-scroll-to-top';
/**
* Internal dependencies
*/
import CheckoutSidebar from './sidebar/index.js';
import './style.scss';
const Block = ( { isEditor = false, ...props } ) => (
<CheckoutProvider isEditor={ isEditor }>
<Checkout { ...props } />
</CheckoutProvider>
);
const Checkout = ( {
attributes,
cartCoupons = [],
cartItems = [],
cartTotals = {},
shippingRates = [],
scrollToTop,
} ) => {
const { isEditor } = useCheckoutContext();
const {
shippingRatesLoading,
shippingAddress,
setShippingAddress,
} = useShippingDataContext();
const { billingData, setBillingData } = useBillingDataContext();
const {
hasValidationErrors,
showAllValidationErrors,
} = useValidationContext();
const [ contactFields, setContactFields ] = useState( {} );
const [ shippingAsBilling, setShippingAsBilling ] = useState( true );
const validateSubmit = () => {
if ( hasValidationErrors() ) {
showAllValidationErrors();
scrollToTop( { focusableSelector: 'input:invalid' } );
return false;
}
return true;
};
const renderShippingRatesControlOption = ( option ) => ( {
label: decodeEntities( option.name ),
value: option.rate_id,
description: decodeEntities( option.description ),
secondaryLabel: (
<FormattedMonetaryAmount
currency={ getCurrencyFromPriceResponse( option ) }
value={ option.price }
/>
),
secondaryDescription: decodeEntities( option.delivery_time ),
} );
const showBillingFields = ! SHIPPING_ENABLED || ! shippingAsBilling;
const addressFields = {
...defaultAddressFields,
company: {
...defaultAddressFields.company,
hidden: ! attributes.showCompanyField,
required: attributes.requireCompanyField,
},
address_2: {
...defaultAddressFields.address_2,
hidden: ! attributes.showAddress2Field,
},
};
const setShippingFields = useCallback(
( address ) => {
if ( shippingAsBilling ) {
setShippingAddress( address );
setBillingData( address );
} else {
setShippingAddress( address );
}
},
[ setShippingAddress, setBillingData, shippingAsBilling ]
);
useEffect( () => {
if ( shippingAsBilling ) {
setBillingData( shippingAddress );
}
}, [ shippingAsBilling, setBillingData ] );
return (
<>
<SidebarLayout className="wc-block-checkout">
<Main>
<ExpressCheckoutFormControl />
<CheckoutForm>
<FormStep
id="contact-fields"
className="wc-block-checkout__contact-fields"
title={ __(
'Contact information',
'woo-gutenberg-products-block'
) }
description={ __(
"We'll use this email to send you details and updates about your order.",
'woo-gutenberg-products-block'
) }
stepHeadingContent={ () => (
<Fragment>
{ __(
'Already have an account? ',
'woo-gutenberg-products-block'
) }
<a href="/wp-login.php">
{ __(
'Log in.',
'woo-gutenberg-products-block'
) }
</a>
</Fragment>
) }
>
<ValidatedTextInput
type="email"
label={ __(
'Email address',
'woo-gutenberg-products-block'
) }
value={ billingData.email }
autoComplete="email"
onChange={ ( newValue ) =>
setBillingData( { email: newValue } )
}
required={ true }
/>
<CheckboxControl
className="wc-block-checkout__keep-updated"
label={ __(
'Keep me up to date on news and exclusive offers',
'woo-gutenberg-products-block'
) }
checked={ contactFields.keepUpdated }
onChange={ () =>
setContactFields( {
...contactFields,
keepUpdated: ! contactFields.keepUpdated,
} )
}
/>
</FormStep>
{ SHIPPING_ENABLED && (
<FormStep
id="shipping-fields"
className="wc-block-checkout__shipping-fields"
title={ __(
'Shipping address',
'woo-gutenberg-products-block'
) }
description={ __(
'Enter the physical address where you want us to deliver your order.',
'woo-gutenberg-products-block'
) }
>
<AddressForm
onChange={ setShippingFields }
values={ shippingAddress }
fields={ Object.keys( addressFields ) }
fieldConfig={ addressFields }
/>
{ attributes.showPhoneField && (
<ValidatedTextInput
type="tel"
label={
attributes.requirePhoneField
? __(
'Phone',
'woo-gutenberg-products-block'
)
: __(
'Phone (optional)',
'woo-gutenberg-products-block'
)
}
value={ billingData.phone }
autoComplete="tel"
onChange={ ( newValue ) =>
setBillingData( {
phone: newValue,
} )
}
required={
attributes.requirePhoneField
}
/>
) }
<CheckboxControl
className="wc-block-checkout__use-address-for-billing"
label={ __(
'Use same address for billing',
'woo-gutenberg-products-block'
) }
checked={ shippingAsBilling }
onChange={ ( isChecked ) =>
setShippingAsBilling( isChecked )
}
required={ attributes.requirePhoneField }
/>
</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={ setBillingData }
type="billing"
values={ billingData }
fields={ Object.keys( addressFields ) }
fieldConfig={ addressFields }
/>
</FormStep>
) }
{ SHIPPING_ENABLED && (
<FormStep
id="shipping-option"
className="wc-block-checkout__shipping-option"
title={ __(
'Shipping options',
'woo-gutenberg-products-block'
) }
description={ __(
'Select a shipping method below.',
'woo-gutenberg-products-block'
) }
>
{ shippingRates.length === 0 && isEditor ? (
<NoShipping />
) : (
<ShippingRatesControl
address={
shippingAddress.country
? {
address_1:
shippingAddress.address_1,
address_2:
shippingAddress.address_2,
city:
shippingAddress.city,
state:
shippingAddress.state,
postcode:
shippingAddress.postcode,
country:
shippingAddress.country,
}
: null
}
noResultsMessage={ __(
'There are no shipping options available. Please ensure that your address has been entered correctly, or contact us if you need any help.',
'woo-gutenberg-products-block'
) }
renderOption={
renderShippingRatesControlOption
}
shippingRates={ shippingRates }
shippingRatesLoading={
shippingRatesLoading
}
/>
) }
{ /*@todo This is not implemented*/ }
<CheckboxControl
className="wc-block-checkout__add-note"
label="Add order notes?"
checked={ false }
onChange={ () => null }
/>
</FormStep>
) }
<FormStep
id="payment-method"
className="wc-block-checkout__payment-method"
title={ __(
'Payment method',
'woo-gutenberg-products-block'
) }
description={ __(
'Select a payment method below.',
'woo-gutenberg-products-block'
) }
>
<PaymentMethods />
</FormStep>
</CheckoutForm>
</Main>
<Sidebar className="wc-block-checkout__sidebar">
<CheckoutSidebar
cartCoupons={ cartCoupons }
cartItems={ cartItems }
cartTotals={ cartTotals }
/>
</Sidebar>
<Main>
<div className="wc-block-checkout__actions">
{ attributes.showReturnToCart && (
<ReturnToCartButton
link={ getSetting(
'page-' + attributes?.cartPageId,
false
) }
/>
) }
<PlaceOrderButton validateSubmit={ validateSubmit } />
</div>
{ attributes.showPolicyLinks && <Policies /> }
</Main>
</SidebarLayout>
</>
);
};
export default withScrollToTop( Block );