0.0.3 • Published 9 months ago

react-manage-form v0.0.3

Weekly downloads
-
License
MIT
Repository
github
Last release
9 months ago

react-manage-form

Implementation of react-hook-form and yup to manage forms in an easy-to-use way based on array of fields. It is written in TypeScript.

Install

npm install react-manage-form --save
yarn add react-manage-form
import { type Field, Form, useFormContext } from 'react-manage-form';

Interfaces

Field

It is a conditional object based on the type prop, other props changed based on the value you set for type. Receives a generic type to typed the prop name.

type Field<T> = Name<T> &
  (
    | TextField
    | NumberField
    | PasswordField
    | RegexField
    | SingleSelectField<T>
    | GroupField<T>
    | MultiSelectField
    | CheckField
    | ConfirmField<T>
    | FileField
  );

Text field

PropTypeDescription
type'text'It is for text fields
namestringUsed to link the schema validation to a field
required?boolean stringMake it required
min?number ErrorSet a min of n characters
max?number ErrorSet a max of n characters
alphanumeric?boolean stringAllow only alphanumeric characters

Number field

PropTypeDescription
type'number'It is for number fields
namestringUsed to link the schema validation to a field
required?boolean stringMake it required
min?number ErrorSet the min possible value
max?number ErrorSet the max possible value

Password field

PropTypeDescription
type'password'It is for password fields
namestringUsed to link the schema validation to a field
required?boolean stringMake it required
min?number ErrorSet a min of n characters
max?number ErrorSet a max of n characters
numbers?number ErrorSet a min of n numbers characters
lowercases?number ErrorSet a min of n lowercases characters
uppercases?number ErrorSet a min of n uppercases characters
specialCharacters?number ErrorSet a min of n special characters

Regex field

PropTypeDescription
type'email' 'phone-number'It is for regex fields
namestringUsed to link the schema validation to a field
required?boolean stringMake it required
invalidMessage?stringChange the default invalid message

Single selection field

PropTypeDescription
type'select' 'radio'It is for single selection fields
namestringUsed to link the schema validation to a field
required?boolean stringMake it required
when?When<T>[]Allow conditional fields

Group selection field

PropTypeDescription
type'checkbox-group'It is for group selection fields
namestringUsed to link the schema validation to a field
min?number ErrorSet a min of selected options
when?When<T>[]Allow conditional fields

Multiple selection field

PropTypeDescription
type'multi-select'It is for multiple selection fields
namestringUsed to link the schema validation to a field
required?boolean stringMake it required

Check field

PropTypeDescription
type'check'It is for check fields like accept terms
namestringUsed to link the schema validation to a field
required?boolean stringMake it required

Confirm field

PropTypeDescription
type'confirm'It is for confirm fields like confirm password
namestringUsed to link the schema validation to a field
required?boolean stringMake it required
refstring RefField name to match its value

File field

PropTypeDescription
type'file'It is for file fields
namestringUsed to link the schema validation to a field
required?boolean stringMake it required
acceptFileExtension[] AcceptFile extensions to accept

Components

<Form />

A component that is in charge of run based on array of fields all the the schema validations.

PropTypeDescription
fieldsField<T>[]Array of fields to use to create a schema to validate the form fields
defaultValuesDefaultValues<T>Optional partial object with default values
id?stringUse to submit the form using a button out of the form
resetAfterSubmit?booleanTo reset all the form fields after submit
gap? = 4numberSpace between form's children
onSubmit(data: T) => voidFunction to be called on submit once the form is valid
watchValues?(values: T) => voidFunction to receive updates anytime a value of a field changed, commonly used to handle fields conditionally

Contexts

useFormContext

It is the context from react-hook-form.

Fields implementation

This is a recommendation of how to implement the useFormContext with an input.

  • Use the context.
  • Register the field using the name prop.
  • Use lodash.get to access to the error message based on the name prop.
  • Pass all props returned by the register function to the input.

<ErrorMessage /> component

This is a recommendation of how to show up error messages in an easy way.

import type { FC, PropsWithChildren } from 'react';

export const ErrorMessage: FC<PropsWithChildren> = ({ children }) =>
  !!children && <p style={{ color: 'red', marginTop: 3 }}>{children}</p>;

<Input /> component

import get from 'lodash.get';
import type { ComponentProps, FC } from 'react';
import { useFormContext } from 'react-manage-form';

import { ErrorMessage } from '@shared/components';

type InputProps = Omit<ComponentProps<'input'>, 'name'> & {
  name: string;
}

export const Input: FC<InputProps> = ({
  name,
  type = 'text',
  ...rest
}) => {
  const {
    register,
    formState: { errors },
  } = useFormContext();
  const registered = register(name);
  const errorMessage = get(errors, name)?.message as string | undefined;

  return (
     <div style={{ display: 'flex', flexDirection: 'column', }}>
      <input
        type={type}
        {...rest}
        {...registered}
        style={{ borderColor: errorMessage ? 'red' : undefined }}
      />
      <ErrorMessage>{errorMessage}</ErrorMessage>
    </div>
  );
};

<Checkbox /> component

import get from 'lodash.get';
import type { ComponentProps, FC } from 'react';
import { useFormContext } from 'react-manage-form';

import { ErrorMessage } from '@shared/components';

type CheckboxProps = Omit<ComponentProps<'input'>, 'type'> & {
  label: string;
  name: string;
}

export const Checkbox: FC<CheckboxProps> = ({
  label,
  name,
  ...rest
}) => {
  const {
    register,
    formState: { errors },
  } = useFormContext();
  const registered = register(name);
  const errorMessage = get(errors, name)?.message as string | undefined;

  return (
     <div style={{ display: 'flex', flexDirection: 'column', }}>
      <input
        type="checkbox"
        {...rest}
        id={name}
        title={title}
        {...registered}
      />
      <label
        htmlFor={name}
        className="form-check-label"
        style={{ marginLeft: '0.5rem' }}
      >
        {label}
      </label>
      <ErrorMessage>{errorMessage}</ErrorMessage>
    </div>
  );
};

<RadioGroup /> component

import get from 'lodash.get';
import type { ComponentProps, FC } from 'react';
import { useFormContext } from 'react-hook-form';

import { ErrorMessage } from '@shared/components';

type RadioOption = {
  label: string;
  value: string;
};

type RadioGroupProps = Pick<ComponentProps<'input'>, 'placeholder' | 'type'> & {
  label?: string;
  name: string;
  options: RadioOption[];
};

export const RadioGroup: FC<RadioGroupProps> = ({ label, name, options, ...rest }) => {
  const {
    register,
    formState: { errors },
  } = useFormContext();
  const registered = register(name, { value: '' });
  const errorMessage = get(errors, name)?.message as string | undefined;

  return (
    <div style={{ display: 'flex', flexDirection: 'column' }}>
      {label && <label style={{ marginBottom: '0.5rem' }}>{label}</Label>}
      <fieldset style={{ display: 'flex', gap: '0.5rem' }} {...rest}>
        {options.map((option) => (
          <label
            key={option.value}
            htmlFor={`radio-${option.value}`}
            style={{ marginBottom: 0 }}
          >
            <input
              type="radio"
              value={option.value}
              id={`radio-${option.value}`}
              {...registered}
            />{' '}
            {option.label}
          </label>
        ))}
      </fieldset>
      <ErrorMessage>{errorMessage}</ErrorMessage>
    </div>
  );
};

<Select /> component

import get from 'lodash.get';
import type { ComponentProps, FC } from 'react';
import { useFormContext } from 'react-hook-form';

import { ErrorMessage } from '@shared/components';

type SelectOption = {
  label: string;
  value: string;
};

type SelectProps = ComponentProps<'select'> & {
  label?: string;
  name: string;
  options: SelectOption[];
};

export const Select: FC<SelectProps> = ({ label, name, options, ...rest }) => {
  const {
    register,
    formState: { errors },
  } = useFormContext();
  const registered = register(name);
  const errorMessage = get(errors, name)?.message as string | undefined;

  return (
    <div style={{ display: 'flex', flexDirection: 'column' }}>
      {label && <label style={{ marginBottom: '0.5rem' }}>{label}</label>}
      <select {...rest} {...registered} style={{ borderColor: errorMessage ? 'red' : undefined }}>
        <option value="">Please choose one option</option>
        {options.map((option) => (
          <option key={option.value} value={option.value}>
            {option.label}
          </option>
        ))}
      </select>
      <ErrorMessage>{errorMessage}</ErrorMessage>
    </div>
  );
};

Quickstart

import { type Field, Form } from 'react-manage-form';

import { Input } from '@shared/components';

type LoginForm = {
  username: string;
  password: string;
};

const fields: Field<LoginForm>[] = [
  {
    name: 'username',
    type: 'text',
    min: 4,
    max: 11,
    required: true,
  },
  {
    name: 'password',
    type: 'text',
    min: 4,
    required: true,
  },
];

const DataForm = Form<ILoginForm>;

const Login = () => {
  const handleLogin = (data: ILoginForm) => {
    console.log('data', data);
  };

  const watchValues = (values: Partial<ILoginForm>) => {
    console.log('watching values:', values);
  };

  return (
    <DataForm fields={fields} watchValues={watchValues} onSubmit={handleLogin}>
      <Input type="text" name="username" />
      <Input type="password" name="password" />
      <button type="submit" formNoValidate>
        Sign In
      </button>
    </DataForm>
  );
};

Fields example

See how fields can look and choose whichever work fir your projects.

import { type Field } from 'react-manage-form';

type Favorite = {
  name: string;
  tShirtSize: string;
  favoriteShoes: string[];
  resume: File;
};

const fields: Field<Favorite>[] = [
  {
    name: 'name',
    type: 'text',
    required: true,
  },
  {
    name: 'tShirtSize',
    type: 'radio',
    required: true,
  },
  {
    name: 'favoriteShoes',
    type: 'checkbox-group',
  },
  {
    name: 'resume',
    type: 'file',
    required: true,
    accept: ['.pdf', '.txt'],
  },
];

Conditional fields example

See how conditional fields can look and choose whichever work fir your projects.

import { type Field } from 'react-manage-form';

type FileForm {
  fileType: string;
  fileUrl?: string;
  file?: File;
}

const fields: Field<FileForm>[] = [
  {
    name: 'fileType',
    type: 'radio',
    when: [
      {
        value: 'url',
        fields: [
          {
            name: 'fileUrl',
            type: 'text',
            required: true,
          },
        ],
      },
      {
        value: 'upload',
        fields: [
          {
            name: 'file',
            type: 'file',
            required: true,
          },
        ],
      },
    ],
  },
];
0.0.3

9 months ago

0.0.2

9 months ago

0.0.1

9 months ago