diff --git a/client/analytics/report/customers/config.js b/client/analytics/report/customers/config.js
index 5ee8e7b1bcb..ab1cac2b101 100644
--- a/client/analytics/report/customers/config.js
+++ b/client/analytics/report/customers/config.js
@@ -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*/
diff --git a/packages/components/src/filters/advanced/date-filter.js b/packages/components/src/filters/advanced/date-filter.js
new file mode 100644
index 00000000000..d4523d3a2c5
--- /dev/null
+++ b/packages/components/src/filters/advanced/date-filter.js
@@ -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: