npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@koliseoapi/react-forms

v1.3.20

Published

HTML5 input components with automatic property type conversion

Downloads

106

Readme

react-forms

Build Status Coverage Status

A library of HTML5 elements that convert user input automatically to state attributes. Give it a try.

import { Form, Input, Button } from '@koliseoapi/react-forms';

const state = { username: 'Foo', age: 20 };

function MyComponent(props) {
  return (
    <Form onSubmit={mySaveFunction} initialValues={state}>
      <Input type="text" name="username" required maxLength="100" />
      <Input type="number" name="age" min="0" step="1" />
      <Button>Save</Button>
    </Form>
  );
});

The object passed to Form.initialValues is shallow copied and stored internally as state. Anything introduced by the user is converted to a JavaScript type (number, date, boolean) and assigned to the attribute indicated by name. name can also be a nested attribute like "location.address", in which case any missing intermediate nodes will be generated automatically.

When submitting, the Form container validates all fields and triggers onSubmit, passing the state object as only argument.

The following components are supported:

  • Form
  • Input (text, number, checkbox, radio, url, email, date, time, datetime-local)
  • TextArea
  • Select

Controlled vs Uncontrolled forms

You can use Form in two ways:

  1. Controlled: You manage the state outside the form and pass it as values. This is useful when you want to manage the state in a parent component.
const [state, setState] = useState({ username: 'Foo', age: 20 });
<Form values={state} setValues={setState}>
  <Input type="text" name="username" required />
  <Input type="number" name="age" />
  <Button>Save</Button>
</Form>
  1. Uncontrolled: You let the form manage the state internally. This is useful when you want to keep the state in the form itself.
<Form initialValues={{ username: 'Foo', age: 20 }}>
  <Input type="text" name="username" required />
  <Input type="number" name="age" />
  <Button>Save</Button>
</Form>

You can also pass controlled values to a specific input element:

const [username, setUsername] = useState('Foo');
const [age, setAge] = useState(20);
<Input type="text" name="username" value={username} setValue={setUsername} />
<Input type="number" name="age" value={age} setValue={setAge} />

In this case the new value (age and username in this example) will both be stored in the local variable and as the root object in the associated Form element.

Conversions

Each input field converts values automatically from string to the expected type:

  • text, url and email are validated for format and passed as is:
<Input type="email" name="email" />
<TextArea name="description" />

The native HTML element is used for data input, and the data is validated again using JavaScript before submission.

  • number is converted to a JavaScript with decimals according to the value of step (default is 1):
<Input type="number" name="age" />
<Input type="number" name="percentage" step="0.01" />
  • checkbox is transformed to a boolean:
<Input type="checkbox" name="subscribed" />
  • date and time are validated for format and min/max restrictions, then passed as strings with format yyyy-MM-dd and HH:mm respectively.
<Input type="date" name="birthdate" min="1900-01-01" max="2020-01-01" pattern="\d{4}-\d{2}-\d{2}"/>
<Input type="time" pattern="[0-9]{2}:[0-9]{2}">

datetime-local does the same with format yyyy-MM-ddTHH:mm. A pattern attribute may be specified for browsers that do not support this input type.

<Input type="datetime-local" name="birthTimestamp" pattern="\d{4}-\d{2}-\d{2}T[0-9]{2}:[0-9]{2}"/>

Keep in mind that these will render the corresponding native HTML elements, which are supported on all major browsers except Safari on MacOS, where input[type=text] will be used instead. For this reason we recommend to set a pattern attribute like the example above. Even though the date picker doesn't use it, the text input fallback will.

Custom converters

You can override the converter associated to any form field:

const AllowedValues = { one: instanceOne, two: instanceTwo };
const state = { defcon: AllowedValues.one }
const myConverter =

    // transform from String to Object
    fromValue: function(value) {
      return AllowedValues[value] || AllowedValues.one;
    },

    // transform from object to String
    toValue: function(value) {
      const key = Object.keys(AllowedValues).find((key) => AllowedValues[key] === value);
      return key || 'one';
    }

};

<Input type="text" name="defcon" converter={myConverter} state={state}/>

Validations

Before submitting the form, all user input is validated. The field validations are configured according to the following input atributes:

  • [required]
  • [pattern]
  • [min] and [max] for [type=number|date|time]
  • [type=email]
  • [type=url]

When the user submits a Form, the onSubmit callback will only be invoked after all validations pass. Otherwise, an error message will be displayed next to every element that didn't pass, and focus will be transferred to the first error component.

Both input fields and alert messages will use the corresponding ARIA attributes (aria-invalid, aria-describedBy and role=alert) to be used by assistive technologies.

Disabling the form during submissions

Use FieldSet and Button to provide with visual feedback. During form submission the fieldset will be disabled and the submit button will display a "loading" class that you can tweak to display a spinner.

Custom Validations

To override the validation applied to a field, pass a function that returns a Promise:

// return undefined if the validation passes, otherwise an i18n error entry
const fetchValidate = async (value, props) => {
  const response = await fetch(`checkAvailability?username=${value}`);
  const json = await response.json();
  return json.ok? undefined : "usernameTaken"
}

<Form onSubmit={save} state={state}>
  <label htmlFor="username">Choose your username</label>
  <Input type="text" name="username" validate={fetchValidate} />
</Form>

The function should return undefined if the validation passes, or an error message otherwise. The error code returned must correspond to an i18n entry.

Interaction between components

The following is an example where a checkbox makes an input appear or disappear based on state:

const values = {
  /** true to ask to be reimbursed for expenses */
  reimburseExpenses: false,

  /** if `reimburseExpenses` is true, location they travel from */
  travelFrom: undefined
}
const [reimburseExpenses, setReimburseExpenses] = useState(values.reimburseExpenses);

<Form state={values}>
  <Input
    type="checkbox"
    name="reimburseExpenses"
    onChange={(e, formContext) => {
      const value = e.target.checked;
      setReimburseExpenses(value);
      // if reimburseExpenses is unchecked, travelFrom should be empty
      !reimburseExpenses &&
        formContext.setValue('travelFrom', undefined);
    }}
  />
  {
    // if reimburseExpenses is checked, travelFrom is required
    reimburseExpenses &&
    <Input
      type="text"
      name="travelFrom"
      required
    />
  }
</Form>

Internationalization

To override the locale for validation messages, use I18nContext:

import { I18nContext } from '@koliseoapi/react-forms';

<I18nContext.Provider value={{
  required: "Por favor, rellena este campo",
  min: "El valor debe ser mayor o igual que ${min}"
}}>

The full list of validation messages is here.