DataForm
Check out the Intro To Forms Tutorial to get familia with how forms work in React with Kyber.
Basic Example
You can describe each field as an object that is passed into the DataForm's fields
prop. The DataForm will then handle rendering and configuring each field for you.
For each field object you need to provide a Kyber form component name for the as
key to tell the DataForm which component you would like to use. For example { as: 'TextField' }
will render the TextField component. You also need to provide values for the name
and fieldLabel
keys. The name
will be the key used in the returned form data. The fieldLabel
will be the label displayed above the field. All other key/value pairs are treated as props that will be spread onto the component. See each component's documentation for which props are available.
<DataForm id="login-form-basic-example" fields={[{ as: 'TextField', name: 'username', fieldLabel: 'Username' }]} onSubmit={(values, actions) => { // The actions argument gives you access to Formik actions which can be used to reset the submitting state actions.setSubmitting(false); console.log({ values }); }} />
All Fields
<DataForm id="all-fields-form" fields={[ { as: 'TextField', name: 'text', fieldLabel: 'Text Field' }, { as: 'AutoComplete', name: 'auto_complete', fieldLabel: 'Auto Complete', options: [ { name: 'Apple', value: 'apple', }, { name: 'Orange', value: 'orange', }, { name: 'Pear', value: 'pear', }, ], }, { as: 'Select', name: 'selection', fieldLabel: 'Select', options: [ { name: 'One', value: '1', }, { name: 'Two', value: '2', }, { name: 'Three', value: '3', }, ], }, { as: 'Select', name: 'selection_searchable', searchable: true, fieldLabel: 'Searchable Select', options: [ { name: 'One', value: '1', }, { name: 'Two', value: '2', }, { name: 'Three', value: '3', }, ], }, { as: 'MultiSelect', name: 'mutli_select', fieldLabel: 'MultiSelect', options: [ { name: 'Option 1', value: 'one', }, { name: 'Option 2', value: 'two', }, { name: 'Option 3', value: 'three', }, ], multiple: true, }, { as: 'MultiSelect', name: 'mutli_select_searchable', fieldLabel: 'Searchable MultiSelect', searchable: true, options: [ { name: 'Option 1', value: 'one', }, { name: 'Option 2', value: 'two', }, { name: 'Option 3', value: 'three', }, ], multiple: true, }, { as: 'RadioGroup', name: 'radio_options', fieldLabel: 'What is going on with that radio?', options: [ { label: 'Option 1', value: '1' }, { label: 'Option 2', value: '2' }, { label: 'Option 3', value: '3' }, ], }, { as: 'CheckboxGroup', name: 'checkbox_options', fieldLabel: 'What is going on with that checkbox?', options: [ { label: 'Option 1', value: '1' }, { label: 'Option 2', value: '2' }, { label: 'Option 3', value: '3' }, ], }, { as: 'NumberField', name: 'number', fieldLabel: 'Number' }, { as: 'DatePicker', name: 'date', fieldLabel: 'Date' }, { as: 'DatePicker', name: 'datetime', fieldLabel: 'Date & Time', type: 'datetime-local' }, { as: 'DatePicker', name: 'time', fieldLabel: 'Time', type: 'time' }, { as: 'Slider', name: 'slider', fieldLabel: 'Slider', min: 0, max: 100 }, { as: 'Switch', name: 'switch', fieldLabel: 'Switch', label: 'Toggle Agreement' }, { as: 'Checkbox', name: 'checkbox', fieldLabel: 'Checkbox', label: 'I agree to the terms of service', }, ]} onSubmit={(values, actions) => { actions.setSubmitting(false); console.log({ values }); }} />
Validation
Yup is a powerful validation library that can be used with the DataForm component. There are a lot of options and ways that Yup validation schemas can be constructed. Review the Yup Documentation for full details.
This example shows basic validation making each field required.
<DataForm id="login-form-with-validation" fields={[ { as: 'TextField', name: 'username', fieldLabel: 'Username' }, { as: 'TextField', name: 'password', fieldLabel: 'Password', type: 'password' }, ]} validationSchema={object().shape({ username: string().required(), password: string().required(), })} onSubmit={(values, actions) => { actions.setSubmitting(false); console.log({ values }); }} submitButtonContents="Submit" />
Cross Field Validation
Using Yup that showcases one way to handle cross field validation. Note the ref
to the age
field in the retirement_age
validation schema.
<DataForm id="login-form-with-cross-field-validation" fields={[ { as: 'NumberField', name: 'age', fieldLabel: 'Age' }, { as: 'NumberField', name: 'retirement_age', fieldLabel: 'Retirement Age' }, ]} validationSchema={object().shape({ age: number().required(), retirement_age: number().required().min(ref('age'), '${label} can not be less than Age'), })} onSubmit={(values, actions) => { actions.setSubmitting(false); console.log({ values }); }} />
Required with Disabled Field
Using Yup to change the conditionality of field validation based on changes outside of the form. In this case toggling the disabled state of the Retirement Age field should allow the form to submit with optional validation to that field.
function Example() { const [disabledRetirement, setDisabledRetirement] = useState(false); const formRef = useRef(); // Force the form to validate when a field prop has changed useEffect(() => { if (formRef.current) { formRef.current.validateForm(); } }, [disabledRetirement]); return ( <> <Button id="disable-retirement-btn" onClick={() => { setDisabledRetirement(!disabledRetirement); }} style={{ marginBottom: 20 }} > {disabledRetirement ? 'Enable' : 'Disable'} Retirement Field </Button> <DataForm ref={formRef} id="login-form-with-disabled" fields={[ { as: 'NumberField', name: 'age', fieldLabel: 'Age' }, { as: 'NumberField', name: 'retirement_age', fieldLabel: 'Retirement Age', disabled: disabledRetirement, }, ]} validationSchema={object().shape({ age: number().required(), retirement_age: number().when([], { is: () => disabledRetirement, then: number().optional(), otherwise: number().required(), }), // Alternatively, 'when' can receive a function that can adjust the field's schema // retirement_age: number().when('$other', (field, schema) => // disabledRetirement ? schema.optional() : schema.required(), // ), })} onSubmit={(values, actions) => { actions.setSubmitting(false); console.log({ values }); }} /> </> ); }
Add/Remove Field
Click the "Toggle Retirement Field" button to add/remove the "Retirement Age" field. Note how when the field is present it is required, but when it's removed the form can submit without it.
function Example() { const [hasRetirement, setHasRetirement] = useState(true); const formRef = useRef(); const fields = [{ as: 'NumberField', name: 'age', fieldLabel: 'Age' }]; const validation = { age: number().required(), }; // Add/remove the field and validation based on values in state if (hasRetirement) { fields.push({ as: 'NumberField', name: 'retirement_age', fieldLabel: 'Retirement Age', }); validation.retirement_age = number().required(); } return ( <> <Button id="toggle-retirement-display" onClick={() => { setHasRetirement(!hasRetirement); }} style={{ marginBottom: 20 }} > Toggle Retirement Field </Button> <DataForm ref={formRef} id="login-form-with-add-remove-field" fields={fields} validationSchema={object().shape(validation)} onSubmit={(values, actions) => { actions.setSubmitting(false); console.log({ values }); }} /> </> ); }
All Fields With Yup Validation
<DataForm id="demo-component" fields={[ { as: 'TextField', name: 'text', fieldLabel: 'Text Field' }, { as: 'Select', name: 'selection', fieldLabel: 'Select Box', options: [ { title: 'I am multi-line', name: 'One', value: '1', }, { name: 'Two', value: '2', }, { name: 'Three', value: '3', }, ], }, { as: 'MultiSelect', name: 'mutli_select', fieldLabel: 'Multiple Select Box', options: [ { name: 'Option 1', value: 'one', }, { name: 'Option 2', value: 'two', }, { name: 'Option 3', value: 'three', }, ], multiple: true, }, { as: 'RadioGroup', name: 'radio_options', fieldLabel: 'What is going on with that radio?', options: [ { label: 'Option 1', value: '1' }, { label: 'Option 2', value: '2' }, { label: 'Option 3', value: '3' }, ], }, { as: 'CheckboxGroup', name: 'checkbox_options', fieldLabel: 'What is going on with that checkbox?', options: [ { label: 'Option 1', value: '1' }, { label: 'Option 2', value: '2' }, { label: 'Option 3', value: '3' }, ], }, { as: 'NumberField', name: 'number', fieldLabel: 'Number' }, { as: 'Slider', name: 'slider', fieldLabel: 'Slider', min: 0, max: 100 }, { as: 'Switch', name: 'switch', fieldLabel: 'Switch', label: 'Toggle Agreement' }, { as: 'Checkbox', name: 'checkbox', fieldLabel: 'Checkbox', label: 'I agree to the terms of service', }, ]} validationSchema={object().shape({ text: string().required(), selection: string().required(), mutli_select: array().of(string()).required(), start_date: date().required(), radio_options: string().required(), checkbox_options: array().of(string()).required(), number: number().required(), plusminus: number().required(), slider: number().required(), switch: boolean().required(), checkbox: boolean().required(), })} onSubmit={(values, actions) => { actions.setSubmitting(false); console.log({ values }); }} />
Custom Footer
Same as the regular Kyber Form component you can customize the form footer with the footerRenderer
prop. The function passed to the footerRenderer
prop will receive all the DataForm
props as an argument. This allows you submit the form with a custom button. The type="submit"
prop should be used on a button to handle keyboard submission.
function Example() { const formRef = useRef(); return ( <DataForm ref={formRef} id="login-form-custom-footer" fields={[ { as: 'TextField', name: 'username', fieldLabel: 'Username' }, { as: 'TextField', name: 'password', fieldLabel: 'Password', type: 'password' }, ]} onSubmit={(values, actions) => { actions.setSubmitting(false); console.log({ values }); }} footerRenderer={(props) => ( <> <Button variant="danger" onClick={() => { formRef.current.resetForm(); }} > Don't Press This Button </Button> <Button type="submit" onClick={props.onSubmit}> Submit </Button> </> )} /> ); }
Using the Form Ref
The Form's ref
prop will return a reference to the Formik instance. This gives you access to Formik's props and methods which let you control the form's state manually. For example this can be used to reset the form and clear out the field values.
function Example() { const formRef = useRef(); const handleSecondaryButtonClick = (event) => { console.log('secondary button clicked'); formRef.current.resetForm(); }; return ( <DataForm id="-custom-footer" fields={[{ as: 'TextField', name: 'username', fieldLabel: 'Username' }]} onSubmit={(values, actions) => { // The actions argument gives you access to Formik actions which can be used to reset the submitting state actions.setSubmitting(false); console.log({ values }); }} onSecondaryButtonClick={handleSecondaryButtonClick} secondaryButtonContents="Reset form" /> ); }
Analytics
The DataForm component is trackable through Kyber Analytics. This is the default analytics config.
export default {
value: 'Form',
actions: {
onSubmit: { type: 'FORM_SUBMIT', payload: 'Submit' },
},
};