Merge pull request woocommerce/woocommerce-admin#235 from woocommerce/add/custom-datepicker-styles
Add custom Datepicker styles and validation
This commit is contained in:
commit
bf9eb38ab0
|
@ -2,7 +2,7 @@
|
||||||
/**
|
/**
|
||||||
* External dependencies
|
* External dependencies
|
||||||
*/
|
*/
|
||||||
import { Component, Fragment } from '@wordpress/element';
|
import { Component } from '@wordpress/element';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import {
|
import {
|
||||||
DayPickerRangeController,
|
DayPickerRangeController,
|
||||||
|
@ -19,11 +19,11 @@ import 'react-dates/lib/css/_datepicker.css';
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
*/
|
*/
|
||||||
import { toMoment } from 'lib/date';
|
import { toMoment } from 'lib/date';
|
||||||
|
import DateInput from './input';
|
||||||
import phrases from './phrases';
|
import phrases from './phrases';
|
||||||
import './style.scss';
|
import './style.scss';
|
||||||
|
|
||||||
const START_DATE = 'startDate';
|
const START_DATE = 'startDate';
|
||||||
const END_DATE = 'endDate';
|
|
||||||
|
|
||||||
// 782px is the width designated by Gutenberg's `</ Popover>` component.
|
// 782px is the width designated by Gutenberg's `</ Popover>` component.
|
||||||
// * https://github.com/WordPress/gutenberg/blob/c8f8806d4465a83c1a0bc62d5c61377b56fa7214/components/popover/utils.js#L6
|
// * https://github.com/WordPress/gutenberg/blob/c8f8806d4465a83c1a0bc62d5c61377b56fa7214/components/popover/utils.js#L6
|
||||||
|
@ -39,6 +39,8 @@ class DateRange extends Component {
|
||||||
focusedInput: START_DATE,
|
focusedInput: START_DATE,
|
||||||
afterText: after ? after.format( shortDateFormat ) : '',
|
afterText: after ? after.format( shortDateFormat ) : '',
|
||||||
beforeText: before ? before.format( shortDateFormat ) : '',
|
beforeText: before ? before.format( shortDateFormat ) : '',
|
||||||
|
afterError: null,
|
||||||
|
beforeError: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.onDatesChange = this.onDatesChange.bind( this );
|
this.onDatesChange = this.onDatesChange.bind( this );
|
||||||
|
@ -47,10 +49,31 @@ class DateRange extends Component {
|
||||||
this.getOutsideRange = this.getOutsideRange.bind( this );
|
this.getOutsideRange = this.getOutsideRange.bind( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentDidUpdate( prevProps ) {
|
||||||
|
const { after, before } = this.props;
|
||||||
|
/**
|
||||||
|
* Check if props have been reset. If so, reset internal state. Disabling
|
||||||
|
* eslint here because this setState cannot cause infinte loop
|
||||||
|
*/
|
||||||
|
/* eslint-disable react/no-did-update-set-state */
|
||||||
|
if ( ( prevProps.before || prevProps.after ) && ( null === after && null === before ) ) {
|
||||||
|
this.setState( {
|
||||||
|
focusedInput: START_DATE,
|
||||||
|
afterText: '',
|
||||||
|
beforeText: '',
|
||||||
|
afterError: null,
|
||||||
|
beforeError: null,
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
/* eslint-enable react/no-did-update-set-state */
|
||||||
|
}
|
||||||
|
|
||||||
onDatesChange( { startDate, endDate } ) {
|
onDatesChange( { startDate, endDate } ) {
|
||||||
this.setState( {
|
this.setState( {
|
||||||
afterText: startDate ? startDate.format( shortDateFormat ) : '',
|
afterText: startDate ? startDate.format( shortDateFormat ) : '',
|
||||||
beforeText: endDate ? endDate.format( shortDateFormat ) : '',
|
beforeText: endDate ? endDate.format( shortDateFormat ) : '',
|
||||||
|
afterError: null,
|
||||||
|
beforeError: null,
|
||||||
} );
|
} );
|
||||||
this.props.onSelect( {
|
this.props.onSelect( {
|
||||||
after: startDate,
|
after: startDate,
|
||||||
|
@ -64,15 +87,46 @@ class DateRange extends Component {
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getValidatedDate( input, value ) {
|
||||||
|
const { after, before } = this.props;
|
||||||
|
const date = toMoment( shortDateFormat, value );
|
||||||
|
if ( ! date ) {
|
||||||
|
return {
|
||||||
|
date: null,
|
||||||
|
error: __( 'Invalid date', 'wc-admin' ),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if ( moment().isBefore( date, 'day' ) ) {
|
||||||
|
return {
|
||||||
|
date: null,
|
||||||
|
error: __( 'Select a date in the past', 'wc-admin' ),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if ( 'after' === input && before && date.isAfter( before, 'day' ) ) {
|
||||||
|
return {
|
||||||
|
date: null,
|
||||||
|
error: __( 'Start date must be before end date', 'wc-admin' ),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if ( 'before' === input && after && date.isBefore( after, 'day' ) ) {
|
||||||
|
return {
|
||||||
|
date: null,
|
||||||
|
error: __( 'Start date must be before end date', 'wc-admin' ),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return { date };
|
||||||
|
}
|
||||||
|
|
||||||
onInputChange( input, event ) {
|
onInputChange( input, event ) {
|
||||||
const value = event.target.value;
|
const value = event.target.value;
|
||||||
this.setState( { [ input + 'Text' ]: value } );
|
const { date, error } = this.getValidatedDate( input, value );
|
||||||
const date = toMoment( shortDateFormat, value );
|
this.setState( {
|
||||||
if ( date ) {
|
[ input + 'Text' ]: value,
|
||||||
this.props.onSelect( {
|
[ input + 'Error' ]: value.length > 0 ? error : null,
|
||||||
[ input ]: date,
|
} );
|
||||||
} );
|
this.props.onSelect( {
|
||||||
}
|
[ input ]: date,
|
||||||
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
getOutsideRange() {
|
getOutsideRange() {
|
||||||
|
@ -91,77 +145,78 @@ class DateRange extends Component {
|
||||||
return 'function' === typeof inValidDays ? inValidDays : undefined;
|
return 'function' === typeof inValidDays ? inValidDays : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setTnitialVisibleMonth( isDoubleCalendar, before ) {
|
||||||
|
return () => {
|
||||||
|
const visibleDate = before || moment();
|
||||||
|
if ( isDoubleCalendar ) {
|
||||||
|
return visibleDate.clone().subtract( 1, 'month' );
|
||||||
|
}
|
||||||
|
return visibleDate;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { focusedInput, afterText, beforeText } = this.state;
|
const { focusedInput, afterText, beforeText, afterError, beforeError } = this.state;
|
||||||
const { after, before } = this.props;
|
const { after, before } = this.props;
|
||||||
const isOutsideRange = this.getOutsideRange();
|
const isOutsideRange = this.getOutsideRange();
|
||||||
const isMobile = isMobileViewport();
|
const isMobile = isMobileViewport();
|
||||||
|
const isDoubleCalendar = isMobile && window.innerWidth > 624;
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<div
|
||||||
<div className="woocommerce-date-picker__date-inputs">
|
className={ classnames( 'woocommerce-calendar', {
|
||||||
<input
|
'is-mobile': isMobile,
|
||||||
|
} ) }
|
||||||
|
>
|
||||||
|
<div className="woocommerce-calendar__inputs">
|
||||||
|
<DateInput
|
||||||
value={ afterText }
|
value={ afterText }
|
||||||
type="text"
|
|
||||||
onChange={ partial( this.onInputChange, 'after' ) }
|
onChange={ partial( this.onInputChange, 'after' ) }
|
||||||
aria-label={ __( 'Start Date', 'wc-admin' ) }
|
dateFormat={ shortDateFormat }
|
||||||
id="after-date-string"
|
label={ __( 'Start Date', 'wc-admin' ) }
|
||||||
aria-describedby="after-date-string-message"
|
error={ afterError }
|
||||||
/>
|
describedBy={ sprintf(
|
||||||
<p className="screen-reader-text" id="after-date-string-message">
|
|
||||||
{ sprintf(
|
|
||||||
__(
|
__(
|
||||||
"Date input describing a selected date range's start date in format %s",
|
"Date input describing a selected date range's start date in format %s",
|
||||||
'wc-admin'
|
'wc-admin'
|
||||||
),
|
),
|
||||||
shortDateFormat
|
shortDateFormat
|
||||||
) }
|
) }
|
||||||
</p>
|
|
||||||
<span>{ __( 'to', 'wc-admin' ) }</span>
|
|
||||||
<input
|
|
||||||
value={ beforeText }
|
|
||||||
type="text"
|
|
||||||
onChange={ partial( this.onInputChange, 'before' ) }
|
|
||||||
aria-label={ __( 'End Date', 'wc-admin' ) }
|
|
||||||
id="before-date-string"
|
|
||||||
aria-describedby="before-date-string-message"
|
|
||||||
/>
|
/>
|
||||||
<p className="screen-reader-text" id="before-date-string-message">
|
<div className="woocommerce-calendar__inputs-to">{ __( 'to', 'wc-admin' ) }</div>
|
||||||
{ sprintf(
|
<DateInput
|
||||||
|
value={ beforeText }
|
||||||
|
onChange={ partial( this.onInputChange, 'before' ) }
|
||||||
|
dateFormat={ shortDateFormat }
|
||||||
|
label={ __( 'End Date', 'wc-admin' ) }
|
||||||
|
error={ beforeError }
|
||||||
|
describedBy={ sprintf(
|
||||||
__(
|
__(
|
||||||
"Date input describing a selected date range's end date in format %s",
|
"Date input describing a selected date range's end date in format %s",
|
||||||
'wc-admin'
|
'wc-admin'
|
||||||
),
|
),
|
||||||
shortDateFormat
|
shortDateFormat
|
||||||
) }
|
) }
|
||||||
</p>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div className="woocommerce-calendar__react-dates">
|
||||||
className={ classnames( 'woocommerce-calendar', {
|
|
||||||
'is-mobile': isMobile,
|
|
||||||
} ) }
|
|
||||||
>
|
|
||||||
<DayPickerRangeController
|
<DayPickerRangeController
|
||||||
onDatesChange={ this.onDatesChange }
|
onDatesChange={ this.onDatesChange }
|
||||||
onFocusChange={ this.onFocusChange }
|
onFocusChange={ this.onFocusChange }
|
||||||
focusedInput={ focusedInput }
|
focusedInput={ focusedInput }
|
||||||
startDate={ after }
|
startDate={ after }
|
||||||
startDateId={ START_DATE }
|
|
||||||
startDatePlaceholderText={ 'Start Date' }
|
|
||||||
endDate={ before }
|
endDate={ before }
|
||||||
endDateId={ END_DATE }
|
|
||||||
endDatePlaceholderText={ 'End Date' }
|
|
||||||
orientation={ 'horizontal' }
|
orientation={ 'horizontal' }
|
||||||
numberOfMonths={ 1 }
|
numberOfMonths={ isDoubleCalendar ? 2 : 1 }
|
||||||
isOutsideRange={ isOutsideRange }
|
isOutsideRange={ isOutsideRange }
|
||||||
minimumNights={ 0 }
|
minimumNights={ 0 }
|
||||||
hideKeyboardShortcutsPanel
|
hideKeyboardShortcutsPanel
|
||||||
noBorder
|
noBorder
|
||||||
initialVisibleMonth={ () => after || moment() }
|
initialVisibleMonth={ this.setTnitialVisibleMonth( isDoubleCalendar, before ) }
|
||||||
phrases={ phrases }
|
phrases={ phrases }
|
||||||
firstDayOfWeek={ Number( wcSettings.date.dow ) }
|
firstDayOfWeek={ Number( wcSettings.date.dow ) }
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Fragment>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
/** @format */
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import { Dashicon, Popover } from '@wordpress/components';
|
||||||
|
import classnames from 'classnames';
|
||||||
|
import { uniqueId } from 'lodash';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
const DateInput = ( { value, onChange, dateFormat, label, describedBy, error } ) => {
|
||||||
|
const classes = classnames( 'woocommerce-calendar__input', {
|
||||||
|
'is-empty': value.length === 0,
|
||||||
|
'is-error': error,
|
||||||
|
} );
|
||||||
|
const id = uniqueId( '_woo-dates-input' );
|
||||||
|
return (
|
||||||
|
<div className={ classes }>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
className="woocommerce-calendar__input-text"
|
||||||
|
value={ value }
|
||||||
|
onChange={ onChange }
|
||||||
|
aria-label={ label }
|
||||||
|
id={ id }
|
||||||
|
aria-describedby={ `${ id }-message` }
|
||||||
|
placeholder={ dateFormat.toLowerCase() }
|
||||||
|
/>
|
||||||
|
{ error && (
|
||||||
|
<Popover
|
||||||
|
className="woocommerce-calendar__input-error"
|
||||||
|
focusOnMount={ false }
|
||||||
|
position="bottom center"
|
||||||
|
>
|
||||||
|
{ error }
|
||||||
|
</Popover>
|
||||||
|
) }
|
||||||
|
<Dashicon icon="calendar" />
|
||||||
|
<p className="screen-reader-text" id={ `${ id }-message` }>
|
||||||
|
{ error || describedBy }
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
DateInput.propTypes = {
|
||||||
|
value: PropTypes.string,
|
||||||
|
onChange: PropTypes.func.isRequired,
|
||||||
|
dateFormat: PropTypes.string.isRequired,
|
||||||
|
label: PropTypes.string.isRequired,
|
||||||
|
describedBy: PropTypes.string.isRequired,
|
||||||
|
error: PropTypes.string,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DateInput;
|
|
@ -1,6 +1,20 @@
|
||||||
/** @format */
|
/** @format */
|
||||||
|
|
||||||
.woocommerce-calendar {
|
.woocommerce-calendar {
|
||||||
|
width: 100%;
|
||||||
|
background-color: $core-grey-light-100;
|
||||||
|
border-top: 1px solid $core-grey-light-700;
|
||||||
|
height: 396px;
|
||||||
|
|
||||||
|
&.is-mobile {
|
||||||
|
height: 100%;
|
||||||
|
min-height: 537px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.woocommerce-calendar__react-dates {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
.DayPicker {
|
.DayPicker {
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
}
|
}
|
||||||
|
@ -9,11 +23,127 @@
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.is-mobile {
|
.CalendarDay__selected_span {
|
||||||
height: 360px;
|
background: $woocommerce;
|
||||||
|
border: 1px solid $core-grey-light-700;
|
||||||
|
}
|
||||||
|
|
||||||
.DayPicker {
|
.CalendarDay__selected {
|
||||||
width: 318px;
|
background: $woocommerce-700;
|
||||||
|
border: 1px solid $core-grey-light-700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CalendarDay__hovered_span {
|
||||||
|
background: $woocommerce;
|
||||||
|
border: 1px solid $core-grey-light-500;
|
||||||
|
color: $white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CalendarDay__blocked_out_of_range {
|
||||||
|
color: $core-grey-light-900;
|
||||||
|
}
|
||||||
|
|
||||||
|
.DayPicker_transitionContainer,
|
||||||
|
.CalendarMonthGrid,
|
||||||
|
.CalendarMonth,
|
||||||
|
.DayPicker {
|
||||||
|
background-color: $core-grey-light-100;
|
||||||
|
}
|
||||||
|
|
||||||
|
.DayPicker_weekHeader_li {
|
||||||
|
color: $core-grey-dark-400;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.woocommerce-calendar__inputs {
|
||||||
|
padding: 1em;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 500px;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 43% 14% 43%;
|
||||||
|
margin: 0 auto;
|
||||||
|
|
||||||
|
.components-base-control {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.woocommerce-calendar__inputs-to {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.woocommerce-calendar__input {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.dashicons-calendar {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
left: 10px;
|
||||||
|
|
||||||
|
path {
|
||||||
|
fill: $core-grey-dark-300;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-empty {
|
||||||
|
.dashicons-calendar path {
|
||||||
|
fill: $core-grey-dark-300;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-error {
|
||||||
|
.dashicons-calendar path {
|
||||||
|
fill: $error-red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.woocommerce-calendar__input-text {
|
||||||
|
border: 1px solid $error-red;
|
||||||
|
box-shadow: inset 0px 0px 8px $error-red;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
box-shadow: inset 0px 0px 8px $error-red, 0 0 6px rgba(30, 140, 190, 0.8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.woocommerce-calendar__input-text {
|
||||||
|
color: $core-grey-dark-500;
|
||||||
|
border-radius: 3px;
|
||||||
|
padding: 10px 10px 10px 30px;
|
||||||
|
width: 100%;
|
||||||
|
@include font-size( 13 );
|
||||||
|
|
||||||
|
&::placeholder {
|
||||||
|
color: $core-grey-dark-300;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus + span .woocommerce-calendar__input-error {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.woocommerce-calendar__input-error {
|
||||||
|
display: none;
|
||||||
|
|
||||||
|
.components-popover__content {
|
||||||
|
background-color: $core-grey-dark-400;
|
||||||
|
color: $white;
|
||||||
|
padding: 0.5em;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.components-popover {
|
||||||
|
.components-popover__content {
|
||||||
|
min-width: 100px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:not(.no-arrow):after {
|
||||||
|
border-color: $core-grey-dark-400;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { __ } from '@wordpress/i18n';
|
||||||
import { Component, Fragment } from '@wordpress/element';
|
import { Component, Fragment } from '@wordpress/element';
|
||||||
import { TabPanel, Button } from '@wordpress/components';
|
import { TabPanel, Button } from '@wordpress/components';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
import classnames from 'classnames';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
|
@ -16,6 +17,8 @@ import PresetPeriods from './preset-periods';
|
||||||
import Link from 'components/link';
|
import Link from 'components/link';
|
||||||
import { DateRange } from 'components/calendar';
|
import { DateRange } from 'components/calendar';
|
||||||
|
|
||||||
|
const isMobileViewport = () => window.innerWidth < 782;
|
||||||
|
|
||||||
class DatePickerContent extends Component {
|
class DatePickerContent extends Component {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
@ -44,9 +47,10 @@ class DatePickerContent extends Component {
|
||||||
onClose,
|
onClose,
|
||||||
getUpdatePath,
|
getUpdatePath,
|
||||||
isValidSelection,
|
isValidSelection,
|
||||||
|
resetCustomValues,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<div>
|
||||||
<H className="screen-reader-text" tabIndex="0">
|
<H className="screen-reader-text" tabIndex="0">
|
||||||
{ __( 'Select date range and comparison', 'wc-admin' ) }
|
{ __( 'Select date range and comparison', 'wc-admin' ) }
|
||||||
</H>
|
</H>
|
||||||
|
@ -89,26 +93,53 @@ class DatePickerContent extends Component {
|
||||||
inValidDays="future"
|
inValidDays="future"
|
||||||
/>
|
/>
|
||||||
) }
|
) }
|
||||||
<H className="woocommerce-date-picker__text">{ __( 'compare to', 'wc-admin' ) }</H>
|
<div
|
||||||
<ComparePeriods onSelect={ onSelect } compare={ compare } />
|
className={ classnames( 'woocommerce-date-picker__content-controls', {
|
||||||
{ isValidSelection( selectedTab ) ? (
|
'is-sticky-bottom': selectedTab === 'custom' && isMobileViewport(),
|
||||||
<Link
|
'is-custom': selectedTab === 'custom',
|
||||||
className="woocommerce-date-picker__update-btn components-button is-button is-primary"
|
} ) }
|
||||||
to={ getUpdatePath( selectedTab ) }
|
>
|
||||||
onClick={ onClose }
|
<H className="woocommerce-date-picker__text">
|
||||||
>
|
{ __( 'compare to', 'wc-admin' ) }
|
||||||
{ __( 'Update', 'wc-admin' ) }
|
</H>
|
||||||
</Link>
|
<ComparePeriods onSelect={ onSelect } compare={ compare } />
|
||||||
) : (
|
<div className="woocommerce-date-picker__content-controls-btns">
|
||||||
<Button className="woocommerce-date-picker__update-btn" isPrimary disabled>
|
{ selectedTab === 'custom' && (
|
||||||
{ __( 'Update', 'wc-admin' ) }
|
<Button
|
||||||
</Button>
|
className="woocommerce-date-picker__content-controls-btn"
|
||||||
) }
|
isPrimary
|
||||||
|
onClick={ resetCustomValues }
|
||||||
|
disabled={ ! ( after || before ) }
|
||||||
|
>
|
||||||
|
{ __( 'Reset', 'wc-admin' ) }
|
||||||
|
</Button>
|
||||||
|
) }
|
||||||
|
{ isValidSelection( selectedTab ) ? (
|
||||||
|
<Link
|
||||||
|
/* eslint-disable max-len */
|
||||||
|
className="woocommerce-date-picker__content-controls-btn components-button is-button is-primary"
|
||||||
|
/* eslint-enable max-len */
|
||||||
|
href={ getUpdatePath( selectedTab ) }
|
||||||
|
onClick={ onClose }
|
||||||
|
>
|
||||||
|
{ __( 'Update', 'wc-admin' ) }
|
||||||
|
</Link>
|
||||||
|
) : (
|
||||||
|
<Button
|
||||||
|
className="woocommerce-date-picker__content-controls-btn"
|
||||||
|
isPrimary
|
||||||
|
disabled
|
||||||
|
>
|
||||||
|
{ __( 'Update', 'wc-admin' ) }
|
||||||
|
</Button>
|
||||||
|
) }
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
) }
|
) }
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
</Section>
|
</Section>
|
||||||
</Fragment>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -119,6 +150,7 @@ DatePickerContent.propTypes = {
|
||||||
onSelect: PropTypes.func.isRequired,
|
onSelect: PropTypes.func.isRequired,
|
||||||
onClose: PropTypes.func.isRequired,
|
onClose: PropTypes.func.isRequired,
|
||||||
getUpdatePath: PropTypes.func.isRequired,
|
getUpdatePath: PropTypes.func.isRequired,
|
||||||
|
resetCustomValues: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default DatePickerContent;
|
export default DatePickerContent;
|
||||||
|
|
|
@ -23,6 +23,7 @@ class DatePicker extends Component {
|
||||||
this.select = this.select.bind( this );
|
this.select = this.select.bind( this );
|
||||||
this.getUpdatePath = this.getUpdatePath.bind( this );
|
this.getUpdatePath = this.getUpdatePath.bind( this );
|
||||||
this.isValidSelection = this.isValidSelection.bind( this );
|
this.isValidSelection = this.isValidSelection.bind( this );
|
||||||
|
this.resetCustomValues = this.resetCustomValues.bind( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
// @TODO change this to `getDerivedStateFromProps` in React 16.4
|
// @TODO change this to `getDerivedStateFromProps` in React 16.4
|
||||||
|
@ -73,6 +74,13 @@ class DatePicker extends Component {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resetCustomValues() {
|
||||||
|
this.setState( {
|
||||||
|
after: null,
|
||||||
|
before: null,
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { period, compare, after, before } = this.state;
|
const { period, compare, after, before } = this.state;
|
||||||
return (
|
return (
|
||||||
|
@ -99,6 +107,7 @@ class DatePicker extends Component {
|
||||||
onClose={ onClose }
|
onClose={ onClose }
|
||||||
getUpdatePath={ this.getUpdatePath }
|
getUpdatePath={ this.getUpdatePath }
|
||||||
isValidSelection={ this.isValidSelection }
|
isValidSelection={ this.isValidSelection }
|
||||||
|
resetCustomValues={ this.resetCustomValues }
|
||||||
/>
|
/>
|
||||||
) }
|
) }
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -13,25 +13,12 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.woocommerce-date-picker__content {
|
.woocommerce-date-picker__content {
|
||||||
.components-popover__content {
|
> .components-popover__content {
|
||||||
width: 320px;
|
width: 320px;
|
||||||
padding: 1em 0;
|
|
||||||
border: 1px solid $gray;
|
|
||||||
background-color: $white;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
& > * {
|
|
||||||
margin: 0 0 1em 0;
|
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&.is-mobile {
|
&.is-mobile {
|
||||||
.components-popover__content {
|
& > .components-popover__content {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
@ -44,19 +31,16 @@
|
||||||
.components-popover__close {
|
.components-popover__close {
|
||||||
transform: translateY(22px);
|
transform: translateY(22px);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.components-tab-panel__tab-content {
|
||||||
|
height: calc(100% - 38px);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.woocommerce-date-picker__date-inputs {
|
|
||||||
max-width: 320px;
|
|
||||||
width: 100%;
|
|
||||||
display: grid;
|
|
||||||
margin: 0 1em 1em 1em;
|
|
||||||
grid-template-columns: 45% 10% 45%;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.woocommerce-date-picker__tabs {
|
.woocommerce-date-picker__tabs {
|
||||||
|
height: calc(100% - 42px);
|
||||||
|
|
||||||
.components-tab-panel__tabs {
|
.components-tab-panel__tabs {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr 1fr;
|
grid-template-columns: 1fr 1fr;
|
||||||
|
@ -68,14 +52,6 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
& > * {
|
|
||||||
margin: 0 0 1em 0;
|
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,21 +77,45 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.woocommerce-date-picker__update-btn {
|
|
||||||
border: 1px solid $gray;
|
|
||||||
background-color: transparent;
|
|
||||||
padding: 0.5em;
|
|
||||||
width: 50%;
|
|
||||||
text-align: center;
|
|
||||||
text-decoration: none;
|
|
||||||
justify-content: center;
|
|
||||||
line-height: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
.woocommerce-date-picker__text {
|
.woocommerce-date-picker__text {
|
||||||
@include font-size( 12 );
|
@include font-size( 12 );
|
||||||
font-weight: 100;
|
font-weight: 100;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: $core-grey-dark-500;
|
color: $core-grey-dark-300;
|
||||||
|
width: 100%;
|
||||||
|
margin: 0;
|
||||||
|
padding: 1em;
|
||||||
|
background-color: $white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.woocommerce-date-picker__content-controls {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
|
align-items: center;
|
||||||
|
padding-bottom: 1em;
|
||||||
|
background-color: $white;
|
||||||
|
|
||||||
|
&.is-custom {
|
||||||
|
border-top: 1px solid $core-grey-light-700;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-sticky-bottom {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.woocommerce-date-picker__content-controls-btns {
|
||||||
|
padding-top: 1em;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.woocommerce-date-picker__content-controls-btn {
|
||||||
|
justify-content: center;
|
||||||
|
width: 50%;
|
||||||
|
margin: 0 1em;
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,7 +48,7 @@ export function toMoment( format, str ) {
|
||||||
}
|
}
|
||||||
if ( 'string' === typeof str ) {
|
if ( 'string' === typeof str ) {
|
||||||
const date = moment( str, [ isoDateFormat, format ], true );
|
const date = moment( str, [ isoDateFormat, format ], true );
|
||||||
return date.isValid ? date : null;
|
return date.isValid() ? date : null;
|
||||||
}
|
}
|
||||||
throw new Error( 'toMoment requires a string to be passed as an argument' );
|
throw new Error( 'toMoment requires a string to be passed as an argument' );
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,6 +51,11 @@ describe( 'toMoment', () => {
|
||||||
const fn = () => toMoment( '', 77 );
|
const fn = () => toMoment( '', 77 );
|
||||||
expect( fn ).toThrow();
|
expect( fn ).toThrow();
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
it( 'shoud return null on invalid date', () => {
|
||||||
|
const invalidDate = toMoment( 'YYYY', '2018-00-00' );
|
||||||
|
expect( invalidDate ).toBe( null );
|
||||||
|
} );
|
||||||
} );
|
} );
|
||||||
|
|
||||||
describe( 'getCurrentPeriod', () => {
|
describe( 'getCurrentPeriod', () => {
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue