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

1282 reactdaypicker updates date ranges when only start date is selected #1299

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
27 changes: 20 additions & 7 deletions client/components/Map/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,11 @@ class MapContainer extends React.Component {

componentDidUpdate(prevProps) {
const { activeMode, pins, startDate, endDate } = this.props;
// Note: we are only checking if the endDate has changed, not the startDate.
// This is so that we don't attempt to get new data until the user has
// selected an endDate.
if (prevProps.activeMode !== activeMode || prevProps.pins !== pins ||
prevProps.startDate != startDate || prevProps.endDate != endDate) {
prevProps.endDate != endDate && endDate != null) {
this.setData();
}
}
Expand All @@ -72,16 +75,26 @@ class MapContainer extends React.Component {
getNonOverlappingRanges = (startA, endA, startB, endB) => {
var leftNonOverlap = null;
var rightNonOverlap = null;
const momentStartA = moment(startA);
const momentEndA = moment(endA);
const momentStartB = moment(startB);
const momentEndB = moment(endB);

// If date range A starts before date range B, then it has a subrange that
// does not overlap with B.
if (moment(startA) < moment(startB)){
if (momentStartA < momentStartB){
// For the left side, we want to choose the earlier of (startB, endA).
// If startB is earlier than endA, that means A and B overlap, so we
// subtract 1 day from startB, since it's already included in A.
const leftNonOverlapEnd = momentStartB < momentEndA ? momentStartB.subtract(1, 'days') : momentEndA;
leftNonOverlap = [startA,
moment(startB).subtract(1, 'days').format(INTERNAL_DATE_SPEC)];
leftNonOverlapEnd.format(INTERNAL_DATE_SPEC)];
}
// If date range A ends after date range B, then it has a subrange that does
// not overlap with B.
if (moment(endB) < moment(endA)){
rightNonOverlap = [moment(endB).add(1, 'days').format(INTERNAL_DATE_SPEC),
if (momentEndB < momentEndA){
var rightNonOverlapStart = momentEndB < momentStartA ? momentStartA : momentEndB.add(1, 'days');
rightNonOverlap = [rightNonOverlapStart.format(INTERNAL_DATE_SPEC),
endA];
}
return [leftNonOverlap, rightNonOverlap];
Expand Down Expand Up @@ -162,8 +175,8 @@ class MapContainer extends React.Component {
currentEnd = dateRange[1];
} else {
resolvedDateRanges.push([currentStart, currentEnd]);
currentStart = null;
currentEnd = null;
currentStart = dateRange[0];
currentEnd = dateRange[1];
}
}
if (currentStart !== null){
Expand Down
92 changes: 30 additions & 62 deletions client/components/common/ReactDayPicker/ReactDayPicker.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,56 +7,25 @@ import {
import moment from 'moment';
import PropTypes from 'prop-types';
import React, { useState } from 'react';
import DayPicker, { DateUtils } from 'react-day-picker';
import DayPicker from 'react-day-picker';
import { connect } from 'react-redux';

import { INTERNAL_DATE_SPEC } from '../CONSTANTS';
import Styles from './Styles';
import WeekDay from './Weekday';

const getInitialState = initialDates => {
const [from, to] = initialDates;
return {
from,
to,
enteredTo: to, // Keep track of the last day for mouseEnter.
};
};

const defaultState = { from: null, to: null };

/** A wrapper around react-day-picker that selects a date range. */
function ReactDayPicker({
initialDates, range, updateStartDate,
updateEndDate,
range, updateStartDate, updateEndDate, startDate, endDate,
}) {
// TODO: consider getting rid of this local date state in favor of Redux.
const [state, setState] = useState(getInitialState(initialDates));

const isSelectingFirstDay = (from, to, day) => {
const isBeforeFirstDay = from && DateUtils.isDayBefore(day, from);
const isRangeSelected = from && to;
return !from || isBeforeFirstDay || isRangeSelected;
};

const resetDays = () => {
setState(defaultState);
};
// enteredTo represents the day that the user is currently hovering over.
const [enteredTo, setEnteredTo] = useState(endDate);

const setFromDay = day => {
setState(() => ({
from: day,
to: null,
enteredTo: null,
}));
updateStartDate(moment(day).format(INTERNAL_DATE_SPEC));
};

const setFromToDay = day => {
setState(prevState => ({
...prevState,
to: day,
enteredTo: day,
}));
const setToDay = day => {
updateEndDate(moment(day).format(INTERNAL_DATE_SPEC));
};

Expand All @@ -66,34 +35,27 @@ function ReactDayPicker({
return;
}

const { from, to } = state;
const dayIsInSelectedRange = from && to && day >= from && day <= to;
const sameDaySelected = from ? day.getTime() === from.getTime() : false;

if (dayIsInSelectedRange || sameDaySelected) {
resetDays();
// Our date range selection logic is very simple: the user is selecting the
// first day in their date range if from and to are set, or if they're both
// unset. Otherwise, they are selecting the last day.
if (!(startDate && endDate)) {
setToDay(day);
return;
}

if (isSelectingFirstDay(from, to, day)) {
setFromDay(day);
} else {
setFromToDay(day);
}
setFromDay(day);
updateEndDate(null);
setEnteredTo(null);
};

const handleDayMouseEnter = day => {
if (!range) return;
const { from, to } = state;
if (!isSelectingFirstDay(from, to, day)) {
setState(prevState => ({
...prevState,
enteredTo: day,
}));
if (startDate && !endDate) {
setEnteredTo(day);
}
};

const { from, enteredTo } = state;
const from = moment(startDate).toDate();
const enteredToDate = moment(enteredTo).toDate();

return (
<>
Expand All @@ -102,9 +64,8 @@ function ReactDayPicker({
className="Range"
numberOfMonths={1}
fromMonth={from}
selectedDays={[from, { from, to: enteredTo }]}
disabledDays={{ ...(range && { before: from }) }}
modifiers={{ start: from, end: enteredTo }}
selectedDays={[from, { from, to: enteredToDate }]}
modifiers={{ start: from, end: enteredToDate }}
onDayClick={handleDayClick}
onDayMouseEnter={handleDayMouseEnter}
weekdayElement={<WeekDay />}
Expand All @@ -115,21 +76,28 @@ function ReactDayPicker({

ReactDayPicker.propTypes = {
range: PropTypes.bool,
initialDates: PropTypes.arrayOf(Date),
updateStartDate: PropTypes.func,
updateEndDate: PropTypes.func,
startDate: PropTypes.string,
endDate: PropTypes.string,
};

ReactDayPicker.defaultProps = {
range: false,
initialDates: [],
updateStartDate: null,
updateEndDate: null,
startDate: null,
endDate: null,
};

const mapDispatchToProps = dispatch => ({
updateStartDate: date => dispatch(reduxUpdateStartDate(date)),
updateEndDate: date => dispatch(reduxUpdateEndDate(date)),
});

export default connect(null, mapDispatchToProps)(ReactDayPicker);
const mapStateToProps = state => ({
startDate: state.filters.startDate,
endDate: state.filters.endDate,
});

export default connect(mapStateToProps, mapDispatchToProps)(ReactDayPicker);