Skip to content
This repository has been archived by the owner on Jul 12, 2024. It is now read-only.

Advanced Filters: Add DatePicker #1146

Merged
merged 1 commit into from
Jan 14, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions client/analytics/report/customers/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,36 @@ export const advancedFilters = {
component: 'Currency',
},
},
registered: {
labels: {
add: __( 'Registered', 'wc-admin' ),
remove: __( 'Remove registered filter', 'wc-admin' ),
rule: __( 'Select a registered filter match', 'wc-admin' ),
/* translators: A sentence describing a Product filter. See screen shot for context: https://cloudup.com/cCsm3GeXJbE */
title: __( 'Registered {{rule /}} {{filter /}}', 'wc-admin' ),
filter: __( 'Select registered date', 'wc-admin' ),
},
rules: [
{
value: 'before',
/* translators: Sentence fragment, logical, "Before" refers to customers registered before a given date. Screenshot for context: https://cloudup.com/cCsm3GeXJbE */
label: _x( 'Before', 'date', 'wc-admin' ),
},
{
value: 'after',
/* translators: Sentence fragment, logical, "after" refers to customers registered after a given date. Screenshot for context: https://cloudup.com/cCsm3GeXJbE */
label: _x( 'After', 'date', 'wc-admin' ),
},
{
value: 'between',
/* translators: Sentence fragment, logical, "Between" refers to average order value of a customer, between two given amounts. Screenshot for context: https://cloudup.com/cCsm3GeXJbE */
label: _x( 'Between', 'date', 'wc-admin' ),
},
],
input: {
component: 'Date',
},
},
},
};
/*eslint-enable max-len*/
223 changes: 223 additions & 0 deletions packages/components/src/filters/advanced/date-filter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
/** @format */
/**
* External dependencies
*/
import { Component, Fragment } from '@wordpress/element';
import interpolateComponents from 'interpolate-components';
import { SelectControl } from '@wordpress/components';
import { find, partial } from 'lodash';
import classnames from 'classnames';
import { __, _x } from '@wordpress/i18n';

/**
* WooCommerce dependencies
*/
import { isoDateFormat, toMoment } from '@woocommerce/date';

/**
* Internal dependencies
*/
import DatePicker from '../../calendar/date-picker';
import { textContent } from './utils';

const dateStringFormat = __( 'MMM D, YYYY', 'wc-admin' );
const dateFormat = __( 'MM/DD/YYYY', 'wc-admin' );

class DateFilter extends Component {
constructor( { filter } ) {
super( ...arguments );

const [ isoAfter, isoBefore ] = ( filter.value || '' ).split( ',' );
const after = isoAfter ? toMoment( isoDateFormat, isoAfter ) : null;
const before = isoBefore ? toMoment( isoDateFormat, isoBefore ) : null;

this.state = {
before,
beforeText: before ? before.format( dateFormat ) : '',
beforeError: null,
after,
afterText: after ? after.format( dateFormat ) : '',
afterError: null,
};

this.onSingleDateChange = this.onSingleDateChange.bind( this );
this.onRangeDateChange = this.onRangeDateChange.bind( this );
}

getBetweenString() {
return _x(
'{{after /}}{{span}} and {{/span}}{{before /}}',
'Date range inputs arranged on a single line',
'wc-admin'
);
}

getScreenReaderText( filter, config ) {
const rule = find( config.rules, { value: filter.rule } ) || {};

const { before, after } = this.state;

// Return nothing if we're missing input(s)
if ( ! before || 'between' === rule.value && ! after ) {
return '';
}

let filterStr = before.format( dateStringFormat );

if ( 'between' === rule.value ) {
filterStr = interpolateComponents( {
mixedString: this.getBetweenString(),
components: {
after: <Fragment>{ after.format( dateStringFormat ) }</Fragment>,
before: <Fragment>{ before.format( dateStringFormat ) }</Fragment>,
span: <Fragment />,
},
} );
}

return textContent( interpolateComponents( {
mixedString: config.labels.title,
components: {
filter: <Fragment>{ filterStr }</Fragment>,
rule: <Fragment>{ rule.label }</Fragment>,
},
} ) );
}

onSingleDateChange( { date, text, error } ) {
const { filter, onFilterChange } = this.props;
this.setState( { before: date, beforeText: text, beforeError: error } );

if ( date ) {
onFilterChange( filter.key, 'value', date.format( isoDateFormat ) );
}
}

onRangeDateChange( input, { date, text, error } ) {
const { filter, onFilterChange } = this.props;

this.setState( {
[ input ]: date,
[ input + 'Text' ]: text,
[ input + 'Error' ]: error,
} );

if ( date ) {
const { before, after } = this.state;
let nextAfter = null;
let nextBefore = null;

if ( 'after' === input ) {
nextAfter = date.format( isoDateFormat );
nextBefore = before ? before.format( isoDateFormat ) : null;
}

if ( 'before' === input ) {
nextAfter = after ? after.format( isoDateFormat ) : null;
nextBefore = date.format( isoDateFormat );
}

if ( nextAfter && nextBefore ) {
onFilterChange( filter.key, 'value', [ nextAfter, nextBefore ].join( ',' ) );
}
}
}

getFilterInputs() {
const { filter } = this.props;
const { before, beforeText, beforeError, after, afterText, afterError } = this.state;

if ( 'between' === filter.rule ) {
return interpolateComponents( {
mixedString: this.getBetweenString(),
components: {
after: (
<DatePicker
date={ after }
text={ afterText }
error={ afterError }
onUpdate={ partial( this.onRangeDateChange, 'after' ) }
dateFormat={ dateFormat }
invalidDays="none"
/>
),
before: (
<DatePicker
date={ before }
text={ beforeText }
error={ beforeError }
onUpdate={ partial( this.onRangeDateChange, 'before' ) }
dateFormat={ dateFormat }
invalidDays="none"
/>
),
span: <span className="separator" />,
},
} );
}

return (
<DatePicker
date={ before }
text={ beforeText }
error={ beforeError }
onUpdate={ this.onSingleDateChange }
dateFormat={ dateFormat }
invalidDays="none"
/>
);
}

render() {
const { config, filter, onFilterChange, isEnglish } = this.props;
const { key, rule } = filter;
const { labels, rules } = config;
const screenReaderText = this.getScreenReaderText( filter, config );
const children = interpolateComponents( {
mixedString: labels.title,
components: {
rule: (
<SelectControl
className="woocommerce-filters-advanced__rule"
options={ rules }
value={ rule }
onChange={ partial( onFilterChange, key, 'rule' ) }
aria-label={ labels.rule }
/>
),
filter: (
<div
className={ classnames( 'woocommerce-filters-advanced__input-range', {
'is-between': 'between' === rule,
} ) }
>
{ this.getFilterInputs() }
</div>
),
},
} );
/*eslint-disable jsx-a11y/no-noninteractive-tabindex*/
return (
<fieldset tabIndex="0">
<legend className="screen-reader-text">
{ labels.add || '' }
</legend>
<div
className={ classnames( 'woocommerce-filters-advanced__fieldset', {
'is-english': isEnglish,
} ) }
>
{ children }
</div>
{ screenReaderText && (
<span className="screen-reader-text">
{ screenReaderText }
</span>
) }
</fieldset>
);
/*eslint-enable jsx-a11y/no-noninteractive-tabindex*/
}
}

export default DateFilter;
10 changes: 10 additions & 0 deletions packages/components/src/filters/advanced/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import Link from '../../link';
import SelectFilter from './select-filter';
import SearchFilter from './search-filter';
import NumberFilter from './number-filter';
import DateFilter from './date-filter';

const matches = [
{ value: 'all', label: __( 'All', 'wc-admin' ) },
Expand Down Expand Up @@ -210,6 +211,15 @@ class AdvancedFilters extends Component {
query={ query }
/>
) }
{ 'Date' === input.component && (
<DateFilter
filter={ filter }
config={ config.filters[ key ] }
onFilterChange={ this.onFilterChange }
isEnglish={ isEnglish }
query={ query }
/>
) }
<IconButton
className="woocommerce-filters-advanced__remove"
label={ labels.remove }
Expand Down
2 changes: 1 addition & 1 deletion packages/components/src/filters/advanced/number-filter.js
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ class NumberFilter extends Component {
),
filter: (
<div
className={ classnames( 'woocommerce-filters-advanced__input-numeric-range', {
className={ classnames( 'woocommerce-filters-advanced__input-range', {
'is-between': 'between' === rule,
} ) }
>
Expand Down
2 changes: 1 addition & 1 deletion packages/components/src/filters/advanced/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@
}
}

.woocommerce-filters-advanced__input-numeric-range {
.woocommerce-filters-advanced__input-range {
align-items: center;
display: grid;
grid-template-columns: 1fr;
Expand Down