Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lifecycle updates for React 17 #698

Merged
merged 9 commits into from
Apr 15, 2018
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
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