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
|
||||
*/
|
||||
import PropTypes from 'prop-types';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { sprintf, __ } from '@wordpress/i18n';
|
||||
import { useState, useEffect } from '@wordpress/element';
|
||||
import {
|
||||
TotalsCouponCodeInput,
|
||||
|
@ -14,6 +14,7 @@ import ShippingRatesControl, {
|
|||
import ShippingCalculator from '@woocommerce/base-components/shipping-calculator';
|
||||
import ShippingLocation from '@woocommerce/base-components/shipping-location';
|
||||
import LoadingMask from '@woocommerce/base-components/loading-mask';
|
||||
import Chip from '@woocommerce/base-components/chip';
|
||||
import {
|
||||
COUPONS_ENABLED,
|
||||
SHIPPING_ENABLED,
|
||||
|
@ -145,7 +146,7 @@ const Cart = ( {
|
|||
( DISPLAY_PRICES_INCLUDING_TAXES
|
||||
? totalDiscount + totalDiscountTax
|
||||
: totalDiscount ) * -1,
|
||||
description: (
|
||||
description: cartCoupons.length !== 0 && (
|
||||
<LoadingMask
|
||||
screenReaderLabel={ __(
|
||||
'Removing coupon…',
|
||||
|
@ -154,17 +155,28 @@ const Cart = ( {
|
|||
isLoading={ isRemovingCoupon }
|
||||
showSpinner={ false }
|
||||
>
|
||||
{ cartCoupons.map( ( cartCoupon ) => (
|
||||
<button
|
||||
key={ 'coupon-' + cartCoupon.code }
|
||||
disabled={ isRemovingCoupon }
|
||||
onClick={ () => {
|
||||
removeCoupon( cartCoupon.code );
|
||||
} }
|
||||
>
|
||||
{ cartCoupon.code }
|
||||
</button>
|
||||
) ) }
|
||||
<ul className="wc-block-cart-coupon-list">
|
||||
{ cartCoupons.map( ( cartCoupon ) => (
|
||||
<Chip
|
||||
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 }
|
||||
onRemove={ () => {
|
||||
removeCoupon( cartCoupon.code );
|
||||
} }
|
||||
radius="large"
|
||||
/>
|
||||
) ) }
|
||||
</ul>
|
||||
</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