From 1347583e9a984d466c501948fe6ac68c3ea46f0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=AA=20Nh=C3=A2n=20Ph=E1=BB=A5ng?= Date: Mon, 8 Dec 2025 10:46:11 +0700 Subject: [PATCH] Update DateTimeFields: improve UI layout and adjust time change logic - Add separator '-' between start time and end time in normal mode - Fix layout logic: keep single row for allday events in normal mode - Remove auto-adjustment of start time when end time changes - Update test case to match new behavior --- __test__/components/DateTimeField.test.tsx | 28 +++-- .../Event/components/DateTimeFields.tsx | 106 ++++++++++++------ 2 files changed, 93 insertions(+), 41 deletions(-) diff --git a/__test__/components/DateTimeField.test.tsx b/__test__/components/DateTimeField.test.tsx index 3ad2448..c41c36b 100644 --- a/__test__/components/DateTimeField.test.tsx +++ b/__test__/components/DateTimeField.test.tsx @@ -97,7 +97,7 @@ describe("DateTimeFields", () => { ); }); - it("moves START backward when END moves before START (normal mode)", async () => { + it("does NOT move START backward when END moves before START (normal mode)", async () => { await renderField({ startDate: "2025-01-01", startTime: "10:00", @@ -114,9 +114,9 @@ describe("DateTimeFields", () => { expect(mockHandlers.onEndTimeChange).toHaveBeenCalledWith("08:00") ); - await waitFor(() => - expect(mockHandlers.onStartTimeChange).toHaveBeenCalledWith("07:00") - ); + // Start time should NOT be automatically adjusted when end time changes + expect(mockHandlers.onStartTimeChange).not.toHaveBeenCalled(); + expect(mockHandlers.onStartDateChange).not.toHaveBeenCalled(); }); it("moves START backward properly when END date jumps before START date", async () => { @@ -256,9 +256,21 @@ describe("DateTimeFields", () => { const endDateInput = screen.getByTestId("end-date-input"); const endTimeInput = screen.getByTestId("end-time-input"); - expect(startDateInput).toHaveAttribute("aria-label", "dateTimeFields.startDate"); - expect(startTimeInput).toHaveAttribute("aria-label", "dateTimeFields.startTime"); - expect(endDateInput).toHaveAttribute("aria-label", "dateTimeFields.endDate"); - expect(endTimeInput).toHaveAttribute("aria-label", "dateTimeFields.endTime"); + expect(startDateInput).toHaveAttribute( + "aria-label", + "dateTimeFields.startDate" + ); + expect(startTimeInput).toHaveAttribute( + "aria-label", + "dateTimeFields.startTime" + ); + expect(endDateInput).toHaveAttribute( + "aria-label", + "dateTimeFields.endDate" + ); + expect(endTimeInput).toHaveAttribute( + "aria-label", + "dateTimeFields.endTime" + ); }); }); diff --git a/src/components/Event/components/DateTimeFields.tsx b/src/components/Event/components/DateTimeFields.tsx index 496b19a..1e6414b 100644 --- a/src/components/Event/components/DateTimeFields.tsx +++ b/src/components/Event/components/DateTimeFields.tsx @@ -94,8 +94,7 @@ export const DateTimeFields: React.FC = ({ const isExpanded = showMore; const shouldShowEndDateNormal = allday || showEndDate; - const shouldShowFullFieldsInNormal = - (!allday && hasEndDateChanged) || spansMultipleDays; + const shouldShowFullFieldsInNormal = isExpanded; const showSingleDateField = !isExpanded && !shouldShowEndDateNormal && !shouldShowFullFieldsInNormal; @@ -205,22 +204,8 @@ export const DateTimeFields: React.FC = ({ const newEnd = toDateTime(endDate, newTimeStr); const currentStart = toDateTime(startDate, startTime); - // If end is before start, adjust start to maintain duration - if (newEnd.isBefore(currentStart)) { - const duration = initialDurationRef.current ?? getCurrentDuration(); - const newStart = newEnd.subtract(duration, "minute"); - - const newStartDate = dtDate(newStart); - const newStartTime = dtTime(newStart); - - if (newStartDate !== startDate) { - onStartDateChange(newStartDate); - } - if (newStartTime !== startTime) { - onStartTimeChange(newStartTime); - } - } else { - // Update duration when user changes end + // Update duration when user changes end (if valid) + if (!newEnd.isBefore(currentStart)) { initialDurationRef.current = newEnd.diff(currentStart, "minute"); } @@ -245,7 +230,11 @@ export const DateTimeFields: React.FC = ({ [endTime] ); - const getSlotProps = (testId: string, hasError = false, testLabel?: string) => ({ + const getSlotProps = ( + testId: string, + hasError = false, + testLabel?: string + ) => ({ textField: { size: "small" as const, margin: "dense" as const, @@ -253,21 +242,25 @@ export const DateTimeFields: React.FC = ({ InputLabelProps: { shrink: true }, error: hasError, sx: { width: "100%" }, - inputProps: { + inputProps: { "data-testid": testId, ...(testLabel ? { "aria-label": testLabel } : {}), }, }, }); - const getFieldSlotProps = (testId: string, hasError = false, testLabel?: string) => ({ + const getFieldSlotProps = ( + testId: string, + hasError = false, + testLabel?: string + ) => ({ size: "small" as const, margin: "dense" as const, fullWidth: true, InputLabelProps: { shrink: true }, error: hasError, sx: { width: "100%" }, - inputProps: { + inputProps: { "data-testid": testId, ...(testLabel ? { "aria-label": testLabel } : {}), }, @@ -316,7 +309,7 @@ export const DateTimeFields: React.FC = ({ flexDirection="column" sx={{ maxWidth: showMore ? "calc(100% - 145px)" : "100%" }} > - {isExpanded || shouldShowFullFieldsInNormal ? ( + {shouldShowFullFieldsInNormal ? ( <> @@ -326,8 +319,16 @@ export const DateTimeFields: React.FC = ({ onChange={handleStartDateChange} slots={{ field: ReadOnlyDateField }} slotProps={{ - ...getSlotProps("start-date-input", false, t("dateTimeFields.startDate")), - field: getFieldSlotProps("start-date-input", false, t("dateTimeFields.startDate")) as any, + ...getSlotProps( + "start-date-input", + false, + t("dateTimeFields.startDate") + ), + field: getFieldSlotProps( + "start-date-input", + false, + t("dateTimeFields.startDate") + ) as any, }} /> @@ -343,7 +344,11 @@ export const DateTimeFields: React.FC = ({ timeSteps={{ minutes: 30 }} slots={{ actionBar: () => null }} slotProps={{ - ...getSlotProps("start-time-input", false, t("dateTimeFields.startTime")), + ...getSlotProps( + "start-time-input", + false, + t("dateTimeFields.startTime") + ), openPickerButton: { sx: { display: "none" } }, popper: { sx: { @@ -355,7 +360,11 @@ export const DateTimeFields: React.FC = ({ }, }, textField: { - ...getSlotProps("start-time-input", false, t("dateTimeFields.startTime")).textField, + ...getSlotProps( + "start-time-input", + false, + t("dateTimeFields.startTime") + ).textField, onClick: (e) => handleTimeInputClick(e, setIsStartTimeOpen), sx: { @@ -446,8 +455,16 @@ export const DateTimeFields: React.FC = ({ onChange={handleStartDateChange} slots={{ field: ReadOnlyDateField }} slotProps={{ - ...getSlotProps("start-date-input", false, t("dateTimeFields.startDate")), - field: getFieldSlotProps("start-date-input", false, t("dateTimeFields.startDate")) as any, + ...getSlotProps( + "start-date-input", + false, + t("dateTimeFields.startDate") + ), + field: getFieldSlotProps( + "start-date-input", + false, + t("dateTimeFields.startDate") + ) as any, }} /> @@ -482,7 +499,11 @@ export const DateTimeFields: React.FC = ({ slots={{ field: ReadOnlyDateField }} slotProps={{ ...getSlotProps("start-date-input", false, startDateLabel), - field: getFieldSlotProps("start-date-input", false, startDateLabel) as any, + field: getFieldSlotProps( + "start-date-input", + false, + startDateLabel + ) as any, }} /> @@ -498,7 +519,11 @@ export const DateTimeFields: React.FC = ({ timeSteps={{ minutes: 30 }} slots={{ actionBar: () => null }} slotProps={{ - ...getSlotProps("start-time-input", false, t("dateTimeFields.startTime")), + ...getSlotProps( + "start-time-input", + false, + t("dateTimeFields.startTime") + ), openPickerButton: { sx: { display: "none" } }, popper: { sx: { @@ -510,7 +535,11 @@ export const DateTimeFields: React.FC = ({ }, }, textField: { - ...getSlotProps("start-time-input", false, t("dateTimeFields.startTime")).textField, + ...getSlotProps( + "start-time-input", + false, + t("dateTimeFields.startTime") + ).textField, onClick: (e) => handleTimeInputClick(e, setIsStartTimeOpen), sx: { "& .MuiPickersSectionList-section": { @@ -521,6 +550,17 @@ export const DateTimeFields: React.FC = ({ }} /> + {!allday && ( + + - + + )} = ({ fullWidth: true, InputLabelProps: { shrink: true }, error: !!validation.errors.dateTime, - inputProps: { + inputProps: { "data-testid": "end-time-input", "aria-label": t("dateTimeFields.endTime"), },