Skip to content

Commit

Permalink
Merge pull request #698 from gpbl/gpbl/685/react-17-2
Browse files Browse the repository at this point in the history
Lifecycle updates for React 17
  • Loading branch information
gpbl authored Apr 15, 2018
2 parents 28bf9ed + 8699670 commit 68bc604
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 45 deletions.
9 changes: 8 additions & 1 deletion docs/src/pages/api/DateUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,14 @@ console.log(newRange.from) // 2015-05-24`}</CodeBlock>
<p>
Clone <code>date</code> returning a new Date with the same time.
</p>

<h3>
<Anchor id="isDate" />
isDate <code>(value) ⇒ Boolean</code>
</h3>
<p>
Returns <code>true</code> if <code>value</code> is a valid Javascript
Date.
</p>
<h3>
<Anchor id="isDayAfter" />
isDayAfter <code>(day1: Date, day2: Date) ⇒ boolean</code>
Expand Down
12 changes: 12 additions & 0 deletions src/DateUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,17 @@ export function clone(d) {
return new Date(d.getTime());
}

/**
* Return `true` if the passed value is a valid JavaScript Date object.
*
* @export
* @param {any} value
* @returns {Boolean}
*/
export function isDate(value) {
return value instanceof Date && !isNaN(value.valueOf());
}

/**
* Return `d` as a new date with `n` months added.
*
Expand Down Expand Up @@ -204,6 +215,7 @@ export default {
addMonths,
clone,
getWeekNumber,
isDate,
isDayAfter,
isDayBefore,
isDayBetween,
Expand Down
34 changes: 24 additions & 10 deletions src/DayPicker.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import React, { Component } from 'react';
import { polyfill } from 'react-lifecycles-compat';
import PropTypes from 'prop-types';

import Caption from './Caption';
Expand Down Expand Up @@ -158,19 +157,32 @@ export class DayPicker extends Component {

constructor(props) {
super(props);
this.state = this.getStateFromProps(props);

const currentMonth = this.getCurrentMonthFromProps(props);
this.state = { currentMonth };
}

componentWillReceiveProps(nextProps) {
componentDidUpdate(prevProps) {
// Changing the `month` props means changing the current displayed month
if (
this.props.month !== nextProps.month &&
!DateUtils.isSameMonth(this.props.month, nextProps.month)
prevProps.month !== this.props.month &&
!DateUtils.isSameMonth(prevProps.month, this.props.month)
) {
this.setState(this.getStateFromProps(nextProps));
const currentMonth = this.getCurrentMonthFromProps(this.props);
// eslint-disable-next-line react/no-did-update-set-state
this.setState({ currentMonth });
}
}

getStateFromProps = props => {
/**
* Return the month to be shown in the calendar based on the component props.
*
* @param {Object} props
* @returns Date
* @memberof DayPicker
* @private
*/
getCurrentMonthFromProps(props) {
const initialMonth = Helpers.startOfMonth(
props.month || props.initialMonth
);
Expand All @@ -192,8 +204,8 @@ export class DayPicker extends Component {
1 - this.props.numberOfMonths
);
}
return { currentMonth };
};
return currentMonth;
}

getNextNavigableMonth() {
return DateUtils.addMonths(
Expand Down Expand Up @@ -583,4 +595,6 @@ DayPicker.DateUtils = DateUtils;
DayPicker.LocaleUtils = LocaleUtils;
DayPicker.ModifiersUtils = ModifiersUtils;

export default polyfill(DayPicker);
export { DateUtils, LocaleUtils, ModifiersUtils };

export default DayPicker;
74 changes: 40 additions & 34 deletions src/DayPickerInput.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
import React from 'react';
import { polyfill } from 'react-lifecycles-compat';
import PropTypes from 'prop-types';

import DayPicker from './DayPicker';
import { isSameMonth, isDate } from './DateUtils';
import { getModifiersForDay } from './ModifiersUtils';
import { ESC, TAB } from './keys';

// When clicking on a day cell, overlay will be hidden after this timeout
export const HIDE_TIMEOUT = 100;

function isDate(date) {
return date instanceof Date && !isNaN(date.valueOf());
}

/**
* The default function used to format a Date to String, passed to the `format` prop.
* @param {Date} d
* @return {String}
*/
export function defaultFormat(d) {
if (isDate(d)) {
const year = d.getFullYear();
Expand All @@ -23,6 +24,11 @@ export function defaultFormat(d) {
return '';
}

/**
* The default function used to parse a String as Date, passed to the `parse` prop.
* @param {String} str
* @return {Date}
*/
export function defaultParse(str) {
if (typeof str !== 'string') {
return undefined;
Expand All @@ -49,7 +55,7 @@ export function defaultParse(str) {
return new Date(year, month, day);
}

export class DayPickerInput extends React.Component {
export default class DayPickerInput extends React.Component {
static propTypes = {
value: PropTypes.oneOfType([PropTypes.string, PropTypes.instanceOf(Date)]),
inputProps: PropTypes.object,
Expand Down Expand Up @@ -129,37 +135,39 @@ export class DayPickerInput extends React.Component {
this.handleOverlayBlur = this.handleOverlayBlur.bind(this);
}

componentWillReceiveProps(nextProps) {
const monthFromProps = this.props.dayPickerProps.month;
const nextMonthFromProps = nextProps.dayPickerProps.month;

const selectedDaysFromProps = this.props.dayPickerProps.selectedDays;
const nextSelectedDaysFromProps = nextProps.dayPickerProps.selectedDays;
componentDidUpdate(prevProps) {
const newState = {};

let nextValue = nextProps.value;
const currentValue = this.props.value;
// Current props
const { value, formatDate, format, dayPickerProps } = this.props;

const monthChanged =
(nextMonthFromProps && !monthFromProps) ||
(nextMonthFromProps &&
(nextMonthFromProps.getFullYear() !== monthFromProps.getFullYear() ||
nextMonthFromProps.getMonth() !== monthFromProps.getMonth()));

if (nextValue !== currentValue) {
if (isDate(nextValue)) {
nextValue = this.props.formatDate(
nextValue,
this.props.format,
this.props.dayPickerProps.locale
);
// Update the input value if the `value` prop has changed
if (value !== prevProps.value) {
if (isDate(value)) {
newState.value = formatDate(value, format, dayPickerProps.locale);
} else {
newState.value = value;
}
this.setState({ value: nextValue });
}
if (monthChanged) {
this.setState({ month: nextMonthFromProps });

// Update the month if the months from props changed
const prevMonth = prevProps.dayPickerProps.month;
if (
dayPickerProps.month &&
dayPickerProps.month !== prevMonth &&
!isSameMonth(dayPickerProps.month, prevMonth)
) {
newState.month = dayPickerProps.month;
}
if (selectedDaysFromProps !== nextSelectedDaysFromProps) {
this.setState({ selectedDays: nextSelectedDaysFromProps });

// Updated the selected days from props if they changed
if (prevProps.dayPickerProps.selectedDays !== dayPickerProps.selectedDays) {
newState.selectedDays = dayPickerProps.selectedDays;
}

if (Object.keys(newState).length > 0) {
// eslint-disable-next-line react/no-did-update-set-state
this.setState(newState);
}
}

Expand Down Expand Up @@ -500,5 +508,3 @@ export class DayPickerInput extends React.Component {
);
}
}

export default polyfill(DayPickerInput);
15 changes: 15 additions & 0 deletions test/DateUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,21 @@ describe('DateUtils', () => {
});
});

describe('isDate', () => {
it('should detect a valid date', () =>
expect(DateUtils.isDate(new Date())).toBe(true));
it('should detect invalid date from String', () =>
expect(DateUtils.isDate('x')).toBe(false));
it('should detect invalid date from Object', () =>
expect(DateUtils.isDate({})).toBe(false));
it('should detect invalid date from Array', () =>
expect(DateUtils.isDate([])).toBe(false));
it('should detect invalid date from `undefined`', () =>
expect(DateUtils.isDate(undefined)).toBe(false));
it('should detect invalid date from `null`', () =>
expect(DateUtils.isDate(null)).toBe(false));
});

describe('isDayBefore', () => {
it('returns true when the day is before the other day', () => {
const day1 = new Date(2015, 10, 11, 5, 25);
Expand Down

0 comments on commit 68bc604

Please sign in to comment.