* Update cart/coupon/shipping design

* Add order summary heading

* Move and style discounts on checkout sidebar

* Add rate to tax lines

* Ensure the option to display taxes itemised is available to Cart block

* Output individual tax items below the total & add styles for this

* Add success notice under coupon input on successful coupon addition

* Add border to bottom of Totals footer

* Show success message when adding coupon

* Add padding to cart item rows

* Add preview data to cart for when taxes are enabled

* Add rate to cart response type

* Add showRateAfterTaxName attribute to Cart block

* Add control to cart block to show rate percentage after rate name

* Add rate % in cart totals only if option is toggled on

* Pass showRateAfterTaxName attribute down to TotalsTaxes

* Add showRateAfterTaxName to Checkout block

* Add control to block editor for showRateAfterTaxName on Checkout

* Pass showRateAfterTaxName down to TotalsTaxes in Checkout

* Change label for showing tax rates in cart and checkout blocks

* Add test to ensure Taxes section shows in Cart block

* Add tests for cart sidebar and rate percentages

* Remove order summary title from checkout sidebar

* Check if taxes are enabled before rendering the option to show rate %s

* Add ShippingVia component to show the selected rate in sidebar

* Remove value from individual tax rates

* Remove bold from Shipping via label

* Remove coupon added successfully message

* Ensure panel headings that are h2s are the same colour as others

* Clean up eslint warnings

* Show rate %s by default

* Update snapshots following design changes

Co-authored-by: Mike Jolley <mike.jolley@me.com>
This commit is contained in:
Thomas Roberts 2021-05-17 15:00:57 +01:00 committed by GitHub
parent b14b557c91
commit 57c5b0c532
30 changed files with 2575 additions and 77 deletions

View File

@ -5,6 +5,7 @@
$no-stock-color: $alert-red; $no-stock-color: $alert-red;
$low-stock-color: $alert-yellow; $low-stock-color: $alert-yellow;
$in-stock-color: $alert-green; $in-stock-color: $alert-green;
$discount-color: $alert-green;
$placeholder-color: var(--global--color-primary, $gray-200); $placeholder-color: var(--global--color-primary, $gray-200);
$input-border-gray: #50575e; $input-border-gray: #50575e;

View File

@ -4,11 +4,10 @@
import classNames from 'classnames'; import classNames from 'classnames';
import { _n, sprintf } from '@wordpress/i18n'; import { _n, sprintf } from '@wordpress/i18n';
import { decodeEntities } from '@wordpress/html-entities'; import { decodeEntities } from '@wordpress/html-entities';
import Label from '@woocommerce/base-components/label';
import Title from '@woocommerce/base-components/title';
import type { ReactElement } from 'react'; import type { ReactElement } from 'react';
import type { PackageRateOption } from '@woocommerce/type-defs/shipping'; import type { PackageRateOption } from '@woocommerce/type-defs/shipping';
import { Panel } from '@woocommerce/blocks-checkout'; import { Panel } from '@woocommerce/blocks-checkout';
import Label from '@woocommerce/base-components/label';
import { useSelectShippingRate } from '@woocommerce/base-context/hooks'; import { useSelectShippingRate } from '@woocommerce/base-context/hooks';
import type { CartShippingPackageShippingRate } from '@woocommerce/type-defs/cart'; import type { CartShippingPackageShippingRate } from '@woocommerce/type-defs/cart';
@ -77,12 +76,9 @@ export const ShippingRatesControlPackage = ( {
const header = ( const header = (
<> <>
{ ( showItems || collapsible ) && ( { ( showItems || collapsible ) && (
<Title <div className="wc-block-components-shipping-rates-control__package-title">
className="wc-block-components-shipping-rates-control__package-title"
headingLevel="3"
>
{ packageData.name } { packageData.name }
</Title> </div>
) } ) }
{ showItems && ( { showItems && (
<ul className="wc-block-components-shipping-rates-control__package-items"> <ul className="wc-block-components-shipping-rates-control__package-items">

View File

@ -6,12 +6,6 @@
padding-top: em($gap-small); padding-top: em($gap-small);
} }
.wc-block-components-shipping-rates-control__package-title {
@include text-heading();
font-weight: bold;
margin: 0;
}
// Remove panel padding because we are adding bottom padding to `.wc-block-components-radio-control` // Remove panel padding because we are adding bottom padding to `.wc-block-components-radio-control`
// and `.wc-block-components-radio-control__option-layout` in the next ruleset. // and `.wc-block-components-radio-control__option-layout` in the next ruleset.
.wc-block-components-panel__content { .wc-block-components-panel__content {

View File

@ -51,11 +51,11 @@ const TotalsCoupon = ( {
title={ title={
<Label <Label
label={ __( label={ __(
'Coupon Code?', 'Coupon code',
'woo-gutenberg-products-block' 'woo-gutenberg-products-block'
) } ) }
screenReaderLabel={ __( screenReaderLabel={ __(
'Introduce Coupon Code', 'Apply a coupon code',
'woo-gutenberg-products-block' 'woo-gutenberg-products-block'
) } ) }
htmlFor={ textInputId } htmlFor={ textInputId }

View File

@ -3,3 +3,7 @@
margin: 0; margin: 0;
padding: 0; padding: 0;
} }
.wc-block-components-totals-discount .wc-block-components-totals-item__value {
color: $discount-color;
}

View File

@ -9,6 +9,7 @@ import { TotalsItem } from '@woocommerce/blocks-checkout';
import type { Currency } from '@woocommerce/price-format'; import type { Currency } from '@woocommerce/price-format';
import type { ReactElement } from 'react'; import type { ReactElement } from 'react';
import { getSetting, EnteredAddress } from '@woocommerce/settings'; import { getSetting, EnteredAddress } from '@woocommerce/settings';
import { ShippingVia } from '@woocommerce/base-components/cart-checkout/totals/shipping/shipping-via';
/** /**
* Internal dependencies * Internal dependencies
@ -146,6 +147,14 @@ const TotalsShipping = ( {
setIsShippingCalculatorOpen, setIsShippingCalculatorOpen,
}; };
const selectedShippingRates = shippingRates.flatMap(
( shippingPackage ) => {
return shippingPackage.shipping_rates
.filter( ( rate ) => rate.selected )
.flatMap( ( rate ) => rate.name );
}
);
return ( return (
<div <div
className={ classnames( className={ classnames(
@ -168,11 +177,18 @@ const TotalsShipping = ( {
description={ description={
<> <>
{ cartHasCalculatedShipping && ( { cartHasCalculatedShipping && (
<ShippingAddress <>
shippingAddress={ shippingAddress } <ShippingVia
showCalculator={ showCalculator } selectedShippingRates={
{ ...calculatorButtonProps } selectedShippingRates
/> }
/>
<ShippingAddress
shippingAddress={ shippingAddress }
showCalculator={ showCalculator }
{ ...calculatorButtonProps }
/>
</>
) } ) }
</> </>
} }

View File

@ -0,0 +1,17 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
export const ShippingVia = ( {
selectedShippingRates,
}: {
selectedShippingRates: string[];
} ): JSX.Element => {
return (
<div className="wc-block-components-totals-item__description wc-block-components-totals-shipping__via">
{ __( 'via', 'woo-gutenberg-products-block' ) }{ ' ' }
{ selectedShippingRates.join( ', ' ) }
</div>
);
};

View File

@ -7,6 +7,10 @@
border: 0; border: 0;
} }
.wc-block-components-totals-shipping__via {
margin-bottom: $gap;
}
.wc-block-components-totals-shipping__options { .wc-block-components-totals-shipping__options {
.wc-block-components-radio-control__label, .wc-block-components-radio-control__label,
.wc-block-components-radio-control__description, .wc-block-components-radio-control__description,

View File

@ -52,6 +52,13 @@
} }
} }
.wc-block-components-sidebar .wc-block-components-panel > h2 {
@include reset-typography();
.wc-block-components-panel__button {
font-weight: 400;
}
}
// For Twenty Twenty we need to increase specificity a bit more. // For Twenty Twenty we need to increase specificity a bit more.
.theme-twentytwenty { .theme-twentytwenty {
.wc-block-components-sidebar .wc-block-components-panel > h2 { .wc-block-components-sidebar .wc-block-components-panel > h2 {

View File

@ -21,6 +21,10 @@ const blockAttributes = {
type: 'boolean', type: 'boolean',
default: getSetting( 'hasDarkEditorStyleSupport', false ), default: getSetting( 'hasDarkEditorStyleSupport', false ),
}, },
showRateAfterTaxName: {
type: 'boolean',
default: true,
},
}; };
export default blockAttributes; export default blockAttributes;

View File

@ -39,6 +39,7 @@ const BlockSettings = ( { attributes, setAttributes } ) => {
isShippingCalculatorEnabled, isShippingCalculatorEnabled,
checkoutPageId, checkoutPageId,
hasDarkControls, hasDarkControls,
showRateAfterTaxName,
} = attributes; } = attributes;
const { currentPostId } = useEditorContext(); const { currentPostId } = useEditorContext();
const { current: savedCheckoutPageId } = useRef( checkoutPageId ); const { current: savedCheckoutPageId } = useRef( checkoutPageId );
@ -95,6 +96,30 @@ const BlockSettings = ( { attributes, setAttributes } ) => {
/> />
</PanelBody> </PanelBody>
) } ) }
{ getSetting( 'taxesEnabled' ) &&
getSetting( 'displayItemizedTaxes', false ) &&
! getSetting( 'displayCartPricesIncludingTax', false ) && (
<PanelBody
title={ __( 'Taxes', 'woo-gutenberg-products-block' ) }
>
<ToggleControl
label={ __(
'Show rate after tax name',
'woo-gutenberg-products-block'
) }
help={ __(
'Show the percentage rate alongside each tax line in the summary.',
'woo-gutenberg-products-block'
) }
checked={ showRateAfterTaxName }
onChange={ () =>
setAttributes( {
showRateAfterTaxName: ! showRateAfterTaxName,
} )
}
/>
</PanelBody>
) }
{ ! ( { ! (
currentPostId === CART_PAGE_ID && savedCheckoutPageId === 0 currentPostId === CART_PAGE_ID && savedCheckoutPageId === 0
) && ( ) && (

View File

@ -48,6 +48,7 @@ interface CartAttributes {
isShippingCalculatorEnabled: boolean; isShippingCalculatorEnabled: boolean;
checkoutPageId: number; checkoutPageId: number;
isPreview: boolean; isPreview: boolean;
showRateAfterTaxName: boolean;
} }
interface CartProps { interface CartProps {
@ -60,7 +61,11 @@ interface CartProps {
* @param {Object} props.attributes Incoming attributes for block. * @param {Object} props.attributes Incoming attributes for block.
*/ */
const Cart = ( { attributes }: CartProps ) => { const Cart = ( { attributes }: CartProps ) => {
const { isShippingCalculatorEnabled, hasDarkControls } = attributes; const {
isShippingCalculatorEnabled,
hasDarkControls,
showRateAfterTaxName,
} = attributes;
const { const {
cartItems, cartItems,
@ -141,6 +146,12 @@ const Cart = ( { attributes }: CartProps ) => {
removeCoupon={ removeCoupon } removeCoupon={ removeCoupon }
values={ cartTotals } values={ cartTotals }
/> />
{ getSetting( 'couponsEnabled', true ) && (
<TotalsCoupon
onSubmit={ applyCoupon }
isLoading={ isApplyingCoupon }
/>
) }
{ cartNeedsShipping && ( { cartNeedsShipping && (
<TotalsShipping <TotalsShipping
showCalculator={ isShippingCalculatorEnabled } showCalculator={ isShippingCalculatorEnabled }
@ -154,16 +165,12 @@ const Cart = ( { attributes }: CartProps ) => {
false false
) && ( ) && (
<TotalsTaxes <TotalsTaxes
showRateAfterTaxName={ showRateAfterTaxName }
currency={ totalsCurrency } currency={ totalsCurrency }
values={ cartTotals } values={ cartTotals }
/> />
) } ) }
{ getSetting( 'couponsEnabled', true ) && (
<TotalsCoupon
onSubmit={ applyCoupon }
isLoading={ isApplyingCoupon }
/>
) }
<TotalsFooterItem <TotalsFooterItem
currency={ totalsCurrency } currency={ totalsCurrency }
values={ cartTotals } values={ cartTotals }

View File

@ -78,8 +78,17 @@ table.wc-block-cart-items {
} }
} }
.wc-block-cart .wc-block-components-shipping-rates-control__package { .wc-block-cart {
@include with-translucent-border(1px 0 0); .wc-block-components-totals-taxes,
.wc-block-components-totals-footer-item {
@include with-translucent-border(1px 0 0);
margin: 0;
padding: em($gap-small) 0;
}
.wc-block-components-totals-footer-item {
@include with-translucent-border(1px 0);
}
} }
// Loading placeholder state. // Loading placeholder state.
@ -236,13 +245,15 @@ table.wc-block-cart-items {
} }
td { td {
@include with-translucent-border(1px 0 0); @include with-translucent-border(1px 0 0);
padding: $gap $gap $gap 0; padding: $gap 0 $gap $gap;
vertical-align: top; vertical-align: top;
} }
th:last-child, th:last-child {
td:last-child {
padding-right: 0; padding-right: 0;
} }
td:last-child {
padding-right: $gap;
}
} }
.wc-block-components-radio-control__input { .wc-block-components-radio-control__input {

View File

@ -12,6 +12,7 @@ import { default as fetchMock } from 'jest-fetch-mock';
*/ */
import CartBlock from '../block'; import CartBlock from '../block';
import { defaultCartState } from '../../../../data/default-states'; import { defaultCartState } from '../../../../data/default-states';
import { allSettings } from '../../../../settings/shared/settings-init';
describe( 'Testing cart', () => { describe( 'Testing cart', () => {
beforeEach( async () => { beforeEach( async () => {
@ -49,6 +50,59 @@ describe( 'Testing cart', () => {
expect( console ).toHaveWarned(); expect( console ).toHaveWarned();
} ); } );
it( 'Contains a Taxes section if Core options are set to show it', async () => {
allSettings.displayCartPricesIncludingTax = false;
// The criteria for showing the Taxes section is:
// Display prices during basket and checkout: 'Excluding tax'.
const { container } = render(
<CartBlock
emptyCart={ null }
attributes={ {
isShippingCalculatorEnabled: false,
} }
/>
);
await waitFor( () => expect( fetchMock ).toHaveBeenCalled() );
expect( container ).toMatchSnapshot();
} );
it( 'Shows individual tax lines if the store is set to do so', async () => {
allSettings.displayCartPricesIncludingTax = false;
allSettings.displayItemizedTaxes = true;
// The criteria for showing the lines in the Taxes section is:
// Display prices during basket and checkout: 'Excluding tax'.
// Display tax totals: 'Itemized';
const { container } = render(
<CartBlock
emptyCart={ null }
attributes={ {
isShippingCalculatorEnabled: false,
} }
/>
);
await waitFor( () => expect( fetchMock ).toHaveBeenCalled() );
expect( container ).toMatchSnapshot();
} );
it( 'Shows rate percentages after tax lines if the block is set to do so', async () => {
allSettings.displayCartPricesIncludingTax = false;
allSettings.displayItemizedTaxes = true;
// The criteria for showing the lines in the Taxes section is:
// Display prices during basket and checkout: 'Excluding tax'.
// Display tax totals: 'Itemized';
const { container } = render(
<CartBlock
emptyCart={ null }
attributes={ {
showRateAfterTaxName: true,
isShippingCalculatorEnabled: false,
} }
/>
);
await waitFor( () => expect( fetchMock ).toHaveBeenCalled() );
expect( container ).toMatchSnapshot();
} );
it( 'renders empty cart if there are no items in the cart', async () => { it( 'renders empty cart if there are no items in the cart', async () => {
fetchMock.mockResponse( ( req ) => { fetchMock.mockResponse( ( req ) => {
if ( req.url.match( /wc\/store\/cart/ ) ) { if ( req.url.match( /wc\/store\/cart/ ) ) {

View File

@ -53,6 +53,10 @@ const blockAttributes = {
type: 'boolean', type: 'boolean',
default: getSetting( 'hasDarkEditorStyleSupport', false ), default: getSetting( 'hasDarkEditorStyleSupport', false ),
}, },
showRateAfterTaxName: {
type: 'boolean',
default: getSetting( 'displayCartPricesIncludingTax', false ),
},
}; };
export default blockAttributes; export default blockAttributes;

View File

@ -155,6 +155,7 @@ const Checkout = ( { attributes, scrollToTop } ) => {
cartItems={ cartItems } cartItems={ cartItems }
cartTotals={ cartTotals } cartTotals={ cartTotals }
cartFees={ cartFees } cartFees={ cartFees }
showRateAfterTaxName={ attributes.showRateAfterTaxName }
/> />
</Sidebar> </Sidebar>
</SidebarLayout> </SidebarLayout>

View File

@ -52,6 +52,7 @@ const BlockSettings = ( { attributes, setAttributes } ) => {
showReturnToCart, showReturnToCart,
cartPageId, cartPageId,
hasDarkControls, hasDarkControls,
showRateAfterTaxName,
} = attributes; } = attributes;
const { currentPostId } = useEditorContext(); const { currentPostId } = useEditorContext();
const { current: savedCartPageId } = useRef( cartPageId ); const { current: savedCartPageId } = useRef( cartPageId );
@ -298,6 +299,30 @@ const BlockSettings = ( { attributes, setAttributes } ) => {
} } } }
/> />
) } ) }
{ getSetting( 'taxesEnabled' ) &&
getSetting( 'displayItemizedTaxes', false ) &&
! getSetting( 'displayCartPricesIncludingTax', false ) && (
<PanelBody
title={ __( 'Taxes', 'woo-gutenberg-products-block' ) }
>
<ToggleControl
label={ __(
'Show rate after tax name',
'woo-gutenberg-products-block'
) }
help={ __(
'Show the percentage rate alongside each tax line in the summary.',
'woo-gutenberg-products-block'
) }
checked={ showRateAfterTaxName }
onChange={ () =>
setAttributes( {
showRateAfterTaxName: ! showRateAfterTaxName,
} )
}
/>
</PanelBody>
) }
<PanelBody title={ __( 'Style', 'woo-gutenberg-products-block' ) }> <PanelBody title={ __( 'Style', 'woo-gutenberg-products-block' ) }>
<ToggleControl <ToggleControl
label={ __( label={ __(

View File

@ -1,6 +1,7 @@
/** /**
* External dependencies * External dependencies
*/ */
import { import {
OrderSummary, OrderSummary,
TotalsCoupon, TotalsCoupon,
@ -14,6 +15,7 @@ import {
TotalsTaxes, TotalsTaxes,
ExperimentalOrderMeta, ExperimentalOrderMeta,
} from '@woocommerce/blocks-checkout'; } from '@woocommerce/blocks-checkout';
import { getCurrencyFromPriceResponse } from '@woocommerce/price-format'; import { getCurrencyFromPriceResponse } from '@woocommerce/price-format';
import { useShippingDataContext } from '@woocommerce/base-context'; import { useShippingDataContext } from '@woocommerce/base-context';
import { import {
@ -27,6 +29,7 @@ const CheckoutSidebar = ( {
cartItems = [], cartItems = [],
cartFees = [], cartFees = [],
cartTotals = {}, cartTotals = {},
showRateAfterTaxName = false,
} ) => { } ) => {
const { const {
applyCoupon, applyCoupon,
@ -59,6 +62,13 @@ const CheckoutSidebar = ( {
removeCoupon={ removeCoupon } removeCoupon={ removeCoupon }
values={ cartTotals } values={ cartTotals }
/> />
{ getSetting( 'couponsEnabled', true ) && (
<TotalsCoupon
onSubmit={ applyCoupon }
initialOpen={ false }
isLoading={ isApplyingCoupon }
/>
) }
{ needsShipping && ( { needsShipping && (
<TotalsShipping <TotalsShipping
showCalculator={ false } showCalculator={ false }
@ -70,16 +80,10 @@ const CheckoutSidebar = ( {
{ ! getSetting( 'displayCartPricesIncludingTax', false ) && ( { ! getSetting( 'displayCartPricesIncludingTax', false ) && (
<TotalsTaxes <TotalsTaxes
currency={ totalsCurrency } currency={ totalsCurrency }
showRateAfterTaxName={ showRateAfterTaxName }
values={ cartTotals } values={ cartTotals }
/> />
) } ) }
{ getSetting( 'couponsEnabled', true ) && (
<TotalsCoupon
onSubmit={ applyCoupon }
initialOpen={ false }
isLoading={ isApplyingCoupon }
/>
) }
<TotalsFooterItem <TotalsFooterItem
currency={ totalsCurrency } currency={ totalsCurrency }
values={ cartTotals } values={ cartTotals }

View File

@ -0,0 +1,151 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Testing checkout sidebar Shows rate percentages after tax lines if the block is set to do so 1`] = `
<div>
<div
class="wc-block-components-totals-item"
>
<span
class="wc-block-components-totals-item__label"
>
Subtotal
</span>
<span
class="wc-block-formatted-money-amount wc-block-components-formatted-money-amount wc-block-components-totals-item__value"
>
$24.00
</span>
<div
class="wc-block-components-totals-item__description"
/>
</div>
<div
class="wc-block-components-totals-coupon wc-block-components-panel has-border"
>
<div>
<button
aria-expanded="false"
class="wc-block-components-panel__button"
>
<svg
aria-hidden="true"
class="wc-block-components-panel__button-icon"
focusable="false"
height="24"
role="img"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M17 9.4L12 14 7 9.4l-1 1.2 6 5.4 6-5.4z"
/>
</svg>
<span
aria-hidden="true"
>
Coupon code
</span>
<span
class="screen-reader-text"
>
Apply a coupon code
</span>
</button>
</div>
<div
class="wc-block-components-panel__content"
hidden=""
>
<div
class="wc-block-components-totals-coupon__content"
>
<form
class="wc-block-components-totals-coupon__form"
>
<div
class="wc-block-components-text-input wc-block-components-totals-coupon__input is-active"
>
<input
aria-describedby="wc-block-components-totals-coupon__input-0"
aria-label="Enter code"
autocapitalize="off"
autocomplete="off"
id="wc-block-components-totals-coupon__input-0"
type="text"
value=""
/>
<label
for="wc-block-components-totals-coupon__input-0"
>
Enter code
</label>
</div>
<button
class="components-button wc-block-components-button wc-block-components-totals-coupon__button"
disabled=""
type="submit"
>
<span
class="wc-block-components-button__text"
>
Apply
</span>
</button>
</form>
</div>
</div>
</div>
<div
class="wc-block-components-totals-item wc-block-components-totals-taxes"
>
<span
class="wc-block-components-totals-item__label"
>
Taxes
</span>
<span
class="wc-block-formatted-money-amount wc-block-components-formatted-money-amount wc-block-components-totals-item__value"
>
$6.00
</span>
<div
class="wc-block-components-totals-item__description"
>
<div
class="wc-block-components-totals-item wc-block-components-totals-taxes__tax-line"
>
<span
class="wc-block-components-totals-item__label"
>
Sales tax 20%
</span>
<div
class="wc-block-components-totals-item__description"
/>
</div>
</div>
</div>
<div
class="wc-block-components-totals-item wc-block-components-totals-footer-item"
>
<span
class="wc-block-components-totals-item__label"
>
Total
</span>
<span
class="wc-block-formatted-money-amount wc-block-components-formatted-money-amount wc-block-components-totals-item__value"
>
$30.00
</span>
<div
class="wc-block-components-totals-item__description"
/>
</div>
<div
class="wc-block-components-order-meta"
/>
</div>
`;

View File

@ -0,0 +1,29 @@
/**
* External dependencies
*/
import { render } from '@testing-library/react';
import { previewCart } from '@woocommerce/resource-previews';
/**
* Internal dependencies
*/
import { allSettings } from '../../../../../settings/shared/settings-init';
import CheckoutSidebar from '../index';
describe( 'Testing checkout sidebar', () => {
it( 'Shows rate percentages after tax lines if the block is set to do so', async () => {
allSettings.displayCartPricesIncludingTax = false;
allSettings.displayItemizedTaxes = true;
const { totals: cartTotals, items: cartItems } = previewCart;
const { container } = render(
<CheckoutSidebar
cartItems={ cartItems }
cartTotals={ cartTotals }
showRateAfterTaxName={ true }
/>
);
expect( container ).toMatchSnapshot();
// ["Components must be wrapped within `SlotFillProvider`. See https://developer.wordpress.org/block-editor/components/slot-fill/"]
expect( console ).toHaveWarned();
} );
} );

View File

@ -10,6 +10,12 @@
// Required by IE11. // Required by IE11.
flex-basis: 0; flex-basis: 0;
} }
.wc-block-components-totals-taxes,
.wc-block-components-totals-footer-item {
@include with-translucent-border(1px 0 0);
margin: 0;
padding: em($gap-small) 0;
}
} }
.wc-block-checkout__actions { .wc-block-checkout__actions {

View File

@ -11,6 +11,7 @@ import { getSetting } from '@woocommerce/settings';
*/ */
import { previewShippingRates } from './shipping-rates'; import { previewShippingRates } from './shipping-rates';
const displayWithTax = getSetting( 'displayCartPricesIncludingTax', false );
// Sample data for cart block. // Sample data for cart block.
// This closely resembles the data returned from the Store API /cart endpoint. // This closely resembles the data returned from the Store API /cart endpoint.
// https://github.com/woocommerce/woocommerce-gutenberg-products-block/tree/trunk/src/RestApi/StoreApi#cart-api // https://github.com/woocommerce/woocommerce-gutenberg-products-block/tree/trunk/src/RestApi/StoreApi#cart-api
@ -66,14 +67,14 @@ export const previewCart: CartResponse = {
currency_thousand_separator: ',', currency_thousand_separator: ',',
currency_prefix: '$', currency_prefix: '$',
currency_suffix: '', currency_suffix: '',
price: '800', price: displayWithTax ? '800' : '640',
regular_price: '800', regular_price: displayWithTax ? '800' : '640',
sale_price: '800', sale_price: displayWithTax ? '800' : '640',
raw_prices: { raw_prices: {
precision: 6, precision: 6,
price: '8000000', price: displayWithTax ? '8000000' : '6400000',
regular_price: '8000000', regular_price: displayWithTax ? '8000000' : '6400000',
sale_price: '8000000', sale_price: displayWithTax ? '8000000' : '6400000',
}, },
}, },
totals: { totals: {
@ -84,10 +85,10 @@ export const previewCart: CartResponse = {
currency_thousand_separator: ',', currency_thousand_separator: ',',
currency_prefix: '$', currency_prefix: '$',
currency_suffix: '', currency_suffix: '',
line_subtotal: '1600', line_subtotal: displayWithTax ? '1600' : '1280',
line_subtotal_tax: '0', line_subtotal_tax: '0',
line_total: '1600', line_total: '1600',
line_total_tax: '0', line_total_tax: displayWithTax ? '0' : '320',
}, },
extensions: {}, extensions: {},
}, },
@ -132,14 +133,14 @@ export const previewCart: CartResponse = {
currency_thousand_separator: ',', currency_thousand_separator: ',',
currency_prefix: '$', currency_prefix: '$',
currency_suffix: '', currency_suffix: '',
price: '1400', price: displayWithTax ? '1400' : '1120',
regular_price: '1600', regular_price: displayWithTax ? '1600' : '1280',
sale_price: '1400', sale_price: displayWithTax ? '1400' : '1120',
raw_prices: { raw_prices: {
precision: 6, precision: 6,
price: '14000000', price: displayWithTax ? '14000000' : '11200000',
regular_price: '16000000', regular_price: displayWithTax ? '16000000' : '12800000',
sale_price: '14000000', sale_price: displayWithTax ? '14000000' : '11200000',
}, },
}, },
totals: { totals: {
@ -150,10 +151,10 @@ export const previewCart: CartResponse = {
currency_thousand_separator: ',', currency_thousand_separator: ',',
currency_prefix: '$', currency_prefix: '$',
currency_suffix: '', currency_suffix: '',
line_subtotal: '1400', line_subtotal: displayWithTax ? '1400' : '1120',
line_subtotal_tax: '0', line_subtotal_tax: displayWithTax ? '0' : '280',
line_total: '1400', line_total: '1400',
line_total_tax: '0', line_total_tax: displayWithTax ? '0' : '280',
}, },
extensions: {}, extensions: {},
}, },
@ -197,7 +198,7 @@ export const previewCart: CartResponse = {
currency_thousand_separator: ',', currency_thousand_separator: ',',
currency_prefix: '$', currency_prefix: '$',
currency_suffix: '', currency_suffix: '',
total_items: '3000', total_items: displayWithTax ? '3000' : '2400',
total_items_tax: '0', total_items_tax: '0',
total_fees: '0', total_fees: '0',
total_fees_tax: '0', total_fees_tax: '0',
@ -205,8 +206,14 @@ export const previewCart: CartResponse = {
total_discount_tax: '0', total_discount_tax: '0',
total_shipping: '0', total_shipping: '0',
total_shipping_tax: '0', total_shipping_tax: '0',
total_tax: '0', total_tax: '600',
total_price: '3000', total_price: '3000',
tax_lines: [], tax_lines: [
{
name: __( 'Sales tax', 'woo-gutenberg-products-block' ),
rate: '20%',
price: 600,
},
],
}, },
}; };

View File

@ -157,6 +157,7 @@ export interface CartResponseItem {
export interface CartResponseTotalsTaxLineItem { export interface CartResponseTotalsTaxLineItem {
name: string; name: string;
price: string; price: string;
rate: string;
} }
export interface CartResponseFeeItemTotals extends CurrencyResponseInfo { export interface CartResponseFeeItemTotals extends CurrencyResponseInfo {

View File

@ -137,6 +137,7 @@ export interface CartItem {
export interface CartTotalsTaxLineItem { export interface CartTotalsTaxLineItem {
name: string; name: string;
price: string; price: string;
rate: string;
} }
export interface CartFeeItemTotals extends CurrencyInfo { export interface CartFeeItemTotals extends CurrencyInfo {

View File

@ -12,6 +12,7 @@ import { ReactElement } from 'react';
* Internal dependencies * Internal dependencies
*/ */
import TotalsItem from '../item'; import TotalsItem from '../item';
import './style.scss';
interface Values { interface Values {
// eslint-disable-next-line camelcase // eslint-disable-next-line camelcase
@ -23,6 +24,7 @@ interface Values {
interface TotalsTaxesProps { interface TotalsTaxesProps {
className?: string; className?: string;
currency: Currency; currency: Currency;
showRateAfterTaxName: boolean;
values: Values | Record< string, never >; values: Values | Record< string, never >;
} }
@ -30,6 +32,7 @@ const TotalsTaxes = ( {
currency, currency,
values, values,
className, className,
showRateAfterTaxName,
}: TotalsTaxesProps ): ReactElement | null => { }: TotalsTaxesProps ): ReactElement | null => {
const { total_tax: totalTax, tax_lines: taxLines } = values; const { total_tax: totalTax, tax_lines: taxLines } = values;
@ -37,8 +40,30 @@ const TotalsTaxes = ( {
return null; return null;
} }
if ( ! getSetting( 'displayItemizedTaxes', false ) ) { const itemisedTaxItems: ReactElement | null = getSetting(
return ( 'displayItemizedTaxes',
false
) ? (
<>
{ taxLines.map( ( { name, rate }, i ) => {
const label = `${ name }${
showRateAfterTaxName ? ` ${ rate }` : ''
}`;
return (
<TotalsItem
key={ `tax-line-${ i }` }
className="wc-block-components-totals-taxes__tax-line"
currency={ currency }
label={ label }
value={ null }
/>
);
} ) }{ ' ' }
</>
) : null;
return (
<>
<TotalsItem <TotalsItem
className={ classnames( className={ classnames(
'wc-block-components-totals-taxes', 'wc-block-components-totals-taxes',
@ -47,21 +72,8 @@ const TotalsTaxes = ( {
currency={ currency } currency={ currency }
label={ __( 'Taxes', 'woo-gutenberg-products-block' ) } label={ __( 'Taxes', 'woo-gutenberg-products-block' ) }
value={ parseInt( totalTax, 10 ) } value={ parseInt( totalTax, 10 ) }
description={ itemisedTaxItems }
/> />
);
}
return (
<>
{ taxLines.map( ( { name, price }, i ) => (
<TotalsItem
key={ `tax-line-${ i }` }
className="wc-block-components-totals-taxes"
currency={ currency }
label={ name }
value={ parseInt( price, 10 ) }
/>
) ) }{ ' ' }
</> </>
); );
}; };

View File

@ -0,0 +1,6 @@
.wc-block-components-totals-item__description
.wc-block-components-totals-item.wc-block-components-totals-taxes__tax-line {
padding: 0;
@include font-size(small);
margin: $gap-smallest / 2;
}

View File

@ -120,6 +120,7 @@ class Cart extends AbstractBlock {
); );
$this->asset_data_registry->add( 'baseLocation', wc_get_base_location(), true ); $this->asset_data_registry->add( 'baseLocation', wc_get_base_location(), true );
$this->asset_data_registry->add( 'isShippingCalculatorEnabled', filter_var( get_option( 'woocommerce_enable_shipping_calc' ), FILTER_VALIDATE_BOOLEAN ), true ); $this->asset_data_registry->add( 'isShippingCalculatorEnabled', filter_var( get_option( 'woocommerce_enable_shipping_calc' ), FILTER_VALIDATE_BOOLEAN ), true );
$this->asset_data_registry->add( 'displayItemizedTaxes', 'itemized' === get_option( 'woocommerce_tax_total_display' ), true );
$this->asset_data_registry->add( 'displayCartPricesIncludingTax', 'incl' === get_option( 'woocommerce_tax_display_cart' ), true ); $this->asset_data_registry->add( 'displayCartPricesIncludingTax', 'incl' === get_option( 'woocommerce_tax_display_cart' ), true );
$this->asset_data_registry->add( 'taxesEnabled', wc_tax_enabled(), true ); $this->asset_data_registry->add( 'taxesEnabled', wc_tax_enabled(), true );
$this->asset_data_registry->add( 'couponsEnabled', wc_coupons_enabled(), true ); $this->asset_data_registry->add( 'couponsEnabled', wc_coupons_enabled(), true );

View File

@ -3,6 +3,7 @@ namespace Automattic\WooCommerce\Blocks\StoreApi\Schemas;
use Automattic\WooCommerce\Blocks\StoreApi\Utilities\CartController; use Automattic\WooCommerce\Blocks\StoreApi\Utilities\CartController;
use Automattic\WooCommerce\Blocks\Domain\Services\ExtendRestApi; use Automattic\WooCommerce\Blocks\Domain\Services\ExtendRestApi;
use WC_Tax;
use WP_Error; use WP_Error;
@ -287,6 +288,12 @@ class CartSchema extends AbstractSchema {
'context' => [ 'view', 'edit' ], 'context' => [ 'view', 'edit' ],
'readonly' => true, 'readonly' => true,
], ],
'rate' => [
'description' => __( 'The rate at which tax is applied.', 'woo-gutenberg-products-block' ),
'type' => 'string',
'context' => [ 'view', 'edit' ],
'readonly' => true,
],
], ],
], ],
], ],
@ -389,6 +396,7 @@ class CartSchema extends AbstractSchema {
$tax_lines[] = array( $tax_lines[] = array(
'name' => $cart_tax_total->label, 'name' => $cart_tax_total->label,
'price' => $this->prepare_money_response( $cart_tax_total->amount, wc_get_price_decimals() ), 'price' => $this->prepare_money_response( $cart_tax_total->amount, wc_get_price_decimals() ),
'rate' => WC_Tax::get_rate_percent( $cart_tax_total->tax_rate_id ),
); );
} }

View File

@ -739,10 +739,10 @@ class CartController {
$index > 1 ? $index > 1 ?
sprintf( sprintf(
/* translators: %d: shipping package number */ /* translators: %d: shipping package number */
_x( 'Shipping %d', 'shipping packages', 'woo-gutenberg-products-block' ), _x( 'Shipping method %d', 'shipping packages', 'woo-gutenberg-products-block' ),
$index $index
) : ) :
_x( 'Shipping', 'shipping packages', 'woo-gutenberg-products-block' ), _x( 'Shipping method', 'shipping packages', 'woo-gutenberg-products-block' ),
$package['package_id'], $package['package_id'],
$package $package
); );