This commit is contained in:
Albert Juhé Lluveras 2020-03-12 10:41:35 +01:00 committed by GitHub
parent 87b87cee8a
commit 2147e8a22a
16 changed files with 507 additions and 320 deletions

View File

@ -0,0 +1,8 @@
/**
* Internal dependencies
*/
import './style.scss';
export { default as Sidebar } from './sidebar';
export { default as Main } from './main';
export { default as SidebarLayout } from './sidebar-layout';

View File

@ -0,0 +1,14 @@
/**
* External dependencies
*/
import classNames from 'classnames';
const Main = ( { children, className } ) => {
return (
<div className={ classNames( 'wc-block-main', className ) }>
{ children }
</div>
);
};
export default Main;

View File

@ -0,0 +1,14 @@
/**
* External dependencies
*/
import classNames from 'classnames';
const SidebarLayout = ( { children, className } ) => {
return (
<div className={ classNames( 'wc-block-sidebar-layout', className ) }>
{ children }
</div>
);
};
export default SidebarLayout;

View File

@ -0,0 +1,14 @@
/**
* External dependencies
*/
import classNames from 'classnames';
const Sidebar = ( { children, className } ) => {
return (
<div className={ classNames( 'wc-block-sidebar', className ) }>
{ children }
</div>
);
};
export default Sidebar;

View File

@ -0,0 +1,86 @@
.wc-block-sidebar-layout {
display: flex;
flex-wrap: wrap;
margin: 0 (-$gap) $gap;
.wc-block-main {
flex: 1 0 65%;
margin: 0;
padding: 0 $gap;
min-width: 500px;
}
.wc-block-sidebar {
flex: 1 1 35%;
margin: 0;
max-width: 35%;
padding: 0 $gap;
// Reset Gutenberg <Panel> styles when used in the sidebar.
.components-panel__body {
border-top: 1px solid $core-grey-light-600;
border-bottom: 1px solid $core-grey-light-600;
&.is-opened {
padding-left: 0;
padding-right: 0;
> .components-panel__body-title {
margin-left: 0;
margin-right: 0;
}
}
> .components-panel__body-title,
.components-panel__body-toggle {
&,
&:hover,
&:focus,
&:active {
background-color: transparent;
color: inherit;
}
}
.components-panel__body-toggle {
font-weight: normal;
font-size: inherit;
padding-left: 0;
padding-right: 36px;
&.components-button,
&.components-button:focus:not(:disabled):not([aria-disabled="true"]) {
color: inherit;
}
}
}
}
}
@include breakpoint( "<782px" ) {
.wc-block-sidebar-layout {
display: block;
margin: 0 0 $gap;
.wc-block-main {
padding: 0;
flex: none;
min-width: 200px;
}
.wc-block-sidebar {
padding: 0;
flex: none;
max-width: 100%;
// Reset (remove) totals "card" styling on mobile.
.components-card {
box-shadow: none;
border: none;
}
.components-card__body {
border: none;
padding: 0;
}
}
}
}

View File

@ -19,6 +19,7 @@ import LoadingMask from '../../loading-mask';
const TotalsCouponCodeInput = ( {
instanceId,
isLoading = false,
initialOpen,
onSubmit = () => {},
} ) => {
const [ couponValue, setCouponValue ] = useState( '' );
@ -49,7 +50,7 @@ const TotalsCouponCodeInput = ( {
htmlFor={ `wc-block-coupon-code__input-${ instanceId }` }
/>
}
initialOpen={ true }
initialOpen={ initialOpen }
>
<LoadingMask
screenReaderLabel={ __(

View File

@ -36,11 +36,15 @@ const TotalsShippingItem = ( {
currency={ currency }
description={
<>
<ShippingLocation address={ shippingAddress } />
<ShippingCalculator
address={ shippingAddress }
setAddress={ updateShippingAddress }
/>
{ shippingAddress && (
<ShippingLocation address={ shippingAddress } />
) }
{ updateShippingAddress && shippingAddress && (
<ShippingCalculator
address={ shippingAddress }
setAddress={ updateShippingAddress }
/>
) }
</>
}
label={ __( 'Shipping', 'woo-gutenberg-products-block' ) }

View File

@ -25,6 +25,11 @@ import FormattedMonetaryAmount from '@woocommerce/base-components/formatted-mone
import { decodeEntities } from '@wordpress/html-entities';
import { useStoreCartCoupons, useShippingRates } from '@woocommerce/base-hooks';
import classnames from 'classnames';
import {
Sidebar,
SidebarLayout,
Main,
} from '@woocommerce/base-components/sidebar-layout';
/**
* Internal dependencies
@ -118,15 +123,15 @@ const Cart = ( {
} );
return (
<div className={ cartClassName }>
<div className="wc-block-cart__main">
<SidebarLayout className={ cartClassName }>
<Main className="wc-block-cart__main">
<CartLineItemsTitle itemCount={ cartItems.length } />
<CartLineItemsTable
lineItems={ cartItems }
isLoading={ isLoading }
/>
</div>
<div className="wc-block-cart__sidebar">
</Main>
<Sidebar className="wc-block-cart__sidebar">
<Card isElevated={ true }>
<CardBody>
<h2 className="wc-block-cart__totals-title">
@ -183,6 +188,7 @@ const Cart = ( {
{ COUPONS_ENABLED && (
<TotalsCouponCodeInput
onSubmit={ applyCoupon }
initialOpen={ true }
isLoading={ isApplyingCoupon }
/>
) }
@ -193,8 +199,8 @@ const Cart = ( {
<CheckoutButton />
</CardBody>
</Card>
</div>
</div>
</Sidebar>
</SidebarLayout>
);
};

View File

@ -1,21 +1,6 @@
.wc-block-cart {
color: $core-grey-dark-600;
display: flex;
flex-wrap: wrap;
margin: 0 (-$gap) $gap;
.wc-block-cart__main {
flex: 1 0 65%;
margin: 0;
padding: 0 $gap;
min-width: 500px;
}
.wc-block-cart__sidebar {
flex: 1 1 35%;
margin: 0;
max-width: 35%;
padding: 0 $gap;
}
.wc-block-cart__item-count {
float: right;
}
@ -275,75 +260,13 @@ table.wc-block-cart-items {
display: flex;
}
// Reset Gutenberg <Panel> styles when used in the sidebar.
.wc-block-cart__sidebar {
.components-panel__body {
border-top: 1px solid $core-grey-light-600;
border-bottom: 1px solid $core-grey-light-600;
&.is-opened {
padding-left: 0;
padding-right: 0;
> .components-panel__body-title {
margin-left: 0;
margin-right: 0;
}
}
> .components-panel__body-title,
.components-panel__body-toggle {
&,
&:hover,
&:focus,
&:active {
background-color: transparent;
color: inherit;
}
}
.components-panel__body-toggle {
font-weight: normal;
font-size: inherit;
padding-left: 0;
padding-right: 36px;
&.components-button,
&.components-button:focus:not(:disabled):not([aria-disabled="true"]) {
color: inherit;
}
}
}
}
// Mobile styles.
@include breakpoint( "<782px" ) {
.wc-block-cart:not(.wc-block-cart--is-loading):not(.wc-block-cart--skeleton) {
display: block;
margin: 0 0 $gap;
.wc-block-cart__main {
padding: 0;
flex: none;
min-width: 200px;
}
.wc-block-cart__sidebar {
padding: 0;
flex: none;
max-width: 100%;
.wc-block-cart {
.wc-block-sidebar {
.wc-block-cart__totals-title {
display: none;
}
// Reset (remove) totals "card" styling on mobile.
.components-card {
box-shadow: none;
border: none;
}
.components-card__body {
border: none;
padding: 0;
}
}
}
table.wc-block-cart-items {

View File

@ -24,14 +24,26 @@ import {
import { SHIPPING_ENABLED } from '@woocommerce/block-settings';
import { decodeEntities } from '@wordpress/html-entities';
import { useShippingRates } from '@woocommerce/base-hooks';
import {
Sidebar,
SidebarLayout,
Main,
} from '@woocommerce/base-components/sidebar-layout'; // @todo
/**
* Internal dependencies
*/
import CheckoutSidebar from './sidebar.js';
import './style.scss';
import '../../../payment-methods-demo';
const Block = ( { attributes, isEditor = false, shippingRates = [] } ) => {
const Block = ( {
attributes,
cartCoupons = [],
cartTotals = {},
isEditor = false,
shippingRates = [],
} ) => {
const [ selectedShippingRate, setSelectedShippingRate ] = useState( {} );
const [ contactFields, setContactFields ] = useState( {} );
const [ shouldSavePayment, setShouldSavePayment ] = useState( true );
@ -80,228 +92,248 @@ const Block = ( { attributes, isEditor = false, shippingRates = [] } ) => {
return (
<CheckoutProvider isEditor={ isEditor }>
<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>
) }
>
<TextInput
type="email"
label={ __(
'Email address',
'woo-gutenberg-products-block'
) }
value={ contactFields.email }
autoComplete="email"
onChange={ ( newValue ) =>
setContactFields( {
...contactFields,
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={ shippingFields }
fields={ Object.keys( addressFields ) }
fieldConfig={ addressFields }
/>
{ attributes.showPhoneField && (
<TextInput
type="tel"
label={
attributes.requirePhoneField
? __(
'Phone',
'woo-gutenberg-products-block'
)
: __(
'Phone (optional)',
'woo-gutenberg-products-block'
)
}
value={ shippingFields.phone }
autoComplete="tel"
onChange={ ( newValue ) =>
setShippingFields( {
...shippingFields,
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={ 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 }
fields={ Object.keys( addressFields ) }
fieldConfig={ addressFields }
/>
</FormStep>
) }
{ SHIPPING_ENABLED &&
( shippingRates.length === 0 && isEditor ? (
<NoShipping />
) : (
<SidebarLayout>
<Main>
<ExpressCheckoutFormControl />
<CheckoutForm>
<FormStep
id="shipping-option"
className="wc-block-checkout__shipping-option"
id="contact-fields"
className="wc-block-checkout__contact-fields"
title={ __(
'Shipping options',
'Contact information',
'woo-gutenberg-products-block'
) }
description={ __(
'Select your shipping method below.',
"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>
) }
>
<ShippingRatesControl
address={
shippingFields.country
? {
address_1:
shippingFields.address_1,
address_2:
shippingFields.apartment,
city: shippingFields.city,
state: shippingFields.state,
postcode:
shippingFields.postcode,
country: shippingFields.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.',
<TextInput
type="email"
label={ __(
'Email address',
'woo-gutenberg-products-block'
) }
renderOption={
renderShippingRatesControlOption
value={ contactFields.email }
autoComplete="email"
onChange={ ( newValue ) =>
setContactFields( {
...contactFields,
email: newValue,
} )
}
shippingRates={ shippingRates }
shippingRatesLoading={ shippingRatesLoading }
required={ true }
/>
<CheckboxControl
className="wc-block-checkout__add-note"
label="Add order notes?"
checked={ selectedShippingRate.orderNote }
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={ () =>
setSelectedShippingRate( {
...selectedShippingRate,
orderNote: ! selectedShippingRate.orderNote,
setContactFields( {
...contactFields,
keepUpdated: ! contactFields.keepUpdated,
} )
}
/>
</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 />
{ /*@todo this should be something the payment method controls*/ }
<CheckboxControl
className="wc-block-checkout__save-card-info"
label={ __(
'Save payment information to my account for future purchases.',
'woo-gutenberg-products-block'
{ 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={ shippingFields }
fields={ Object.keys( addressFields ) }
fieldConfig={ addressFields }
/>
{ attributes.showPhoneField && (
<TextInput
type="tel"
label={
attributes.requirePhoneField
? __(
'Phone',
'woo-gutenberg-products-block'
)
: __(
'Phone (optional)',
'woo-gutenberg-products-block'
)
}
value={ shippingFields.phone }
autoComplete="tel"
onChange={ ( newValue ) =>
setShippingFields( {
...shippingFields,
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={ useShippingAddressAsBilling }
onChange={ ( isChecked ) =>
setUseShippingAsBilling( isChecked )
}
/>
</FormStep>
) }
checked={ shouldSavePayment }
onChange={ () =>
setShouldSavePayment( ! shouldSavePayment )
}
{ 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 }
fields={ Object.keys( addressFields ) }
fieldConfig={ addressFields }
/>
</FormStep>
) }
{ SHIPPING_ENABLED &&
( shippingRates.length === 0 && isEditor ? (
<NoShipping />
) : (
<FormStep
id="shipping-option"
className="wc-block-checkout__shipping-option"
title={ __(
'Shipping options',
'woo-gutenberg-products-block'
) }
description={ __(
'Select your shipping method below.',
'woo-gutenberg-products-block'
) }
>
<ShippingRatesControl
address={
shippingFields.country
? {
address_1:
shippingFields.address_1,
address_2:
shippingFields.apartment,
city:
shippingFields.city,
state:
shippingFields.state,
postcode:
shippingFields.postcode,
country:
shippingFields.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
}
/>
<CheckboxControl
className="wc-block-checkout__add-note"
label="Add order notes?"
checked={
selectedShippingRate.orderNote
}
onChange={ () =>
setSelectedShippingRate( {
...selectedShippingRate,
orderNote: ! selectedShippingRate.orderNote,
} )
}
/>
</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 />
{ /*@todo this should be something the payment method controls*/ }
<CheckboxControl
className="wc-block-checkout__save-card-info"
label={ __(
'Save payment information to my account for future purchases.',
'woo-gutenberg-products-block'
) }
checked={ shouldSavePayment }
onChange={ () =>
setShouldSavePayment( ! shouldSavePayment )
}
/>
</FormStep>
{ attributes.showPolicyLinks && <Policies /> }
</CheckoutForm>
</Main>
<Sidebar>
<CheckoutSidebar
cartCoupons={ cartCoupons }
cartTotals={ cartTotals }
shippingRates={ shippingRates }
/>
</FormStep>
{ attributes.showPolicyLinks && <Policies /> }
</CheckoutForm>
</Sidebar>
</SidebarLayout>
</CheckoutProvider>
);
};

View File

@ -3,7 +3,10 @@
*/
import { __ } from '@wordpress/i18n';
import { withFeedbackPrompt } from '@woocommerce/block-hocs';
import { previewShippingRates } from '@woocommerce/resource-previews';
import {
previewCart,
previewShippingRates,
} from '@woocommerce/resource-previews';
import { InspectorControls } from '@wordpress/block-editor';
import {
PanelBody,
@ -216,6 +219,7 @@ const CheckoutEditor = ( { attributes, setAttributes } ) => {
>
<Block
attributes={ attributes }
cartTotals={ previewCart.totals }
isEditor={ true }
shippingRates={
SHIPPING_METHODS_EXIST ? previewShippingRates : []

View File

@ -24,7 +24,12 @@ import renderFrontend from '../../../utils/render-frontend.js';
* @param {Object} props Props for the block.
*/
const CheckoutFrontend = ( props ) => {
const { cartErrors, shippingRates } = useStoreCart();
const {
cartCoupons,
cartErrors,
cartTotals,
shippingRates,
} = useStoreCart();
if ( cartErrors && cartErrors.length > 0 ) {
throw new Error( cartErrors[ 0 ].message );
@ -50,7 +55,12 @@ const CheckoutFrontend = ( props ) => {
) }
showErrorMessage={ CURRENT_USER_IS_ADMIN }
>
<Block { ...props } shippingRates={ shippingRates } />
<Block
{ ...props }
cartCoupons={ cartCoupons }
cartTotals={ cartTotals }
shippingRates={ shippingRates }
/>
</BlockErrorBoundary>
);
};

View File

@ -0,0 +1,71 @@
/**
* External dependencies
*/
import {
SubtotalsItem,
TotalsFeesItem,
TotalsCouponCodeInput,
TotalsDiscountItem,
TotalsFooterItem,
TotalsShippingItem,
TotalsTaxesItem,
} from '@woocommerce/base-components/totals';
import { getCurrencyFromPriceResponse } from '@woocommerce/base-utils';
import {
COUPONS_ENABLED,
DISPLAY_CART_PRICES_INCLUDING_TAX,
} from '@woocommerce/block-settings';
import { useStoreCartCoupons } from '@woocommerce/base-hooks';
const CheckoutSidebar = ( {
cartCoupons = [],
cartTotals = {},
shippingRates,
} ) => {
const {
applyCoupon,
removeCoupon,
isApplyingCoupon,
isRemovingCoupon,
} = useStoreCartCoupons();
const shippingAddress = shippingRates[ 0 ]?.destination;
const totalsCurrency = getCurrencyFromPriceResponse( cartTotals );
return (
<>
<SubtotalsItem currency={ totalsCurrency } values={ cartTotals } />
<TotalsFeesItem currency={ totalsCurrency } values={ cartTotals } />
<TotalsDiscountItem
cartCoupons={ cartCoupons }
currency={ totalsCurrency }
isRemovingCoupon={ isRemovingCoupon }
removeCoupon={ removeCoupon }
values={ cartTotals }
/>
<TotalsShippingItem
currency={ totalsCurrency }
shippingAddress={ shippingAddress }
values={ cartTotals }
/>
{ ! DISPLAY_CART_PRICES_INCLUDING_TAX && (
<TotalsTaxesItem
currency={ totalsCurrency }
values={ cartTotals }
/>
) }
{ COUPONS_ENABLED && (
<TotalsCouponCodeInput
onSubmit={ applyCoupon }
initialOpen={ false }
isLoading={ isApplyingCoupon }
/>
) }
<TotalsFooterItem
currency={ totalsCurrency }
values={ cartTotals }
/>
</>
);
};
export default CheckoutSidebar;

View File

@ -59,16 +59,16 @@ const reducer = (
currency_thousand_separator: ',',
currency_prefix: '',
currency_suffix: '',
total_items: 0,
total_items_tax: 0,
total_fees: 0,
total_fees_tax: 0,
total_discount: 0,
total_discount_tax: 0,
total_shipping: 0,
total_shipping_tax: 0,
total_price: 0,
total_tax: 0,
total_items: '0',
total_items_tax: '0',
total_fees: '0',
total_fees_tax: '0',
total_discount: '0',
total_discount_tax: '0',
total_shipping: '0',
total_shipping_tax: '0',
total_price: '0',
total_tax: '0',
tax_lines: [],
},
},

View File

@ -37,16 +37,16 @@ export const getCartTotals = ( state ) => {
currency_thousand_separator: ',',
currency_prefix: '',
currency_suffix: '',
total_items: 0,
total_items_tax: 0,
total_fees: 0,
total_fees_tax: 0,
total_discount: 0,
total_discount_tax: 0,
total_shipping: 0,
total_shipping_tax: 0,
total_price: 0,
total_tax: 0,
total_items: '0',
total_items_tax: '0',
total_fees: '0',
total_fees_tax: '0',
total_discount: '0',
total_discount_tax: '0',
total_shipping: '0',
total_shipping_tax: '0',
total_price: '0',
total_tax: '0',
tax_lines: [],
}
);

View File

@ -80,8 +80,8 @@ class Cart extends AbstractBlock {
*/
protected function get_skeleton() {
return '
<div class="wc-block-cart wc-block-cart--is-loading wc-block-cart--skeleton" aria-hidden="true">
<div class="wc-block-cart__main">
<div class="wc-block-sidebar-layout wc-block-cart wc-block-cart--is-loading wc-block-cart--skeleton" aria-hidden="true">
<div class="wc-block-main wc-block-cart__main">
<h2><span></span></h2>
<table class="wc-block-cart-items">
<thead>
@ -153,7 +153,7 @@ class Cart extends AbstractBlock {
</tbody>
</table>
</div>
<div class="wc-block-cart__sidebar">
<div class="wc-block-sidebar wc-block-cart__sidebar">
<div class="components-card"></div>
</div>
</div>