@devseed-ui/form
A package to build custom forms. List of available components:
- Form
- FormGroup
- FormGroupBody
- FormGroupHeader
- FormLabel
- FormInput
- FormCheckable
- FormCheckableGroup
- FormSwitch
- FormSelect
- FormTextarea
- FormHelper
- FormHelperCounter
- FormHelperMessage
- FormFieldset
- FormFieldsetBody
- FormFieldsetHeader
- FormLegend
Example
<DevseedUiThemeProvider>
<CollecticonsGlobalStyle />
<Form>
<FormFieldset>
<FormFieldsetHeader>
<FormLegend>Form legend</FormLegend>
<Toolbar>
<ToolbarIconButton useIcon="xmark--small">
Close
</ToolbarIconButton>
</Toolbar>
</FormFieldsetHeader>
<FormFieldsetBody>
<FormGroup>
<FormGroupHeader>
<FormLabel htmlFor="input-text-a">Form label</FormLabel>
<Toolbar size="small">
<ToolbarIconButton useIcon="circle-information">
More information
</ToolbarIconButton>
</Toolbar>
</FormGroupHeader>
<FormGroupBody>
<FormInput
type="text"
size="large"
id="input-text-a"
placeholder="This is a text input"
invalid
/>
<FormHelper>
<div>
<FormHelperMessage>
This is some help text.
</FormHelperMessage>
<FormHelperMessage invalid>
This is an error message.
</FormHelperMessage>
</div>
<FormHelperCounter>0 / 80</FormHelperCounter>
</FormHelper>
</FormGroupBody>
</FormGroup>
<FormGroup>
<FormGroupHeader>
<FormLabel>Form label</FormLabel>
<Toolbar size="small">
<ToolbarLabel>Select</ToolbarLabel>
<ToolbarButton>All</ToolbarButton>
<ToolbarButton>None</ToolbarButton>
</Toolbar>
</FormGroupHeader>
<FormGroupBody>
<FormCheckableGroup>
<FormCheckable
checked={undefined}
type="checkbox"
name="checkbox-a"
id="checkbox-a"
>
Checkbox A
</FormCheckable>
<FormCheckable
checked={undefined}
type="checkbox"
name="checkbox-b"
id="checkbox-b"
>
Checkbox B
</FormCheckable>
</FormCheckableGroup>
</FormGroupBody>
</FormGroup>
<FormGroup>
<FormGroupHeader>
<FormLabel>Form label</FormLabel>
</FormGroupHeader>
<FormGroupBody>
<FormCheckableGroup>
<FormCheckable
textPlacement="right"
checked={undefined}
type="radio"
name="radio-a"
id="radio-a1"
>
Radio A
</FormCheckable>
<FormCheckable
textPlacement="right"
checked={undefined}
type="radio"
name="radio-a"
id="radio-a2"
>
Radio B
</FormCheckable>
<FormCheckable
textPlacement="right"
checked={undefined}
type="radio"
name="radio-a"
id="radio-a3"
>
Radio C
</FormCheckable>
<FormCheckable
textPlacement="right"
checked={undefined}
type="radio"
name="radio-a"
id="radio-a4"
>
Radio D
</FormCheckable>
</FormCheckableGroup>
</FormGroupBody>
</FormGroup>
<FormFieldset>
<FormFieldsetHeader>
<FormLegend>Form legend</FormLegend>
<Button
variation="base-plain"
size="small"
hideText
data-tip="This is a super helpful tool tip."
>
Remove fieldset
</Button>
</FormFieldsetHeader>
<FormFieldsetBody>
<FormGroup>
<FormGroupHeader>
<FormLabel htmlFor="textarea-b">Form label</FormLabel>
</FormGroupHeader>
<FormGroupBody>
<FormTextarea
size="large"
id="textarea-b"
placeholder="This is a textarea"
/>
<FormHelper>
<FormHelperCounter>0 / 80</FormHelperCounter>
</FormHelper>
</FormGroupBody>
</FormGroup>
</FormFieldsetBody>
</FormFieldset>
<FormGroup>
<FormGroupHeader>
<FormLabel htmlFor="select-a" optional>
Form label
</FormLabel>
</FormGroupHeader>
<FormGroupBody>
<FormSelect size="large" id="select-a">
<option value="option-1">Option 1</option>
<option value="option-2">Option 2</option>
<option value="option-3">Option 3</option>
<option value="option-4">Option 4</option>
</FormSelect>
<FormHelper>
<FormHelperMessage>This is some help text.</FormHelperMessage>
</FormHelper>
</FormGroupBody>
</FormGroup>
<FormGroup>
<FormGroupHeader>
<FormLabel htmlFor="textarea-a">Form label</FormLabel>
</FormGroupHeader>
<FormGroupBody>
<FormTextarea
size="large"
id="textarea-a"
placeholder="This is a textarea"
invalid
/>
<FormHelper>
<FormHelperMessage invalid>
This is an error message.
</FormHelperMessage>
</FormHelper>
</FormGroupBody>
</FormGroup>
</FormFieldsetBody>
</FormFieldset>
</Form>
</DevseedUiThemeProvider>
Usage
The following section explains the base structure of a form and how to use the different components.
Form
Form wrapper, should be the parent component in every form.
Form Groups
Except for some specific cases, the form elements shouldn't be used by themselves.
They should be included in a form group with a header and a body. This allows for proper styling and adding companion components like for example a toolbar.
The normal form component structure is:
<FormGroup>
<FormGroupHeader>
<FormLabel>Form label</FormLabel>
</FormGroupHeader>
<FormGroupBody>
</FormGroupBody>
</FormGroup>
The FormGroupHeader
contains the FormLabel
which for accessibility reasons should always be present. If the label is not desired, it can be hidden using <FormGroupHeader isHidden>
.
The FormGroupBody
is where the components can be added. Thus, the structure for a simple text input would be:
<DevseedUiThemeProvider>
<Form>
<FormGroup>
<FormGroupHeader>
<FormLabel htmlFor="input-text1">Form label</FormLabel>
</FormGroupHeader>
<FormGroupBody>
<FormInput type="text" id="input-text1" />
</FormGroupBody>
</FormGroup>
</Form>
</DevseedUiThemeProvider>
This is the base structure and should be used with all input types
Default Properties
The following properties are available for all the elements.
rows:
- Prop name: "name"
Type: "string"
Description: "String to be used as HTML property `name`"
Default value: "N/A"
- Prop name: "id"
Type: "string"
Description: "String to be used as HTML property `id`"
Default value: "N/A"
- Prop name: "title"
Type: "string"
Description: "String to be used as HTML property `title`"
Default value: "N/A"
FormInput
<DevseedUiThemeProvider>
<Form>
<FormGroup>
<FormGroupHeader>
<FormLabel htmlFor="input-text2">Form label</FormLabel>
</FormGroupHeader>
<FormGroupBody>
<FormInput type="text" id="input-text2" />
</FormGroupBody>
</FormGroup>
</Form>
</DevseedUiThemeProvider>
Properties
rows:
- Prop name: "type"
Type: "string"
Description: "Determines the type of input to render. One of (number | text | password)"
- Prop name: "invalid"
Type: "boolean"
Description: "Whether or not the field is invalid. Will render with the danger color"
Default value: "false"
- Prop name: "stressed"
Type: "boolean"
Description: "Animates the field with the shake animation"
Default value: "false"
- Prop name: "size"
Type: "string"
Description: "Size of the input field. One of (small | medium | large)"
Default value: "medium"
FormTextarea
<DevseedUiThemeProvider>
<Form>
<FormGroup>
<FormGroupHeader isHidden>
<FormLabel htmlFor="textarea2">Form label</FormLabel>
</FormGroupHeader>
<FormGroupBody>
<FormTextarea id="textarea2" placeholder="This is a textarea" />
</FormGroupBody>
</FormGroup>
</Form>
</DevseedUiThemeProvider>
Properties
rows:
- Prop name: "invalid"
Type: "boolean"
Description: "Whether or not the field is invalid. Will render with the danger color"
Default value: "false"
- Prop name: "stressed"
Type: "boolean"
Description: "Animates the field with the shake animation"
Default value: "false"
- Prop name: "size"
Type: "string"
Description: "Size of the input field. One of (small | medium | large)"
Default value: "medium"
FormCheckable
Element to build radio or checkboxes.
Wrapping them in FormCheckableGroup
ensures a consistent display
<DevseedUiThemeProvider>
<Form>
<FormGroup>
<FormGroupHeader>
<FormLabel>Form label</FormLabel>
</FormGroupHeader>
<FormGroupBody>
<FormCheckableGroup>
<FormCheckable
checked={undefined}
type="checkbox"
name="checkbox2"
id="checkbox2"
>
Checkbox A
</FormCheckable>
<FormCheckable
checked={undefined}
type="checkbox"
name="checkbox3"
id="checkbox3"
>
Checkbox B
</FormCheckable>
</FormCheckableGroup>
</FormGroupBody>
</FormGroup>
</Form>
</DevseedUiThemeProvider>
Properties
rows:
- Prop name: "type"
Type: "string"
Description: "Determines the type of input to render, can be one of `checkbox` or `radio`"
Default value: "N/A"
- Prop name: "checked"
Type: "boolean"
Description: "Whether or not the element is checked"
Default value: "N/A"
- Prop name: "onChange"
Type: "string"
Description: "A function to execute on change"
Default value: "N/A"
- Prop name: "children"
Type: "string"
Description: "Text content"
Default value: "N/A"
- Prop name: "textPlacement"
Type: "string"
Description: "Where to position the text. `left` or `right` of the control"
Default value: "right"
- Prop name: "hideText"
Type: "boolean"
Description: "Whether to visually hide the checkable text"
Default value: "false"
FormSwitch
The FormSwitch
has the same basic behavior as a single checkbox but with a different presentation. It is used mostly for binary type options.
<DevseedUiThemeProvider>
<Form>
<FormGroup>
<FormGroupHeader isHidden>
<FormLabel>Form label</FormLabel>
</FormGroupHeader>
<FormGroupBody>
<FormSwitch
name="switch4"
title="Toggle layer on/off"
checked={true}
onChange={() => {}}
>
Show all options
</FormSwitch>
</FormGroupBody>
</FormGroup>
</Form>
</DevseedUiThemeProvider>
Properties
rows:
- Prop name: "checked"
Type: "boolean"
Description: "Whether or not the element is checked"
Default value: "N/A"
- Prop name: "onChange"
Type: "string"
Description: "A function to execute on change"
Default value: "N/A"
- Prop name: "children"
Type: "string"
Description: "Text content"
Default value: "N/A"
- Prop name: "textPlacement"
Type: "string"
Description: "Where to position the text. `left` or `right` of the control"
Default value: "right"
- Prop name: "hideText"
Type: "boolean"
Description: "Whether to visually hide the switch text"
Default value: "false"
FormSelect
<DevseedUiThemeProvider>
<Form>
<FormGroup>
<FormGroupHeader isHidden>
<FormLabel>Form label</FormLabel>
</FormGroupHeader>
<FormGroupBody>
<FormSelect id="select4">
<option value="option-1">Option 1</option>
<option value="option-2">Option 2</option>
<option value="option-3">Option 3</option>
<option value="option-4">Option 4</option>
</FormSelect>
</FormGroupBody>
</FormGroup>
</Form>
</DevseedUiThemeProvider>
Properties
rows:
- Prop name: "invalid"
Type: "boolean"
Description: "Whether or not the field is invalid. Will render with the danger color"
Default value: "false"
- Prop name: "stressed"
Type: "boolean"
Description: "Animates the field with the shake animation"
Default value: "false"
- Prop name: "size"
Type: "string"
Description: "Size of the input field. One of (small | medium | large)"
Default value: "medium"
Helpers
The form helpers are used to display messages below the fields.
There are 2 helpers available which should always be wrapped by the parent FromHelper
and placed inside the FormGroupBody
.
FormHelperMessage
Displays a simple message on the left side. Using the property invalid
will display it using the theme danger color.
<DevseedUiThemeProvider>
<Form>
<FormGroup>
<FormGroupHeader>
<FormLabel htmlFor="input-text5">Form label</FormLabel>
</FormGroupHeader>
<FormGroupBody>
<FormInput type="text" id="input-text5" />
<FormHelper>
<div>
<FormHelperMessage>
This is some help text.
</FormHelperMessage>
<FormHelperMessage invalid>
This is an error message.
</FormHelperMessage>
</div>
</FormHelper>
</FormGroupBody>
</FormGroup>
</Form>
</DevseedUiThemeProvider>
Properties
rows:
- Prop name: "invalid"
Type: "boolean"
Description: "Whether or not the field is invalid. Will render with the danger color"
Default value: "false"
- Prop name: "stressed"
Type: "boolean"
Description: "Animates the field with the shake animation"
Default value: "false"
- Prop name: "children"
Type: "node"
Description: "The message content"
Default value: ""
When using multiple FormHelperMessage
(one for help and another for an error for example), they should be wrapped in an extra div to ensure they're correctly positioned.
<FormHelper>
<div>
<FormHelperMessage>
This is some help text.
</FormHelperMessage>
<FormHelperMessage invalid>
This is an error message.
</FormHelperMessage>
</div>
</FormHelper>
FormHelperCounter
Used to display a counter on the right side of the field, this is useful to indicate that the field has a maximum character count.
The color of the text will change to warning
when reaching the warnAt
value and will change to danger
when over the max
.
state: {value: ''}
---
<DevseedUiThemeProvider>
<Form>
<FormGroup>
<FormGroupHeader>
<FormLabel htmlFor="input-text6">Form label</FormLabel>
</FormGroupHeader>
<FormGroupBody>
<FormInput
type="text"
id="input-text6"
value={state.value}
onChange={(e) => setState({value: e.target.value})}
placeholder='Type in something'
/>
<FormHelper>
<FormHelperCounter max={50} value={state.value.length} warnAt={35}/>
</FormHelper>
</FormGroupBody>
</FormGroup>
</Form>
</DevseedUiThemeProvider>
Properties
rows:
- Prop name: "max"
Type: "Number"
Description: "Maximum value allowed for the counter, after which the danger color is applied"
Default value: ""
- Prop name: "warnAt"
Type: "Number"
Description: "Value after which the warning color is applied"
Default value: "90% of max"
- Prop name: "value"
Type: "Number"
Description: "Current counter value"
Default value: ""
FormFieldset
A form fieldset is useful to contain other form elements and must follow a structure similar to the form groups.
<FormFieldset>
<FormFieldsetHeader>
<FormLegend>Form legend</FormLegend>
</FormFieldsetHeader>
<FormFieldsetBody>
</FormFieldsetBody>
</FormFieldset>
Example of a fieldset with am input field.
<DevseedUiThemeProvider>
<CollecticonsGlobalStyle />
<Form>
<FormFieldset>
<FormFieldsetHeader>
<FormLegend>Form legend</FormLegend>
</FormFieldsetHeader>
<FormFieldsetBody>
<FormGroup>
<FormGroupHeader>
<FormLabel htmlFor="input-text7">Form label</FormLabel>
</FormGroupHeader>
<FormGroupBody>
<FormInput type="text" id="input-text7" />
</FormGroupBody>
</FormGroup>
</FormFieldsetBody>
</FormFieldset>
</Form>
</DevseedUiThemeProvider>
Toolbar
Both the FormFieldset
and FormGroup
support the inclusion of a Toolbar
in the header. This is useful to provide contextual actions for the fields.
The toolbar is part of a separate ui package, but will pair well with the form elements.
import {
Toolbar,
ToolbarButton,
ToolbarLabel,
ToolbarIconButton,
} from '@devseed-ui/toolbar';
Example:
<DevseedUiThemeProvider>
<CollecticonsGlobalStyle />
<Form>
<FormFieldset>
<FormFieldsetHeader>
<FormLegend>Form legend</FormLegend>
<Toolbar>
<ToolbarIconButton useIcon="xmark--small">
Close
</ToolbarIconButton>
</Toolbar>
</FormFieldsetHeader>
<FormFieldsetBody>
<FormGroup>
<FormGroupHeader>
<FormLabel>Form label</FormLabel>
<Toolbar size="small">
<ToolbarLabel>Select</ToolbarLabel>
<ToolbarButton>All</ToolbarButton>
<ToolbarButton>None</ToolbarButton>
</Toolbar>
</FormGroupHeader>
<FormGroupBody>
<FormCheckableGroup>
<FormCheckable
checked={undefined}
type="checkbox"
name="checkbox8"
id="checkbox8"
>
Checkbox A
</FormCheckable>
<FormCheckable
checked={undefined}
type="checkbox"
name="checkbox9"
id="checkbox9"
>
Checkbox B
</FormCheckable>
</FormCheckableGroup>
</FormGroupBody>
</FormGroup>
</FormFieldsetBody>
</FormFieldset>
</Form>
</DevseedUiThemeProvider>
Forms with Formik
If you are using Formik, you can use the library components by passing it through the as
prop like so
import { Formik, Field, Form, ErrorMessage } from "formik"
import {
FormFieldset,
FormFieldsetBody,
FormGroup,
FormGroupHeader,
FormGroupBody,
FormInput,
FormLabel,
FormHelper,
FormHelperMessage,
} from "@devseed-ui/form"
const InputGroup = ({ id, label, placeholder, helperMessage }) => (
<FormGroup>
<FormGroupHeader>
<FormLabel htmlFor={id}>{label}</FormLabel>
</FormGroupHeader>
<FormGroupBody>
<Field
as={FormInput}
type="text"
id={id}
name={id}
placeholder={placeholder}
/>
<FormHelper>
{helperMessage && (
<FormHelperMessage>{helperMessage}</FormHelperMessage>
)}
<FormHelperMessage as={ErrorMessage} name={id} />
</FormHelper>
</FormGroupBody>
</FormGroup>
)