Repository: gpbl/react-day-picker (2025-08-05T19:50:07Z)

Issue:
bug: selecting the same day in range mode when range is open
Based on the discussion https://github.com/gpbl/react-day-picker/issues/2796#issuecomment-3146073931

## To reproduce

Using the following Code Sandbox: https://codesandbox.io/p/devbox/daypicker-bug-report-forked-wwk9mh

Click on any day in the calendar, then click again on the same day.

## Actual Behavior

When providing a date range with only `from` date, clicking on the same day unselects the date range.

## Expected Behavior

When providing a date range with only `from` date, clicking on the same day completes the date range selection, with the range being the same day.

## Notes

This seems like an edge case when `min` is equal to `zero`.



Discussion:
I'm not sure I get the use case. I understand this: 

Given:

- the initial range is open (no `to` is set)
- `min` is undefined (or `0`)

Then, 

- clicking the `from` date the first time should close the range with that date, instead of resetting it (or instead ignoring the click when `required` is set)

Correct?
@gpbl  
Exactly 👍 
It makes more sense to close the range first.



Patch:
diff --git a/examples/RangeResetSelection.tsx b/examples/RangeResetSelection.tsx
new file mode 100644
index 000000000..6d1adf2dd
--- /dev/null
+++ b/examples/RangeResetSelection.tsx
@@ -0,0 +1,49 @@
+import { format } from "date-fns";
+import React, { useState } from "react";
+
+import {
+  type DateRange,
+  type DayEventHandler,
+  DayPicker,
+  type OnSelectHandler,
+} from "react-day-picker";
+
+export function RangeResetSelection() {
+  const [selected, setSelected] = useState<DateRange>();
+
+  // use onSelect event which properly handles valid range selection
+  // based on valid days in the calendar
+  const handleSelect: OnSelectHandler<DateRange | undefined> = (range) => {
+    // the other cases are handled by onDayClick handler
+    if (selected?.from && !selected.to) {
+      setSelected(range);
+    }
+  };
+
+  const handleDayClick: DayEventHandler<React.MouseEvent> = (date) => {
+    // handled by onSelect handler
+    if (selected?.from && !selected.to) {
+      return;
+    }
+    setSelected({ from: date });
+  };
+
+  return (
+    <DayPicker
+      mode="range"
+      selected={selected}
+      onSelect={handleSelect}
+      onDayClick={handleDayClick}
+      footer={
+        <div>
+          <p data-testid="from">
+            from: {selected?.from && format(selected?.from, "yyyy-MM-dd")}
+          </p>
+          <p data-testid="to">
+            to: {selected?.to && format(selected?.to, "yyyy-MM-dd")}
+          </p>
+        </div>
+      }
+    />
+  );
+}
diff --git a/examples/index.ts b/examples/index.ts
index d3a4cbf1a..812a39cb4 100644
--- a/examples/index.ts
+++ b/examples/index.ts
@@ -68,6 +68,7 @@ export * from "./RangeLong";
 export * from "./RangeLongExcludeDisabled";
 export * from "./RangeMinMax";
 export * from "./RangeRequired";
+export * from "./RangeResetSelection";
 export * from "./RangeShiftKey";
 export * from "./Rtl";
 export * from "./Single";
diff --git a/src/utils/addToRange.ts b/src/utils/addToRange.ts
index b983b5765..62dd07cd3 100644
--- a/src/utils/addToRange.ts
+++ b/src/utils/addToRange.ts
@@ -34,7 +34,9 @@ export function addToRange(
     // adding date to an incomplete range
     if (isSameDay(from, date)) {
       // adding a date equal to the start of the range
-      if (required) {
+      if (min === 0) {
+        range = { from, to: date };
+      } else if (required) {
         range = { from, to: undefined };
       } else {
         range = undefined;


Test patch:
diff --git a/examples/RangeResetSelection.test.tsx b/examples/RangeResetSelection.test.tsx
new file mode 100644
index 000000000..86bb22d70
--- /dev/null
+++ b/examples/RangeResetSelection.test.tsx
@@ -0,0 +1,41 @@
+import { screen } from "@testing-library/react";
+import { addDays } from "date-fns";
+import React from "react";
+import { dateButton } from "@/test/elements";
+import { render } from "@/test/render";
+import { user } from "@/test/user";
+import { RangeResetSelection } from "./RangeResetSelection";
+
+const today = new Date(2022, 8, 12);
+
+beforeAll(() => jest.setSystemTime(today));
+afterAll(() => jest.useRealTimers());
+
+beforeEach(() => render(<RangeResetSelection />));
+
+const getFrom = () => screen.getByTestId("from");
+const getTo = () => screen.getByTestId("to");
+
+test("select same day range", async () => {
+  await user.click(dateButton(today));
+  expect(getFrom()).toHaveTextContent("from: 2022-09-12");
+  expect(getTo()).toHaveTextContent("to:");
+  await user.click(dateButton(today));
+  expect(getFrom()).toHaveTextContent("from: 2022-09-12");
+  expect(getTo()).toHaveTextContent("to: 2022-09-12");
+});
+
+test("start range after click on day with range selected", async () => {
+  await user.click(dateButton(today));
+  expect(getFrom()).toHaveTextContent("from: 2022-09-12");
+  expect(getTo()).toHaveTextContent("to:");
+  await user.click(dateButton(addDays(today, 1)));
+  expect(getFrom()).toHaveTextContent("from: 2022-09-12");
+  expect(getTo()).toHaveTextContent("to: 2022-09-13");
+  await user.click(dateButton(addDays(today, 4)));
+  expect(getFrom()).toHaveTextContent("from: 2022-09-16");
+  expect(getTo()).toHaveTextContent("to:");
+  await user.click(dateButton(today));
+  expect(getFrom()).toHaveTextContent("from: 2022-09-12");
+  expect(getTo()).toHaveTextContent("to: 2022-09-16");
+});
diff --git a/src/utils/addToRange.test.ts b/src/utils/addToRange.test.ts
index 49f71b30e..a70e1cdfb 100644
--- a/src/utils/addToRange.test.ts
+++ b/src/utils/addToRange.test.ts
@@ -13,12 +13,24 @@ describe("addToRange", () => {
     expect(range).toEqual({ from: date, to: date });
   });
 
-  test("add a date to an incomplete range with same start date", () => {
+  test("add a date to an incomplete range with same start date and min = 0", () => {
     const date = new Date(2022, 0, 1);
     const range = addToRange(date, { from: date, to: undefined });
+    expect(range).toEqual({ from: date, to: date });
+  });
+
+  test("add a date to an incomplete range with same start date and min > 0", () => {
+    const date = new Date(2022, 0, 1);
+    const range = addToRange(date, { from: date, to: undefined }, 1, 0, false);
     expect(range).toEqual(undefined);
   });
 
+  test("add a date to an incomplete range with same start date and min > 0 and required", () => {
+    const date = new Date(2022, 0, 1);
+    const range = addToRange(date, { from: date, to: undefined }, 1, 0, true);
+    expect(range).toEqual({ from: date, to: undefined });
+  });
+
   test("add a date to an incomplete range with earlier date", () => {
     const from = new Date(2022, 0, 1);
     const earlierDate = new Date(2021, 11, 31);