react-superior-forms
v1.4.0
Published
A simple yet convenient way to build forms in react.
Downloads
28
Readme
Helps you easily build a form with input processing, validation and formatting.
npm install react-superior-forms
Features include:
- Customizable Input
- Basic Inputs (Text, Email, Number, Password)
- Grouping
- Repeaters
- AJAX Submit & Loader
- Validation (including the displayal of messages)
- Full customization using hooks, events and options API
- Form Builder API (JSON -> Form)
Aims for customizable UI with classes, includes minimal CSS.
1. Introduction:
A basic form to create a user:
import Form, {TextInput, NumberInput, SubmitButton} from 'react-superior-forms';
<Form route="/create/user" json={true}>
<label>Username</label>
<TextInput name="username"/>
<label>Favourite Number</label>
<NumberInput name="favourite_number" process={true}/>
<SubmitButton/>
</Form>;
Example request:
{username: "edward_baldwin", favourite_number: 10}
Let's put the fields into groups!
import Form, {InputGroup, TextInput, NumberInput, SubmitButton} from 'react-superior-forms';
<Form route="/create/user" json={true}>
<InputGroup name="user">
<label>Username</label>
<TextInput name="username"/>
</InputGroup>
<InputGroup name="user_preferences">
<label>Favourite Number</label>
<NumberInput name="favourite_number" process={true}/>
</InputGroup>
<SubmitButton/>
</Form>;
Example request:
{user: {username: "edward_baldwin"}, user_preferences: {favourite_number: 10}}
Let's repeat these fields!
import Form, {InputGroupRepeater, TextInput, NumberInput, SubmitButton} from 'react-superior-forms';
<Form route="/create/users" json={true}>
<InputGroupRepeater name="users" entries={2} minEntries={1} maxEntries={5}>
<label>Username</label>
<TextInput name="username"/>
<label>Favourite Number</label>
<NumberInput name="favourite_number" process={true}/>
</InputGroupRepeater>
<SubmitButton/>
</Form>;
Example request:
{
users: [
{username: "edward_baldwin", favourite_number: 10},
{username: "gordo_stevens", favourite_number: 22}
]
}
2. Format, Process, Validate
It is important to understand the concept react-superior-forms uses to manage the values of the inputs.
Each step is optional. Formatting removes all the junk from the input value. Processing will transform the value into the desired type. Validating will check whether the value passes all constraints.
For example, let's say we have a number field with all steps enabled:
<NumberInput name="favourite_number" format={true} process={true} validate={'max:10'} defaultValue="b58a"/>
- Initial value: "b58a" (string)
- After formatting: "58" (string)
- After processing: 58 (number)
Now the validation can easily proceed with statement: 58 <= 10
Had we skipped the processing, the value would have remained a string, causing the validation to check whether the length of string "58" is less or equal to 10 using statement: "58".length <= 10
This flow will help us write reuseable validations for multiple scenarios, such as arrays, strings, numbers and more!
3. Components
3.1. Form
The <Form/> component, just like the native <form/> element, is responsible for collecting all input data of its childrens and handling the submit event.
The <Form/> component only recognizes the <Input/> components as inputs, and will ignore all other native elements such as <input/>, <select/>, <textarea/>, etc.
The submit data is available in both JSON and FormData.
Example usage:
import Form from 'react-superior-forms';
<Form
route="/user/3/update"
method="PATCH"
json={true}
>
...
</Form>
Example rendered element:
<form class="rsf-form">
...
</form>
or
<form class="rsf-form rsf-form--invalid">
...
any invalid input (recursively)
...
</form>
Parameters:
| Parameter | Description | Type | Example value(s) | Default | | - | - | - | - | - | | route | The endpoint to submit the data to | string | "/example" | "/" | | method | The method used when submitting the data | string | "GET", "POST", "PATCH", etc. | "POST" | | json | Whether to submit the data in JSON (also sets the Content-type header to application/json) | boolean | false = submits FormDatatrue = submits JSON | false | | headers | The headers to send with the submitted request | object | {'X-CSRF-TOKEN': csrfToken} | null | | className | The classname to append to the list of classes. | string | "my-form" | null | | onSuccess | The function to call when the submit results in success (Status is 200) | function | (response, event, data) => console.log(event, data) | null | | onFail | The function to call when the submit results in failure (Status is not 200) | function | (response, event, data) => console.log(event, data) | null | | onSend | The function to call before sending the XHR | function | (data) => console.log(data) | null | | onSubmit | The function to call when trying to submit. If it returns 'false' then the form will not proceed to submit. | function | () => console.log('Trying to submit!') | null | | inputDefaults | The default props the <Input/> components will inherit. | object | {validate : true, hideValidateMessage: true, required: true, disabled: true, process: true, format: true}Check out the <Input/> props here. | null |
3.2. Input
The <Input/> component is recognized as an input of the <Form/> component and will be included in the submitted data if the disabled property is not set to false.
This component should only be used when creating a custom input of your own choice. A handful of inputs were already created to help you skip this process. Check out the basic inputs.
To create a custom input, pass your custom input component as the component property.
This component will inherit a handful of properties passed to the <Input> component (e.g. name, value, type, disabled, required, className) plus the current value of the input, and the onChange function which should be called whenever your input component's value has changed.
Example usage:
import Form, {Input, SubmitButton} from 'react-superior-forms';
const YourCustomInputComponent = React.forwardRef((props, ref) => {
return <input type="text" disabled={props.disabled} onChange={(ev) => props.onChange(ev.target.value)} value={props.value ?? ''} ref={ref}/>;
});
<Form route="/example">
<Input name="custom_stuff" component={<YourCustomInputComponent/>} disabled={Math.random() >= 0.5}/>
<SubmitButton/>
</Form>;
Example output:
<form class="rsf-form">
<input type="text" disabled>
</form>
or if "validate" property is used, and the input is currently invalid
<form class="rsf-form rsf-form--invalid">
<div class="rsf-input-validation-wrapper rsf-input-validation-wrapper--invalid">
<input type="text">
<label>Validation error message</label>
</div>
</form>
Parameters:
| Parameter | Description | Type | Example value(s) | Default | | - | - | - | - | - | | name | The key to use when submitting the data | string | "username" | "" | | defaultValue | The value to initialize the input with | any | "edward_baldwin", 123, [1,2,3] | null | | type | The input type. This helps in recognizing the default validator, formatter when they are set to auto (true). | "email", "password", "text", "number", "custom" | "text" | null | | validate | The validation to use on the input value. When set to true, the validators will be recognized automatically based on the type and required properties. | boolean | array | function | string | false = No validationtrue = The validators will be recognized based on the type property.(value) => String(value).startsWith('ez')"max:255""between:10,20"[{type: 'custom', assert: (value) => !value, message: () => 'Custom message!'}, 'max:10', {type: 'between', arguments: [10, 20]}] | false | | hideValidateMessage | Whether to hide the validation message or not | boolean | false | true | false | | format | The formatting to use on the input value. When set to true, the formatters will be recognized based on the type and validate properties. | boolean | string | function | array | false = No formattingtrue = The formatters will be recognized based on the type and validate properties.(value) => String(value).replace('a', 'b')"max:255""alphanumeric""regex_discard:/[0-9]/"['max:255', 'alphanumeric', {type: 'regex_discard', arguments: ['/0-9/']}] | false | | process | The processing to use on the input value. When set to true, the processing will be based on the type property. | boolean | function | false = No processingtrue = The processing is recognized based on the type property.(value) => parseFloat(value) | false | | required | Whether the field is required | boolean | false | true | false | | disabled | Whether the field is disabled. When set to true the input key will not be present in the submitted data | boolean | false | true | false | | onChange | The function to call when the change event occurs | function | (value) => console.log(`Value changed to ${value}`) | null | | onValidate | The function to call when the validation event occurs | function | (failedValidators) => console.log(failedValidators) | null |
Basic Inputs
These inputs were created to supply what the native <input> elements have to offer.
List of available basic inputs:
- TextInput
- NumberInput
- EmailInput
- PasswordInput
Example usage:
import Form, {TextInput, NumberInput, EmailInput, PasswordInput} from 'react-superior-forms';
const form = <Form
route="/example"
inputDefaults={{format: true, validate: true, process: true}}
>
<label>Username:</label>
<TextInput name="username" defaultValue="gordo"/>
<label>Number:</label>
<NumberInput name="favourite_number" defaultValue={123}/>
<label>Email:</label>
<EmailInput name="email" defaultValue="[email protected]"/>
<label>Password:</label>
<PasswordInput name="password"/>
</Form>;
3.3. InputGroup
The <InputGroup/> helps you group your input values.
The <InputGroup/> will wrap all <Input/>, <InputGroup/> and <InputGroupRepeater/> components.
It both stands as a wrapper component with helpful modifier classes such as --invalid — and as a way to group your key, value pairs into a parent key.
Example usage:
import Form, {InputGroup, TextInput, NumberInput} from 'react-superior-forms';
<Form route="/create/user" json={true}>
<InputGroup name="user">
<label>Username</label>
<TextInput name="name"/>
</InputGroup>
<InputGroup name="user_preferences">
<label>Favourite Number</label>
<NumberInput name="favourite_number"/>
</InputGroup>
</Form>;
Example output:
<form class="rsf-form">
<fieldset class="rsf-input-group input-group-name-user">
<label>Username</label>
<input class="rsf-input rsf-input-type-text" name="name" type="text">
</fieldset>
...
</form>
Parameters:
| Parameter | Description | Type | Example value(s) | Default | | - | - | - | - | - | | legend | The element or string to insert for the <legend/> of the <fieldset/>. | string | JSX | "My group"<span>My <b>fancy</b> group!</span> | null | | className | The classname to append to the list of classes. | string | "my-group" | null | | name | The key to group the values into | string | "users" | null | | defaultValue | The initial values for the entries placed in the group. Does not overwrite the defaultValue props of children inputs if there is any | array | {username: "edward_baldwin", favourite_number: 10, ...} | null | | before | The element to display before the <fieldset/> element | JSX | <h1>Buzz!</h1> | null | | after | The element to display after the <fieldset/> element | JSX | <h1>Buzz again!</h1> | null | | beforeInputs | The element to display before the inputs, in the <fieldset/> element | JSX | <h1>Buzz!</h1> | null | | afterInputs | The element to display after the inputs, in the <fieldset/> element | JSX | <h1>Buzz again!</h1> | null |
3.4. InputGroupRepeater
The <InputGroupRepeater/> helps you repeat certain groups of input values.
The <InputGroupRepeater/> wraps its children into one or multiple <InputGroup/> components depending on the entry count.
It also comes with a default add button to add new entries, and a remove button to remove the current entries one by one.
Example usage:
<Form route="/create/users">
<InputGroupRepeater name="users" entries={1} minEntries={1} maxEntries={5}>
<label>Username</label>
<TextInput name="name"/>
<label>Favourite Number</label>
<NumberInput name="favourite_number" validate={true} format={true} process={true}/>
</InputGroupRepeater>
</Form>;
Example output:
<form class="rsf-form">
<fieldset class="input-group-repeater input-group-repeater-name-users">
<div class="rsf-input-group-repeater-entry">
<span>
<fieldset class="rsf-input-group input-group-name-users">
<label>Username</label>
<input class="rsf-input rsf-input-type-text" name="name" type="text" value="">
...
</fieldset>
</span>
<button class="rsf-...-button-remove" .../>
</div>
...
repeated entries
...
<button class="rsf-...-button-add" .../>
</fieldset>
</form>
Example visuals:
Parameters
| Parameter | Description | Type | Example value(s) | Default | | - | - | - | - | - | | legend | The legend to display for each repeated entries' <fieldset/> element. | string | function | "User" = will result in "User 1", "User 2"(props) => <span>My <b>{props.index}.</b> User</span> | null | | defaultValue | The initial values for the entries placed in the repeater. Does not overwrite the defaultValue props of children inputs if there is any | array | [{username: "edward_baldwin", favourite_number: 10, ...}, ...] | null | | entries | The initial amount of entries to display | number | 3 | 0 | | minEntries | The minimum amount of entries to display | number | 1 | 0 | | maxEntries | The maximum amount of entries to display | number | 5 | 0 | | addComponent | The component used for the button that is responsible for inserting a new entry | JSX | <button>Add new!</button> | The default add component | | removeComponent | The component used for the button that is responsible for removing an entry | JSX | <button>Remove this!</button> | The default remove component |
3.5. SubmitButton
The <SubmitButton/> is used as the button that fires the onSubmit event of the <Form/>.
It acts as the native <button type="submit"></button> or <input type="submit"/> element.
Example usage:
import Form, {SubmitButton} from 'react-superior-forms';
<Form route="/example">
...
<SubmitButton/>
</Form>;
Example output:
<button class="rsf-submit-button" type="submit">Submit</button>
Parameters:
| Parameter | Description | Type | Example value(s) | Default | | - | - | - | - | - | | children | The children of the <button>...</button> element. | JSX | "Submit" | "Submit" |
3.6. SubmitStatus
The <SubmitButton/> is used to display the status text of the last submit attempt.
Example usage:
import Form, {SubmitStatus} from 'react-superior-forms';
<Form route="/example">
...
<SubmitStatus/>
</Form>;
Example output:
<span class="rsf-submit-status rsf-submit-status--fail">Failed to submit</span>
4. Builder API, <FormBuilder/>
The <FormBuilder/> is used to transform a JSON into a form.
Example usage:
import {FormBuilder, InputTypes} from 'react-superior-forms';
<FormBuilder
route="/example"
inputGroups={[
{
repeater: {
entries: 1,
legend: 'Entry',
},
legend: 'My group',
className: 'my-group',
name: 'users',
inputs: [
{
label: 'This users\'s username',
name: 'username',
type: InputTypes.Text,
wrapperClassName: 'my-wrapper',
className: 'my-input',
before: <h1>Hello world!</h1>,
defaultValue: 'test',
onChange: (value) => console.log(value),
onValidate: (validators) => console.log(validators),
},
{
name: 'email',
type: InputTypes.Email,
defaultValue: '[email protected]',
className: 'email-input',
validate: true,
format: true,
process: true,
},
],
inputGroups: [
{
legend: 'My nested input group',
className: 'my-nested-group',
name: 'sub-users',
inputs: [
{
label: 'Username',
name: 'username',
type: InputTypes.Text,
className: 'my-input',
},
],
},
],
},
]}
/>;
Parameter documentation coming soon.
Dependencies:
Other Links: