All files / packages/ui/src/components/Form FormSelect.tsx

100% Statements 13/13
100% Branches 2/2
100% Functions 4/4
100% Lines 13/13

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                                                                            2x               9x   2x 2x         9x                     2x       2x       6x 6x           9x                       2x 2x   2x            
import {
  ForwardedRef,
  forwardRef,
  memo,
  ReactElement,
  Ref,
  useCallback,
} from "react";
import {
  Control,
  Controller,
  ControllerFieldState,
  ControllerRenderProps,
  FieldValues,
  Path,
} from "react-hook-form";
 
import { Select, SelectProps, SelectRef } from "../Select";
 
interface FormSelectProps<TField extends FieldValues, TOption> extends Omit<
  SelectProps<TOption>,
  "value" | "onChange"
> {
  control: Control<TField>;
  name: Path<TField>;
  rules?: object;
  onChange?: (value: TOption) => void;
}
 
interface SelectControllerProps<TField extends FieldValues, TOption> {
  field: ControllerRenderProps<TField, Path<TField>>;
  fieldState: ControllerFieldState;
  props: Omit<SelectProps<TOption>, "value" | "onChange" | "errorMessage">;
  errorMessage?: string;
  onPropsChange?: (value: TOption) => void;
  selectRef: ForwardedRef<SelectRef>;
}
 
const SelectControllerInstance = <TField extends FieldValues, TOption>({
  field: { onChange, value },
  fieldState: { error },
  props,
  errorMessage,
  onPropsChange,
  selectRef,
}: SelectControllerProps<TField, TOption>) => {
  const handleChange = useCallback(
    (val: TOption) => {
      onChange(val);
      onPropsChange?.(val);
    },
    [onChange, onPropsChange],
  );
 
  return (
    <Select
      {...props}
      ref={selectRef}
      value={value as TOption}
      onChange={handleChange}
      errorMessage={error?.message || errorMessage}
    />
  );
};
 
const SelectController = memo(
  SelectControllerInstance,
) as typeof SelectControllerInstance;
 
const FormSelectInner = <TField extends FieldValues, TOption>(
  { control, name, rules, ...props }: FormSelectProps<TField, TOption>,
  ref: Ref<SelectRef>,
) => {
  const { onChange, errorMessage, ...rest } = props;
  return (
    <Controller
      control={control}
      name={name}
      rules={rules}
      render={(renderProps) => (
        <SelectController
          {...renderProps}
          props={rest}
          errorMessage={errorMessage}
          onPropsChange={onChange}
          selectRef={ref as ForwardedRef<SelectRef>}
        />
      )}
    />
  );
};
 
const FormSelectComponent = forwardRef(FormSelectInner);
FormSelectComponent.displayName = "FormSelect";
 
export const FormSelect = FormSelectComponent as <
  TField extends FieldValues,
  TOption,
>(
  props: FormSelectProps<TField, TOption> & { ref?: Ref<SelectRef> },
) => ReactElement;