From 35e5a81641e53aa57241744921dc9b2769ab2709 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20Su=CC=88kan?= Date: Sun, 13 Jun 2021 18:15:34 +0300 Subject: [PATCH 1/4] improve calculate new selection behavior --- src/components/DateRange/index.js | 8 ++++- src/components/DateRange/index.test.js | 50 ++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/src/components/DateRange/index.js b/src/components/DateRange/index.js index e2d968ead..3223471dc 100644 --- a/src/components/DateRange/index.js +++ b/src/components/DateRange/index.js @@ -32,8 +32,14 @@ class DateRange extends Component { } else if (focusedRange[1] === 0) { // startDate selection const dayOffset = differenceInCalendarDays(endDate, startDate); + const calculateEndDate = () => { + if (moveRangeOnFirstSelection) { + return addDays(value, dayOffset); + } + return !isBefore(value, endDate) ? value : endDate; + }; startDate = value; - endDate = moveRangeOnFirstSelection ? addDays(value, dayOffset) : value; + endDate = calculateEndDate(); if (maxDate) endDate = min([endDate, maxDate]); nextFocusRange = [focusedRange[0], 1]; } else { diff --git a/src/components/DateRange/index.test.js b/src/components/DateRange/index.test.js index e26aa6630..daf513dbe 100644 --- a/src/components/DateRange/index.test.js +++ b/src/components/DateRange/index.test.js @@ -1,7 +1,57 @@ +import React from 'react'; +import { subDays, addDays, isSameDay } from 'date-fns'; import DateRange from '../DateRange'; +import renderer from 'react-test-renderer'; + +let testRenderer = null; +const endDate = new Date(); +const startDate = subDays(endDate, 7); + +const commonProps = { + ranges: [{ startDate, endDate, key: 'selection' }], + onChange: () => {}, + moveRangeOnFirstSelection: false, +}; + +const compareRanges = (newRange, assertionRange) => { + expect(isSameDay(newRange.startDate, assertionRange.startDate)).toEqual(true); + expect(isSameDay(newRange.endDate, assertionRange.endDate)).toEqual(true); +}; + +beforeEach(() => { + testRenderer = renderer.create(); +}); describe('DateRange', () => { test('Should resolve', () => { expect(DateRange).toEqual(expect.anything()); }); + + test('calculate new selection without moving end date', () => { + const instance = testRenderer.getInstance(); + const methodResult = instance.calcNewSelection(subDays(endDate, 10), true); + compareRanges(methodResult.range, { + startDate: subDays(endDate, 10), + endDate, + }); + }); + + test('calculate new selection by resetting end date if start date is not before', () => { + const instance = testRenderer.getInstance(); + 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 instance = testRenderer.getInstance(); + const methodResult = instance.calcNewSelection(subDays(endDate, 10), true); + compareRanges(methodResult.range, { + startDate: subDays(endDate, 10), + endDate: subDays(endDate, 3), + }); + }); }); From d20e0ea4a77234e4937f21ddd5d4ee420950bf55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20Su=CC=88kan?= Date: Sun, 13 Jun 2021 18:31:43 +0300 Subject: [PATCH 2/4] let null end date stay as is on start date selection --- src/components/DateRange/index.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/DateRange/index.js b/src/components/DateRange/index.js index 3223471dc..2f9aebf6a 100644 --- a/src/components/DateRange/index.js +++ b/src/components/DateRange/index.js @@ -24,7 +24,6 @@ class DateRange extends Component { if (!selectedRange || !onChange) return {}; let { startDate, endDate } = selectedRange; - if (!endDate) endDate = new Date(startDate); let nextFocusRange; if (!isSingleValue) { startDate = value.startDate; @@ -36,6 +35,10 @@ class DateRange extends Component { if (moveRangeOnFirstSelection) { return addDays(value, dayOffset); } + // allow continous range to stay as-is + if (!endDate) { + return endDate; + } return !isBefore(value, endDate) ? value : endDate; }; startDate = value; From a530f57987f25026e7c7928b66f2706d8aa11f12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20Su=CC=88kan?= Date: Sun, 13 Jun 2021 21:00:38 +0300 Subject: [PATCH 3/4] introduce retainEndDateOnFirstSelection prop for backwards compatibility --- README.md | 3 +- src/components/DateRange/README.md | 7 +++-- src/components/DateRange/index.js | 26 ++++++++++++----- src/components/DateRange/index.test.js | 39 +++++++++++++++++++++----- 4 files changed, 57 insertions(+), 18 deletions(-) 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 2f9aebf6a..019b94d2d 100644 --- a/src/components/DateRange/index.js +++ b/src/components/DateRange/index.js @@ -18,28 +18,38 @@ 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; + 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); } - // allow continous range to stay as-is - if (!endDate) { - return endDate; + if (retainEndDateOnFirstSelection) { + // allow the unset end date to stay as-is + if (!endDate) { + return endDate; + } + return !isBefore(value, endDate) ? value : endDate; } - return !isBefore(value, endDate) ? value : endDate; + return value || now; }; startDate = value; endDate = calculateEndDate(); @@ -140,6 +150,7 @@ DateRange.defaultProps = { classNames: {}, ranges: [], moveRangeOnFirstSelection: false, + retainEndDateOnFirstSelection: false, rangeColors: ['#3d91ff', '#3ecf8e', '#fed14c'], disabledDates: [], }; @@ -151,6 +162,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 daf513dbe..d593d7993 100644 --- a/src/components/DateRange/index.test.js +++ b/src/components/DateRange/index.test.js @@ -4,6 +4,7 @@ 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); @@ -14,12 +15,17 @@ const commonProps = { }; const compareRanges = (newRange, assertionRange) => { - expect(isSameDay(newRange.startDate, assertionRange.startDate)).toEqual(true); - expect(isSameDay(newRange.endDate, assertionRange.endDate)).toEqual(true); + ['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', () => { @@ -27,17 +33,15 @@ describe('DateRange', () => { expect(DateRange).toEqual(expect.anything()); }); - test('calculate new selection without moving end date', () => { - const instance = testRenderer.getInstance(); + test('calculate new selection by resetting end date', () => { const methodResult = instance.calcNewSelection(subDays(endDate, 10), true); compareRanges(methodResult.range, { startDate: subDays(endDate, 10), - endDate, + endDate: subDays(endDate, 10), }); }); test('calculate new selection by resetting end date if start date is not before', () => { - const instance = testRenderer.getInstance(); const methodResult = instance.calcNewSelection(addDays(endDate, 2), true); compareRanges(methodResult.range, { startDate: addDays(endDate, 2), @@ -47,11 +51,32 @@ describe('DateRange', () => { test('calculate new selection based on moveRangeOnFirstSelection prop', () => { testRenderer.update(); - const instance = testRenderer.getInstance(); 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, + }); + }); }); From 67aee9f4a4ecbf0d253a4b5c82add25045d7e658 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20Su=CC=88kan?= Date: Sun, 13 Jun 2021 21:08:16 +0300 Subject: [PATCH 4/4] linter fixes, pr suggestion --- src/components/DateRange/index.js | 5 ++--- src/components/DateRange/index.test.js | 14 ++++++++------ 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/components/DateRange/index.js b/src/components/DateRange/index.js index 019b94d2d..3c963ecd2 100644 --- a/src/components/DateRange/index.js +++ b/src/components/DateRange/index.js @@ -43,11 +43,10 @@ class DateRange extends Component { return addDays(value, dayOffset); } if (retainEndDateOnFirstSelection) { - // allow the unset end date to stay as-is - if (!endDate) { + if (!endDate || isBefore(value, endDate)) { return endDate; } - return !isBefore(value, endDate) ? value : endDate; + return value; } return value || now; }; diff --git a/src/components/DateRange/index.test.js b/src/components/DateRange/index.test.js index d593d7993..9c3fcdf1d 100644 --- a/src/components/DateRange/index.test.js +++ b/src/components/DateRange/index.test.js @@ -20,7 +20,7 @@ const compareRanges = (newRange, assertionRange) => { return expect(newRange[key]).toEqual(assertionRange[key]); } return expect(isSameDay(newRange[key], assertionRange[key])).toEqual(true); - }) + }); }; beforeEach(() => { @@ -68,11 +68,13 @@ describe('DateRange', () => { }); test('calculate new selection by retaining the unset end date, based on retainEndDateOnFirstSelection prop', () => { - testRenderer.update(); + testRenderer.update( + + ); const methodResult = instance.calcNewSelection(subDays(endDate, 10), true); compareRanges(methodResult.range, { startDate: subDays(endDate, 10),