Merge pull request woocommerce/woocommerce-admin#276 from woocommerce/add/advanced-filters-card
Add/advanced filters card
This commit is contained in:
commit
8683726d06
|
@ -0,0 +1,89 @@
|
|||
/** @format */
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
|
||||
export const filters = [
|
||||
{ label: __( 'All Orders', 'wc-admin' ), value: 'all' },
|
||||
{
|
||||
label: __( 'Single Order', 'wc-admin' ),
|
||||
value: 'single',
|
||||
subFilters: [
|
||||
{
|
||||
label: __( 'Single Order', 'wc-admin' ),
|
||||
component: 'Search',
|
||||
value: 'single_order',
|
||||
},
|
||||
],
|
||||
},
|
||||
{ label: __( 'Top Orders by Items Sold', 'wc-admin' ), value: 'top_items' },
|
||||
{ label: __( 'Top Orders by Gross Sales', 'wc-admin' ), value: 'top_sales' },
|
||||
{ label: __( 'Advanced Filters', 'wc-admin' ), value: 'advanced' },
|
||||
];
|
||||
|
||||
export const filterPaths = {
|
||||
all: [],
|
||||
single: [],
|
||||
single_order: [ 'single' ],
|
||||
top_items: [],
|
||||
top_sales: [],
|
||||
advanced: [],
|
||||
};
|
||||
|
||||
export const advancedFilterConfig = {
|
||||
status: {
|
||||
label: __( 'Order Status', 'wc-admin' ),
|
||||
addLabel: __( 'Order Status', 'wc-admin' ),
|
||||
rules: [
|
||||
{ value: 'is', label: __( 'Is', 'wc-admin' ) },
|
||||
{ value: 'is-not', label: __( 'Is Not', 'wc-admin' ) },
|
||||
],
|
||||
input: {
|
||||
component: 'SelectControl',
|
||||
options: [
|
||||
{ value: 'pending', label: __( 'Pending', 'wc-admin' ) },
|
||||
{ value: 'processing', label: __( 'Processing', 'wc-admin' ) },
|
||||
{ value: 'on-hold', label: __( 'On Hold', 'wc-admin' ) },
|
||||
{ value: 'completed', label: __( 'Completed', 'wc-admin' ) },
|
||||
{ value: 'refunded', label: __( 'Refunded', 'wc-admin' ) },
|
||||
{ value: 'cancelled', label: __( 'Cancelled', 'wc-admin' ) },
|
||||
{ value: 'failed', label: __( 'Failed', 'wc-admin' ) },
|
||||
],
|
||||
},
|
||||
},
|
||||
product: {
|
||||
label: __( 'Product', 'wc-admin' ),
|
||||
addLabel: __( 'Products', 'wc-admin' ),
|
||||
rules: [
|
||||
{ value: 'includes', label: __( 'Includes', 'wc-admin' ) },
|
||||
{ value: 'excludes', label: __( 'Excludes', 'wc-admin' ) },
|
||||
{ value: 'is', label: __( 'Is', 'wc-admin' ) },
|
||||
{ value: 'is-not', label: __( 'Is Not', 'wc-admin' ) },
|
||||
],
|
||||
input: {
|
||||
component: 'FormTokenField',
|
||||
},
|
||||
},
|
||||
coupon: {
|
||||
label: __( 'Coupon Code', 'wc-admin' ),
|
||||
addLabel: __( 'Coupon Codes', 'wc-admin' ),
|
||||
rules: [
|
||||
{ value: 'includes', label: __( 'Includes', 'wc-admin' ) },
|
||||
{ value: 'excludes', label: __( 'Excludes', 'wc-admin' ) },
|
||||
{ value: 'is', label: __( 'Is', 'wc-admin' ) },
|
||||
{ value: 'is-not', label: __( 'Is Not', 'wc-admin' ) },
|
||||
],
|
||||
input: {
|
||||
component: 'FormTokenField',
|
||||
},
|
||||
},
|
||||
customer: {
|
||||
label: __( 'Customer is', 'wc-admin' ),
|
||||
addLabel: __( 'Customer Type', 'wc-admin' ),
|
||||
rules: [
|
||||
{ value: 'new', label: __( 'New', 'wc-admin' ) },
|
||||
{ value: 'returning', label: __( 'Returning', 'wc-admin' ) },
|
||||
],
|
||||
},
|
||||
};
|
|
@ -15,6 +15,11 @@ import { partial } from 'lodash';
|
|||
*/
|
||||
import Header from 'layout/header/index';
|
||||
import Card from 'components/card';
|
||||
import DatePicker from 'components/date-picker';
|
||||
import FilterPicker, { FILTER_PARAM } from 'components/filter-picker';
|
||||
import AdvancedFilters from 'components/advanced-filters';
|
||||
import { filters, filterPaths, advancedFilterConfig } from './constants';
|
||||
import './style.scss';
|
||||
|
||||
class OrdersReport extends Component {
|
||||
constructor( props ) {
|
||||
|
@ -32,7 +37,7 @@ class OrdersReport extends Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
const { orders, orderIds } = this.props;
|
||||
const { orders, orderIds, query, path } = this.props;
|
||||
return (
|
||||
<Fragment>
|
||||
<Header
|
||||
|
@ -41,6 +46,22 @@ class OrdersReport extends Component {
|
|||
__( 'Orders', 'wc-admin' ),
|
||||
] }
|
||||
/>
|
||||
<div className="woocommerce-orders__pickers">
|
||||
<DatePicker query={ query } path={ path } key={ JSON.stringify( query ) } />
|
||||
<FilterPicker
|
||||
query={ query }
|
||||
path={ path }
|
||||
filters={ filters }
|
||||
filterPaths={ filterPaths }
|
||||
/>
|
||||
</div>
|
||||
{ 'advanced' === query[ FILTER_PARAM ] && (
|
||||
<AdvancedFilters
|
||||
config={ advancedFilterConfig }
|
||||
filterTitle={ __( 'Orders', 'wc-admin' ) }
|
||||
/>
|
||||
) }
|
||||
<p>Below is a temporary example</p>
|
||||
<Card title="Orders">
|
||||
<table style={ { width: '100%' } }>
|
||||
<thead>
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
/** @format */
|
||||
|
||||
.woocommerce-orders__pickers {
|
||||
display: flex;
|
||||
|
||||
> div {
|
||||
margin-right: $gap-large;
|
||||
}
|
||||
|
||||
@include breakpoint( '<1100px' ) {
|
||||
flex-direction: column;
|
||||
|
||||
> div {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -15,17 +15,6 @@ import { filters, filterPaths } from './constants';
|
|||
import './style.scss';
|
||||
|
||||
export default class extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.getQueryParamValue = this.getQueryParamValue.bind( this );
|
||||
}
|
||||
|
||||
getQueryParamValue() {
|
||||
const { query } = this.props;
|
||||
return query.product || 'all';
|
||||
}
|
||||
|
||||
render() {
|
||||
const { query, path } = this.props;
|
||||
|
||||
|
@ -44,8 +33,6 @@ export default class extends Component {
|
|||
path={ path }
|
||||
filters={ filters }
|
||||
filterPaths={ filterPaths }
|
||||
queryParam="product"
|
||||
getQueryParamValue={ this.getQueryParamValue }
|
||||
/>
|
||||
</div>
|
||||
</Fragment>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
display: flex;
|
||||
|
||||
> div {
|
||||
margin-right: 24px;
|
||||
margin-right: $gap-large;
|
||||
}
|
||||
|
||||
@include breakpoint( '<1100px' ) {
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
Advanced Filters
|
||||
============
|
||||
|
||||
Displays a configurable set of filters which can modify query parameters.
|
||||
|
||||
## How to use:
|
||||
|
||||
```jsx
|
||||
import AdvancedFilters from 'components/advanced-filters';
|
||||
|
||||
filters = {
|
||||
status: {
|
||||
label: __( 'Order Status', 'wc-admin' ),
|
||||
addLabel: __( 'Order Status', 'wc-admin' ),
|
||||
rules: [
|
||||
{ value: 'is', label: __( 'Is', 'wc-admin' ) },
|
||||
{ value: 'is-not', label: __( 'Is Not', 'wc-admin' ) },
|
||||
],
|
||||
input: {
|
||||
component: 'SelectControl',
|
||||
options: [
|
||||
{ value: 'pending', label: __( 'Pending', 'wc-admin' ) },
|
||||
{ value: 'processing', label: __( 'Processing', 'wc-admin' ) },
|
||||
{ value: 'on-hold', label: __( 'On Hold', 'wc-admin' ) },
|
||||
],
|
||||
},
|
||||
},
|
||||
product: {
|
||||
label: __( 'Product', 'wc-admin' ),
|
||||
addLabel: __( 'Products', 'wc-admin' ),
|
||||
rules: [
|
||||
{ value: 'includes', label: __( 'Includes', 'wc-admin' ) },
|
||||
{ value: 'excludes', label: __( 'Excludes', 'wc-admin' ) },
|
||||
{ value: 'is', label: __( 'Is', 'wc-admin' ) },
|
||||
{ value: 'is-not', label: __( 'Is Not', 'wc-admin' ) },
|
||||
],
|
||||
input: {
|
||||
component: 'FormTokenField',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<AdvancedFilters config={ filters } />
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## AdvancedFilters Props
|
||||
|
||||
* `config` (required): The configuration object required to render filters
|
||||
|
||||
## config object jsDoc
|
||||
|
||||
```js
|
||||
/**
|
||||
* @type filterConfig {{
|
||||
* key: {
|
||||
* label: {string},
|
||||
* addLabel: {string},
|
||||
* rules: [{{ value:{string}, label:{string} }}],
|
||||
* input: {
|
||||
* component: {string},
|
||||
* [options]: [*]
|
||||
* },
|
||||
* }
|
||||
* }}
|
||||
*/
|
||||
```
|
|
@ -0,0 +1,238 @@
|
|||
/** @format */
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __, sprintf } from '@wordpress/i18n';
|
||||
import { Component, Fragment, createRef } from '@wordpress/element';
|
||||
import { SelectControl, Button, FormTokenField, Dropdown, IconButton } from '@wordpress/components';
|
||||
import { partial, findIndex, find, difference } from 'lodash';
|
||||
import PropTypes from 'prop-types';
|
||||
import Gridicon from 'gridicons';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import Card from 'components/card';
|
||||
import './style.scss';
|
||||
|
||||
const matches = [
|
||||
{ value: 'all', label: __( 'All', 'wc-admin' ) },
|
||||
{ value: 'any', label: __( 'Any', 'wc-admin' ) },
|
||||
];
|
||||
|
||||
class AdvancedFilters extends Component {
|
||||
constructor( props ) {
|
||||
super( props );
|
||||
this.state = {
|
||||
match: matches[ 0 ],
|
||||
activeFilters: [
|
||||
/**
|
||||
* Example activeFilter
|
||||
* { key: ‘product’, rule: ‘includes’, value: [ ‘one’, ‘two’ ] }
|
||||
*/
|
||||
],
|
||||
};
|
||||
|
||||
this.filterListRef = createRef();
|
||||
|
||||
this.onMatchChange = this.onMatchChange.bind( this );
|
||||
this.onFilterChange = this.onFilterChange.bind( this );
|
||||
this.getSelector = this.getSelector.bind( this );
|
||||
this.getAvailableFilterKeys = this.getAvailableFilterKeys.bind( this );
|
||||
this.addFilter = this.addFilter.bind( this );
|
||||
this.removeFilter = this.removeFilter.bind( this );
|
||||
this.clearAllFilters = this.clearAllFilters.bind( this );
|
||||
}
|
||||
|
||||
onMatchChange( value ) {
|
||||
this.setState( {
|
||||
match: find( matches, match => value === match.value ),
|
||||
} );
|
||||
}
|
||||
|
||||
onFilterChange( key, property, value ) {
|
||||
const activeFilters = this.state.activeFilters.map( activeFilter => {
|
||||
if ( key === activeFilter.key ) {
|
||||
return Object.assign( {}, activeFilter, { [ property ]: value } );
|
||||
}
|
||||
return activeFilter;
|
||||
} );
|
||||
|
||||
this.setState( { activeFilters } );
|
||||
}
|
||||
|
||||
removeFilter( key ) {
|
||||
const activeFilters = [ ...this.state.activeFilters ];
|
||||
const index = findIndex( activeFilters, filter => filter.key === key );
|
||||
activeFilters.splice( index, 1 );
|
||||
this.setState( { activeFilters } );
|
||||
}
|
||||
|
||||
getTitle() {
|
||||
const { match } = this.state;
|
||||
const { filterTitle } = this.props;
|
||||
return (
|
||||
<Fragment>
|
||||
<span>{ sprintf( __( '%s Match', 'wc-admin' ), filterTitle ) }</span>
|
||||
<SelectControl
|
||||
className="woocommerce-advanced-filters__title-select"
|
||||
options={ matches }
|
||||
value={ match.value }
|
||||
onChange={ this.onMatchChange }
|
||||
aria-label={ __( 'Match any or all filters', 'wc-admin' ) }
|
||||
/>
|
||||
<span>{ __( 'Filters', 'wc-admin' ) }</span>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
getSelector( filter ) {
|
||||
const filterConfig = this.props.config[ filter.key ];
|
||||
const { input } = filterConfig;
|
||||
if ( ! input ) {
|
||||
return null;
|
||||
}
|
||||
if ( 'SelectControl' === input.component ) {
|
||||
return (
|
||||
<SelectControl
|
||||
className="woocommerce-advanced-filters__list-select"
|
||||
options={ input.options }
|
||||
value={ filter.value }
|
||||
onChange={ partial( this.onFilterChange, filter.key, 'value' ) }
|
||||
aria-label={ sprintf( __( 'Select %s', 'wc-admin' ), filterConfig.label ) }
|
||||
/>
|
||||
);
|
||||
}
|
||||
if ( 'FormTokenField' === input.component ) {
|
||||
return (
|
||||
<FormTokenField
|
||||
value={ filter.value }
|
||||
onChange={ partial( this.onFilterChange, filter.key, 'value' ) }
|
||||
placeholder={ sprintf( __( 'Add %s', 'wc-admin' ), filterConfig.label ) }
|
||||
/>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
getAvailableFilterKeys() {
|
||||
const { config } = this.props;
|
||||
const activeFilterKeys = this.state.activeFilters.map( f => f.key );
|
||||
return difference( Object.keys( config ), activeFilterKeys );
|
||||
}
|
||||
|
||||
addFilter( key, onClose ) {
|
||||
const filterConfig = this.props.config[ key ];
|
||||
const newFilter = { key, rule: filterConfig.rules[ 0 ] };
|
||||
if ( filterConfig.input && filterConfig.input.options ) {
|
||||
newFilter.value = filterConfig.input.options[ 0 ];
|
||||
}
|
||||
this.setState( state => {
|
||||
return {
|
||||
activeFilters: [ ...state.activeFilters, newFilter ],
|
||||
};
|
||||
} );
|
||||
onClose();
|
||||
// after render, focus the newly added filter's first focusable element
|
||||
setTimeout( () => {
|
||||
const addedFilter = this.filterListRef.current.querySelector( 'li:last-of-type fieldset' );
|
||||
addedFilter.focus();
|
||||
} );
|
||||
}
|
||||
|
||||
clearAllFilters() {
|
||||
this.setState( {
|
||||
activeFilters: [],
|
||||
} );
|
||||
}
|
||||
|
||||
render() {
|
||||
const { config } = this.props;
|
||||
const availableFilterKeys = this.getAvailableFilterKeys();
|
||||
return (
|
||||
<Card className="woocommerce-advanced-filters" title={ this.getTitle() }>
|
||||
<ul className="woocommerce-advanced-filters__list" ref={ this.filterListRef }>
|
||||
{ this.state.activeFilters.map( filter => {
|
||||
const { key, rule } = filter;
|
||||
const filterConfig = config[ key ];
|
||||
return (
|
||||
<li className="woocommerce-advanced-filters__list-item" key={ key }>
|
||||
{ /*eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex*/ }
|
||||
<fieldset tabIndex="0">
|
||||
{ /*eslint-enable-next-line jsx-a11y/no-noninteractive-tabindex*/ }
|
||||
<legend className="screen-reader-text">{ filterConfig.label }</legend>
|
||||
<div className="woocommerce-advanced-filters__fieldset">
|
||||
<div className="woocommerce-advanced-filters__fieldset-legend">
|
||||
{ filterConfig.label }
|
||||
</div>
|
||||
<SelectControl
|
||||
className="woocommerce-advanced-filters__list-specifier"
|
||||
options={ filterConfig.rules }
|
||||
value={ rule }
|
||||
onChange={ partial( this.onFilterChange, key, 'rule' ) }
|
||||
aria-label={ sprintf(
|
||||
__( 'Select a %s filter match', 'wc-admin' ),
|
||||
filterConfig.addLabel
|
||||
) }
|
||||
/>
|
||||
<div className="woocommerce-advanced-filters__list-selector">
|
||||
{ this.getSelector( filter ) }
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
<IconButton
|
||||
className="woocommerce-advanced-filters__remove"
|
||||
label={ sprintf( __( 'Remove %s filter', 'wc-admin' ), filterConfig.label ) }
|
||||
onClick={ partial( this.removeFilter, key ) }
|
||||
icon={ <Gridicon icon="cross-small" /> }
|
||||
/>
|
||||
</li>
|
||||
);
|
||||
} ) }
|
||||
</ul>
|
||||
{ availableFilterKeys.length > 0 && (
|
||||
<div className="woocommerce-advanced-filters__add-filter">
|
||||
<Dropdown
|
||||
position="bottom center"
|
||||
renderToggle={ ( { isOpen, onToggle } ) => (
|
||||
<IconButton
|
||||
className="woocommerce-advanced-filters__add-btn"
|
||||
icon={ <Gridicon icon="add-outline" /> }
|
||||
onClick={ onToggle }
|
||||
aria-expanded={ isOpen }
|
||||
>
|
||||
{ __( 'Add a Filter', 'wc-admin' ) }
|
||||
</IconButton>
|
||||
) }
|
||||
renderContent={ ( { onClose } ) => (
|
||||
<ul className="woocommerce-advanced-filters__add-dropdown">
|
||||
{ availableFilterKeys.map( key => (
|
||||
<li key={ key }>
|
||||
<Button onClick={ partial( this.addFilter, key, onClose ) }>
|
||||
{ config[ key ].addLabel }
|
||||
</Button>
|
||||
</li>
|
||||
) ) }
|
||||
</ul>
|
||||
) }
|
||||
/>
|
||||
</div>
|
||||
) }
|
||||
|
||||
<div className="woocommerce-advanced-filters__controls">
|
||||
<Button isPrimary>{ __( 'Filter', 'wc-admin' ) }</Button>
|
||||
<Button isLink onClick={ this.clearAllFilters }>
|
||||
{ __( 'Clear all filters', 'wc-admin' ) }
|
||||
</Button>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
AdvancedFilters.propTypes = {
|
||||
config: PropTypes.object.isRequired,
|
||||
filterTitle: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
export default AdvancedFilters;
|
|
@ -0,0 +1,158 @@
|
|||
/** @format */
|
||||
|
||||
.woocommerce-advanced-filters {
|
||||
margin: $gap-large 0;
|
||||
|
||||
.woocommerce-card__header {
|
||||
padding: $gap-smaller $gap;
|
||||
}
|
||||
|
||||
.woocommerce-card__body {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.components-select-control__input {
|
||||
height: 38px;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.woocommerce-advanced-filters__title-select {
|
||||
width: 70px;
|
||||
display: inline-block;
|
||||
margin: 0 $gap-smaller;
|
||||
}
|
||||
|
||||
.woocommerce-advanced-filters__list {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.woocommerce-advanced-filters__list-item {
|
||||
padding: $gap-smaller $gap;
|
||||
margin: 0;
|
||||
display: grid;
|
||||
grid-template-columns: auto 40px;
|
||||
background-color: $core-grey-light-100;
|
||||
border-bottom: 1px solid $core-grey-light-700;
|
||||
|
||||
&:hover {
|
||||
background-color: $core-grey-light-200;
|
||||
}
|
||||
|
||||
.components-base-control {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.woocommerce-advanced-filters__remove {
|
||||
width: 40px;
|
||||
height: 38px;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.components-form-token-field {
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.woocommerce-advanced-filters__add-filter {
|
||||
padding: $gap-small;
|
||||
margin: 0;
|
||||
color: $woocommerce;
|
||||
display: block;
|
||||
background-color: $core-grey-light-100;
|
||||
border-bottom: 1px solid $core-grey-light-700;
|
||||
|
||||
&:hover {
|
||||
background-color: $core-grey-light-200;
|
||||
}
|
||||
|
||||
div div {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.components-popover:not(.is-mobile) .components-popover__content {
|
||||
min-width: 180px;
|
||||
}
|
||||
}
|
||||
|
||||
.woocommerce-advanced-filters__fieldset {
|
||||
display: grid;
|
||||
grid-template-columns: 100px 150px auto;
|
||||
|
||||
@include breakpoint( '<782px' ) {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
.woocommerce-advanced-filters__fieldset-legend {
|
||||
align-self: center;
|
||||
|
||||
@include breakpoint( '<782px' ) {
|
||||
align-self: initial;
|
||||
padding: $gap-smallest 0;
|
||||
}
|
||||
}
|
||||
|
||||
.woocommerce-advanced-filters__add-btn {
|
||||
color: inherit;
|
||||
padding: $gap-smaller;
|
||||
|
||||
svg {
|
||||
fill: currentColor;
|
||||
}
|
||||
|
||||
&.components-icon-button:not(:disabled):not([aria-disabled='true']):not(.is-default):hover {
|
||||
color: $woocommerce-300;
|
||||
}
|
||||
|
||||
&:not(:disabled):not([aria-disabled='true']):focus {
|
||||
color: $woocommerce;
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
.woocommerce-advanced-filters__controls {
|
||||
padding: $gap-smaller $gap;
|
||||
|
||||
& > button {
|
||||
margin-right: $gap;
|
||||
}
|
||||
}
|
||||
|
||||
.woocommerce-advanced-filters__add-dropdown {
|
||||
padding: $gap-smaller 0;
|
||||
|
||||
li {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.components-button {
|
||||
width: 100%;
|
||||
padding: $gap-smaller;
|
||||
|
||||
&:hover {
|
||||
background-color: $core-grey-light-200;
|
||||
}
|
||||
|
||||
&:not(:disabled):not([aria-disabled='true']):focus {
|
||||
background-color: $core-grey-light-300;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.woocommerce-advanced-filters__list-selector {
|
||||
padding: 0 0 0 $gap-smaller;
|
||||
|
||||
@include breakpoint( '<782px' ) {
|
||||
padding: $gap-smallest 0;
|
||||
}
|
||||
}
|
||||
|
||||
.woocommerce-advanced-filters__list-specifier {
|
||||
@include breakpoint( '<782px' ) {
|
||||
padding: $gap-smallest 0;
|
||||
}
|
||||
}
|
|
@ -39,7 +39,7 @@ Card.propTypes = {
|
|||
menu: PropTypes.shape( {
|
||||
type: PropTypes.oneOf( [ EllipsisMenu ] ),
|
||||
} ),
|
||||
title: PropTypes.string.isRequired,
|
||||
title: PropTypes.oneOfType( [ PropTypes.string, PropTypes.node ] ).isRequired,
|
||||
};
|
||||
|
||||
export default Card;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
Filter Picker
|
||||
===
|
||||
|
||||
Modify a url query parameter via a dropdown selection of configurable options
|
||||
Modify a url query parameter via a dropdown selection of configurable options. This component manipulates the `filter` query parameter.
|
||||
|
||||
## Usage
|
||||
|
||||
|
@ -36,21 +36,12 @@ const renderFilterPicker = ( { path, query } ) {
|
|||
other_fish: [ 'lunch', 'fish' ],
|
||||
};
|
||||
|
||||
const queryParam = 'meal';
|
||||
|
||||
const getQueryParamValue = () => {
|
||||
const { query } = this.props;
|
||||
return return query[ queryParam ] || 'breakfast';
|
||||
}
|
||||
|
||||
return (
|
||||
<FilterPicker
|
||||
query={ query }
|
||||
path={ path }
|
||||
filters={ filters }
|
||||
filterPaths={ filterPaths }
|
||||
queryParam={ queryParam }
|
||||
getQueryParamValue={ getQueryParamValue }
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -62,5 +53,3 @@ const renderFilterPicker = ( { path, query } ) {
|
|||
* `path` (required): Parameter supplied by React-Router
|
||||
* `filters` (required): An array of filters and subFilters to construct the menu
|
||||
* `filterPaths` (required): A map of representing the structure of the tree. Required for faster lookups than searches
|
||||
* `queryParam` (required): The query parameter to update
|
||||
* `getQueryParamValue` (required): A function used to obtain the current value represented in the url
|
||||
|
|
|
@ -18,13 +18,16 @@ import AnimationSlider from 'components/animation-slider';
|
|||
import Link from 'components/link';
|
||||
import './style.scss';
|
||||
|
||||
export const FILTER_PARAM = 'filter';
|
||||
export const DEFAULT_FILTER_PARAM = 'all';
|
||||
|
||||
class FilterPicker extends Component {
|
||||
constructor( props ) {
|
||||
super( props );
|
||||
|
||||
const { filterPaths, getQueryParamValue } = props;
|
||||
const { filterPaths, query } = props;
|
||||
this.state = {
|
||||
nav: filterPaths[ getQueryParamValue() ],
|
||||
nav: filterPaths[ this.getQueryParamValue( query ) ],
|
||||
animate: null,
|
||||
};
|
||||
|
||||
|
@ -36,24 +39,27 @@ class FilterPicker extends Component {
|
|||
this.goBack = this.goBack.bind( this );
|
||||
}
|
||||
|
||||
getQueryParamValue( query ) {
|
||||
return query[ FILTER_PARAM ] || DEFAULT_FILTER_PARAM;
|
||||
}
|
||||
|
||||
getOtherQueries( query ) {
|
||||
const { queryParam } = this.props;
|
||||
return omit( query, queryParam );
|
||||
return omit( query, FILTER_PARAM );
|
||||
}
|
||||
|
||||
getSelectionPath( filter ) {
|
||||
const { path, query, queryParam } = this.props;
|
||||
const { path, query } = this.props;
|
||||
const otherQueries = this.getOtherQueries( query );
|
||||
const data = {
|
||||
[ queryParam ]: filter.value,
|
||||
[ FILTER_PARAM ]: filter.value,
|
||||
};
|
||||
const queryString = stringifyQueryObject( Object.assign( otherQueries, data ) );
|
||||
return `${ path }?${ queryString }`;
|
||||
}
|
||||
|
||||
getSelectedFilter() {
|
||||
const { filters, getQueryParamValue, filterPaths } = this.props;
|
||||
const value = getQueryParamValue();
|
||||
const { filters, filterPaths, query } = this.props;
|
||||
const value = this.getQueryParamValue( query );
|
||||
const filterPath = filterPaths[ value ];
|
||||
const visibleFilters = this.getVisibleFilters( filters, [ ...filterPath ] );
|
||||
return find( visibleFilters, filter => filter.value === value );
|
||||
|
@ -176,8 +182,6 @@ FilterPicker.propTypes = {
|
|||
query: PropTypes.object.isRequired,
|
||||
filters: PropTypes.array.isRequired,
|
||||
filterPaths: PropTypes.object.isRequired,
|
||||
queryParam: PropTypes.string.isRequired,
|
||||
getQueryParamValue: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default FilterPicker;
|
||||
|
|
Loading…
Reference in New Issue