diff --git a/__test__/components/DateTimeField.test.tsx b/__test__/components/DateTimeField.test.tsx index 95e4de8..3ad2448 100644 --- a/__test__/components/DateTimeField.test.tsx +++ b/__test__/components/DateTimeField.test.tsx @@ -241,4 +241,24 @@ describe("DateTimeFields", () => { ) ); }); + + it("should have aria-label for accessibility and testing", async () => { + await renderField({ + startDate: "2025-01-01", + startTime: "10:00", + endDate: "2025-01-01", + endTime: "11:00", + showMore: true, + }); + + const startDateInput = screen.getByTestId("start-date-input"); + const startTimeInput = screen.getByTestId("start-time-input"); + 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"); + }); }); diff --git a/src/components/Event/components/DateTimeFields.tsx b/src/components/Event/components/DateTimeFields.tsx index 86fccfe..496b19a 100644 --- a/src/components/Event/components/DateTimeFields.tsx +++ b/src/components/Event/components/DateTimeFields.tsx @@ -245,7 +245,7 @@ export const DateTimeFields: React.FC = ({ [endTime] ); - const getSlotProps = (testId: string, hasError = false) => ({ + const getSlotProps = (testId: string, hasError = false, testLabel?: string) => ({ textField: { size: "small" as const, margin: "dense" as const, @@ -253,18 +253,24 @@ export const DateTimeFields: React.FC = ({ InputLabelProps: { shrink: true }, error: hasError, sx: { width: "100%" }, - inputProps: { "data-testid": testId }, + inputProps: { + "data-testid": testId, + ...(testLabel ? { "aria-label": testLabel } : {}), + }, }, }); - const getFieldSlotProps = (testId: string, hasError = false) => ({ + 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: { "data-testid": testId }, + inputProps: { + "data-testid": testId, + ...(testLabel ? { "aria-label": testLabel } : {}), + }, }); const [isStartTimeOpen, setIsStartTimeOpen] = React.useState(false); @@ -281,6 +287,20 @@ export const DateTimeFields: React.FC = ({ } }; + const handleStartTimeChangeWithClose = (value: PickerValue) => { + setIsStartTimeOpen(false); + setTimeout(() => { + handleStartTimeChange(value); + }, 100); + }; + + const handleEndTimeChangeWithClose = (value: PickerValue) => { + setIsEndTimeOpen(false); + setTimeout(() => { + handleEndTimeChange(value); + }, 100); + }; + return ( = ({ {!allday && ( setIsStartTimeOpen(false)} thresholdToRenderTimeInASingleColumn={48} timeSteps={{ minutes: 30 }} + slots={{ actionBar: () => null }} slotProps={{ - ...getSlotProps("start-time-input"), + ...getSlotProps("start-time-input", false, t("dateTimeFields.startTime")), openPickerButton: { sx: { display: "none" } }, + popper: { + sx: { + "& .MuiMultiSectionDigitalClockSection-item": { + justifyContent: "flex-start", + width: "100%", + textAlign: "left", + }, + }, + }, textField: { - ...getSlotProps("start-time-input").textField, + ...getSlotProps("start-time-input", false, t("dateTimeFields.startTime")).textField, onClick: (e) => handleTimeInputClick(e, setIsStartTimeOpen), sx: { @@ -344,7 +372,6 @@ export const DateTimeFields: React.FC = ({ = ({ slotProps={{ ...getSlotProps( "end-date-input", - !!validation.errors.dateTime + !!validation.errors.dateTime, + t("dateTimeFields.endDate") ), field: getFieldSlotProps( "end-date-input", - !!validation.errors.dateTime + !!validation.errors.dateTime, + t("dateTimeFields.endDate") ) as any, }} /> @@ -364,24 +393,35 @@ export const DateTimeFields: React.FC = ({ {!allday && ( setIsEndTimeOpen(false)} thresholdToRenderTimeInASingleColumn={48} timeSteps={{ minutes: 30 }} + slots={{ actionBar: () => null }} slotProps={{ ...getSlotProps( "end-time-input", - !!validation.errors.dateTime + !!validation.errors.dateTime, + t("dateTimeFields.endTime") ), openPickerButton: { sx: { display: "none" } }, + popper: { + sx: { + "& .MuiMultiSectionDigitalClockSection-item": { + justifyContent: "flex-start", + width: "100%", + textAlign: "left", + }, + }, + }, textField: { ...getSlotProps( "end-time-input", - !!validation.errors.dateTime + !!validation.errors.dateTime, + t("dateTimeFields.endTime") ).textField, onClick: (e) => handleTimeInputClick(e, setIsEndTimeOpen), @@ -401,20 +441,18 @@ export const DateTimeFields: React.FC = ({ = ({ slotProps={{ ...getSlotProps( "end-date-input", - !!validation.errors.dateTime + !!validation.errors.dateTime, + t("dateTimeFields.endDate") ), field: getFieldSlotProps( "end-date-input", - !!validation.errors.dateTime + !!validation.errors.dateTime, + t("dateTimeFields.endDate") ) as any, }} /> @@ -436,33 +476,41 @@ export const DateTimeFields: React.FC = ({ setIsStartTimeOpen(false)} thresholdToRenderTimeInASingleColumn={48} timeSteps={{ minutes: 30 }} + slots={{ actionBar: () => null }} slotProps={{ - ...getSlotProps("start-time-input"), + ...getSlotProps("start-time-input", false, t("dateTimeFields.startTime")), openPickerButton: { sx: { display: "none" } }, + popper: { + sx: { + "& .MuiMultiSectionDigitalClockSection-item": { + justifyContent: "flex-start", + width: "100%", + textAlign: "left", + }, + }, + }, textField: { - ...getSlotProps("start-time-input").textField, + ...getSlotProps("start-time-input", false, t("dateTimeFields.startTime")).textField, onClick: (e) => handleTimeInputClick(e, setIsStartTimeOpen), sx: { "& .MuiPickersSectionList-section": { @@ -475,15 +523,15 @@ export const DateTimeFields: React.FC = ({ setIsEndTimeOpen(false)} thresholdToRenderTimeInASingleColumn={48} timeSteps={{ minutes: 30 }} + slots={{ actionBar: () => null }} slotProps={{ textField: { size: "small", @@ -491,7 +539,10 @@ export const DateTimeFields: React.FC = ({ fullWidth: true, InputLabelProps: { shrink: true }, error: !!validation.errors.dateTime, - inputProps: { "data-testid": "end-time-input" }, + inputProps: { + "data-testid": "end-time-input", + "aria-label": t("dateTimeFields.endTime"), + }, onClick: (e) => handleTimeInputClick(e, setIsEndTimeOpen), sx: { width: "100%", @@ -501,6 +552,15 @@ export const DateTimeFields: React.FC = ({ }, }, openPickerButton: { sx: { display: "none" } }, + popper: { + sx: { + "& .MuiMultiSectionDigitalClockSection-item": { + justifyContent: "flex-start", + width: "100%", + textAlign: "left", + }, + }, + }, }} /> diff --git a/src/components/Event/components/ReadOnlyPickerField.tsx b/src/components/Event/components/ReadOnlyPickerField.tsx index 9ddd4cb..2c7a0b3 100644 --- a/src/components/Event/components/ReadOnlyPickerField.tsx +++ b/src/components/Event/components/ReadOnlyPickerField.tsx @@ -77,7 +77,6 @@ function ReadOnlyPickerField(props: GenericPickerFieldProps) { sx={pickerContext.rootSx} ref={pickerContext.rootRef} name={pickerContext.name} - label={pickerContext.label} /> ); }