Skip to content

Add FormControl component #1836

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 26 commits into from
Feb 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
216178c
adds FormControl component and stories
mperrotti Feb 2, 2022
2df1836
adds tests
mperrotti Feb 3, 2022
5df760e
adds FormControl React docs
mperrotti Feb 3, 2022
416e198
deprecates components replaced by FormControl
mperrotti Feb 3, 2022
2231c63
adds changeset
mperrotti Feb 3, 2022
801000a
addresses PR feedback
mperrotti Feb 4, 2022
da5557b
Update docs/content/FormControl.mdx
mperrotti Feb 4, 2022
b34b55a
Update docs/content/FormControl.mdx
mperrotti Feb 4, 2022
b35d43a
Update docs/content/FormControl.mdx
mperrotti Feb 4, 2022
32fb5e2
Update docs/content/FormControl.mdx
mperrotti Feb 4, 2022
25dcce0
Update src/FormControl/FormControl.tsx
mperrotti Feb 4, 2022
f2494b5
Update src/FormControl/FormControl.tsx
mperrotti Feb 4, 2022
3784b96
Merge branch 'main' into mp/formcontrol
mperrotti Feb 4, 2022
91c9fa3
addresses PR feedback
mperrotti Feb 4, 2022
da064ff
renames Props to FormControlProps
mperrotti Feb 4, 2022
f2a5fb0
changes props interface to a type
mperrotti Feb 4, 2022
5af48b0
Merge branch 'main' of github.com:primer/react into mp/formcontrol
mperrotti Feb 4, 2022
7553fda
Update docs/content/FormControl.mdx
mperrotti Feb 7, 2022
450a257
Merge branch 'main' into mp/formcontrol
mperrotti Feb 7, 2022
da8a185
Update docs/content/FormControl.mdx
mperrotti Feb 7, 2022
128c65e
Merge branch 'main' into mp/formcontrol
mperrotti Feb 8, 2022
90033a8
suppresses warning and error logs in Jest
mperrotti Feb 8, 2022
260ca68
removes variant prop
mperrotti Feb 8, 2022
0e724a4
docs cleanup
mperrotti Feb 8, 2022
ca7a015
Merge branch 'main' into mp/formcontrol
mperrotti Feb 8, 2022
acb9a8c
Merge branch 'main' into mp/formcontrol
mperrotti Feb 9, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/sharp-cats-return.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@primer/react': minor
---

Introduces FormControl component. The FormControl component combines the functionality of InputField and ChoiceInputField, and will replace FormGroup, InputField, and ChoiceInputField.
6 changes: 5 additions & 1 deletion docs/content/ChoiceInputField.mdx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
componentId: choiceInputField
title: ChoiceInputField
status: Alpha
status: Deprecated
description: The ChoiceInputField component is used to render a labelled checkbox or radio inputs with optional hint text.
source: https://github.com/primer/react/blob/main/src/ChoiceInputField.tsx
storybook: '/react/storybook?path=/story/forms-choiceinputfield--checkbox-input-field'
Expand All @@ -12,6 +12,10 @@ import {MarkGithubIcon} from '@primer/octicons-react'
import {PropsTable} from '../src/props-table'
import {ComponentChecklist} from '../src/component-checklist'

## Deprecation

Use [FormControl](/FormControl) instead.

## Examples

### Checkbox
Expand Down
356 changes: 356 additions & 0 deletions docs/content/FormControl.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,356 @@
---
componentId: form_control
title: FormControl
status: Alpha
description: The FormControl component is used to render a labelled text input and, optionally, associated validation text and hint text.
source: https://github.com/primer/react/blob/main/src/FormControl/FormControl.tsx
storybook: '/react/storybook?path=/story/forms-inputfield--text-input-field'
---

import {FormControl, TextInputWithTokens, Autocomplete, Select, Textarea, Checkbox, Radio, Text} from '@primer/react'
import {MarkGithubIcon} from '@primer/octicons-react'

## Examples

### Basic

```jsx live
<FormControl>
<FormControl.Label>Name</FormControl.Label>
<TextInput />
</FormControl>
```

### With complex inputs

```javascript live noinline
const DifferentInputs = () => {
const [tokens, setTokens] = React.useState([
{text: 'zero', id: 0},
{text: 'one', id: 1},
{text: 'two', id: 2}
])
const onTokenRemove = tokenId => {
setTokens(tokens.filter(token => token.id !== tokenId))
}

return (
<Box display="grid" gridGap={3}>
<FormControl>
<FormControl.Label>TextInputWithTokens</FormControl.Label>
<TextInputWithTokens onTokenRemove={onTokenRemove} tokens={tokens} />
</FormControl>
<FormControl>
<FormControl.Label>Autocomplete</FormControl.Label>
<Autocomplete>
<Autocomplete.Input block />
<Autocomplete.Overlay>
<Autocomplete.Menu
items={[
{text: 'css', id: 0},
{text: 'css-in-js', id: 1},
{text: 'styled-system', id: 2},
{text: 'javascript', id: 3},
{text: 'typescript', id: 4},
{text: 'react', id: 5},
{text: 'design-systems', id: 6}
]}
selectedItemIds={[]}
/>
</Autocomplete.Overlay>
</Autocomplete>
</FormControl>
<FormControl>
<FormControl.Label>Select</FormControl.Label>
<Select>
<Select.Option value="figma">Figma</Select.Option>
<Select.Option value="css">Primer CSS</Select.Option>
<Select.Option value="prc">Primer React components</Select.Option>
<Select.Option value="pvc">Primer ViewComponents</Select.Option>
</Select>
</FormControl>
<FormControl>
<FormControl.Label>Textarea</FormControl.Label>
<Textarea />
</FormControl>
</Box>
)
}

render(DifferentInputs)
```

### With checkbox and radio inputs

```jsx live
<Box display="grid" gridGap={3}>
<fieldset style={{marginLeft: 0, marginRight: 0, padding: 0, border: 0}}>
<FormControl>
<FormControl.Label>Checkbox option one</FormControl.Label>
<Checkbox />
</FormControl>
<FormControl>
<FormControl.Label>Checkbox option two</FormControl.Label>
<Checkbox />
</FormControl>
</fieldset>
<fieldset style={{marginLeft: 0, marginRight: 0, padding: 0, border: 0}}>
<FormControl>
<FormControl.Label>Radio option one</FormControl.Label>
<Radio name="radioExample" value="one" />
</FormControl>
<FormControl>
<FormControl.Label>Radio option two</FormControl.Label>
<Radio name="radioExample" value="two" />
</FormControl>
</fieldset>
</Box>
```

### Required

```jsx live
<FormControl required>
<FormControl.Label>Name</FormControl.Label>
<TextInput />
</FormControl>
```

<Note>
Checkbox and radio form controls cannot be required individually. Instead, you can require a selection from the entire
group of checkboxes or radios.
</Note>

### Disabled

```jsx live
<Box display="grid" gridGap={3}>
<FormControl disabled>
<FormControl.Label>Name</FormControl.Label>
<TextInput />
</FormControl>

<FormControl disabled>
<FormControl.Label>Checkbox option</FormControl.Label>
<Checkbox />
</FormControl>
</Box>
```

### With a visually hidden label

```jsx live
<Box display="grid" gridGap={3}>
<FormControl>
<FormControl.Label visuallyHidden>Name</FormControl.Label>
<TextInput />
</FormControl>
<FormControl>
<FormControl.Label visuallyHidden>Checkbox option</FormControl.Label>
<Checkbox />
</FormControl>
</Box>
```

<Note>

We encourage using `FormControl` alongside all standalone form components like [`TextInput`](/TextInput), as every input must have a corresponding label to be accessible to assistive technology.
``

`FormControl` also provides an interface for showing a hint text caption and a validation message, and associating those with the input for assistive technology.

</Note>

### With a caption

```jsx live
<Box display="grid" gridGap={3}>
<FormControl>
<FormControl.Label>Name</FormControl.Label>
<TextInput />
<FormControl.Caption>Hint: your first name</FormControl.Caption>
</FormControl>
<FormControl>
<FormControl.Label>Option one</FormControl.Label>
<Checkbox />
<FormControl.Caption>Hint: the first and only option</FormControl.Caption>
</FormControl>
</Box>
```

### With validation

<Note>Validation messages are not used for an individual checkbox or radio form control.</Note>

```javascript live noinline
const ValidationExample = () => {
const [value, setValue] = React.useState('mona lisa')
const [validationResult, setValidationResult] = React.useState()
const doesValueContainSpaces = inputValue => /\s/g.test(inputValue)
const handleInputChange = e => {
setValue(e.currentTarget.value)
}

React.useEffect(() => {
if (doesValueContainSpaces(value)) {
setValidationResult('noSpaces')
} else if (value) {
setValidationResult('validName')
}
}, [value])

return (
<FormControl>
<FormControl.Label>GitHub handle</FormControl.Label>
<TextInput block value={value} onChange={handleInputChange} />
{validationResult === 'noSpaces' && (
<FormControl.Validation>GitHub handles cannot contain spaces</FormControl.Validation>
)}
{validationResult === 'validName' && <FormControl.Validation>Valid name</FormControl.Validation>}
<FormControl.Caption>With or without "@". For example "monalisa" or "@monalisa"</FormControl.Caption>
</FormControl>
)
}

render(ValidationExample)
```

### With a leading visual

<Note>Only a checkbox or radio form control may have a leading visual.</Note>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, I think most of these notes could go after the code example and should maybe be marked as warnings?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had them marked as warnings at first, but it felt aggressive


```jsx live
<>
<FormControl>
<FormControl.Label>Option one</FormControl.Label>
<FormControl.LeadingVisual>
<MarkGithubIcon />
</FormControl.LeadingVisual>
<Checkbox />
</FormControl>

<FormControl>
<FormControl.Label>Option two</FormControl.Label>
<FormControl.LeadingVisual>
<MarkGithubIcon />
</FormControl.LeadingVisual>
<Checkbox />
<FormControl.Caption>This one has a caption</FormControl.Caption>
</FormControl>
</>
```

## Props

### FormControl

The container that handles the layout and passes the relevant IDs and ARIA attributes it's children.

<PropsTable>
<PropsTableRow
name="children"
type="FormControl.Label | FormControl.Caption | FormControl.Validation | Autocomplete | TextInput | TextInputWithTokens | Select"
required
/>
<PropsTableRow
name="disabled"
type="boolean"
defaultValue="false"
description="Whether the control allows user input"
/>
<PropsTableRow
name="id"
type="string"
defaultValue="a generated string"
description="The unique identifier for this control. Used to associate the label, validation text, and caption text"
/>
<PropsTableRow
name="required"
type="boolean"
defaultValue="false"
description="If true, the user must specify a value for the input before the owning form can be submitted"
/>
</PropsTable>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you intentionally omit the sx prop from the FormControl.* components?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes


### FormControl.Label

A `FormControl.Label` must be passed for the field to be accessible to assistive technology, but it may be visually hidden.

<PropsTable>
<PropsTableRow
name="boolean"
type="FormControl.Label | FormControl.Caption | FormControl.Validation | Autocomplete | TextInput | TextInputWithTokens"
defaultValue="false"
description="Whether the label should be visually hidden"
/>
</PropsTable>

### FormControl.Caption

`FormControl.Caption` may be used to render hint text for fields that require additional context.

<PropsTable>
<PropsTableRow
name="children"
type="React.ReactNode"
description="The content (usually just text) that is rendered to give contextual info about the field"
/>
</PropsTable>

### FormControl.Validation

Use `FormControl.Validation` to render contextual validation information if necessary.

<PropsTable>
<PropsTableRow
name="children"
type="React.ReactNode"
description="The content (usually just text) that is rendered to give contextual info about the validation result for the field"
/>
</PropsTable>

<Note>
Validation messages should not be shown for an individual checkbox or radio form control, so `FormControl.Validation`
will not be rendered when a `Checkbox` or `Radio` is not a child of `FormControl`. Validation messages for checkbox
and radio selections should only apply to the entire group of inputs.
</Note>

### FormControl.LeadingVisual

Use `FormControl.LeadingVisual` if the selectable option is easier to understand with a visual.

<PropsTable>
<PropsTableRow
name="children"
type="React.ReactNode"
description="The visual to render before the choice input's label"
/>
</PropsTable>

<Note>

Only a checkbox or radio form control may have a leading visual. If you want to render a leading visual
**inside** an input, check if that input component supports a leading visual.

</Note>

## Component status

<ComponentChecklist
items={{
propsDocumented: true,
noUnnecessaryDeps: true,
adaptsToThemes: true,
adaptsToScreenSizes: true,
fullTestCoverage: true,
usedInProduction: false,
usageExamplesDocumented: true,
hasStorybookStories: true,
designReviewed: false,
a11yReviewed: true,
stableApi: false,
addressedApiFeedback: false,
hasDesignGuidelines: false,
hasFigmaComponent: false
}}
/>
6 changes: 5 additions & 1 deletion docs/content/FormGroup.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
---
componentId: form_group
title: FormGroup
status: Alpha
status: Deprecated
---

Adds styles for multiple form elements used together.

## Deprecation

Use [FormControl](/FormControl) instead.

## Default example

```jsx live
Expand Down
Loading