Skip to content

Commit

Permalink
Datepicker: Allow null value for currentDate on mounting (#12963)
Browse files Browse the repository at this point in the history
* Datepicker: Allow null value for currentDate on mounting

* flexible assertion, FTW
  • Loading branch information
psealock authored and aduth committed Feb 8, 2019
1 parent b5a1e0f commit a59aaf2
Show file tree
Hide file tree
Showing 4 changed files with 131 additions and 4 deletions.
1 change: 1 addition & 0 deletions packages/components/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

- `Dropdown` now has a `focusOnMount` prop which is passed directly to the contained `Popover`.
- `DatePicker` has new prop `isInvalidDate` exposing react-dates' `isOutsideRange`.
- `DatePicker` allows `null` as accepted value for `currentDate` prop to signify no date selection.

## 7.0.5 (2019-01-03)

Expand Down
5 changes: 3 additions & 2 deletions packages/components/src/date-time/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,11 @@ The component accepts the following props:

### currentDate

The current date and time at initialization.
The current date and time at initialization. Optionally pass in a `null` value to specify no date is currently selected.

- Type: `string`
- Required: Yes
- Required: No
- Default: today's date

### onChange

Expand Down
19 changes: 17 additions & 2 deletions packages/components/src/date-time/date.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class DatePicker extends Component {
onChangeMoment( newDate ) {
const { currentDate, onChange } = this.props;

// If currentDate is null, use now as momentTime to designate hours, minutes, seconds.
const momentDate = currentDate ? moment( currentDate ) : moment();
const momentTime = {
hours: momentDate.hours(),
Expand All @@ -35,10 +36,24 @@ class DatePicker extends Component {
onChange( newDate.set( momentTime ).format( TIMEZONELESS_FORMAT ) );
}

/**
* Create a Moment object from a date string. With no currentDate supplied, default to a Moment
* object representing now. If a null value is passed, return a null value.
*
* @param {?string} currentDate Date representing the currently selected date or null to signify no selection.
* @return {?Moment} Moment object for selected date or null.
*/
getMomentDate( currentDate ) {
if ( null === currentDate ) {
return null;
}
return currentDate ? moment( currentDate ) : moment();
}

render() {
const { currentDate, isInvalidDate } = this.props;

const momentDate = currentDate ? moment( currentDate ) : moment();
const momentDate = this.getMomentDate( currentDate );

return (
<div className="components-datetime__date">
Expand All @@ -49,7 +64,7 @@ class DatePicker extends Component {
hideKeyboardShortcutsPanel
// This is a hack to force the calendar to update on month or year change
// https://github.com/airbnb/react-dates/issues/240#issuecomment-361776665
key={ `datepicker-controller-${ momentDate.format( 'MM-YYYY' ) }` }
key={ `datepicker-controller-${ momentDate ? momentDate.format( 'MM-YYYY' ) : 'null' }` }
noBorder
numberOfMonths={ 1 }
onDateChange={ this.onChangeMoment }
Expand Down
110 changes: 110 additions & 0 deletions packages/components/src/date-time/test/date.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/**
* External dependencies
*/
import { shallow } from 'enzyme';
import moment from 'moment';

/**
* Internal dependencies
*/
import DatePicker from '../date';

const TIMEZONELESS_FORMAT = 'YYYY-MM-DDTHH:mm:ss';

describe( 'DatePicker', () => {
it( 'should pass down a moment object for currentDate', () => {
const currentDate = '1986-10-18T23:00:00';
const wrapper = shallow( <DatePicker currentDate={ currentDate } /> );
const date = wrapper.children().props().date;
expect( moment.isMoment( date ) ).toBe( true );
expect( date.isSame( moment( currentDate ) ) ).toBe( true );
} );

it( 'should pass down a null date when currentDate is set to null', () => {
const wrapper = shallow( <DatePicker currentDate={ null } /> );
expect( wrapper.children().props().date ).toBeNull();
} );

it( 'should pass down a moment object for now when currentDate is undefined', () => {
const wrapper = shallow( <DatePicker /> );
const date = wrapper.children().props().date;
expect( moment.isMoment( date ) ).toBe( true );
expect( date.isSame( moment(), 'second' ) ).toBe( true );
} );

describe( 'getMomentDate', () => {
it( 'should return a Moment object representing a given date string', () => {
const currentDate = '1986-10-18T23:00:00';
const wrapper = shallow( <DatePicker /> );
const momentDate = wrapper.instance().getMomentDate( currentDate );

expect( moment.isMoment( momentDate ) ).toBe( true );
expect( momentDate.isSame( moment( currentDate ) ) ).toBe( true );
} );

it( 'should return null when given a null agrument', () => {
const currentDate = null;
const wrapper = shallow( <DatePicker /> );
const momentDate = wrapper.instance().getMomentDate( currentDate );

expect( momentDate ).toBeNull();
} );

it( 'should return a Moment object representing now when given an undefined argument', () => {
const wrapper = shallow( <DatePicker /> );
const momentDate = wrapper.instance().getMomentDate();

expect( moment.isMoment( momentDate ) ).toBe( true );
expect( momentDate.isSame( moment(), 'second' ) ).toBe( true );
} );
} );

describe( 'onChangeMoment', () => {
it( 'should call onChange with a formated date of the input', () => {
const onChangeSpy = jest.fn();
const currentDate = '1986-10-18T11:00:00';
const wrapper = shallow( <DatePicker currentDate={ currentDate } onChange={ onChangeSpy } /> );
const newDate = moment();

wrapper.instance().onChangeMoment( newDate );

expect( onChangeSpy ).toHaveBeenCalledWith( newDate.format( TIMEZONELESS_FORMAT ) );
} );

it( 'should call onChange with hours, minutes, seconds of the current time when currentDate is undefined', () => {
let onChangeSpyArgument;
const onChangeSpy = ( arg ) => onChangeSpyArgument = arg;
const wrapper = shallow( <DatePicker onChange={ onChangeSpy } /> );
const newDate = moment( '1986-10-18T11:00:00' );
const current = moment();
const newDateWithCurrentTime = newDate
.clone()
.set( {
hours: current.hours(),
minutes: current.minutes(),
seconds: current.seconds(),
} );
wrapper.instance().onChangeMoment( newDate );

expect( moment( onChangeSpyArgument ).isSame( newDateWithCurrentTime, 'minute' ) ).toBe( true );
} );

it( 'should call onChange with hours, minutes, seconds of the current time when currentDate is null', () => {
let onChangeSpyArgument;
const onChangeSpy = ( arg ) => onChangeSpyArgument = arg;
const wrapper = shallow( <DatePicker currentDate={ null } onChange={ onChangeSpy } /> );
const newDate = moment( '1986-10-18T11:00:00' );
const current = moment();
const newDateWithCurrentTime = newDate
.clone()
.set( {
hours: current.hours(),
minutes: current.minutes(),
seconds: current.seconds(),
} );
wrapper.instance().onChangeMoment( newDate );

expect( moment( onChangeSpyArgument ).isSame( newDateWithCurrentTime, 'minute' ) ).toBe( true );
} );
} );
} );

0 comments on commit a59aaf2

Please sign in to comment.