Component – Filters: Add "comparison" card (https://github.com/woocommerce/woocommerce-admin/pull/368)

* Add a new component with product search for the compare card

* Get product names from API when passed through URL

* Abstract out the products-related code into filter settings

* Update filters to provide multiple comparisons

Alternative to the “Compare [dropdown]” approach

* Update documentation

* Wipe selected items when the compare-type is updated

* Update labels & order of items in filter dropdown

* Add getLabels prop & description
This commit is contained in:
Kelly Dwan 2018-09-10 09:59:14 -04:00 committed by GitHub
parent cbce093df1
commit eee638b12b
3 changed files with 222 additions and 51 deletions

View File

@ -3,6 +3,12 @@
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import apiFetch from '@wordpress/api-fetch';
/**
* Internal dependencies
*/
import { stringifyQuery } from 'lib/nav-utils';
export const filters = [
{ label: __( 'All Products', 'wc-admin' ), value: 'all' },
@ -17,7 +23,52 @@ export const filters = [
},
],
},
{
label: __( 'Product Comparison', 'wc-admin' ),
value: 'compare-product',
settings: {
type: 'products',
param: 'product',
getLabels: function( queryString ) {
const idList = queryString
.split( ',' )
.map( id => parseInt( id, 10 ) )
.filter( Boolean );
const payload = stringifyQuery( {
include: idList.join( ',' ),
per_page: idList.length,
} );
return apiFetch( { path: '/wc/v3/products' + payload } );
},
labels: {
title: __( 'Compare Products', 'wc-admin' ),
update: __( 'Compare', 'wc-admin' ),
},
},
},
{
label: __( 'Product Category Comparison', 'wc-admin' ),
value: 'compare-product_cat',
settings: {
type: 'product_cats',
param: 'product_cat',
getLabels: function( queryString ) {
const idList = queryString
.split( ',' )
.map( id => parseInt( id, 10 ) )
.filter( Boolean );
const payload = stringifyQuery( {
include: idList.join( ',' ),
per_page: idList.length,
} );
return apiFetch( { path: '/wc/v3/products/categories' + payload } );
},
labels: {
title: __( 'Compare Product Categories', 'wc-admin' ),
update: __( 'Compare', 'wc-admin' ),
},
},
},
{ label: __( 'Top Products by Items Sold', 'wc-admin' ), value: 'top_items' },
{ label: __( 'Top Products by Gross Sales', 'wc-admin' ), value: 'top_sales' },
{ label: __( 'Comparison', 'wc-admin' ), value: 'compare' },
];

View File

@ -0,0 +1,120 @@
/** @format */
/**
* External dependencies
*/
import { Button } from '@wordpress/components';
import { Component } from '@wordpress/element';
import PropTypes from 'prop-types';
/**
* Internal dependencies
*/
import Card from 'components/card';
import Search from 'components/search';
import { updateQueryString } from 'lib/nav-utils';
/**
* Displays a card + search used to filter results as a comparison between objects.
*/
class CompareFilter extends Component {
constructor( { getLabels, param, query } ) {
super( ...arguments );
this.state = {
selected: [],
};
this.updateQuery = this.updateQuery.bind( this );
this.updateLabels = this.updateLabels.bind( this );
if ( query[ param ] ) {
getLabels( query[ param ] ).then( this.updateLabels );
}
}
componentDidUpdate( { param } ) {
if ( param !== this.props.param ) {
/* eslint-disable react/no-did-update-set-state */
this.setState( { selected: [] } );
updateQueryString( { [ param ]: '' }, this.props.path, this.props.query );
/* eslint-enable react/no-did-update-set-state */
}
}
updateLabels( data ) {
const selected = data.map( p => ( { id: p.id, label: p.name } ) );
this.setState( { selected } );
}
updateQuery() {
const { param, path, query } = this.props;
const { selected } = this.state;
const idList = selected.map( p => p.id );
updateQueryString( { [ param ]: idList.join( ',' ) }, path, query );
}
render() {
const { labels, type } = this.props;
const { selected } = this.state;
return (
<Card title={ labels.title } className="woocommerce-filters__compare">
<div className="woocommerce-filters__compare-body">
<Search
type={ type }
selected={ selected }
onChange={ value => {
this.setState( { selected: value } );
} }
/>
</div>
<div className="woocommerce-filters__compare-footer">
<Button onClick={ this.updateQuery } isDefault>
{ labels.update }
</Button>
</div>
</Card>
);
}
}
CompareFilter.propTypes = {
/**
* Function used to fetch object labels via an API request, returns a Promise.
*/
getLabels: PropTypes.func.isRequired,
/**
* Object of localized labels.
*/
labels: PropTypes.shape( {
/**
* Label for the card title.
*/
title: PropTypes.string,
/**
* Label for button which updates the URL/report.
*/
update: PropTypes.string,
} ),
/**
* The parameter to use in the querystring.
*/
param: PropTypes.string.isRequired,
/**
* The `path` parameter supplied by React-Router
*/
path: PropTypes.string.isRequired,
/**
* The query string represented in object form
*/
query: PropTypes.object,
/**
* Which type of autocompleter should be used in the Search
*/
type: PropTypes.string.isRequired,
};
CompareFilter.defaultProps = {
labels: {},
query: {},
};
export default CompareFilter;

View File

@ -3,16 +3,15 @@
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { Button } from '@wordpress/components';
import { Fragment } from '@wordpress/element';
import { noop } from 'lodash';
import { Component, Fragment } from '@wordpress/element';
import { find } from 'lodash';
import PropTypes from 'prop-types';
/**
* Internal dependencies
*/
import AdvancedFilters from './advanced';
import Card from 'components/card';
import CompareFilter from './compare';
import DatePicker from './date';
import FilterPicker from './filter';
import { H, Section } from 'layout/section';
@ -24,56 +23,57 @@ import './style.scss';
*
* @return { object } -
*/
const ReportFilters = ( { advancedConfig, filters, query, path } ) => {
let advancedCard = false;
switch ( query.filter ) {
case 'compare':
advancedCard = (
<Card
title={ __( 'Compare Products', 'wc-admin' ) }
className="woocommerce-filters__compare"
>
<div className="woocommerce-filters__compare-body">
<input type="search" />
<div>Tokens</div>
</div>
<div className="woocommerce-filters__compare-footer">
<Button onClick={ noop } isDefault>
{ __( 'Compare', 'wc-admin' ) }
</Button>
</div>
</Card>
class ReportFilters extends Component {
renderCard() {
const { advancedConfig, filters, query, path } = this.props;
if ( ! query.filter ) {
return null;
}
if ( 0 === query.filter.indexOf( 'compare' ) ) {
const filter = find( filters, { value: query.filter } );
if ( ! filter ) {
return null;
}
const { settings = {} } = filter;
return (
<div className="woocommerce-filters__advanced-filters">
<CompareFilter path={ path } query={ query } { ...settings } />
</div>
);
break;
case 'advanced':
advancedCard = (
<AdvancedFilters
config={ advancedConfig }
filterTitle={ __( 'Orders', 'wc-admin' ) }
path={ path }
query={ query }
/>
}
if ( 'advanced' === query.filter ) {
return (
<div className="woocommerce-filters__advanced-filters">
<AdvancedFilters
config={ advancedConfig }
filterTitle={ __( 'Orders', 'wc-admin' ) }
path={ path }
query={ query }
/>
</div>
);
break;
}
}
return (
<Fragment>
<H className="screen-reader-text">{ __( 'Filters', 'wc-admin' ) }</H>
<Section component="div" className="woocommerce-filters">
<div className="woocommerce-filters__basic-filters">
<DatePicker key={ JSON.stringify( query ) } query={ query } path={ path } />
{ !! filters.length && (
<FilterPicker filters={ filters } query={ query } path={ path } />
) }
</div>
{ false !== advancedCard && (
<div className="woocommerce-filters__advanced-filters">{ advancedCard }</div>
) }
</Section>
</Fragment>
);
};
render() {
const { filters, query, path } = this.props;
return (
<Fragment>
<H className="screen-reader-text">{ __( 'Filters', 'wc-admin' ) }</H>
<Section component="div" className="woocommerce-filters">
<div className="woocommerce-filters__basic-filters">
<DatePicker key={ JSON.stringify( query ) } query={ query } path={ path } />
{ !! filters.length && (
<FilterPicker filters={ filters } query={ query } path={ path } />
) }
</div>
{ this.renderCard() }
</Section>
</Fragment>
);
}
}
ReportFilters.propTypes = {
/**