diff --git a/__test__/components/DateTimeField.test.tsx b/__test__/components/DateTimeField.test.tsx index 184f218..95e4de8 100644 --- a/__test__/components/DateTimeField.test.tsx +++ b/__test__/components/DateTimeField.test.tsx @@ -84,8 +84,7 @@ describe("DateTimeFields", () => { showMore: true, }); - const calendarButton = screen.getAllByRole("button"); - await userEvent.click(calendarButton[0]); + await userEvent.click(screen.getByTestId("start-date-input")); const dayButton = screen.getByRole("gridcell", { name: "3" }); await userEvent.click(dayButton); @@ -129,8 +128,7 @@ describe("DateTimeFields", () => { showMore: true, }); - const calendarButton = screen.getAllByRole("button"); - await userEvent.click(calendarButton[2]); + await userEvent.click(screen.getByTestId("end-date-input")); const dayButton = screen.getByRole("gridcell", { name: "3" }); await userEvent.click(dayButton); @@ -151,8 +149,7 @@ describe("DateTimeFields", () => { showEndDate: true, }); - const calendarButton = screen.getAllByRole("button"); - await userEvent.click(calendarButton[0]); + await userEvent.click(screen.getByTestId("start-date-input")); const dayButton = screen.getByRole("gridcell", { name: "10" }); await userEvent.click(dayButton); @@ -173,8 +170,7 @@ describe("DateTimeFields", () => { showEndDate: true, }); - const calendarButton = screen.getAllByRole("button"); - await userEvent.click(calendarButton[1]); + await userEvent.click(screen.getByTestId("end-date-input")); const dayButton = screen.getByRole("gridcell", { name: "1" }); await userEvent.click(dayButton); diff --git a/src/components/Event/components/DateTimeFields.tsx b/src/components/Event/components/DateTimeFields.tsx index 89cf6cb..53d1568 100644 --- a/src/components/Event/components/DateTimeFields.tsx +++ b/src/components/Event/components/DateTimeFields.tsx @@ -15,6 +15,7 @@ import "dayjs/locale/vi"; import { PickerValue } from "@mui/x-date-pickers/internals"; import { dtDate, dtTime, toDateTime } from "../utils/dateTimeHelpers"; +import { ReadOnlyDateField } from "./ReadOnlyPickerField"; dayjs.extend(customParseFormat); @@ -256,6 +257,16 @@ export const DateTimeFields: React.FC = ({ }, }); + const getFieldSlotProps = (testId: string, hasError = false) => ({ + size: "small" as const, + margin: "dense" as const, + fullWidth: true, + InputLabelProps: { shrink: true }, + error: hasError, + sx: { width: "100%" }, + inputProps: { "data-testid": testId }, + }); + return ( = ({ format={LONG_DATE_FORMAT} value={startDateValue} onChange={handleStartDateChange} - slotProps={getSlotProps("start-date-input")} + slots={{ field: ReadOnlyDateField }} + slotProps={{ + ...getSlotProps("start-date-input"), + field: getFieldSlotProps("start-date-input") as any, + }} /> {!allday && ( @@ -302,10 +317,14 @@ export const DateTimeFields: React.FC = ({ format={LONG_DATE_FORMAT} value={endDateValue} onChange={handleEndDateChange} - slotProps={getSlotProps( - "end-date-input", - !!validation.errors.dateTime - )} + slots={{ field: ReadOnlyDateField }} + slotProps={{ + ...getSlotProps("end-date-input", !!validation.errors.dateTime), + field: getFieldSlotProps( + "end-date-input", + !!validation.errors.dateTime + ) as any, + }} /> {!allday && ( @@ -332,7 +351,11 @@ export const DateTimeFields: React.FC = ({ format={LONG_DATE_FORMAT} value={startDateValue} onChange={handleStartDateChange} - slotProps={getSlotProps("start-date-input")} + slots={{ field: ReadOnlyDateField }} + slotProps={{ + ...getSlotProps("start-date-input"), + field: getFieldSlotProps("start-date-input") as any, + }} /> @@ -341,10 +364,14 @@ export const DateTimeFields: React.FC = ({ format={LONG_DATE_FORMAT} value={endDateValue} onChange={handleEndDateChange} - slotProps={getSlotProps( - "end-date-input", - !!validation.errors.dateTime - )} + slots={{ field: ReadOnlyDateField }} + slotProps={{ + ...getSlotProps("end-date-input", !!validation.errors.dateTime), + field: getFieldSlotProps( + "end-date-input", + !!validation.errors.dateTime + ) as any, + }} /> @@ -356,7 +383,11 @@ export const DateTimeFields: React.FC = ({ format={LONG_DATE_FORMAT} value={startDateValue} onChange={handleStartDateChange} - slotProps={getSlotProps("start-date-input")} + slots={{ field: ReadOnlyDateField }} + slotProps={{ + ...getSlotProps("start-date-input"), + field: getFieldSlotProps("start-date-input") as any, + }} /> diff --git a/src/components/Event/components/ReadOnlyPickerField.tsx b/src/components/Event/components/ReadOnlyPickerField.tsx new file mode 100644 index 0000000..20421a5 --- /dev/null +++ b/src/components/Event/components/ReadOnlyPickerField.tsx @@ -0,0 +1,95 @@ +import React from "react"; +import TextField from "@mui/material/TextField"; +import { + PickerFieldAdapter, + PickerValidationScope, + useValidation, + validateDate, +} from "@mui/x-date-pickers/validation"; +import { + useSplitFieldProps, + useParsedFormat, + usePickerContext, +} from "@mui/x-date-pickers/hooks"; +import { PickerFieldProps } from "@mui/x-date-pickers/models"; +import { DatePickerFieldProps } from "@mui/x-date-pickers/DatePicker"; + +type FieldType = "date" | "time" | "date-time"; + +type GenericPickerFieldProps = PickerFieldProps & { + fieldType: FieldType; + validator: ( + value: any, + context: PickerValidationScope, + adapter: PickerFieldAdapter + ) => string | null; +}; + +/** + * Shared read-only field for date/time pickers. Disables typing, removes icon, + * and opens the picker when clicking anywhere in the field. + */ +function ReadOnlyPickerField(props: GenericPickerFieldProps) { + const { fieldType, validator, ...fieldProps } = props; + const { internalProps, forwardedProps } = useSplitFieldProps( + fieldProps, + fieldType + ); + + const pickerContext = usePickerContext(); + const parsedFormat = useParsedFormat(); + + const { hasValidationError } = useValidation({ + validator, + value: pickerContext.value, + timezone: pickerContext.timezone, + props: internalProps, + }); + + const valueToDisplay = + pickerContext.value == null + ? "" + : pickerContext.value.isValid() + ? pickerContext.value.format(pickerContext.fieldFormat) + : ""; + + const mergedInputProps = { + ...forwardedProps.InputProps, + ref: pickerContext.triggerRef, + readOnly: true, + sx: { + cursor: "pointer", + "& *": { cursor: "inherit" }, + ...forwardedProps.InputProps?.sx, + }, + }; + + return ( + pickerContext.setOpen((prev) => !prev)} + className={pickerContext.rootClassName} + sx={pickerContext.rootSx} + ref={pickerContext.rootRef} + name={pickerContext.name} + label={pickerContext.label} + /> + ); +} + +export function ReadOnlyDateField(props: DatePickerFieldProps) { + return ( + + ); +} + +