ソースを参照

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 <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: Claude Fable 5 <[email protected]>
PathGao 9 時間 前
コミット
604986598f

+ 2 - 0
frontend/src/components/form/DateTimePicker.tsx

@@ -121,7 +121,9 @@ export default function DateTimePicker({
     <DatePicker
       value={value}
       onChange={(next) => 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}

+ 43 - 0
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(<DateTimePicker value={null} onChange={onChange} />);
+
+    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(<DateTimePicker value={null} onChange={vi.fn()} />);
+
+    openPicker();
+
+    expect(document.querySelector('.ant-picker-ok')).toBeNull();
+  });
+});