Add chip component for coupons in the cart (https://github.com/woocommerce/woocommerce-blocks/pull/1807)
* Chip componet and styling * Tests * Move coupon code for API requests to body - fixes issues with coupon codes containing special characters * Implement chip component in cart page * Revert "Move coupon code for API requests to body - fixes issues with coupon codes containing special characters" This reverts commit ac5a72f55d51d939bb989f3936e28cf993af19a6. * Update comment * prevent overflow * Add screen reader text for coupon name * Adjust icon alignment and padding/hit box * update string
This commit is contained in:
parent
7603b1391f
commit
6ac5b8288f
|
@ -0,0 +1,70 @@
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import { __, sprintf } from '@wordpress/i18n';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
import './style.scss';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component used to render a "chip" -- a list item containing some text with
|
||||||
|
* an X button to remove/dismiss each chip.
|
||||||
|
*
|
||||||
|
* Each chip defaults to a list element but this can be customized by providing
|
||||||
|
* a wrapperElement.
|
||||||
|
*/
|
||||||
|
const Chip = ( {
|
||||||
|
text,
|
||||||
|
screenReaderText,
|
||||||
|
element = 'li',
|
||||||
|
className = '',
|
||||||
|
onRemove = () => {},
|
||||||
|
disabled = false,
|
||||||
|
radius = 'small',
|
||||||
|
} ) => {
|
||||||
|
const Wrapper = element;
|
||||||
|
const wrapperClassName = classNames(
|
||||||
|
className,
|
||||||
|
'wc-block-components-chip',
|
||||||
|
'wc-block-components-chip--radius-' + radius
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
// @ts-ignore
|
||||||
|
<Wrapper className={ wrapperClassName }>
|
||||||
|
<span aria-hidden="true" className="wc-block-components-chip__text">
|
||||||
|
{ text }
|
||||||
|
</span>
|
||||||
|
<span className="screen-reader-text">
|
||||||
|
{ screenReaderText ? screenReaderText : text }
|
||||||
|
</span>
|
||||||
|
<button
|
||||||
|
className="wc-block-components-chip__remove"
|
||||||
|
onClick={ onRemove }
|
||||||
|
disabled={ disabled }
|
||||||
|
aria-label={ sprintf(
|
||||||
|
/* translators: %s chip text. */
|
||||||
|
__( 'Remove coupon "%s"', 'woo-gutenberg-products-block' ),
|
||||||
|
text
|
||||||
|
) }
|
||||||
|
>
|
||||||
|
✕
|
||||||
|
</button>
|
||||||
|
</Wrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
Chip.propTypes = {
|
||||||
|
text: PropTypes.string.isRequired,
|
||||||
|
screenReaderText: PropTypes.string,
|
||||||
|
element: PropTypes.elementType,
|
||||||
|
className: PropTypes.string,
|
||||||
|
onRemove: PropTypes.func,
|
||||||
|
radius: PropTypes.oneOf( [ 'none', 'small', 'medium', 'large' ] ),
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Chip;
|
|
@ -0,0 +1,44 @@
|
||||||
|
.wc-block-components-chip {
|
||||||
|
display: inline-block;
|
||||||
|
background: $core-grey-light-500;
|
||||||
|
padding: 0.365em 0.5em;
|
||||||
|
margin: 0 0.365em 0.365em 0;
|
||||||
|
color: $core-grey-dark-800;
|
||||||
|
border-radius: 0;
|
||||||
|
line-height: 1em;
|
||||||
|
max-width: 100%;
|
||||||
|
|
||||||
|
&.wc-block-components-chip--radius-small {
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
&.wc-block-components-chip--radius-medium {
|
||||||
|
border-radius: 0.433em;
|
||||||
|
}
|
||||||
|
&.wc-block-components-chip--radius-large {
|
||||||
|
border-radius: 0.865em;
|
||||||
|
padding: 0.365em 0.75em;
|
||||||
|
}
|
||||||
|
.wc-block-components-chip__text {
|
||||||
|
padding-right: 0.5em;
|
||||||
|
}
|
||||||
|
.wc-block-components-chip__remove {
|
||||||
|
background: transparent;
|
||||||
|
border: 0;
|
||||||
|
appearance: none;
|
||||||
|
float: none;
|
||||||
|
vertical-align: middle;
|
||||||
|
font-size: 0.75em; // If chip is 16px, this is 12px.
|
||||||
|
line-height: 1.33em;
|
||||||
|
padding: 0.66em; // Should equate to ~8px; chip has ~6px padding, and font size difference/2 is 2px.
|
||||||
|
margin: -0.66em;
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus {
|
||||||
|
color: #d94f4f;
|
||||||
|
}
|
||||||
|
&:disabled {
|
||||||
|
color: $core-grey-dark-100;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,105 @@
|
||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`Chip with custom wrapper should render a chip made up of a div instead of a li 1`] = `
|
||||||
|
<div
|
||||||
|
className="wc-block-components-chip wc-block-components-chip--radius-small"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
aria-hidden="true"
|
||||||
|
className="wc-block-components-chip__text"
|
||||||
|
>
|
||||||
|
Test
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
className="screen-reader-text"
|
||||||
|
>
|
||||||
|
Test
|
||||||
|
</span>
|
||||||
|
<button
|
||||||
|
aria-label="Remove \\"Test\\""
|
||||||
|
className="wc-block-components-chip__remove"
|
||||||
|
disabled={false}
|
||||||
|
onClick={[Function]}
|
||||||
|
>
|
||||||
|
✕
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Chip without custom wrapper should render defined radius 1`] = `
|
||||||
|
<li
|
||||||
|
className="wc-block-components-chip wc-block-components-chip--radius-large"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
aria-hidden="true"
|
||||||
|
className="wc-block-components-chip__text"
|
||||||
|
>
|
||||||
|
Test
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
className="screen-reader-text"
|
||||||
|
>
|
||||||
|
Test
|
||||||
|
</span>
|
||||||
|
<button
|
||||||
|
aria-label="Remove \\"Test\\""
|
||||||
|
className="wc-block-components-chip__remove"
|
||||||
|
disabled={false}
|
||||||
|
onClick={[Function]}
|
||||||
|
>
|
||||||
|
✕
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Chip without custom wrapper should render text and the remove button 1`] = `
|
||||||
|
<li
|
||||||
|
className="wc-block-components-chip wc-block-components-chip--radius-small"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
aria-hidden="true"
|
||||||
|
className="wc-block-components-chip__text"
|
||||||
|
>
|
||||||
|
Test
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
className="screen-reader-text"
|
||||||
|
>
|
||||||
|
Test
|
||||||
|
</span>
|
||||||
|
<button
|
||||||
|
aria-label="Remove \\"Test\\""
|
||||||
|
className="wc-block-components-chip__remove"
|
||||||
|
disabled={false}
|
||||||
|
onClick={[Function]}
|
||||||
|
>
|
||||||
|
✕
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Chip without custom wrapper should render with disabled remove button 1`] = `
|
||||||
|
<li
|
||||||
|
className="wc-block-components-chip wc-block-components-chip--radius-small"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
aria-hidden="true"
|
||||||
|
className="wc-block-components-chip__text"
|
||||||
|
>
|
||||||
|
Test
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
className="screen-reader-text"
|
||||||
|
>
|
||||||
|
Test
|
||||||
|
</span>
|
||||||
|
<button
|
||||||
|
aria-label="Remove \\"Test\\""
|
||||||
|
className="wc-block-components-chip__remove"
|
||||||
|
disabled={true}
|
||||||
|
onClick={[Function]}
|
||||||
|
>
|
||||||
|
✕
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
`;
|
|
@ -0,0 +1,45 @@
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import TestRenderer from 'react-test-renderer';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
import Chip from '../';
|
||||||
|
|
||||||
|
describe( 'Chip', () => {
|
||||||
|
describe( 'without custom wrapper', () => {
|
||||||
|
test( 'should render text and the remove button', () => {
|
||||||
|
const component = TestRenderer.create( <Chip text="Test" /> );
|
||||||
|
|
||||||
|
expect( component.toJSON() ).toMatchSnapshot();
|
||||||
|
} );
|
||||||
|
|
||||||
|
test( 'should render defined radius', () => {
|
||||||
|
const component = TestRenderer.create(
|
||||||
|
<Chip text="Test" radius="large" />
|
||||||
|
);
|
||||||
|
|
||||||
|
expect( component.toJSON() ).toMatchSnapshot();
|
||||||
|
} );
|
||||||
|
|
||||||
|
test( 'should render with disabled remove button', () => {
|
||||||
|
const component = TestRenderer.create(
|
||||||
|
<Chip text="Test" disabled={ true } />
|
||||||
|
);
|
||||||
|
|
||||||
|
expect( component.toJSON() ).toMatchSnapshot();
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
|
||||||
|
describe( 'with custom wrapper', () => {
|
||||||
|
test( 'should render a chip made up of a div instead of a li', () => {
|
||||||
|
const component = TestRenderer.create(
|
||||||
|
<Chip text="Test" element="div" />
|
||||||
|
);
|
||||||
|
|
||||||
|
expect( component.toJSON() ).toMatchSnapshot();
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
} );
|
|
@ -2,7 +2,7 @@
|
||||||
* External dependencies
|
* External dependencies
|
||||||
*/
|
*/
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { __ } from '@wordpress/i18n';
|
import { sprintf, __ } from '@wordpress/i18n';
|
||||||
import { useState, useEffect } from '@wordpress/element';
|
import { useState, useEffect } from '@wordpress/element';
|
||||||
import {
|
import {
|
||||||
TotalsCouponCodeInput,
|
TotalsCouponCodeInput,
|
||||||
|
@ -14,6 +14,7 @@ import ShippingRatesControl, {
|
||||||
import ShippingCalculator from '@woocommerce/base-components/shipping-calculator';
|
import ShippingCalculator from '@woocommerce/base-components/shipping-calculator';
|
||||||
import ShippingLocation from '@woocommerce/base-components/shipping-location';
|
import ShippingLocation from '@woocommerce/base-components/shipping-location';
|
||||||
import LoadingMask from '@woocommerce/base-components/loading-mask';
|
import LoadingMask from '@woocommerce/base-components/loading-mask';
|
||||||
|
import Chip from '@woocommerce/base-components/chip';
|
||||||
import {
|
import {
|
||||||
COUPONS_ENABLED,
|
COUPONS_ENABLED,
|
||||||
SHIPPING_ENABLED,
|
SHIPPING_ENABLED,
|
||||||
|
@ -145,7 +146,7 @@ const Cart = ( {
|
||||||
( DISPLAY_PRICES_INCLUDING_TAXES
|
( DISPLAY_PRICES_INCLUDING_TAXES
|
||||||
? totalDiscount + totalDiscountTax
|
? totalDiscount + totalDiscountTax
|
||||||
: totalDiscount ) * -1,
|
: totalDiscount ) * -1,
|
||||||
description: (
|
description: cartCoupons.length !== 0 && (
|
||||||
<LoadingMask
|
<LoadingMask
|
||||||
screenReaderLabel={ __(
|
screenReaderLabel={ __(
|
||||||
'Removing coupon…',
|
'Removing coupon…',
|
||||||
|
@ -154,17 +155,28 @@ const Cart = ( {
|
||||||
isLoading={ isRemovingCoupon }
|
isLoading={ isRemovingCoupon }
|
||||||
showSpinner={ false }
|
showSpinner={ false }
|
||||||
>
|
>
|
||||||
|
<ul className="wc-block-cart-coupon-list">
|
||||||
{ cartCoupons.map( ( cartCoupon ) => (
|
{ cartCoupons.map( ( cartCoupon ) => (
|
||||||
<button
|
<Chip
|
||||||
key={ 'coupon-' + cartCoupon.code }
|
key={ 'coupon-' + cartCoupon.code }
|
||||||
|
className="wc-block-cart-coupon-list__item"
|
||||||
|
text={ cartCoupon.code }
|
||||||
|
screenReaderText={ sprintf(
|
||||||
|
/* Translators: %s Coupon code. */
|
||||||
|
__(
|
||||||
|
'Coupon: %s',
|
||||||
|
'woo-gutenberg-products-block'
|
||||||
|
),
|
||||||
|
cartCoupon.code
|
||||||
|
) }
|
||||||
disabled={ isRemovingCoupon }
|
disabled={ isRemovingCoupon }
|
||||||
onClick={ () => {
|
onRemove={ () => {
|
||||||
removeCoupon( cartCoupon.code );
|
removeCoupon( cartCoupon.code );
|
||||||
} }
|
} }
|
||||||
>
|
radius="large"
|
||||||
{ cartCoupon.code }
|
/>
|
||||||
</button>
|
|
||||||
) ) }
|
) ) }
|
||||||
|
</ul>
|
||||||
</LoadingMask>
|
</LoadingMask>
|
||||||
),
|
),
|
||||||
} );
|
} );
|
||||||
|
|
|
@ -322,3 +322,8 @@ table.wc-block-cart-items {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.wc-block-cart-coupon-list {
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue