@simosol/forms

These forms work only with react stateless components. React hooks should be available.

Usage no npm install needed!

<script type="module">
  import simosolForms from 'https://cdn.skypack.dev/@simosol/forms';
</script>

README

Forms

Requirements

These forms work only with react stateless components. React hooks should be available.

Installation

npm install @simosol/forms --save

Basic usage

The example, which includes every use case and possible api call.

import * as React from 'react';
import { Form, useForm, useError, useValidated, ValidationRuleSimple } from '@simosol/forms';
import * as rules from '@simosol/forms/lib/rules';
import FormInput, { KeysOfType } from '@simosol/forms/lib/FormInput';

// prepare common rules
const required = rules.required('Field is required'); // accepts message, returns rule function
const email = rules.email('Invalid e-mail'); // accepts message, returns rule function
const ruleNumber = rules.number('Should be a number'); // accepts message, returns rule function

// custom simple validation rule, check min string length
const customRuleMinLength =
  (min: number, message: string): ValidationRuleSimple =>
    (value: any) => {
      if (typeof value !== 'string' || value.length < min) return message;
    };

// custom complex rule with dependence of other fields
// one field should be less than other field, if values are numeric
const customRuleLess =
  <T, >(otherField: keyof T, message: string) =>
    (value: T[keyof T], form: Form<T>) => {
      const fieldIsNotEmpty = rules.required()(value) === undefined;
      const fieldIsNumber = rules.number()(value) === undefined;
      const otherFieldValue = form.getValue(otherField);
      const otherFieldValidated = form.isValidated(otherField);
      const otherFieldIsNumber = rules.number()(otherFieldValue) === undefined;
      if (
        fieldIsNotEmpty &&
        otherFieldValidated &&
        otherFieldIsNumber &&
        fieldIsNumber &&
        Number(value) >= Number(otherFieldValue)
      ) {
        return message;
      }
    };

const CompanyRegister = () => {
  // prepare data
  const data = React.useState(() => ({
    companyName: 'Your company name', // pre-filled value
    email: '',
    revenue: '',
    profit: '',
    password: '',
    passwordConfirm: '',
  }))[0];

  // create form, using data and validation rules
  const form = useForm(
    data,
    {
      companyName: rules.required('Company name is required'), // custom required message
      email: [email],
      revenue: [required, ruleNumber],
      profit: [ruleNumber, customRuleLess('revenue', 'Should be less than revenue')],
      password: [rules.required('Password is required'), customRuleMinLength(6, 'Minimum password length is 6')],
      passwordConfirm: [
        required,
        rules.same('password', 'Should be same as password'),
      ],
    },
  );

  const onSubmitClick = () => {
    // if all fields are valid, then do something (send request etc.)
    if (form.validateAll()) {
      console.log('do something');
    }
  };
  const onErrorClick = () => {
    // this is needed, when something went wrong
    // show custom email error
    form.setError('email', 'E-mail already exists');
  };

  return (
    <div>
      <Field field={'companyName'} form={form} label={'Company name'}/>
      <Field field={'email'} form={form} label={'E-mail'}/>
      <Field field={'revenue'} form={form} label={'Revenue'}/>
      <Field field={'profit'} form={form} label={'Profit'}/>
      <Field field={'password'} form={form} label={'Password'}/>
      <Field field={'passwordConfirm'} form={form} label={'Confirm password'}/>
      <div>
        <button onClick={onSubmitClick}>Submit</button>
      </div>
      <div>
        <button onClick={onErrorClick}>Error</button>
      </div>
    </div>
  );
};

// simple field component with label and error
const Field = <T, >(
  props: { label: string, field: KeysOfType<T, string>, form: Form<T>},
) => {
  const { field, label, form } = props;
  const error = useError(form, field);
  const validated = useValidated(form, field); // if field was validated at least once
  const borderColor = validated ? (error ? 'red' : 'green') : 'none';
  return (
    <div style={{ paddingBottom: 16 }}>
      <div>{label}</div>
      <div>
        <FormInput style={{ borderColor }} field={field} form={form}/>
      </div>
      <div>{error}</div>
    </div>
  );
};

export default CompanyRegister;