Skip to content

fix: dropdowns should update the month based on their index #2742

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

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
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
41 changes: 41 additions & 0 deletions examples/MultipleMonthsWithDropdown.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import React from "react";

import { render, screen } from "@/test/render";
import { user } from "@/test/user";

import { MultipleMonthsWithDropdown } from "./MultipleMonthsWithDropdown";

const today = new Date(2025, 5, 16);

beforeAll(() => jest.setSystemTime(today));
afterAll(() => jest.useRealTimers());

beforeEach(() => {
render(<MultipleMonthsWithDropdown />);
});

describe("when choosing a month from the second dropdown", () => {
beforeEach(() => {
const select = screen.getAllByRole("combobox")[2];
user.selectOptions(select, "June");
});
test('should update the first month to "May 2025"', () => {
expect(screen.getAllByRole("grid")[0]).toHaveAccessibleName("May 2025");
});
test('should update the second month to "June 2025"', () => {
expect(screen.getAllByRole("grid")[1]).toHaveAccessibleName("June 2025");
});
});

describe("when choosing a year from the second dropdown", () => {
beforeEach(() => {
const select = screen.getAllByRole("combobox")[3];
user.selectOptions(select, "2022");
});
test('should update the first month to "April 2022"', () => {
expect(screen.getAllByRole("grid")[0]).toHaveAccessibleName("April 2022");
});
test('should update the second month to "May 2025"', () => {
expect(screen.getAllByRole("grid")[1]).toHaveAccessibleName("May 2022");
});
});
7 changes: 7 additions & 0 deletions examples/MultipleMonthsWithDropdown.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import React from "react";

import { DayPicker } from "react-day-picker";

export function MultipleMonthsWithDropdown() {
return <DayPicker numberOfMonths={4} captionLayout="dropdown" />;
}
1 change: 1 addition & 0 deletions examples/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export * from "./MultipleMinMax";
export * from "./MultipleRequired";
export * from "./MultipleMonths";
export * from "./MultipleMonthsPaged";
export * from "./MultipleMonthsWithDropdown";
export * from "./Numerals";
export * from "./OutsideDays";
export * from "./PastDatesDisabled";
Expand Down
36 changes: 24 additions & 12 deletions src/DayPicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -228,20 +228,26 @@ export function DayPicker(props: DayPickerProps) {
);

const handleMonthChange = useCallback(
(date: Date) => (e: ChangeEvent<HTMLSelectElement>) => {
const selectedMonth = Number(e.target.value);
const month = dateLib.setMonth(dateLib.startOfMonth(date), selectedMonth);
goToMonth(month);
},
(date: Date, displayIndex: number) =>
(e: ChangeEvent<HTMLSelectElement>) => {
const selectedMonth = Number(e.target.value);
Copy link
Preview

Copilot AI Apr 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Subtracting displayIndex from selectedMonth directly can be prone to off-by-one errors if the dropdown indices do not align exactly with the month values. Please add an inline comment explaining the rationale and ensuring that the arithmetic correctly transforms the select input value to the intended month.

Suggested change
const selectedMonth = Number(e.target.value);
const selectedMonth = Number(e.target.value);
// Subtracting `displayIndex` from `selectedMonth` ensures that the selected dropdown value
// corresponds to the correct month. This assumes that `selectedMonth` is zero-based (0 = January)
// and `displayIndex` aligns with the dropdown's offset for the displayed months.

Copilot uses AI. Check for mistakes.

const month = dateLib.setMonth(
dateLib.startOfMonth(date),
selectedMonth - displayIndex
);
goToMonth(month);
},
[dateLib, goToMonth]
);

const handleYearChange = useCallback(
(date: Date) => (e: ChangeEvent<HTMLSelectElement>) => {
const selectedYear = Number(e.target.value);
const month = dateLib.setYear(dateLib.startOfMonth(date), selectedYear);
goToMonth(month);
},
(date: Date, displayIndex: number) =>
(e: ChangeEvent<HTMLSelectElement>) => {
const selectedYear = Number(e.target.value);
let month = dateLib.setYear(dateLib.startOfMonth(date), selectedYear);
month = dateLib.addMonths(month, displayIndex * -1);
Copy link
Preview

Copilot AI Apr 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using displayIndex multiplied by -1 to adjust the month when setting the year may introduce errors if displayIndex is not consistently defined. Consider adding documentation or refactoring to clarify the expected behavior and arithmetic justification.

Suggested change
month = dateLib.addMonths(month, displayIndex * -1);
// Adjust the month based on the display index. A negative displayIndex moves the month backward.
month = dateLib.addMonths(month, -displayIndex);

Copilot uses AI. Check for mistakes.

goToMonth(month);
},
[dateLib, goToMonth]
);

Expand Down Expand Up @@ -357,7 +363,10 @@ export function DayPicker(props: DayPickerProps) {
classNames={classNames}
components={components}
disabled={Boolean(props.disableNavigation)}
onChange={handleMonthChange(calendarMonth.date)}
onChange={handleMonthChange(
calendarMonth.date,
displayIndex
)}
options={dropdownMonths}
style={styles?.[UI.Dropdown]}
value={dateLib.getMonth(calendarMonth.date)}
Expand All @@ -375,7 +384,10 @@ export function DayPicker(props: DayPickerProps) {
classNames={classNames}
components={components}
disabled={Boolean(props.disableNavigation)}
onChange={handleYearChange(calendarMonth.date)}
onChange={handleYearChange(
calendarMonth.date,
displayIndex
)}
options={dropdownYears}
style={styles?.[UI.Dropdown]}
value={dateLib.getYear(calendarMonth.date)}
Expand Down
Loading