Merge pull request woocommerce/woocommerce-admin#287 from woocommerce/add/report-filter

Add new ReportFilters component
This commit is contained in:
Paul Sealock 2018-08-15 11:22:47 +12:00 committed by GitHub
commit 07c1bae2a0
24 changed files with 383 additions and 297 deletions

View File

@ -13,12 +13,10 @@ import { partial } from 'lodash';
/**
* Internal dependencies
*/
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 Header from 'layout/header/index';
import { ReportFilters } from 'components/filters';
import './style.scss';
class OrdersReport extends Component {
@ -46,21 +44,14 @@ 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' ) }
/>
) }
<ReportFilters
query={ query }
path={ path }
filters={ filters }
filterPaths={ filterPaths }
advancedConfig={ advancedFilterConfig }
/>
<p>Below is a temporary example</p>
<Card title="Orders">
<table style={ { width: '100%' } }>

View File

@ -1,17 +1 @@
/** @format */
.woocommerce-orders__pickers {
display: flex;
> div {
margin-right: $gap-large;
}
@include breakpoint( '<1100px' ) {
flex-direction: column;
> div {
margin-right: 0;
}
}
}

View File

@ -8,10 +8,9 @@ import { Component, Fragment } from '@wordpress/element';
/**
* Internal dependencies
*/
import { filterPaths, filters } from './constants';
import Header from 'layout/header';
import DatePicker from 'components/date-picker';
import FilterPicker from 'components/filter-picker';
import { filters, filterPaths } from './constants';
import { ReportFilters } from 'components/filters';
import './style.scss';
export default class extends Component {
@ -26,15 +25,12 @@ export default class extends Component {
__( 'Products', 'wc-admin' ),
] }
/>
<div className="woocommerce-products__pickers">
<DatePicker query={ query } path={ path } key={ JSON.stringify( query ) } />
<FilterPicker
query={ query }
path={ path }
filters={ filters }
filterPaths={ filterPaths }
/>
</div>
<ReportFilters
query={ query }
path={ path }
filters={ filters }
filterPaths={ filterPaths }
/>
</Fragment>
);
}

View File

@ -1,17 +1 @@
/** @format */
.woocommerce-products__pickers {
display: flex;
> div {
margin-right: $gap-large;
}
@include breakpoint( '<1100px' ) {
flex-direction: column;
> div {
margin-right: 0;
}
}
}

View File

@ -11,11 +11,11 @@ import PropTypes from 'prop-types';
/**
* Internal dependencies
*/
import DatePicker from 'components/date-picker';
import { formatCurrency, getCurrencyFormatDecimal } from 'lib/currency';
import { getAdminLink, updateQueryString } from 'lib/nav-utils';
import { getReportData } from 'lib/swagger';
import Header from 'layout/header';
import { ReportFilters } from 'components/filters';
import { SummaryList, SummaryNumber } from 'components/summary';
import { TableCard } from 'components/table';
@ -227,7 +227,7 @@ class RevenueReport extends Component {
__( 'Revenue', 'wc-admin' ),
] }
/>
<DatePicker query={ query } path={ path } key={ JSON.stringify( query ) } />
<ReportFilters query={ query } path={ path } />
<SummaryList>
<SummaryNumber

View File

@ -0,0 +1,20 @@
ReportFilters
=============
Add a collection of report filters to a page. This uses `DatePicker` & `FilterPicker` for the "basic" filters, and `AdvancedFilters` or a comparison card if "advanced" or "compare" are picked from `FilterPicker`.
## Usage
```jsx
// For just DatePicker…
<ReportFilters />
// To add FilterPicker too, pass through filter config:
<ReportFilters
filters={ filters }
filterPaths={ filterPaths } />
```
- `advancedConfig`: Config option passed through to `AdvancedFilters`
- `filters`: Config option passed through to `FilterPicker` - if not used, `FilterPicker` is not displayed.
- `filterPaths`: Config option passed through to `FilterPicker`

View File

@ -75,7 +75,7 @@ class AdvancedFilters extends Component {
<Fragment>
<span>{ sprintf( __( '%s Match', 'wc-admin' ), filterTitle ) }</span>
<SelectControl
className="woocommerce-advanced-filters__title-select"
className="woocommerce-filters-advanced__title-select"
options={ matches }
value={ match.value }
onChange={ this.onMatchChange }
@ -95,7 +95,7 @@ class AdvancedFilters extends Component {
if ( 'SelectControl' === input.component ) {
return (
<SelectControl
className="woocommerce-advanced-filters__list-select"
className="woocommerce-filters-advanced__list-select"
options={ input.options }
value={ filter.value }
onChange={ partial( this.onFilterChange, filter.key, 'value' ) }
@ -150,23 +150,23 @@ class AdvancedFilters extends Component {
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 }>
<Card className="woocommerce-filters-advanced" title={ this.getTitle() }>
<ul className="woocommerce-filters-advanced__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 }>
<li className="woocommerce-filters-advanced__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">
<div className="woocommerce-filters-advanced__fieldset">
<div className="woocommerce-filters-advanced__fieldset-legend">
{ filterConfig.label }
</div>
<SelectControl
className="woocommerce-advanced-filters__list-specifier"
className="woocommerce-filters-advanced__list-specifier"
options={ filterConfig.rules }
value={ rule }
onChange={ partial( this.onFilterChange, key, 'rule' ) }
@ -175,13 +175,13 @@ class AdvancedFilters extends Component {
filterConfig.addLabel
) }
/>
<div className="woocommerce-advanced-filters__list-selector">
<div className="woocommerce-filters-advanced__list-selector">
{ this.getSelector( filter ) }
</div>
</div>
</fieldset>
<IconButton
className="woocommerce-advanced-filters__remove"
className="woocommerce-filters-advanced__remove"
label={ sprintf( __( 'Remove %s filter', 'wc-admin' ), filterConfig.label ) }
onClick={ partial( this.removeFilter, key ) }
icon={ <Gridicon icon="cross-small" /> }
@ -191,12 +191,12 @@ class AdvancedFilters extends Component {
} ) }
</ul>
{ availableFilterKeys.length > 0 && (
<div className="woocommerce-advanced-filters__add-filter">
<div className="woocommerce-filters-advanced__add-filter">
<Dropdown
position="bottom center"
renderToggle={ ( { isOpen, onToggle } ) => (
<IconButton
className="woocommerce-advanced-filters__add-btn"
className="woocommerce-filters-advanced__add-button"
icon={ <Gridicon icon="add-outline" /> }
onClick={ onToggle }
aria-expanded={ isOpen }
@ -205,7 +205,7 @@ class AdvancedFilters extends Component {
</IconButton>
) }
renderContent={ ( { onClose } ) => (
<ul className="woocommerce-advanced-filters__add-dropdown">
<ul className="woocommerce-filters-advanced__add-dropdown">
{ availableFilterKeys.map( key => (
<li key={ key }>
<Button onClick={ partial( this.addFilter, key, onClose ) }>
@ -219,7 +219,7 @@ class AdvancedFilters extends Component {
</div>
) }
<div className="woocommerce-advanced-filters__controls">
<div className="woocommerce-filters-advanced__controls">
<Button isPrimary>{ __( 'Filter', 'wc-admin' ) }</Button>
<Button isLink onClick={ this.clearAllFilters }>
{ __( 'Clear all filters', 'wc-admin' ) }

View File

@ -1,6 +1,6 @@
/** @format */
.woocommerce-advanced-filters {
.woocommerce-filters-advanced {
margin: $gap-large 0;
.woocommerce-card__header {
@ -18,17 +18,17 @@
}
}
.woocommerce-advanced-filters__title-select {
.woocommerce-filters-advanced__title-select {
width: 70px;
display: inline-block;
margin: 0 $gap-smaller;
}
.woocommerce-advanced-filters__list {
.woocommerce-filters-advanced__list {
margin: 0;
}
.woocommerce-advanced-filters__list-item {
.woocommerce-filters-advanced__list-item {
padding: $gap-smaller $gap;
margin: 0;
display: grid;
@ -44,7 +44,7 @@
margin: 0;
}
.woocommerce-advanced-filters__remove {
.woocommerce-filters-advanced__remove {
width: 40px;
height: 38px;
align-self: center;
@ -55,7 +55,7 @@
}
}
.woocommerce-advanced-filters__add-filter {
.woocommerce-filters-advanced__add-filter {
padding: $gap-small;
margin: 0;
color: $woocommerce;
@ -76,7 +76,7 @@
}
}
.woocommerce-advanced-filters__fieldset {
.woocommerce-filters-advanced__fieldset {
display: grid;
grid-template-columns: 100px 150px auto;
@ -86,7 +86,7 @@
}
}
.woocommerce-advanced-filters__fieldset-legend {
.woocommerce-filters-advanced__fieldset-legend {
align-self: center;
@include breakpoint( '<782px' ) {
@ -95,7 +95,7 @@
}
}
.woocommerce-advanced-filters__add-btn {
.woocommerce-filters-advanced__add-button {
color: inherit;
padding: $gap-smaller;
@ -113,7 +113,7 @@
}
}
.woocommerce-advanced-filters__controls {
.woocommerce-filters-advanced__controls {
padding: $gap-smaller $gap;
& > button {
@ -121,7 +121,7 @@
}
}
.woocommerce-advanced-filters__add-dropdown {
.woocommerce-filters-advanced__add-dropdown {
padding: $gap-smaller 0;
li {
@ -143,7 +143,7 @@
}
}
.woocommerce-advanced-filters__list-selector {
.woocommerce-filters-advanced__list-selector {
padding: 0 0 0 $gap-smaller;
@include breakpoint( '<782px' ) {
@ -151,7 +151,7 @@
}
}
.woocommerce-advanced-filters__list-specifier {
.woocommerce-filters-advanced__list-specifier {
@include breakpoint( '<782px' ) {
padding: $gap-smallest 0;
}

View File

@ -6,7 +6,7 @@ Select a range of dates or single dates
## Usage
```jsx
<DatePicker query={ query } path={ path } key={ JSON.stringify( query ) } />
<DatePicker key={ JSON.stringify( query ) } />
```
### Props
@ -15,18 +15,15 @@ Required props are marked with `*`.
Name | Type | Default | Description
--- | --- | --- | ---
`query`* | `object` | none | The query string represented in object form
`path`* | `string` | none | `path` parameter supplied by React-Router
`key`| `*` | none | Force a recalculation or reset of internal state when this key changes. Useful for a url change, for instance
## URL as the source of truth
The Date Picker reads parameters from the URL querystring and updates them by creating a link to reflect newly selected parameters, which is rendered as the "Update" button.
URL Parameter | Default | Possible Values
--- | --- | ---
`period` | `today` | `today`, `yesterday`, `week`, `last_week`, `month`, `last_month`, `quarter`, `last_quarter`, `year`, `last_year`, `custom`
`compare` | `previous_period` | `previous_period`, `previous_year`
`start` | none | start date for custom periods `2018-04-15`. [ISO 8601 format](https://en.wikipedia.org/wiki/ISO_8601)
`end` | none | end date for custom periods `2018-04-15`. [ISO 8601 format](https://en.wikipedia.org/wiki/ISO_8601)

View File

@ -10,18 +10,14 @@ import PropTypes from 'prop-types';
* Internal dependencies
*/
import SegmentedSelection from 'components/segmented-selection';
const compareValues = [
{ value: 'previous_period', label: __( 'Previous Period', 'wc-admin' ) },
{ value: 'previous_year', label: __( 'Previous Year', 'wc-admin' ) },
];
import { periods } from 'lib/date';
class ComparePeriods extends Component {
render() {
const { onSelect, compare } = this.props;
return (
<SegmentedSelection
options={ compareValues }
options={ periods }
selected={ compare }
onSelect={ onSelect }
name="compare"
@ -36,5 +32,4 @@ ComparePeriods.propTypes = {
compare: PropTypes.string,
};
export { compareValues };
export default ComparePeriods;

View File

@ -12,10 +12,10 @@ import classnames from 'classnames';
* Internal dependencies
*/
import ComparePeriods from './compare-periods';
import { H, Section } from 'layout/section';
import PresetPeriods from './preset-periods';
import Link from 'components/link';
import { DateRange } from 'components/calendar';
import { H, Section } from 'layout/section';
import Link from 'components/link';
import PresetPeriods from './preset-periods';
const isMobileViewport = () => window.innerWidth < 782;
@ -61,7 +61,7 @@ class DatePickerContent extends Component {
{ __( 'Select date range and comparison', 'wc-admin' ) }
</H>
<Section component={ false }>
<H className="woocommerce-date-picker__text">
<H className="woocommerce-filters-date__text">
{ __( 'select a date range', 'wc-admin' ) }
</H>
<TabPanel
@ -69,15 +69,15 @@ class DatePickerContent extends Component {
{
name: 'period',
title: __( 'Presets', 'wc-admin' ),
className: 'woocommerce-date-picker__tab',
className: 'woocommerce-filters-date__tab',
},
{
name: 'custom',
title: __( 'Custom', 'wc-admin' ),
className: 'woocommerce-date-picker__tab',
className: 'woocommerce-filters-date__tab',
},
] }
className="woocommerce-date-picker__tabs"
className="woocommerce-filters-date__tabs"
activeClass="is-active"
initialTabName={
'custom' === period
@ -106,19 +106,19 @@ class DatePickerContent extends Component {
/>
) }
<div
className={ classnames( 'woocommerce-date-picker__content-controls', {
className={ classnames( 'woocommerce-filters-date__content-controls', {
'is-sticky-bottom': selectedTab === 'custom' && isMobileViewport(),
'is-custom': selectedTab === 'custom',
} ) }
>
<H className="woocommerce-date-picker__text">
<H className="woocommerce-filters-date__text">
{ __( 'compare to', 'wc-admin' ) }
</H>
<ComparePeriods onSelect={ onUpdate } compare={ compare } />
<div className="woocommerce-date-picker__content-controls-btns">
<div className="woocommerce-filters-date__button-group">
{ selectedTab === 'custom' && (
<Button
className="woocommerce-date-picker__content-controls-btn"
className="woocommerce-filters-date__button"
isPrimary
onClick={ resetCustomValues }
disabled={ ! ( after || before ) }
@ -128,20 +128,14 @@ class DatePickerContent extends Component {
) }
{ isValidSelection( selectedTab ) ? (
<Link
/* eslint-disable max-len */
className="woocommerce-date-picker__content-controls-btn components-button is-button is-primary"
/* eslint-enable max-len */
className="woocommerce-filters-date__button components-button is-button is-primary"
href={ getUpdatePath( selectedTab ) }
onClick={ onClose }
>
{ __( 'Update', 'wc-admin' ) }
</Link>
) : (
<Button
className="woocommerce-date-picker__content-controls-btn"
isPrimary
disabled
>
<Button className="woocommerce-filters-date__button" isPrimary disabled>
{ __( 'Update', 'wc-admin' ) }
</Button>
) }

View File

@ -5,23 +5,22 @@
import { Component } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import { Dropdown } from '@wordpress/components';
import { stringify as stringifyQueryObject } from 'qs';
import PropTypes from 'prop-types';
/**
* Internal dependencies
*/
import './style.scss';
import DropdownButton from 'components/dropdown-button';
import DatePickerContent from './content';
import DropdownButton from 'components/dropdown-button';
import { getCurrentDates, getDateParamsFromQuery, isoDateFormat } from 'lib/date';
import { getNewPath, getQuery } from 'lib/nav-utils';
import './style.scss';
const shortDateFormat = __( 'MM/DD/YYYY', 'wc-admin' );
class DatePicker extends Component {
constructor( props ) {
super( props );
this.state = this.getResetState( props );
this.state = this.getResetState();
this.update = this.update.bind( this );
this.getUpdatePath = this.getUpdatePath.bind( this );
@ -29,8 +28,8 @@ class DatePicker extends Component {
this.resetCustomValues = this.resetCustomValues.bind( this );
}
getResetState( props ) {
const { period, compare, before, after } = getDateParamsFromQuery( props.query );
getResetState() {
const { period, compare, before, after } = getDateParamsFromQuery( getQuery() );
return {
period,
compare,
@ -48,31 +47,21 @@ class DatePicker extends Component {
this.setState( update );
}
getOtherQueries( query ) {
const { period, compare, after, before, ...otherQueries } = query; // eslint-disable-line no-unused-vars
return otherQueries;
}
getUpdatePath( selectedTab ) {
const { path, query } = this.props;
const otherQueries = this.getOtherQueries( query );
const { period, compare, after, before } = this.state;
const data = {
period: 'custom' === selectedTab ? 'custom' : period,
compare,
};
if ( 'custom' === selectedTab ) {
Object.assign( data, {
after: after ? after.format( isoDateFormat ) : '',
before: before ? before.format( isoDateFormat ) : '',
} );
data.after = after ? after.format( isoDateFormat ) : '';
data.before = before ? before.format( isoDateFormat ) : '';
}
const queryString = stringifyQueryObject( Object.assign( otherQueries, data ) );
return `${ path }?${ queryString }`;
return getNewPath( data );
}
getButtonLabel() {
const { primary, secondary } = getCurrentDates( this.props.query );
const { primary, secondary } = getCurrentDates( getQuery() );
return [
`${ primary.label } (${ primary.range })`,
`${ __( 'vs.', 'wc-admin' ) } ${ secondary.label } (${ secondary.range })`,
@ -112,10 +101,10 @@ class DatePicker extends Component {
beforeError,
} = this.state;
return (
<div className="woocommerce-date-picker">
<div className="woocommerce-filters-date">
<p>{ __( 'Date Range', 'wc-admin' ) }:</p>
<Dropdown
contentClassName="woocommerce-date-picker__content"
contentClassName="woocommerce-filters-date__content"
position="bottom"
expandOnMobile
renderToggle={ ( { isOpen, onToggle } ) => (
@ -150,9 +139,4 @@ class DatePicker extends Component {
}
}
DatePicker.propTypes = {
path: PropTypes.string.isRequired,
query: PropTypes.object.isRequired,
};
export default DatePicker;

View File

@ -11,20 +11,7 @@ import PropTypes from 'prop-types';
* Internal dependencies
*/
import SegmentedSelection from 'components/segmented-selection';
const presetValues = [
{ value: 'today', label: __( 'Today', 'wc-admin' ) },
{ value: 'yesterday', label: __( 'Yesterday', 'wc-admin' ) },
{ value: 'week', label: __( 'Week to Date', 'wc-admin' ) },
{ value: 'last_week', label: __( 'Last Week', 'wc-admin' ) },
{ value: 'month', label: __( 'Month to Date', 'wc-admin' ) },
{ value: 'last_month', label: __( 'Last Month', 'wc-admin' ) },
{ value: 'quarter', label: __( 'Quarter to Date', 'wc-admin' ) },
{ value: 'last_quarter', label: __( 'Last Quarter', 'wc-admin' ) },
{ value: 'year', label: __( 'Year to Date', 'wc-admin' ) },
{ value: 'last_year', label: __( 'Last Year', 'wc-admin' ) },
{ value: 'custom', label: __( 'Custom', 'wc-admin' ) },
];
import { presetValues } from 'lib/date';
class PresetPeriods extends Component {
render() {
@ -46,5 +33,4 @@ PresetPeriods.propTypes = {
period: PropTypes.string,
};
export { presetValues };
export default PresetPeriods;

View File

@ -1,28 +1,7 @@
/** @format */
.woocommerce-date-picker {
width: 33.3%;
@include breakpoint( '<1100px' ) {
width: 50%;
}
@include breakpoint( '<782px' ) {
width: 100%;
}
}
.woocommerce-date-picker__content {
> .components-popover__content {
width: 320px;
}
.woocommerce-filters-date__content {
&.is-mobile {
& > .components-popover__content {
width: 100%;
height: 100%;
}
.components-popover__header {
border: none;
height: 0;
@ -38,7 +17,7 @@
}
}
.woocommerce-date-picker__tabs {
.woocommerce-filters-date__tabs {
height: calc(100% - 42px);
.components-tab-panel__tabs {
@ -55,7 +34,7 @@
}
}
.woocommerce-date-picker__tab {
.woocommerce-filters-date__tab {
outline: none;
border: 1px solid $woocommerce;
padding: 10px;
@ -77,7 +56,7 @@
}
}
.woocommerce-date-picker__text {
.woocommerce-filters-date__text {
@include font-size( 12 );
font-weight: 100;
text-transform: uppercase;
@ -89,7 +68,7 @@
background-color: $white;
}
.woocommerce-date-picker__content-controls {
.woocommerce-filters-date__content-controls {
display: flex;
flex-direction: column;
width: 100%;
@ -107,14 +86,14 @@
}
}
.woocommerce-date-picker__content-controls-btns {
.woocommerce-filters-date__button-group {
padding-top: 1em;
display: flex;
justify-content: center;
width: 100%;
}
.woocommerce-date-picker__content-controls-btn {
.woocommerce-filters-date__button {
justify-content: center;
width: 50%;
margin: 0 1em;

View File

@ -8,7 +8,7 @@ Modify a url query parameter via a dropdown selection of configurable options. T
```jsx
import FilterPicker from 'components/filter-picker';
const renderFilterPicker = ( { path, query } ) {
const renderFilterPicker = () => {
const filters = [
{ label: 'Breakfast', value: 'breakfast' },
{ label: 'Lunch', value: 'lunch', subFilters: [
@ -23,7 +23,7 @@ const renderFilterPicker = ( { path, query } ) {
] },
{ label: 'Dinner', value: 'dinner' },
];
const filterPaths = {
breakfast: [],
lunch: [],
@ -35,11 +35,9 @@ const renderFilterPicker = ( { path, query } ) {
cod: [ 'lunch', 'fish' ],
other_fish: [ 'lunch', 'fish' ],
};
return (
<FilterPicker
query={ query }
path={ path }
filters={ filters }
filterPaths={ filterPaths }
/>
@ -49,7 +47,5 @@ const renderFilterPicker = ( { path, query } ) {
### Props
* `query` (required): The query string represented in object form
* `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

View File

@ -5,61 +5,50 @@
import { __ } from '@wordpress/i18n';
import { Component, Fragment } from '@wordpress/element';
import { Dropdown, Button, Dashicon } from '@wordpress/components';
import { stringify as stringifyQueryObject } from 'qs';
import { omit, find, partial } from 'lodash';
import { find, partial } from 'lodash';
import classnames from 'classnames';
import PropTypes from 'prop-types';
/**
* Internal dependencies
*/
import DropdownButton from 'components/dropdown-button';
import AnimationSlider from 'components/animation-slider';
import DropdownButton from 'components/dropdown-button';
import { getNewPath, getQuery } from 'lib/nav-utils';
import Link from 'components/link';
import './style.scss';
export const FILTER_PARAM = 'filter';
export const DEFAULT_FILTER_PARAM = 'all';
export const DEFAULT_FILTER = 'all';
class FilterPicker extends Component {
constructor( props ) {
super( props );
const { filterPaths, query } = props;
const { filterPaths } = props;
this.state = {
nav: filterPaths[ this.getQueryParamValue( query ) ],
nav: filterPaths[ this.getFilterValue() ],
animate: null,
};
this.getSelectionPath = this.getSelectionPath.bind( this );
this.getOtherQueries = this.getOtherQueries.bind( this );
this.getSelectedFilter = this.getSelectedFilter.bind( this );
this.selectSubFilters = this.selectSubFilters.bind( this );
this.getVisibleFilters = this.getVisibleFilters.bind( this );
this.goBack = this.goBack.bind( this );
}
getQueryParamValue( query ) {
return query[ FILTER_PARAM ] || DEFAULT_FILTER_PARAM;
}
getOtherQueries( query ) {
return omit( query, FILTER_PARAM );
getFilterValue() {
const query = getQuery();
return query.filter || DEFAULT_FILTER;
}
getSelectionPath( filter ) {
const { path, query } = this.props;
const otherQueries = this.getOtherQueries( query );
const data = {
[ FILTER_PARAM ]: filter.value,
};
const queryString = stringifyQueryObject( Object.assign( otherQueries, data ) );
return `${ path }?${ queryString }`;
return getNewPath( { filter: filter.value } );
}
getSelectedFilter() {
const { filters, filterPaths, query } = this.props;
const value = this.getQueryParamValue( query );
const { filters, filterPaths } = this.props;
const value = this.getFilterValue();
const filterPath = filterPaths[ value ];
const visibleFilters = this.getVisibleFilters( filters, [ ...filterPath ] );
return find( visibleFilters, filter => filter.value === value );
@ -95,7 +84,7 @@ class FilterPicker extends Component {
if ( filter.subFilters ) {
return (
<Button
className="woocommerce-filter-picker__content-list-item-btn"
className="woocommerce-filters-filter__button"
onClick={ partial( this.selectSubFilters, filter.value ) }
>
{ filter.label }
@ -107,7 +96,7 @@ class FilterPicker extends Component {
return (
<Fragment>
<Button
className="woocommerce-filter-picker__content-list-item-btn has-parent-nav"
className="woocommerce-filters-filter__button has-parent-nav"
onClick={ this.goBack }
>
<Dashicon icon="arrow-left-alt2" />
@ -124,7 +113,7 @@ class FilterPicker extends Component {
return (
<Link
className="woocommerce-filter-picker__content-list-item-btn components-button"
className="woocommerce-filters-filter__button components-button"
href={ this.getSelectionPath( filter ) }
onClick={ onClose }
>
@ -139,10 +128,10 @@ class FilterPicker extends Component {
const visibleFilters = this.getVisibleFilters( filters, [ ...nav ] );
const selectedFilter = this.getSelectedFilter();
return (
<div className="woocommerce-filter-picker">
<div className="woocommerce-filters-filter">
<p>{ __( 'Show', 'wc-admin' ) }:</p>
<Dropdown
contentClassName="woocommerce-filter-picker__content"
contentClassName="woocommerce-filters-filter__content"
position="bottom"
expandOnMobile
headerTitle={ __( 'filter report to show:', 'wc-admin' ) }
@ -156,11 +145,11 @@ class FilterPicker extends Component {
renderContent={ ( { onClose } ) => (
<AnimationSlider animationKey={ nav } animate={ animate } focusOnChange>
{ () => (
<ul className="woocommerce-filter-picker__content-list">
<ul className="woocommerce-filters-filter__content-list">
{ visibleFilters.map( filter => (
<li
key={ filter.value }
className={ classnames( 'woocommerce-filter-picker__content-list-item', {
className={ classnames( 'woocommerce-filters-filter__content-list-item', {
'is-selected': selectedFilter.value === filter.value,
} ) }
>
@ -178,8 +167,6 @@ class FilterPicker extends Component {
}
FilterPicker.propTypes = {
path: PropTypes.string.isRequired,
query: PropTypes.object.isRequired,
filters: PropTypes.array.isRequired,
filterPaths: PropTypes.object.isRequired,
};

View File

@ -1,30 +1,7 @@
/** @format */
.woocommerce-filter-picker {
width: 33.3%;
@include breakpoint( '<1100px' ) {
width: 50%;
}
@include breakpoint( '<782px' ) {
width: 100%;
}
}
.woocommerce-filter-picker__content {
.components-popover__content {
width: 320px;
border: 1px solid $core-grey-light-700;
background-color: $white;
}
.woocommerce-filters-filter__content {
&.is-mobile {
.components-popover__content {
width: 100%;
height: 100%;
}
.components-popover__header-title {
@include font-size( 12 );
font-weight: 100;
@ -33,20 +10,19 @@
color: $core-grey-dark-500;
}
.woocommerce-filter-picker__content-list
.woocommerce-filter-picker__content-list-item:last-child {
.woocommerce-filters-filter__content-list-item:last-child {
border-bottom: 1px solid $core-grey-light-700;
}
}
}
.woocommerce-filter-picker__content-list {
.woocommerce-filters-filter__content-list {
margin: 0;
width: 100%;
min-width: 100%;
}
.woocommerce-filter-picker__content-list-item {
.woocommerce-filters-filter__content-list-item {
border-bottom: 1px solid $core-grey-light-700;
margin: 0;
@ -55,7 +31,7 @@
}
&.is-selected {
.woocommerce-filter-picker__content-list-item-btn {
.woocommerce-filters-filter__button {
background-color: $white;
&.components-button:not(:disabled):not([aria-disabled='true']):focus {
@ -75,7 +51,7 @@
}
}
.woocommerce-filter-picker__content-list-item-btn {
.woocommerce-filters-filter__button {
position: relative;
display: block;
width: 100%;

View File

@ -0,0 +1,82 @@
/** @format */
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { Button } from '@wordpress/components';
import { Fragment } from '@wordpress/element';
import { noop } from 'lodash';
import PropTypes from 'prop-types';
/**
* Internal dependencies
*/
import AdvancedFilters from './advanced';
import Card from 'components/card';
import DatePicker from './date';
import FilterPicker from './filter';
import { getQuery } from 'lib/nav-utils';
import { H, Section } from 'layout/section';
import './style.scss';
const ReportFilters = ( { advancedConfig, filters, filterPaths } ) => {
const query = getQuery();
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>
);
break;
case 'advanced':
advancedCard = (
<AdvancedFilters config={ advancedConfig } filterTitle={ __( 'Orders', 'wc-admin' ) } />
);
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 ) } />
{ !! filters.length && <FilterPicker filters={ filters } filterPaths={ filterPaths } /> }
</div>
{ false !== advancedCard && (
<div className="woocommerce-filters__advanced-filters">{ advancedCard }</div>
) }
</Section>
</Fragment>
);
};
ReportFilters.propTypes = {
advancedConfig: PropTypes.object,
filters: PropTypes.array,
filterPaths: PropTypes.object,
};
ReportFilters.defaultProps = {
advancedConfig: {},
filters: [],
filterPaths: {},
};
export { ReportFilters };
export { AdvancedFilters };
export { DatePicker };
export { FilterPicker };

View File

@ -0,0 +1,63 @@
/** @format */
.woocommerce-filters {
.woocommerce-filters__basic-filters {
display: flex;
margin-bottom: $gap-large;
> div {
margin-right: $gap-large;
}
@include breakpoint( '<1100px' ) {
flex-direction: column;
> div {
margin-right: 0;
}
}
}
}
.woocommerce-filters-date,
.woocommerce-filters-filter {
width: 33.3%;
@include breakpoint( '<1100px' ) {
width: 50%;
}
@include breakpoint( '<782px' ) {
width: 100%;
}
}
.woocommerce-filters-date__content,
.woocommerce-filters-filter__content {
.components-popover__content {
width: 320px;
border: 1px solid $core-grey-light-700;
background-color: $white;
}
&.is-mobile {
.components-popover__content {
width: 100%;
height: 100%;
}
}
}
.woocommerce-filters__compare .woocommerce-card__body {
padding: 0;
}
.woocommerce-filters__compare-body {
padding: $gap;
background-color: $core-grey-light-100;
border-bottom: 1px solid $core-grey-light-500;
}
.woocommerce-filters__compare-footer {
padding: $gap;
}

View File

@ -7,12 +7,6 @@ import { find } from 'lodash';
import { __ } from '@wordpress/i18n';
import { getSettings } from '@wordpress/date';
/**
* Internal dependencies
*/
import { presetValues } from 'components/date-picker/preset-periods';
import { compareValues } from 'components/date-picker/compare-periods';
/**
* DateValue Object
*
@ -35,6 +29,25 @@ import { compareValues } from 'components/date-picker/compare-periods';
export const isoDateFormat = 'YYYY-MM-DD';
export const presetValues = [
{ value: 'today', label: __( 'Today', 'wc-admin' ) },
{ value: 'yesterday', label: __( 'Yesterday', 'wc-admin' ) },
{ value: 'week', label: __( 'Week to Date', 'wc-admin' ) },
{ value: 'last_week', label: __( 'Last Week', 'wc-admin' ) },
{ value: 'month', label: __( 'Month to Date', 'wc-admin' ) },
{ value: 'last_month', label: __( 'Last Month', 'wc-admin' ) },
{ value: 'quarter', label: __( 'Quarter to Date', 'wc-admin' ) },
{ value: 'last_quarter', label: __( 'Last Quarter', 'wc-admin' ) },
{ value: 'year', label: __( 'Year to Date', 'wc-admin' ) },
{ value: 'last_year', label: __( 'Last Year', 'wc-admin' ) },
{ value: 'custom', label: __( 'Custom', 'wc-admin' ) },
];
export const periods = [
{ value: 'previous_period', label: __( 'Previous Period', 'wc-admin' ) },
{ value: 'previous_year', label: __( 'Previous Year', 'wc-admin' ) },
];
/**
* Convert a string to Moment object
*
@ -262,7 +275,7 @@ export const getCurrentDates = query => {
before: primaryEnd,
},
secondary: {
label: find( compareValues, item => item.value === compare ).label,
label: find( periods, item => item.value === compare ).label,
range: getRangeLabel( secondaryStart, secondaryEnd ),
after: secondaryStart,
before: secondaryEnd,

View File

@ -1,27 +0,0 @@
/** @format */
/**
* External dependencies
*/
import history from './history';
import { parse, stringify as stringifyQueryObject } from 'qs';
/* Returns a string with the site's wp-admin URL appended. JS version of `admin_url`.
*
* @param {String} path Relative path.
* @return {String} Full admin URL.
*/
export const getAdminLink = path => {
return wcSettings.adminUrl + path;
};
/* Updates the query parameters of the current page.
*
* @param {Object} Query parameters to be updated.
*/
export const updateQueryString = query => {
const path = history.location.pathname;
const currentQuery = parse( history.location.search.substring( 1 ) );
const queryString = stringifyQueryObject( Object.assign( currentQuery, query ) );
history.push( `${ path }?${ queryString }` );
};

View File

@ -0,0 +1,24 @@
Nav Utils
=========
This is a library of functions used in navigation.
## `getPath()`
Get the current path from history.
## `getQuery()`
Get the current query string, parsed into an object, from history.
## `getAdminLink( string: path )`
JS version of `admin_url`. Returns the full URL for a page in wp-admin.
## `getNewPath( object: query, string: path, object: currentQuery )`
Return a URL with set query parameters. Optional `path`, `currentQuery`, both will default to the current value fetched from `history` if not provided.
## `updateQueryString( object: query )`
Updates the query parameters of the current page.

View File

@ -0,0 +1,62 @@
/** @format */
/**
* External dependencies
*/
import history from 'lib/history';
import { parse, stringify } from 'qs';
/**
* Get the current path from history.
*
* @return {String} Current path.
*/
export function getPath() {
return history.location.pathname;
}
/**
* Get the current query string, parsed into an object, from history.
*
* @return {Object} Current query object, defaults to empty object.
*/
export function getQuery() {
const search = history.location.search;
if ( search.length ) {
return parse( search.substring( 1 ) ) || {};
}
return {};
}
/**
* Returns a string with the site's wp-admin URL appended. JS version of `admin_url`.
*
* @param {String} path Relative path.
* @return {String} Full admin URL.
*/
export const getAdminLink = path => {
return wcSettings.adminUrl + path;
};
/**
* Return a URL with set query parameters.
*
* @param {Object} query object of params to be updated.
* @param {String} path Relative path (defaults to current path).
* @param {Object} currentQuery object of current query params (defaults to current querystring).
* @return {String} Updated URL merging query params into existing params.
*/
export const getNewPath = ( query, path = getPath(), currentQuery = getQuery() ) => {
const queryString = stringify( { ...currentQuery, ...query } );
return `${ path }?${ queryString }`;
};
/**
* Updates the query parameters of the current page.
*
* @param {Object} query object of params to be updated.
*/
export const updateQueryString = query => {
const newPath = getNewPath( query );
history.push( newPath );
};