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
Each input field converts values automatically from string
to the expected type:
text
,url
andemail
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 ofstep
(default is1
):
<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
andtime
are validated for format and min/max restrictions, then passed as strings with formatyyyy-MM-dd
andHH: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.
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}/>
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.
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.
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.
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>
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.