react-form-lib - Facilitate React form integration and validation
React-form-lib is a Form framework which provides auto-validated components
With react-form-lib create forms faster in a simple and sementic way.
Avoid validation complexity by externalize and modularize all your business or technical form's validation rules in one place,
inject rules in the form and let the framework validate automatically
Features
- Easy to maintain validation rules
- Easy to implement click-stream with native show/hide capability
- Semantic form integration and illimited DOM depth
- Some basic form components (Select, RadioList, Label, ErrorMsg)
- Label and ErrorMsg customization. you can pass custom component in place of default provided components
- Support HTML5 form components (input...)
Install
npm install --save react-form-lib
API
<Form />
The Form wrapper component
- The component accepts the following props:
Name | Type | Description |
---|---|---|
validationRules? |
Object |
A formatted Object which contains all business validation rules by field names |
customLabel? |
Component |
React component to customize input labels. Inherit labelParams props from FormItems |
customError? |
Component |
React component to customize input errors. Inherit error props from FormItems |
- How to format validationRules : (replace <> by "name" of field (see example))
const myRules = {
<name_of_file_to_validate>: [
{
key: string | unique,
message: string,
isValid: function
},
...
],
<name_of_another_field_to_validate>: [
{...},
...
],
...
}
- Schema for each rule in
validationRules
Name | Type | Description |
---|---|---|
key |
Object |
Unique key for this rule |
message |
string |
Message returned if validation failed |
isValid |
function |
function which got next field value and existing others form data, as param and return boolean |
- NOTE : for the message of fields marked as
required
(see FormItem props), You can add a simple rule with the namecustomGlobalRequiredMessage
which contain only a message to override default required message
EXAMPLE
rules = {
customGlobalRequiredMessage: {message: 'myCustom required Message which will appear if my formItem is marked as required and is empty'}
}
<FormItem />
This component is a child of the Form component
- The component accepts the following props:
Name | Type | Description |
---|---|---|
name |
string |
The schema name of the data of the field (The one which has to be used for validationRules and schema persistence) |
type |
string |
Reference the type of the field passed as child. Takes only text or radio or select or submit or reset
|
required? |
boolean |
Define if field is required (add red star to label and validate if filled or not with message (or customRequiredMessage => see validationRules notes) |
initialValue? |
? |
Initial or Default field value |
validationTrigger? |
string |
Define when field validation is triggered. takes only onChange for now |
inputSubInfo? |
? |
String or Component to add subinfo under input field |
labelParams? |
Object |
The label params of the field (see schema below) |
show? |
function |
Function which got all values stored in Form and return boolean to add some logic for show/hide field. |
className? |
string |
className for field wrapper |
callBackAction? |
function |
Function which got all values stored in Form called after validationTrigger is triggered. |
resetAfterSubmit? |
boolean |
Only on FormItem of type 'submit'. if callBackAction Promise is resolved, reset the form |
- Schema of
labelParams?
object
Name | Type | Description |
---|---|---|
text |
string |
The label of the field |
subLabel? |
? |
String or Component to add subinfo under label text |
inlinePosition? |
boolean |
Determine if label is on inline position with field or block position |
fixedWidth? |
number |
fix the width of the label |
<Select /> AND <RadioList />
These components are both child of FormItem
- They accepts only one props:
Name | Type | Description |
---|---|---|
className |
string |
className for component |
dataSet |
array |
An array of {label, value} objects |
name |
string |
inherited from FormItem |
onChange |
function |
inherited from FormItem |
placeHolder? |
string |
only for Select : add first disabled option in select |
value |
string |
inherited from Form : default value |
- Schema of dataSet
[
{
label: string,
value: string
},
{...}
]
<Label />
This components is the built in component to display input labels
- It accepts these props:
Name | Type | Description |
---|---|---|
label |
string |
content of label |
required? |
boolean |
default false : display red star if true |
subLabel? |
string |
Add a small text under label |
<ErrorMsg />
This components is the built in component to display input errors
- It accepts this props:
Name | Type | Description |
---|---|---|
error |
string |
content of error |
Example
- Here the full example with various use cases.
import React, {useState} from 'react'
import {Form, FormItem, Select, RadioList, ErrorMsg} from "../src";
import "./App.scss";
const enumMrMrs = [
{label: "Mr", value: "Mr"}, {label: "Mrs", value: "Mrs"}
];
const personTypes = [
{label: "Parent1", value: "1"},
{label: "Parent2", value: "2"}
];
function isNotEmpty(v) {
return v !== undefined && v !== "" && v !== null && v.length !== 0 && v !== [];
}
// You can put these rules in external file and import it
/*All rules have to respect this format to be parsed by validation method in Form.
for each field you want to be validated you have to reference his exact "name" props in this array
then for each entry you have to put a "key" property (what you want : unique), a "message" property as an Error message to display after validation
and a "isValid" property that is a function which takes the value of the field and do whatever you want to return a boolean
You can put more than one rule per field reference.*/
const rules = {
customGlobalRequiredMessage: "is empty. Please fill it.",
maidenName: [
{
key: "maidenname_invalid",
message: "This name is not allowed",
isValid: (nextVal) => (nextVal !== "test")
},
{
key: "maidenname_empty",
message: "Maiden name is required if you are a girl. Please fill it.",
isValid: (nextVal, values) => (values.title === '' || values.title === 'Mr' || (values.title === 'Mrs' && isNotEmpty(nextVal)))
}
],
lastName: [
{
key: "lastname_empty",
message: "Last name can't contains numbers.",
isValid: (nextVal) => (nextVal.match(/^[a-zA-Z]+$/))
},
],
firstName: [
{
key: "lastname_nonempty_but_firstname_empty",
message: "FirstName has to contain more than 1 charachter.",
isValid: (nextVal) => (nextVal.match(/^([A-Za-z]{2,})$/))
}
],
};
const App = () => {
const [persistOk, setPersistOk] = useState(false);
const [apiErr, setApiErr] = useState(null);
const [dataStore, setDataStore] = useState({});
const persist = (data) => {
//simulate api call
let prom = new Promise((resolve, reject) => {
// comment the resolve method call and uncomment the reject method call to simulate Service error and test ErrorMsg component
resolve(setDataStore({data: data}))
//reject("PERSISTENCE_MOCK_ERR_CODE")
});
return prom.then(() => {
setPersistOk(true)
})
.catch((err) => {
setApiErr(err)
return Promise.reject(err)
})
};
let customLabel = null;
let customError = null;
// uncomment these lines to test custom component for input label and input errors
/*customLabel = (props) => <div style={{color: 'violet'}}>My Custom Label : {props && props.labelParams.text}</div>
customError = (props) => <div style={{color: 'orange'}}>My Custom Error : {props && props.error}</div>*/
const inputInfoExample = <label style={{display: 'block'}}>inputSubInfo</label>
const myLabelWithGlobalParams = ({...myCustomParams}) => {
return {...myCustomParams, inlinePosition:true, fixedWidth: 200}
}
return (
<>
<div className={`${persistOk ? 'filter-active' : ""} prf_container inline-block`}>
{apiErr && <ErrorMsg error={`An error occured ! ${apiErr}`}/>}
<Form validationRules={rules} customLabel={customLabel} customError={customError} >
<FormItem
name="person"
type={"select"}
labelParams={myLabelWithGlobalParams({text:"Person"})}
className={"prf_form-item"}
>
<Select dataSet={personTypes} placeHolder={"Select a type"} className={"prf_select prf_input"}/>
</FormItem>
<FormItem
name="title"
type={"radio"}
labelParams={myLabelWithGlobalParams({text:"Title civility"})}
className={"prf_form-item"}
>
<RadioList dataSet={enumMrMrs} />
</FormItem>
<div className={"prf-divider"}/>
<FormItem
name="maidenName"
type={"text"}
initialValue={"initial name"}
validationTrigger={'onChange'}
inputSubInfo={inputInfoExample}
labelParams={myLabelWithGlobalParams({text: "Maiden name", subLabel: "Name 'test' is not allowed"})}
className={"prf_form-item"}
>
<input className="prf_input" />
</FormItem>
<FormItem
name="lastName"
type={"text"}
required={true}
labelParams={myLabelWithGlobalParams({text: "Last name"})}
className={"prf_form-item"}
>
<input className="prf_input" />
</FormItem>
<FormItem
name="firstName"
type={"text"}
required={true}
labelParams={myLabelWithGlobalParams({text: "First name"})}
show={(values) => (values && values.lastName !== "")}
className={"prf_form-item"}
>
<input className="prf_input" />
</FormItem>
<div className={"prf-btn-toolbar"}>{/*Here example of wrapper for FormItem inside Form*/}
<FormItem
type={"submit"}
callBackAction={persist}
resetAfterSubmit={true} //if callBackAction Promise is resolved form will be reseted
className={"inline-block"}
>
<button className="prf-btn prf-btn--primary" type={"button"}>Submit</button>
</FormItem>
<FormItem
callBackAction={() => setApiErr(null)}
type={"reset"}
className={"inline-block"}
>
<button className="prf-btn prf-btn--primary" type={"button"}>Reset</button>
</FormItem>
</div>
</Form>
{persistOk && <div className={`modal`}><div className={"modal-message"}>Data saved correctly <pre>dataStore = {JSON.stringify(dataStore.data, null, 4)}</pre></div><button onClick={() => (setPersistOk(false))} className="prf-btn prf-btn--primary" type={"button"}>OK</button></div>}
</div>
</>
)
}
export default App;
Running locally
go in project folder and run following commands
npm install
npm start