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

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

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 112 113 114 115 116                                                                                          2x                 10x   1x 1x         10x                               2x   2x                     8x           10x                       2x 2x   2x      
import {
  ForwardedRef,
  forwardRef,
  memo,
  ReactElement,
  ReactNode,
  Ref,
  useCallback,
} from "react";
import {
  Control,
  Controller,
  ControllerFieldState,
  ControllerRenderProps,
  FieldValues,
  Path,
} from "react-hook-form";
import { TextInput as RNTextInput, TextInputProps } from "react-native";
 
import { Input } from "../Input";
 
interface FormInputProps<T extends FieldValues> extends Omit<
  TextInputProps,
  "value" | "onChangeText"
> {
  control: Control<T>;
  name: Path<T>;
  label?: string;
  leftIcon?: ReactNode;
  disabled?: boolean;
  testID?: string;
  errorMessage?: string;
  rules?: object;
  onChangeText?: (text: string) => void;
}
 
interface InputControllerProps<T extends FieldValues> {
  field: ControllerRenderProps<T, Path<T>>;
  fieldState: ControllerFieldState;
  props: Omit<TextInputProps, "value" | "onChangeText">;
  onChangeText?: (text: string) => void;
  errorMessage?: string;
  inputRef: ForwardedRef<RNTextInput>;
}
 
const InputController = memo(
  <T extends FieldValues>({
    field: { onChange, value },
    fieldState: { error },
    props,
    onChangeText,
    errorMessage,
    inputRef,
  }: InputControllerProps<T>) => {
    const handleChange = useCallback(
      (text: string) => {
        onChange(text);
        onChangeText?.(text);
      },
      [onChange, onChangeText],
    );
 
    return (
      <Input
        {...props}
        ref={inputRef}
        value={value}
        onChangeText={handleChange}
        errorMessage={error?.message || errorMessage}
      />
    );
  },
) as (<T extends FieldValues>(
  props: InputControllerProps<T>,
) => ReactElement) & {
  displayName?: string;
};
 
InputController.displayName = "InputController";
 
const FormInputInner = <T extends FieldValues>(
  {
    control,
    name,
    rules,
    errorMessage,
    onChangeText,
    ...props
  }: FormInputProps<T>,
  ref: Ref<RNTextInput>,
) => {
  return (
    <Controller
      control={control}
      name={name}
      rules={rules}
      render={(renderProps) => (
        <InputController
          {...renderProps}
          props={props}
          onChangeText={onChangeText}
          errorMessage={errorMessage}
          inputRef={ref as ForwardedRef<RNTextInput>}
        />
      )}
    />
  );
};
 
const FormInputComponent = forwardRef(FormInputInner);
FormInputComponent.displayName = "FormInput";
 
export const FormInput = FormInputComponent as <T extends FieldValues>(
  props: FormInputProps<T> & { ref?: Ref<RNTextInput> },
) => ReactElement;