Skip to content

Commit

Permalink
fix: disabled modifiers with min/max range selections (#1566)
Browse files Browse the repository at this point in the history
  • Loading branch information
gpbl authored Sep 26, 2022
1 parent 235ed89 commit 146fc6a
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 101 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -147,11 +147,13 @@ describe('when a complete range of days is selected', () => {
});

describe('when the max number of the selected days is reached', () => {
const from = today;
const to = addDays(today, 6);
const selected = { from, to };
const dayPickerProps: DayPickerRangeProps = {
...initialProps,
selected,
max: Math.abs(differenceInCalendarDays(to, from))
max: 7
};
beforeAll(() => {
setup(dayPickerProps);
Expand Down Expand Up @@ -182,9 +184,6 @@ describe('when the max number of the selected days is reached', () => {
stubEvent
);
});
test('should not call onSelect', () => {
expect(dayPickerProps.onSelect).not.toHaveBeenCalled();
});
});
});

Expand Down Expand Up @@ -263,19 +262,4 @@ describe('when the minimum number of days are selected', () => {
);
});
});

describe('when "onDayClick" is called with a day before "to"', () => {
const day = subDays(to, 1);
const activeModifiers: ActiveModifiers = { selected: true };

beforeAll(() => {
result.current.onDayClick?.(day, activeModifiers, stubEvent);
});
afterAll(() => {
jest.resetAllMocks();
});
test('should not call "onSelect"', () => {
expect(dayPickerProps.onSelect).not.toHaveBeenCalled();
});
});
});
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import React, { createContext, ReactNode, useContext } from 'react';

import addDays from 'date-fns/addDays';
import differenceInCalendarDays from 'date-fns/differenceInCalendarDays';
import isAfter from 'date-fns/isAfter';
import isBefore from 'date-fns/isBefore';
import subDays from 'date-fns/subDays';

import { DayPickerBase } from 'types/DayPickerBase';
import { DayPickerRangeProps, isDayPickerRange } from 'types/DayPickerRange';
Expand Down Expand Up @@ -90,20 +90,8 @@ export function SelectRangeProviderInternal({

const onDayClick: DayClickEventHandler = (day, activeModifiers, e) => {
initialProps.onDayClick?.(day, activeModifiers, e);
const range = addToRange(day, selected);
if (
(min || max) &&
selected &&
range?.to &&
range.from &&
range.from !== range.to
) {
const diff = Math.abs(differenceInCalendarDays(range?.to, range?.from));
if ((min && diff < min) || (max && diff >= max)) {
return;
}
}
initialProps.onSelect?.(range, day, activeModifiers, e);
const newRange = addToRange(day, selected);
initialProps.onSelect?.(newRange, day, activeModifiers, e);
};

const modifiers: SelectRangeModifiers = {
Expand All @@ -128,26 +116,40 @@ export function SelectRangeProviderInternal({
}
}

if (min && selectedFrom && selectedTo) {
modifiers.disabled.push((date: Date) => {
return (
(isBefore(date, selectedFrom) &&
differenceInCalendarDays(selectedFrom, date) < min) ||
(isAfter(date, selectedTo) &&
differenceInCalendarDays(date, selectedFrom) < min)
);
});
if (min) {
if (selectedFrom && !selectedTo) {
modifiers.disabled.push({
after: subDays(selectedFrom, min - 1),
before: addDays(selectedFrom, min - 1)
});
}
if (selectedFrom && selectedTo) {
modifiers.disabled.push({
after: selectedFrom,
before: addDays(selectedFrom, min - 1)
});
}
}

if (max && selectedFrom && selectedTo) {
modifiers.disabled.push((date: Date) => {
return (
(isBefore(date, selectedFrom) &&
differenceInCalendarDays(selectedTo, date) >= max) ||
(isAfter(date, selectedTo) &&
differenceInCalendarDays(date, selectedFrom) >= max)
);
});
if (max) {
if (selectedFrom && !selectedTo) {
modifiers.disabled.push({
before: addDays(selectedFrom, -max + 1)
});
modifiers.disabled.push({
after: addDays(selectedFrom, max - 1)
});
}
if (selectedFrom && selectedTo) {
const selectedCount =
differenceInCalendarDays(selectedTo, selectedFrom) + 1;
const offset = max - selectedCount;
modifiers.disabled.push({
before: subDays(selectedFrom, offset)
});
modifiers.disabled.push({
after: addDays(selectedTo, offset)
});
}
}

return (
Expand Down
5 changes: 3 additions & 2 deletions website/examples/range-min-max.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,10 @@ export default function App() {

return (
<DayPicker
defaultMonth={new Date(2022, 8)}
mode="range"
min={1}
max={5}
min={3}
max={6}
selected={range}
onSelect={setRange}
footer={footer}
Expand Down
69 changes: 24 additions & 45 deletions website/test-integration/examples/range-min-max.test.tsx
Original file line number Diff line number Diff line change
@@ -1,67 +1,46 @@
import React from 'react';

import { render } from '@testing-library/react';
import { setDate } from 'date-fns';

import { clickDay } from 'react-day-picker/test/actions';
import { getAllEnabledDays, getDayButton } from 'react-day-picker/test/po';
import { getDayButton } from 'react-day-picker/test/po';
import { freezeBeforeAll } from 'react-day-picker/test/utils';

import Example from '@examples/range-min-max';

const today = new Date(2021, 10, 15);
const today = new Date(2022, 8, 25);

freezeBeforeAll(today);
beforeEach(() => {
render(<Example />);
});

describe('when the first day is clicked', () => {
const fromDay = new Date(2021, 10, 15);
const fromDay = setDate(today, 14);
beforeEach(() => clickDay(fromDay));
test('should disable before the allowed range', () => {
expect(getAllEnabledDays()[0]).toHaveAttribute(
'aria-label',
'1st November (Monday)'
);
test('the clicked day should be selected', () => {
expect(getDayButton(fromDay)).toHaveAttribute('aria-pressed', 'true');
});
test('should disable after the allowed range', () => {
const enabledDays = getAllEnabledDays();
expect(enabledDays[enabledDays.length - 1]).toHaveAttribute(
'aria-label',
'30th November (Tuesday)'
);
test('the days below the min value should be disabled', () => {
expect(getDayButton(setDate(today, 13))).toBeDisabled();
expect(getDayButton(setDate(today, 14))).toBeDisabled();
expect(getDayButton(setDate(today, 15))).toBeDisabled();
});
describe('when clicking a day after the from date', () => {
const toDay = new Date(2021, 10, 17);
const expectedSelectedDays = [
new Date(2021, 10, 15),
new Date(2021, 10, 16),
new Date(2021, 10, 17)
];
beforeEach(() => clickDay(toDay));
test.each(expectedSelectedDays)('%s should be selected', (day) => {
expect(getDayButton(day)).toHaveAttribute('aria-pressed', 'true');
});
test('should enable the days up to the clicked day', () => {
const enabledDays = getAllEnabledDays();
expect(enabledDays[enabledDays.length - 1]).toHaveAttribute(
'aria-label',
'19th November (Friday)'
);
});
test('the days between max and min should be enabled', () => {
expect(getDayButton(setDate(today, 9))).not.toBeDisabled();
expect(getDayButton(setDate(today, 10))).not.toBeDisabled();
expect(getDayButton(setDate(today, 11))).not.toBeDisabled();
expect(getDayButton(setDate(today, 12))).not.toBeDisabled();
expect(getDayButton(setDate(today, 16))).not.toBeDisabled();
expect(getDayButton(setDate(today, 17))).not.toBeDisabled();
expect(getDayButton(setDate(today, 18))).not.toBeDisabled();
expect(getDayButton(setDate(today, 19))).not.toBeDisabled();
});
describe('when clicking a day before the from date', () => {
const toDay = new Date(2021, 10, 11);
const expectedSelectedDays = [
new Date(2021, 10, 11),
new Date(2021, 10, 12),
new Date(2021, 10, 13),
new Date(2021, 10, 14),
new Date(2021, 10, 15)
];
beforeEach(() => clickDay(toDay));
test.each(expectedSelectedDays)('%s should be selected', (day) => {
expect(getDayButton(day)).toHaveAttribute('aria-pressed', 'true');
});
test('the days above the max value should be disabled', () => {
expect(getDayButton(setDate(today, 7))).toBeDisabled();
expect(getDayButton(setDate(today, 8))).toBeDisabled();
expect(getDayButton(setDate(today, 20))).toBeDisabled();
expect(getDayButton(setDate(today, 21))).toBeDisabled();
});
});

0 comments on commit 146fc6a

Please sign in to comment.