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:
parent
cbce093df1
commit
eee638b12b
|
@ -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' },
|
||||
];
|
||||
|
|
|
@ -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;
|
|
@ -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 = {
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue