diff --git a/README.md b/README.md index 590d0ba3d..27bc4ae3f 100644 --- a/README.md +++ b/README.md @@ -113,7 +113,8 @@ scroll | Object | { enabled: false }| infinite showMonthArrow | Boolean | true | show/hide month arrow button navigatorRenderer | Func | | renderer for focused date navigation area. fn(currentFocusedDate: Date, changeShownDate: func, props: object) ranges | *Object[] | [] | Defines ranges. array of range object -moveRangeOnFirstSelection(DateRange) | Boolean | false | move range on startDate selection. Otherwise endDate will replace with startDate. +moveRangeOnFirstSelection(DateRange) | Boolean | false | move range on startDate selection. Otherwise endDate will replace with startDate unless `retainEndDateOnFirstSelection` is set to true. +retainEndDateOnFirstSelection(DateRange) | Boolean | false | Retain end date when the start date is changed, unless start date is later than end date. Ignored if `moveRangeOnFirstSelection` is set to true. onChange(Calendar) | Func | | callback function for date changes. fn(date: Date) onChange(DateRange) | Func | | callback function for range changes. fn(changes). changes contains changed ranges with new `startDate`/`endDate` properties. color(Calendar) | String | `#3d91ff` | defines color for selected date in Calendar diff --git a/src/components/DateRange/README.md b/src/components/DateRange/README.md index f26451ce5..73ad65a8b 100644 --- a/src/components/DateRange/README.md +++ b/src/components/DateRange/README.md @@ -3,9 +3,10 @@ This component extends all the props of **[Calendar](#calendar)** component. In | Prop Name | Type | |---|---| | **moveRangeOnFirstSelection** | boolean | -| **onRangeFocusChange** | function | -| **rangeColors** | array | -| **ranges** | array | +| **retainEndDateOnFirstSelection** | boolean | +| **onRangeFocusChange** | function | +| **rangeColors** | array | +| **ranges** | array | #### Example: Editable Date Inputs diff --git a/src/components/DateRange/index.js b/src/components/DateRange/index.js index e2d968ead..3c963ecd2 100644 --- a/src/components/DateRange/index.js +++ b/src/components/DateRange/index.js @@ -18,22 +18,40 @@ class DateRange extends Component { } calcNewSelection = (value, isSingleValue = true) => { const focusedRange = this.props.focusedRange || this.state.focusedRange; - const { ranges, onChange, maxDate, moveRangeOnFirstSelection, disabledDates } = this.props; + const { + ranges, + onChange, + maxDate, + moveRangeOnFirstSelection, + retainEndDateOnFirstSelection, + disabledDates, + } = this.props; const focusedRangeIndex = focusedRange[0]; const selectedRange = ranges[focusedRangeIndex]; if (!selectedRange || !onChange) return {}; - let { startDate, endDate } = selectedRange; - if (!endDate) endDate = new Date(startDate); + const now = new Date(); let nextFocusRange; if (!isSingleValue) { startDate = value.startDate; endDate = value.endDate; } else if (focusedRange[1] === 0) { // startDate selection - const dayOffset = differenceInCalendarDays(endDate, startDate); + const dayOffset = differenceInCalendarDays(endDate || now, startDate); + const calculateEndDate = () => { + if (moveRangeOnFirstSelection) { + return addDays(value, dayOffset); + } + if (retainEndDateOnFirstSelection) { + if (!endDate || isBefore(value, endDate)) { + return endDate; + } + return value; + } + return value || now; + }; startDate = value; - endDate = moveRangeOnFirstSelection ? addDays(value, dayOffset) : value; + endDate = calculateEndDate(); if (maxDate) endDate = min([endDate, maxDate]); nextFocusRange = [focusedRange[0], 1]; } else { @@ -131,6 +149,7 @@ DateRange.defaultProps = { classNames: {}, ranges: [], moveRangeOnFirstSelection: false, + retainEndDateOnFirstSelection: false, rangeColors: ['#3d91ff', '#3ecf8e', '#fed14c'], disabledDates: [], }; @@ -142,6 +161,7 @@ DateRange.propTypes = { className: PropTypes.string, ranges: PropTypes.arrayOf(rangeShape), moveRangeOnFirstSelection: PropTypes.bool, + retainEndDateOnFirstSelection: PropTypes.bool, }; export default DateRange; diff --git a/src/components/DateRange/index.test.js b/src/components/DateRange/index.test.js index e26aa6630..9c3fcdf1d 100644 --- a/src/components/DateRange/index.test.js +++ b/src/components/DateRange/index.test.js @@ -1,7 +1,84 @@ +import React from 'react'; +import { subDays, addDays, isSameDay } from 'date-fns'; import DateRange from '../DateRange'; +import renderer from 'react-test-renderer'; + +let testRenderer = null; +let instance = null; +const endDate = new Date(); +const startDate = subDays(endDate, 7); + +const commonProps = { + ranges: [{ startDate, endDate, key: 'selection' }], + onChange: () => {}, + moveRangeOnFirstSelection: false, +}; + +const compareRanges = (newRange, assertionRange) => { + ['startDate', 'endDate'].forEach(key => { + if (!newRange[key] || !assertionRange[key]) { + return expect(newRange[key]).toEqual(assertionRange[key]); + } + return expect(isSameDay(newRange[key], assertionRange[key])).toEqual(true); + }); +}; + +beforeEach(() => { + testRenderer = renderer.create(); + instance = testRenderer.getInstance(); +}); describe('DateRange', () => { test('Should resolve', () => { expect(DateRange).toEqual(expect.anything()); }); + + test('calculate new selection by resetting end date', () => { + const methodResult = instance.calcNewSelection(subDays(endDate, 10), true); + compareRanges(methodResult.range, { + startDate: subDays(endDate, 10), + endDate: subDays(endDate, 10), + }); + }); + + test('calculate new selection by resetting end date if start date is not before', () => { + const methodResult = instance.calcNewSelection(addDays(endDate, 2), true); + compareRanges(methodResult.range, { + startDate: addDays(endDate, 2), + endDate: addDays(endDate, 2), + }); + }); + + test('calculate new selection based on moveRangeOnFirstSelection prop', () => { + testRenderer.update(); + const methodResult = instance.calcNewSelection(subDays(endDate, 10), true); + compareRanges(methodResult.range, { + startDate: subDays(endDate, 10), + endDate: subDays(endDate, 3), + }); + }); + + test('calculate new selection by retaining end date, based on retainEndDateOnFirstSelection prop', () => { + testRenderer.update(); + const methodResult = instance.calcNewSelection(subDays(endDate, 10), true); + compareRanges(methodResult.range, { + startDate: subDays(endDate, 10), + endDate, + }); + }); + + test('calculate new selection by retaining the unset end date, based on retainEndDateOnFirstSelection prop', () => { + testRenderer.update( + + ); + const methodResult = instance.calcNewSelection(subDays(endDate, 10), true); + compareRanges(methodResult.range, { + startDate: subDays(endDate, 10), + endDate: null, + }); + }); });