All files / app/components Timepicker.tsx

100% Statements 20/20
100% Branches 8/8
100% Functions 5/5
100% Lines 19/19

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 1125x 5x 5x 5x 5x 5x 5x 5x                                       298x 298x       28x     5x                         592x 592x 66x                                   29x     29x 29x                                                                           588x  
import { TimePicker } from "@mui/x-date-pickers";
import { useTranslation } from "i18n";
import { GLOBAL } from "i18n/namespaces";
import React, { useMemo } from "react";
import { Control, Controller, UseControllerProps } from "react-hook-form";
import { Temporal } from "temporal-polyfill";
import { getMuiTimeFormat } from "utils/date";
import dayjs, { Dayjs } from "utils/dayjs";
 
interface TimepickerProps {
  className?: string;
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  control: Control<any>;
  defaultValue?: Temporal.PlainTime;
  error: boolean;
  helperText: React.ReactNode;
  id: string;
  rules?: UseControllerProps["rules"];
  label?: string;
  name: string;
  onPostChange?(value: Temporal.PlainTime | null): void;
  testId?: string;
}
 
// Convert between our API's Temporal.PlainTime and MUI's expected Dayjs values.
// Use the browser timezone in case we compare to now, aka dayjs().
function temporalToDayjs(value: Temporal.PlainTime): Dayjs {
  const timeString = value.toString({ smallestUnit: "minute" });
  return dayjs(`1970-01-01T${timeString}`); // We don't care about the date, but dayjs needs one.
}
 
function dayjsToTemporal(value: Dayjs): Temporal.PlainTime {
  return Temporal.PlainTime.from(value.format("HH:mm"));
}
 
const Timepicker = ({
  className,
  control,
  defaultValue,
  error,
  helperText,
  id,
  rules,
  label,
  name,
  onPostChange,
  testId,
}: TimepickerProps) => {
  const { t, i18n } = useTranslation([GLOBAL]);
  const format = useMemo(
    () => getMuiTimeFormat(i18n.language),
    [i18n.language],
  );
 
  return (
    <Controller
      control={control}
      defaultValue={defaultValue ?? null}
      name={name}
      rules={rules}
      render={({ field }) => (
        <TimePicker
          data-testid={testId}
          {...field}
          label={label}
          value={field.value ? temporalToDayjs(field.value) : null}
          onChange={(valueDayjs: Dayjs | null) => {
            const valueTemporal =
              valueDayjs && valueDayjs.isValid()
                ? dayjsToTemporal(valueDayjs)
                : null;
            field.onChange(valueTemporal);
            onPostChange?.(valueTemporal);
          }}
          format={format}
          ampm={format.includes("a")} // Clock picker uses am/pm iff format also uses it
          slotProps={{
            textField: {
              fullWidth: true,
              id,
              error,
              helperText: (
                <span data-testid={`${name}-helper-text`}>{helperText}</span>
              ),
              variant: "standard",
              sx: {
                "& .MuiOutlinedInput-root": {
                  backgroundColor: "var(--mui-palette-primary-main)",
                  color: "var(--mui-palette-text-primary)",
                },
                "& .MuiPaper-root": {
                  backgroundColor: "var(--mui-palette-primary-main)",
                  color: "var(--mui-palette-text-primary)",
                },
              },
              slotProps: {
                inputLabel: { shrink: true },
                input: {
                  className,
                  "aria-label": t("global:change_time"),
                },
              },
            },
          }}
        />
      )}
    />
  );
};
 
export default Timepicker;