Skip to main content

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.

Result
Loading...
Live Editor
<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

Result
Loading...
Live Editor
<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.

Result
Loading...
Live Editor
<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.

Result
Loading...
Live Editor
<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.

Result
Loading...
Live Editor
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.

Result
Loading...
Live Editor
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

Result
Loading...
Live Editor
<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 });
	}}
/>

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.

Result
Loading...
Live Editor
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.

Result
Loading...
Live Editor
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' },
},
};

Props