Skip to content
Open
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 packages/react-date-picker/src/DatePicker.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,34 @@ describe('DatePicker', () => {
expect(calendar2).toBeInTheDocument();
});

it('resets calendar view to selected value when reopened', async () => {
const value = new Date(2026, 3, 15);
const { container } = await render(
<DatePicker {...defaultProps} locale="en-US" value={value} onChange={() => {}} />,
);

const button = page.getByTestId('calendar-button');
await userEvent.click(button);

const navigationLabel = container.querySelector('button.react-calendar__navigation__label');
expect(navigationLabel).toHaveTextContent('April 2026');

const arrows = container.querySelectorAll('button.react-calendar__navigation__arrow');
const nextArrow = arrows[2] as HTMLElement; // Index 2 is the next month arrow (›)

await userEvent.click(nextArrow);

expect(navigationLabel).toHaveTextContent('May 2026');

await userEvent.click(button);
await userEvent.click(button);

const reopenedNavigationLabel = container.querySelector(
'button.react-calendar__navigation__label',
);
expect(reopenedNavigationLabel).toHaveTextContent('April 2026');
});

function triggerFocusInEvent(locator: Locator) {
const element = locator.element();

Expand Down Expand Up @@ -590,6 +618,19 @@ describe('DatePicker', () => {
expect(onChange).toHaveBeenCalledWith(new Date(2023, 0, 1));
});

it('updates the displayed value when selecting a date from the calendar without a controlled value', async () => {
const { container } = await render(<DatePicker {...defaultProps} />);

const firstTile = container.querySelector('.react-calendar__tile') as HTMLButtonElement;
const nativeInput = container.querySelector('input[type="date"]') as HTMLInputElement;

await act(async () => {
await userEvent.click(firstTile);
});

expect(nativeInput.value).not.toBe('');
});

it('does not call onChange for leap day until yyyy year is complete', async () => {
const onChange = vi.fn();

Expand Down
35 changes: 32 additions & 3 deletions packages/react-date-picker/src/DatePicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@ import type {
const baseClassName = 'react-date-picker';
const outsideActionEvents = ['mousedown', 'focusin', 'touchstart'] as const;

function getActiveStartDateFromValue(value: LooseValue | undefined) {
if (Array.isArray(value)) {
value = value[0];
}

return value instanceof Date ? value : undefined;
}

const iconProps = {
xmlns: 'http://www.w3.org/2000/svg',
width: 19,
Expand Down Expand Up @@ -369,20 +377,35 @@ export default function DatePicker(props: DatePickerProps): React.ReactElement {
} = props;

const [isOpen, setIsOpen] = useState<boolean | null>(isOpenProps);
const [activeStartDate, setActiveStartDate] = useState<Date | null | undefined>(
getActiveStartDateFromValue(value),
);
const [selectedValue, setSelectedValue] = useState<LooseValue | undefined>(value);
const wrapper = useRef<HTMLDivElement>(null);
const calendarWrapper = useRef<HTMLDivElement>(null);

useEffect(() => {
if (isOpen) {
setActiveStartDate(getActiveStartDateFromValue(selectedValue ?? value));
}
}, [isOpen, selectedValue, value]);

useEffect(() => {
setIsOpen(isOpenProps);
}, [isOpenProps]);

useEffect(() => {
setSelectedValue(value);
}, [value]);

function openCalendar({ reason }: { reason: OpenReason }) {
if (shouldOpenCalendar) {
if (!shouldOpenCalendar({ reason })) {
return;
}
}

setActiveStartDate(getActiveStartDateFromValue(value));
setIsOpen(true);

if (onCalendarOpen) {
Expand Down Expand Up @@ -416,6 +439,8 @@ export default function DatePicker(props: DatePickerProps): React.ReactElement {
}

function onChange(value: Value, shouldCloseCalendar: boolean = shouldCloseCalendarOnSelect) {
setSelectedValue(value);

if (shouldCloseCalendar) {
closeCalendar({ reason: 'select' });
}
Expand Down Expand Up @@ -510,7 +535,8 @@ export default function DatePicker(props: DatePickerProps): React.ReactElement {
}, [handleOutsideActionListeners]);

function renderInputs() {
const [valueFrom] = Array.isArray(value) ? value : [value];
const currentValue = selectedValue ?? value;
const [valueFrom] = Array.isArray(currentValue) ? currentValue : [currentValue];

const ariaLabelProps = {
dayAriaLabel,
Expand Down Expand Up @@ -583,19 +609,22 @@ export default function DatePicker(props: DatePickerProps): React.ReactElement {
return null;
}

const { calendarProps, portalContainer, value } = props;
const { calendarProps, portalContainer } = props;
const currentValue = selectedValue ?? value;

const className = `${baseClassName}__calendar`;
const classNames = clsx(className, `${className}--${isOpen ? 'open' : 'closed'}`);

const calendar = (
<Calendar
activeStartDate={activeStartDate ?? undefined}
locale={locale}
maxDate={maxDate}
maxDetail={maxDetail}
minDate={minDate}
onActiveStartDateChange={({ activeStartDate }) => setActiveStartDate(activeStartDate)}
onChange={(value) => onChange(value)}
value={value}
value={currentValue}
{...calendarProps}
/>
);
Expand Down