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:
parent
5e87febf04
commit
1c6fe0e970
|
@ -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' ),
|
||||||
},
|
},
|
||||||
|
|
|
@ -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;
|
|
@ -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.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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' } }>
|
||||||
|
|
Loading…
Reference in New Issue