From 604986598f22fcf99fc73c4c39e78ac4d2be7594 Mon Sep 17 00:00:00 2001 From: PathGao <42336971+PathGao@users.noreply.github.com> Date: Wed, 29 Jul 2026 02:57:46 +0800 Subject: [PATCH] fix(ui): commit date-picker selections immediately instead of on confirm (#6122) * fix(ui): commit date-picker selections immediately instead of on confirm With showTime, Ant Design's DatePicker stages a clicked date until the OK button confirms it. Closing the dropdown any other way - clicking elsewhere in the form or hitting Create/Save directly - discarded the staged date without a hint, so an inbound saved this way ended up with expiryTime=0 (never expires). The Now shortcut commits in one click, which made it look like only the current time could ever be set. Drop the confirm step (needConfirm=false) and propagate every calendar selection through onCalendarChange, so the picked date reaches the form state the moment it is clicked and can no longer be lost to a race with the submit button. Co-Authored-By: Claude Fable 5 * test(ui): pin calendar clicks committing without a confirm press A clicked day cell must reach onChange with the exact selected timestamp while the dropdown is still open, and the footer must not render a confirm button. Pins the needConfirm-free behavior so a picker dependency bump cannot silently bring the staged-value discard back. Co-Authored-By: Claude Fable 5 --------- Co-authored-by: Claude Fable 5 --- .../src/components/form/DateTimePicker.tsx | 2 + frontend/src/test/date-time-picker.test.tsx | 43 +++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 frontend/src/test/date-time-picker.test.tsx diff --git a/frontend/src/components/form/DateTimePicker.tsx b/frontend/src/components/form/DateTimePicker.tsx index 714687d14..f407e2b6f 100644 --- a/frontend/src/components/form/DateTimePicker.tsx +++ b/frontend/src/components/form/DateTimePicker.tsx @@ -121,7 +121,9 @@ export default function DateTimePicker({ onChange(next || null)} + onCalendarChange={(next) => onChange((Array.isArray(next) ? next[0] : next) || null)} showTime={showTime ? { format: 'HH:mm:ss' } : false} + needConfirm={false} format={format} placeholder={placeholder} disabled={disabled} diff --git a/frontend/src/test/date-time-picker.test.tsx b/frontend/src/test/date-time-picker.test.tsx new file mode 100644 index 000000000..9521fb0fb --- /dev/null +++ b/frontend/src/test/date-time-picker.test.tsx @@ -0,0 +1,43 @@ +import { fireEvent } from '@testing-library/react'; +import dayjs from 'dayjs'; +import type { Dayjs } from 'dayjs'; +import { describe, expect, it, vi } from 'vitest'; + +import DateTimePicker from '@/components/form/DateTimePicker'; +import { renderWithProviders } from './test-utils'; + +function openPicker(): void { + const input = document.querySelector('.ant-picker input'); + if (!input) throw new Error('picker input not rendered'); + fireEvent.mouseDown(input); + fireEvent.click(input); +} + +function clickDayCell(title: string): void { + const cell = document.querySelector(`.ant-picker-cell[title="${title}"] .ant-picker-cell-inner`); + if (!cell) throw new Error(`day cell ${title} not rendered`); + fireEvent.click(cell); +} + +describe('DateTimePicker', () => { + it('commits a clicked calendar date without an OK press', () => { + const onChange = vi.fn<(next: Dayjs | null) => void>(); + renderWithProviders(); + + openPicker(); + const tomorrow = dayjs().add(1, 'day').format('YYYY-MM-DD'); + clickDayCell(tomorrow); + + expect(onChange).toHaveBeenCalled(); + const committed = onChange.mock.calls.at(-1)?.[0]; + expect(committed?.format('YYYY-MM-DD HH:mm:ss')).toBe(`${tomorrow} 00:00:00`); + }); + + it('renders no OK confirm button in the picker footer', () => { + renderWithProviders(); + + openPicker(); + + expect(document.querySelector('.ant-picker-ok')).toBeNull(); + }); +});