Skip to content

Commit 928b2cf

Browse files
Merge pull request #6058 from Hacker0x01/fix/change-month-offset-reset
fix: reset monthSelectedIn when changeMonth is called in custom header
2 parents bd3ab11 + fe8e4b8 commit 928b2cf

File tree

4 files changed

+106
-1
lines changed

4 files changed

+106
-1
lines changed

src/calendar.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,7 @@ type CalendarProps = React.PropsWithChildren<
180180
showPreviousMonths?: boolean;
181181
monthsShown?: number;
182182
monthSelectedIn?: number;
183+
onMonthSelectedInChange?: (monthSelectedIn: number) => void;
183184
onSelect: (
184185
day: Date,
185186
event?:
@@ -455,7 +456,12 @@ export default class Calendar extends Component<CalendarProps, CalendarState> {
455456
({ date }) => ({
456457
date: setMonth(date, Number(month)),
457458
}),
458-
() => this.handleMonthChange(this.state.date),
459+
() => {
460+
this.handleMonthChange(this.state.date);
461+
// Reset monthSelectedIn to 0 so the target month appears in the leftmost position
462+
// This ensures consistent behavior when using changeMonth in custom headers
463+
this.props.onMonthSelectedInChange?.(0);
464+
},
459465
);
460466
};
461467

src/index.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1463,6 +1463,10 @@ export class DatePicker extends Component<DatePickerProps, DatePickerState> {
14631463
}
14641464
};
14651465

1466+
handleMonthSelectedInChange = (monthSelectedIn: number): void => {
1467+
this.setState({ monthSelectedIn });
1468+
};
1469+
14661470
renderCalendar = () => {
14671471
if (!this.props.inline && !this.isCalendarOpen()) {
14681472
return null;
@@ -1494,6 +1498,7 @@ export class DatePicker extends Component<DatePickerProps, DatePickerState> {
14941498
dropdownMode={
14951499
this.props.dropdownMode ?? DatePicker.defaultProps.dropdownMode
14961500
}
1501+
onMonthSelectedInChange={this.handleMonthSelectedInChange}
14971502
>
14981503
{this.props.children}
14991504
</Calendar>

src/test/calendar_test.test.tsx

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -984,6 +984,47 @@ describe("Calendar", () => {
984984
expect(header).toHaveLength(1);
985985
expect(time).toHaveLength(1);
986986
});
987+
988+
it("should display the target month in the leftmost position when changeMonth is called with monthsShown >= 2", () => {
989+
// This test verifies the fix for issue #3829
990+
// When using changeMonth in a custom header with monthsShown >= 2,
991+
// the target month should always appear in the leftmost position
992+
// regardless of which calendar panel the user last selected a date in
993+
const onMonthSelectedInChangeSpy = jest.fn();
994+
995+
const renderCustomHeaderWithMonthSelect = ({
996+
changeMonth,
997+
}: {
998+
changeMonth: (month: number) => void;
999+
}) => (
1000+
<div className="custom-header">
1001+
<select
1002+
className="month-select"
1003+
onChange={({ target: { value } }) => changeMonth(Number(value))}
1004+
>
1005+
{months.map((option, index) => (
1006+
<option key={option} value={index}>
1007+
{option}
1008+
</option>
1009+
))}
1010+
</select>
1011+
</div>
1012+
);
1013+
1014+
const { calendar } = getCalendar({
1015+
renderCustomHeader: renderCustomHeaderWithMonthSelect,
1016+
monthsShown: 2,
1017+
onMonthSelectedInChange: onMonthSelectedInChangeSpy,
1018+
});
1019+
1020+
// Select June (month index 5) from the month dropdown
1021+
const monthSelect = safeQuerySelector(calendar, ".month-select");
1022+
fireEvent.change(monthSelect, { target: { value: 5 } });
1023+
1024+
// Verify that onMonthSelectedInChange was called with 0
1025+
// This ensures the target month appears in the leftmost position
1026+
expect(onMonthSelectedInChangeSpy).toHaveBeenCalledWith(0);
1027+
});
9871028
});
9881029

9891030
describe("when showDisabledMonthNavigation is enabled", () => {

src/test/datepicker_test.test.tsx

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2897,6 +2897,59 @@ describe("DatePicker", () => {
28972897
expect(instance!.state.monthSelectedIn).toEqual(undefined);
28982898
});
28992899

2900+
it("should reset monthSelectedIn to 0 when changeMonth is called from custom header", () => {
2901+
let instance: DatePicker | null = null;
2902+
let changeMonthFn: ((month: number) => void) | null = null;
2903+
2904+
const { container } = render(
2905+
<DatePicker
2906+
ref={(node) => {
2907+
instance = node;
2908+
}}
2909+
inline
2910+
monthsShown={2}
2911+
selected={newDate("2024-06-15")}
2912+
renderCustomHeader={({ changeMonth }) => {
2913+
changeMonthFn = changeMonth;
2914+
return (
2915+
<div>
2916+
<button
2917+
data-testid="change-month-btn"
2918+
onClick={() => changeMonth(0)}
2919+
>
2920+
Go to January
2921+
</button>
2922+
</div>
2923+
);
2924+
}}
2925+
/>,
2926+
);
2927+
2928+
expect(instance).toBeTruthy();
2929+
2930+
// First, select a day in the second month panel to set monthSelectedIn to 1
2931+
const dayButtonsInSecondMonth = container
2932+
.querySelectorAll(".react-datepicker__month-container")[1]
2933+
?.querySelectorAll(
2934+
".react-datepicker__day:not(.react-datepicker__day--outside-month)",
2935+
);
2936+
expect(dayButtonsInSecondMonth).toBeTruthy();
2937+
expect(dayButtonsInSecondMonth!.length).toBeGreaterThan(0);
2938+
2939+
// Click a day in the second month to set monthSelectedIn to 1
2940+
fireEvent.click(dayButtonsInSecondMonth![10]!);
2941+
expect(instance!.state.monthSelectedIn).toEqual(1);
2942+
2943+
// Now call changeMonth from the custom header
2944+
expect(changeMonthFn).toBeTruthy();
2945+
act(() => {
2946+
changeMonthFn!(0); // Change to January
2947+
});
2948+
2949+
// monthSelectedIn should be reset to 0
2950+
expect(instance!.state.monthSelectedIn).toEqual(0);
2951+
});
2952+
29002953
it("should show the popper arrow when showPopperArrow is true", () => {
29012954
const { container } = render(<DatePicker showPopperArrow />);
29022955
const input = safeQuerySelector(container, "input");

0 commit comments

Comments
 (0)