* 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:
Mike Jolley 2020-02-26 17:09:18 +00:00 committed by GitHub
parent 7603b1391f
commit 6ac5b8288f
6 changed files with 294 additions and 13 deletions

View File

@ -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;

View File

@ -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;
}
}
}

View File

@ -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>
`;

View File

@ -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();
} );
} );
} );

View File

@ -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>
), ),
} ); } );

View File

@ -322,3 +322,8 @@ table.wc-block-cart-items {
} }
} }
} }
.wc-block-cart-coupon-list {
list-style: none;
margin: 0;
padding: 0;
}