Compare Filter: Wrap up compare card functionality (https://github.com/woocommerce/woocommerce-admin/pull/431)

* Disable compare button if less than 2 items are selected

* Add a placeholder prop to the Search component

* Pass a placeholder label from filter configs for Search

* Add support for a label in Search

* Add Clear All link to reset comparison card

* Add a label when compare button is disabled

* Abstract the conditional tooltip/disabled button logic to a new component

* Add helpText as a customizable label

* Add icon to the search field

* Remove the parameter from query by passing in undefined
This commit is contained in:
Kelly Dwan 2018-09-19 13:16:52 -04:00 committed by GitHub
parent 5e87febf04
commit 1c6fe0e970
7 changed files with 109 additions and 10 deletions

View File

@ -56,6 +56,8 @@ export const filters = [
param: 'product', param: 'product',
getLabels: getProductLabelsById, getLabels: getProductLabelsById,
labels: { labels: {
helpText: __( 'Select at least two products to compare', 'wc-admin' ),
placeholder: __( 'Search for products to compare', 'wc-admin' ),
title: __( 'Compare Products', 'wc-admin' ), title: __( 'Compare Products', 'wc-admin' ),
update: __( 'Compare', 'wc-admin' ), update: __( 'Compare', 'wc-admin' ),
}, },
@ -69,6 +71,8 @@ export const filters = [
param: 'product_cat', param: 'product_cat',
getLabels: getCategoryLabelsById, getLabels: getCategoryLabelsById,
labels: { labels: {
helpText: __( 'Select at least two product categories to compare', 'wc-admin' ),
placeholder: __( 'Search for product categories to compare', 'wc-admin' ),
title: __( 'Compare Product Categories', 'wc-admin' ), title: __( 'Compare Product Categories', 'wc-admin' ),
update: __( 'Compare', 'wc-admin' ), update: __( 'Compare', 'wc-admin' ),
}, },

View File

@ -0,0 +1,48 @@
/** @format */
/**
* External dependencies
*/
import PropTypes from 'prop-types';
import { Button, Tooltip } from '@wordpress/components';
/**
* A button used when comparing items, if `count` is less than 2 a hoverable tooltip is added with `helpText`.
*
* @return { object } -
*/
const CompareButton = ( { count, children, helpText, onClick } ) =>
count < 2 ? (
<Tooltip text={ helpText }>
<span>
<Button isDefault disabled={ true }>
{ children }
</Button>
</span>
</Tooltip>
) : (
<Button isDefault onClick={ onClick }>
{ children }
</Button>
);
CompareButton.propTypes = {
/**
* The count of items selected.
*/
count: PropTypes.number.isRequired,
/**
* The button content.
*/
children: PropTypes.node.isRequired,
/**
* Text displayed when hovering over a disabled button.
*/
helpText: PropTypes.string.isRequired,
/**
* The function called when the button is clicked.
*/
onClick: PropTypes.func.isRequired,
};
export default CompareButton;

View File

@ -2,7 +2,7 @@
/** /**
* External dependencies * External dependencies
*/ */
import { Button } from '@wordpress/components'; import { __ } from '@wordpress/i18n';
import { Component } from '@wordpress/element'; import { Component } from '@wordpress/element';
import { isEqual } from 'lodash'; import { isEqual } from 'lodash';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
@ -11,7 +11,9 @@ import PropTypes from 'prop-types';
* Internal dependencies * Internal dependencies
*/ */
import Card from 'components/card'; import Card from 'components/card';
import { getIdsFromQuery, updateQueryString } from 'lib/nav-utils'; import CompareButton from './button';
import Link from 'components/link';
import { getIdsFromQuery, getNewPath, updateQueryString } from 'lib/nav-utils';
import Search from 'components/search'; import Search from 'components/search';
/** /**
@ -24,6 +26,7 @@ class CompareFilter extends Component {
selected: [], selected: [],
}; };
this.clearQuery = this.clearQuery.bind( this );
this.updateQuery = this.updateQuery.bind( this ); this.updateQuery = this.updateQuery.bind( this );
this.updateLabels = this.updateLabels.bind( this ); this.updateLabels = this.updateLabels.bind( this );
@ -49,6 +52,11 @@ class CompareFilter extends Component {
} }
} }
clearQuery() {
const { param, path, query } = this.props;
return getNewPath( { [ param ]: undefined }, path, query );
}
updateLabels( data ) { updateLabels( data ) {
const selected = data.map( p => ( { id: p.id, label: p.name } ) ); const selected = data.map( p => ( { id: p.id, label: p.name } ) );
this.setState( { selected } ); this.setState( { selected } );
@ -70,15 +78,23 @@ class CompareFilter extends Component {
<Search <Search
type={ type } type={ type }
selected={ selected } selected={ selected }
placeholder={ labels.placeholder }
onChange={ value => { onChange={ value => {
this.setState( { selected: value } ); this.setState( { selected: value } );
} } } }
/> />
</div> </div>
<div className="woocommerce-filters__compare-footer"> <div className="woocommerce-filters__compare-footer">
<Button onClick={ this.updateQuery } isDefault> <CompareButton
count={ selected.length }
helpText={ labels.helpText }
onClick={ this.updateQuery }
>
{ labels.update } { labels.update }
</Button> </CompareButton>
<Link type="wc-admin" href={ this.clearQuery() }>
{ __( 'Clear all', 'wc-admin' ) }
</Link>
</div> </div>
</Card> </Card>
); );
@ -94,6 +110,10 @@ CompareFilter.propTypes = {
* Object of localized labels. * Object of localized labels.
*/ */
labels: PropTypes.shape( { labels: PropTypes.shape( {
/**
* Label for the search placeholder.
*/
placeholder: PropTypes.string,
/** /**
* Label for the card title. * Label for the card title.
*/ */

View File

@ -61,4 +61,10 @@
.woocommerce-filters__compare-footer { .woocommerce-filters__compare-footer {
padding: $gap; padding: $gap;
display: flex;
align-items: center;
.components-button {
margin-right: $gap;
}
} }

View File

@ -5,6 +5,7 @@
import { __, sprintf } from '@wordpress/i18n'; import { __, sprintf } from '@wordpress/i18n';
import { Component } from '@wordpress/element'; import { Component } from '@wordpress/element';
import { findIndex, noop } from 'lodash'; import { findIndex, noop } from 'lodash';
import Gridicon from 'gridicons';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
/** /**
@ -70,17 +71,20 @@ class Search extends Component {
render() { render() {
const autocompleter = this.getAutocompleter(); const autocompleter = this.getAutocompleter();
const { selected } = this.props; const { ariaLabelledby, placeholder, selected } = this.props;
const { value = '' } = this.state; const { value = '' } = this.state;
return ( return (
<div className="woocommerce-search"> <div className="woocommerce-search">
<Gridicon className="woocommerce-search__icon" icon="search" />
<Autocomplete completer={ autocompleter } onSelect={ this.selectResult }> <Autocomplete completer={ autocompleter } onSelect={ this.selectResult }>
{ ( { listBoxId, activeId, onChange } ) => ( { ( { listBoxId, activeId, onChange } ) => (
<input <input
type="search" type="search"
value={ value } value={ value }
placeholder={ placeholder }
className="woocommerce-search__input" className="woocommerce-search__input"
onChange={ this.updateSearch( onChange ) } onChange={ this.updateSearch( onChange ) }
aria-labelledby={ ariaLabelledby }
aria-owns={ listBoxId } aria-owns={ listBoxId }
aria-activedescendant={ activeId } aria-activedescendant={ activeId }
/> />
@ -122,6 +126,10 @@ Search.propTypes = {
* The object type to be used in searching. * The object type to be used in searching.
*/ */
type: PropTypes.oneOf( [ 'products', 'product_cats', 'orders', 'customers' ] ).isRequired, type: PropTypes.oneOf( [ 'products', 'product_cats', 'orders', 'customers' ] ).isRequired,
/**
* A placeholder for the search input.
*/
placeholder: PropTypes.string,
/** /**
* An array of objects describing selected values. * An array of objects describing selected values.
*/ */

View File

@ -3,9 +3,16 @@
.woocommerce-search { .woocommerce-search {
position: relative; position: relative;
.woocommerce-search__icon {
position: absolute;
top: 10px;
left: 10px;
fill: $core-grey-light-900;
}
.woocommerce-search__input { .woocommerce-search__input {
width: 100%; width: 100%;
padding: $gap-small; padding: $gap-small $gap-small $gap-small 36px;
border: 1px solid $core-grey-light-700; border: 1px solid $core-grey-light-700;
} }

View File

@ -3,10 +3,10 @@
* External dependencies * External dependencies
*/ */
import { __ } from '@wordpress/i18n'; import { __ } from '@wordpress/i18n';
import { Button, IconButton, ToggleControl } from '@wordpress/components';
import classnames from 'classnames'; import classnames from 'classnames';
import { Component } from '@wordpress/element'; import { Component } from '@wordpress/element';
import { fill, find, findIndex, first, isEqual, noop, partial, uniq } from 'lodash'; import { fill, find, findIndex, first, isEqual, noop, partial, uniq } from 'lodash';
import { IconButton, ToggleControl } from '@wordpress/components';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
/** /**
@ -14,6 +14,7 @@ import PropTypes from 'prop-types';
*/ */
import './style.scss'; import './style.scss';
import Card from 'components/card'; import Card from 'components/card';
import CompareButton from 'components/filters/compare/button';
import EllipsisMenu from 'components/ellipsis-menu'; import EllipsisMenu from 'components/ellipsis-menu';
import { getIdsFromQuery } from 'lib/nav-utils'; import { getIdsFromQuery } from 'lib/nav-utils';
import MenuItem from 'components/ellipsis-menu/menu-item'; import MenuItem from 'components/ellipsis-menu/menu-item';
@ -163,7 +164,7 @@ class TableCard extends Component {
title, title,
totalRows, totalRows,
} = this.props; } = this.props;
const { showCols } = this.state; const { selectedRows, showCols } = this.state;
const allHeaders = this.props.headers; const allHeaders = this.props.headers;
let headers = this.filterCols( this.props.headers ); let headers = this.filterCols( this.props.headers );
let rows = this.filterCols( this.props.rows ); let rows = this.filterCols( this.props.rows );
@ -185,9 +186,14 @@ class TableCard extends Component {
title={ title } title={ title }
action={ [ action={ [
compareBy && ( compareBy && (
<Button key="compare" onClick={ this.onCompare } isDefault> <CompareButton
key="compare"
count={ selectedRows.length }
helpText={ __( 'Select at least two items to compare', 'wc-admin' ) }
onClick={ this.onCompare }
>
{ __( 'Compare', 'wc-admin' ) } { __( 'Compare', 'wc-admin' ) }
</Button> </CompareButton>
), ),
compareBy && ( compareBy && (
<div key="search" style={ { padding: '4px 12px', color: '#6c7781' } }> <div key="search" style={ { padding: '4px 12px', color: '#6c7781' } }>