All files / Form/Field FormField.jsx

85.48% Statements 53/62
66.66% Branches 14/21
100% Functions 4/4
85.48% Lines 53/62

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  1x 1x   1x 1x 1x   1x 4x 4x 4x   4x   4x 4x 5x     5x 2x 2x 5x 2x 2x   1x 4x   4x 4x 4x   4x 2x 2x 2x 2x 2x 2x 2x   2x   4x                   2x 2x 2x 4x 4x 4x   4x   1x 4x 4x 4x   1x 4x 4x 4x   1x 4x 4x 4x  
/* eslint-disable react/prop-types */ /* TODO : remove disable */
import React from 'react';
import { findTypeElement } from './findTypeElement';
 
FormField.Error = FormFieldError;
FormField.Input = FormFieldInput;
FormField.Label = FormFieldLabel;
 
export function FormField({ children, describedBy = null, errorMessage = null, label, name, type = 'text', inputTagName = 'input', tagName, ...args }) {
  const inputId = React.useId();
  const errorId = `${inputId}__error`;
  const Tag = tagName || 'div';
 
  const hasError = Boolean(errorMessage);
 
  const childs = React.Children.toArray(children)
    .map((child) => {
      if (child.type === FormFieldError) {
        return React.cloneElement(child, { errorId }, errorMessage);
      }
      if (child.type === FormFieldInput) {
        return React.cloneElement(child, { id: inputId, inputTagName, name, type, 'aria-describedby': `${errorId} ${describedBy}` });
      }
      if (child.type === FormFieldLabel) {
        return React.cloneElement(child, { inputId }, label);
      }
 
      return child;
    });
 
  const hasErrorElement = findTypeElement(childs, FormFieldError);
  const hasInputElement = findTypeElement(childs, FormFieldInput);
  const hasLabelElement = findTypeElement(childs, FormFieldLabel);
 
  if (!hasLabelElement && !hasInputElement && !hasErrorElement) {
    return (
      <Tag {...args}>
        <FormFieldLabel inputId={inputId}>{label}</FormFieldLabel>
        <FormFieldInput id={inputId} inputTagName={inputTagName} type={type} aria-describedby={`${errorId} ${describedBy}`} />
        <FormFieldError id={errorId}>{errorMessage}</FormFieldError>
        {childs}
      </Tag>
    );
  }
 
  if (!hasLabelElement && hasInputElement && !hasErrorElement) {
    return (
      <Tag {...args}>
        {!hasLabelElement && <FormFieldLabel inputId={inputId}>{label}</FormFieldLabel>}
        {!hasErrorElement && <FormFieldError id={errorId}>{errorMessage}</FormFieldError>}
        {childs}
      </Tag>
    );
  }
 
  return (
    <Tag {...args}>
      {!hasLabelElement && <FormFieldLabel inputId={inputId}>{label}</FormFieldLabel>}
      {childs}
      {!hasErrorElement && <FormFieldError id={errorId} hasError={hasError}>{errorMessage}</FormFieldError>}
    </Tag>
  );
}
 
export function FormFieldInput({ inputTagName, ...args }) {
  const Input = inputTagName || 'input';
  return <Input {...args} />;
}
 
export function FormFieldLabel({ inputId, tagName, ...args }) {
  const Label = tagName || 'label';
  return <Label {...args} htmlFor={inputId} />;
}
 
export function FormFieldError({ hasError, tagName, ...args }) {
  const ErrorTag = tagName || 'span';
  return <ErrorTag {...args} />;
}