Skip to content

Commit d4625d4

Browse files
Merge pull request #6180 from Hacker0x01/fix/issue-5814-mask-input-clearing
fix: clear selection when masked input is cleared on blur
2 parents 668468a + e5d4948 commit d4625d4

File tree

2 files changed

+86
-0
lines changed

2 files changed

+86
-0
lines changed

src/index.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -707,6 +707,20 @@ export class DatePicker extends Component<DatePickerProps, DatePickerState> {
707707
this.props.onBlur?.(event);
708708
}
709709

710+
// If user cleared the input via a mask library (inputValue has no date-like
711+
// characters), clear the selection on blur (fixes issue #5814 with mask inputs)
712+
const { inputValue } = this.state;
713+
if (typeof inputValue === "string" && inputValue.length > 0) {
714+
// Check if input looks like a cleared mask (no alphanumeric characters)
715+
// This distinguishes between:
716+
// - "__/__/____" (cleared mask) → should clear selection
717+
// - "2025-02-45" (invalid date) → should keep previous selection
718+
const hasDateCharacters = /[a-zA-Z0-9]/.test(inputValue);
719+
if (!hasDateCharacters && this.props.selected) {
720+
this.setSelected(null, undefined, true);
721+
}
722+
}
723+
710724
this.resetInputValue();
711725

712726
if (this.state.open && this.props.open === false) {

src/test/datepicker_test.test.tsx

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2447,6 +2447,78 @@ describe("DatePicker", () => {
24472447

24482448
expect(onChangeSpy.mock.calls.at(-1)[0]).toStrictEqual([null, null]);
24492449
});
2450+
it("should keep input cleared after blur when using controlled component (issue #5814)", () => {
2451+
function ControlledDatePicker() {
2452+
const [selected, setSelected] = useState<Date | null>(
2453+
newDate("2024-12-25"),
2454+
);
2455+
2456+
return (
2457+
<DatePicker
2458+
selected={selected}
2459+
onChange={(date: Date | null) => setSelected(date)}
2460+
dateFormat="MM/dd/yyyy"
2461+
/>
2462+
);
2463+
}
2464+
2465+
const { container } = render(<ControlledDatePicker />);
2466+
const input = safeQuerySelector<HTMLInputElement>(container, "input");
2467+
2468+
// Initial value should be the formatted date
2469+
expect(input.value).toBe("12/25/2024");
2470+
2471+
// Clear the input
2472+
fireEvent.change(input, { target: { value: "" } });
2473+
2474+
// Blur the input
2475+
fireEvent.blur(input);
2476+
2477+
// After blur, the input should still be empty (not revert to the date)
2478+
expect(input.value).toBe("");
2479+
});
2480+
it("should handle mask pattern clearing (issue #5814)", () => {
2481+
// Mask libraries often show "__/__/____" when cleared instead of empty string
2482+
const onChangeSpy = jest.fn();
2483+
2484+
function MaskedDatePicker() {
2485+
const [selected, setSelected] = useState<Date | null>(
2486+
newDate("2024-12-25"),
2487+
);
2488+
2489+
const handleChange = (date: Date | null) => {
2490+
onChangeSpy(date);
2491+
setSelected(date);
2492+
};
2493+
2494+
return (
2495+
<DatePicker
2496+
selected={selected}
2497+
onChange={handleChange}
2498+
dateFormat="MM/dd/yyyy"
2499+
/>
2500+
);
2501+
}
2502+
2503+
const { container } = render(<MaskedDatePicker />);
2504+
const input = safeQuerySelector<HTMLInputElement>(container, "input");
2505+
2506+
// Initial value should be the formatted date
2507+
expect(input.value).toBe("12/25/2024");
2508+
2509+
// Simulate mask library clearing - value becomes mask pattern, not empty
2510+
fireEvent.change(input, { target: { value: "__/__/____" } });
2511+
2512+
// onChange should NOT be called with null because mask pattern isn't a valid date
2513+
// But it also shouldn't be called with the old date
2514+
// The key issue: what happens on blur?
2515+
2516+
fireEvent.blur(input);
2517+
2518+
// The input should show the mask pattern, not revert to the old date
2519+
// This is where the bug manifests - in React 18, it might revert
2520+
expect(input.value).not.toBe("12/25/2024");
2521+
});
24502522
it("should update preSelection when input changes for selectsRange", () => {
24512523
let instance: DatePicker | null = null;
24522524
const onChangeSpy = jest.fn();

0 commit comments

Comments
 (0)