Skip to content

Commit 3e8ab89

Browse files
mperrottiPrinceSumberia
authored andcommitted
Correct and improve Storybook and docs for form controls (primer#2143)
* updates checkbox props tables, storybook stories, and storybook controls * updates Radio props table, Storybook stories, and Storybook controls * updates FormControl Storybook stories, and Storybook controls * updates TextInput and TextInputWithWrapper props tables, Storybook stories, and Storybook controls * updates Select stories * fixes more issues with Select stories and prop docs * updates Textarea prop tables, Storybook stories, and Storybook controls * updates CheckboxGroup and RadioGroup Storybook stories, and Storybook controls * updates Autocomplete Storybook stories, and Storybook controls * fixes 'size' control in TextInput stories * fixes a11y issues with Autocomplete examples * updates input stories to use the FormControl component and controls, and updates sidebar to reflect that relationship * updates tests * adds changeset * fix linting issue * updates after merging from main * excludes story-helpers.tsx from build * fixes regression in CheckboxGroup and RadioGroup fixture stories
1 parent e2685ed commit 3e8ab89

31 files changed

+1735
-1493
lines changed

.changeset/tough-coats-allow.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@primer/react': patch
3+
---
4+
5+
Fixes bugs in form components discovered while fixing/improving Storybook and docs.

docs/content/Autocomplete.mdx

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ title: Autocomplete
44
status: Alpha
55
description: Used to render a text input that allows a user to quickly filter through a list of options to pick one or more values.
66
source: https://github.com/primer/react/tree/main/src/Autocomplete
7-
storybook: '/react/storybook?path=/story/forms-autocomplete--single-select'
7+
storybook: '/react/storybook?path=/story/form-controls-autocomplete--default'
88
---
99

1010
import {Autocomplete} from '@primer/react'
@@ -56,7 +56,7 @@ A function may be passed to the `filterFn` prop if this default filtering behavi
5656

5757
```jsx live
5858
<FormControl>
59-
<FormControl.Label>Pick a branch</FormControl.Label>
59+
<FormControl.Label id="autocompleteLabel-basic">Pick a branch</FormControl.Label>
6060
<Autocomplete>
6161
<Autocomplete.Input />
6262
<Autocomplete.Overlay>
@@ -72,6 +72,7 @@ A function may be passed to the `filterFn` prop if this default filtering behavi
7272
{text: 'visual-design-tweaks', id: 7}
7373
]}
7474
selectedItemIds={[]}
75+
aria-labelledby="autocompleteLabel-basic"
7576
/>
7677
</Autocomplete.Overlay>
7778
</Autocomplete>
@@ -114,7 +115,7 @@ const CustomTextInputExample = () => {
114115

115116
return (
116117
<FormControl>
117-
<FormControl.Label>Pick options</FormControl.Label>
118+
<FormControl.Label id="autocompleteLabel-customInput">Pick options</FormControl.Label>
118119
<Autocomplete>
119120
<Autocomplete.Input as={TextInputWithTokens} tokens={tokens} onTokenRemove={onTokenRemove} />
120121
<Autocomplete.Overlay>
@@ -132,6 +133,7 @@ const CustomTextInputExample = () => {
132133
selectedItemIds={selectedItemIds}
133134
onSelectedChange={onSelectedChange}
134135
selectionVariant="multiple"
136+
aria-labelledby="autocompleteLabel-customInput"
135137
/>
136138
</Autocomplete.Overlay>
137139
</Autocomplete>
@@ -146,7 +148,7 @@ render(<CustomTextInputExample />)
146148

147149
```jsx live
148150
<FormControl>
149-
<FormControl.Label>Pick a branch</FormControl.Label>
151+
<FormControl.Label id="autocompleteLabel-withoutOverlay">Pick a branch</FormControl.Label>
150152
<Autocomplete>
151153
<Autocomplete.Input />
152154
<Autocomplete.Menu
@@ -161,6 +163,7 @@ render(<CustomTextInputExample />)
161163
{text: 'visual-design-tweaks', id: 7}
162164
]}
163165
selectedItemIds={[]}
166+
aria-labelledby="autocompleteLabel-withoutOverlay"
164167
/>
165168
</Autocomplete>
166169
</FormControl>
@@ -221,7 +224,7 @@ const CustomRenderedItemExample = () => {
221224

222225
return (
223226
<FormControl>
224-
<FormControl.Label>Pick labels</FormControl.Label>
227+
<FormControl.Label id="autocompleteLabel-customRenderedItem">Pick labels</FormControl.Label>
225228
<Autocomplete>
226229
<Autocomplete.Input
227230
as={TextInputWithTokens}
@@ -248,7 +251,7 @@ const CustomRenderedItemExample = () => {
248251
selectedItemIds={selectedItemIds}
249252
onSelectedChange={onSelectedChange}
250253
selectionVariant="multiple"
251-
aria-labelledby="autocompleteLabel-issueLabels"
254+
aria-labelledby="autocompleteLabel-customRenderedItem"
252255
/>
253256
</Autocomplete.Overlay>
254257
</Autocomplete>
@@ -279,9 +282,9 @@ const CustomSortAfterMenuClose = () => {
279282

280283
return (
281284
<FormControl>
282-
<FormControl.Label>Pick branches</FormControl.Label>
285+
<FormControl.Label id="autocompleteLabel-sortAfterClose">Pick branches</FormControl.Label>
283286
<Autocomplete>
284-
<Autocomplete.Input id="autocompleteInput-sortAfterClose" />
287+
<Autocomplete.Input />
285288
<Autocomplete.Overlay>
286289
<Autocomplete.Menu
287290
items={[
@@ -323,9 +326,9 @@ const CustomSearchFilter = () => {
323326

324327
return (
325328
<FormControl>
326-
<FormControl.Label>Pick a branch</FormControl.Label>
329+
<FormControl.Label id="autocompleteLabel-customFilter">Pick a branch</FormControl.Label>
327330
<Autocomplete>
328-
<Autocomplete.Input id="autocompleteInput-customFilter" onChange={handleChange} />
331+
<Autocomplete.Input onChange={handleChange} />
329332
<Autocomplete.Overlay>
330333
<Autocomplete.Menu
331334
items={[
@@ -382,7 +385,9 @@ const InOverlayWithCustomScrollContainerRef = () => {
382385
)}
383386
>
384387
<FormControl>
385-
<FormControl.Label visuallyHidden>Pick branches</FormControl.Label>
388+
<FormControl.Label visuallyHidden id="autocompleteLabel-withCustomScrollRef">
389+
Pick branches
390+
</FormControl.Label>
386391
<Autocomplete>
387392
<Box display="flex" flexDirection="column" height="100%">
388393
<Box
@@ -424,7 +429,7 @@ const InOverlayWithCustomScrollContainerRef = () => {
424429
]}
425430
selectedItemIds={[]}
426431
customScrollContainerRef={scrollContainerRef}
427-
aria-labelledby="autocompleteLabel"
432+
aria-labelledby="autocompleteLabel-withCustomScrollRef"
428433
/>
429434
</Box>
430435
</Box>
@@ -465,14 +470,14 @@ const MultiSelect = () => {
465470
return (
466471
<Box display="flex" flexDirection="column" sx={{gap: '1em'}}>
467472
<FormControl>
468-
<FormControl.Label>Pick branches</FormControl.Label>
473+
<FormControl.Label id="autocompleteLabel-multiselect">Pick branches</FormControl.Label>
469474
<Autocomplete>
470-
<Autocomplete.Input id="autocompleteInput" />
475+
<Autocomplete.Input />
471476
<Autocomplete.Overlay>
472477
<Autocomplete.Menu
473478
items={items}
474479
selectedItemIds={selectedItemIds}
475-
aria-labelledby="autocompleteLabel"
480+
aria-labelledby="autocompleteLabel-multiselect"
476481
onSelectedChange={onSelectedChange}
477482
selectionVariant="multiple"
478483
/>
@@ -535,9 +540,9 @@ const MultiSelectAddNewItem = () => {
535540
return (
536541
<Box display="flex" flexDirection="column" sx={{gap: '1em'}}>
537542
<FormControl>
538-
<FormControl.Label>Pick or add branches</FormControl.Label>
543+
<FormControl.Label id="autocompleteLabel-addItem">Pick or add branches</FormControl.Label>
539544
<Autocomplete>
540-
<Autocomplete.Input onChange={handleChange} id="autocompleteInput" />
545+
<Autocomplete.Input onChange={handleChange} />
541546
<Autocomplete.Overlay>
542547
<Autocomplete.Menu
543548
addNewItem={
@@ -559,7 +564,7 @@ const MultiSelectAddNewItem = () => {
559564
selectedItemIds={selectedItemIds}
560565
onSelectedChange={onSelectedChange}
561566
selectionVariant="multiple"
562-
aria-labelledby="autocompleteLabel"
567+
aria-labelledby="autocompleteLabel-addItem"
563568
/>
564569
</Autocomplete.Overlay>
565570
</Autocomplete>

docs/content/Checkbox.mdx

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ title: Checkbox
33
description: Use checkboxes to toggle between checked and unchecked states in a list or as a standalone form field
44
status: Alpha
55
source: https://github.com/primer/react/blob/main/src/Checkbox.tsx
6-
storybook: '/react/storybook?path=/story/forms-checkbox--default'
6+
storybook: '/react/storybook?path=/story/form-controls-checkbox--default'
77
componentId: checkbox
88
---
99

@@ -87,15 +87,10 @@ An `indeterminate` checkbox state should be used if the input value is neither t
8787
type="boolean"
8888
description="Checks the input by default in uncontrolled mode"
8989
/>
90-
<PropsTableRow
91-
name="onChange"
92-
type="(event: React.ChangeEvent) => void"
93-
description="A callback function that is triggered when the checked state has been changed"
94-
/>
9590
<PropsTableRow
9691
name="disabled"
9792
type="boolean"
98-
description="Modifies the native disabled state of the native checkbox"
93+
description="Modifies the native disabled state of the native checkbox"
9994
/>
10095
<PropsTableRow
10196
name="indeterminate"
@@ -111,6 +106,31 @@ An `indeterminate` checkbox state should be used if the input value is neither t
111106
</>
112107
}
113108
/>
109+
<PropsTableRow
110+
name="onChange"
111+
type="(event: React.ChangeEvent) => void"
112+
description="A callback function that is triggered when the checked state has been changed"
113+
/>
114+
<PropsTableRow
115+
name="validationStatus"
116+
type="'error' | 'success' | 'warning'"
117+
description={
118+
<>
119+
Only used to inform ARIA attributes.<br />
120+
Individual checkboxes do not have validation styles.
121+
</>
122+
}
123+
/>
124+
<PropsTableRow
125+
name="value"
126+
type="string"
127+
description={
128+
<>
129+
A unique value that is never shown to the user.<br />
130+
Used during form submission and to identify which checkbox inputs are selected.
131+
</>
132+
}
133+
/>
114134
<PropsTableBasePropRows
115135
elementType="input"
116136
refType="HTMLInputElement"

docs/content/FormControl.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ title: FormControl
44
status: Alpha
55
description: Renders a labelled input and, optionally, associated validation text and/or hint text.
66
source: https://github.com/primer/react/blob/main/src/FormControl/FormControl.tsx
7-
storybook: '/react/storybook?path=/story/forms-inputfield--text-input-field'
7+
storybook: '/react/storybook?path=/story/forms-form-controls-text-input--default'
88
---
99

1010
import {

docs/content/Radio.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ title: Radio
44
description: Use radios when a user needs to select one option from a list
55
status: Alpha
66
source: https://github.com/primer/react/blob/main/src/Radio.tsx
7-
storybook: '/react/storybook?path=/story/forms-radio-button--default'
7+
storybook: '/react/storybook?path=/story/forms-form-controls-radio--default'
88
---
99

1010
## Examples
@@ -62,7 +62,7 @@ If you're not building something custom, you should use the [ChoiceFieldset](/Ch
6262

6363
<PropsTable>
6464
<PropsTableRow name="value" type="string" required description="A unique value that is never shown to the user" />
65-
<PropsTableRow name="name" type="string" required description="Required for grouping multiple radios" />
65+
<PropsTableRow name="name" type="string" description="Required for grouping multiple radios" />
6666
<PropsTableRow name="checked" type="boolean" description="Modifies true/false value of the native radio" />
6767
<PropsTableRow name="defaultChecked" type="boolean" description="Selects the radio by default in uncontrolled mode" />
6868
<PropsTableRow

docs/content/Select.mdx

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ title: Select
44
description: Use a select input when a user needs to select one option from a long list
55
status: Alpha
66
source: https://github.com/primer/react/blob/main/src/Select.tsx
7-
storybook: '/react/storybook?path=/story/forms-select--default'
7+
storybook: '/react/storybook?path=/story/forms-form-controls-select--default'
88
---
99

1010
import {Select, Text} from '@primer/react'
@@ -66,20 +66,28 @@ import {Select, Text} from '@primer/react'
6666
name="block"
6767
type="boolean"
6868
defaultValue="false"
69-
description={<>Creates a full width input element</>}
69+
description="Creates a full width input element"
7070
/>
7171
<PropsTableRow
7272
name="contrast"
7373
type="boolean"
7474
defaultValue="false"
7575
description="Changes background color to a higher contrast color"
7676
/>
77+
<PropsTableRow
78+
name="placeholder"
79+
type="string"
80+
description={<>
81+
Placeholder text to show when no option is selected. <br />
82+
This option is hidden from the dropdown menu when the 'required' prop is set
83+
</>}
84+
/>
7785
<PropsTableRow
7886
name="size"
7987
type="'small' | 'medium' | 'large'"
8088
description="Creates a smaller or larger input than the default."
8189
/>
82-
<PropsTableRow name="validationStatus" type="'warning' | 'error'" description="Style the input to match the status" />
90+
<PropsTableRow name="validationStatus" type="'error' | 'success' | 'warning'" description="Style the input to match the status" />
8391
<PropsTablePassthroughPropsRow
8492
elementName="select"
8593
passthroughPropsLink={

docs/content/TextInput.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ componentId: text_input
33
title: TextInput
44
status: Alpha
55
source: https://github.com/primer/react/blob/main/src/TextInput.tsx
6+
storybook: '/react/storybook?path=/story/forms-form-controls-text-input--default'
67
---
78

89
TextInput is a form component to add default styling to the native text input.

docs/content/TextInputWithTokens.mdx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ title: TextInputWithTokens
44
status: Alpha
55
description: Used to show multiple values in one field
66
source: https://github.com/primer/react/tree/main/src/TextInputWithTokens.tsx
7-
storybook: '/react/storybook?path=/story/forms-text-input-with-tokens--default'
7+
storybook: '/react/storybook?path=/story/forms-form-controls-text-input-with-tokens--default'
88
---
99

1010
import {TextInputWithTokens} from '@primer/react'
@@ -439,6 +439,11 @@ render(<WithIconAndLoadingIndicator />)
439439
type="number"
440440
description="The number of tokens to display before truncating"
441441
/>
442+
<PropsTablePassthroughPropsRow
443+
elementName="TextInput"
444+
isPolymorphic
445+
passthroughPropsLink={<Link href="/react/TextInput">TextInput docs</Link>}
446+
/>
442447
</PropsTable>
443448

444449
### Adding and removing tokens
@@ -464,6 +469,7 @@ There is no function that gets called to "add" a token, so the user needs to be
464469
fullTestCoverage: true,
465470
usedInProduction: false,
466471
usageExamplesDocumented: true,
472+
hasStorybookStories: true,
467473
designReviewed: false,
468474
a11yReviewed: false,
469475
stableApi: false,

docs/content/Textarea.mdx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ title: Textarea
44
description: Use Textarea for multi-line text input form fields
55
status: Alpha
66
source: https://github.com/primer/react/blob/main/src/Textarea.tsx
7-
storybook: /react/storybook?path=/story/forms-textarea--default
7+
storybook: '/react/storybook?path=/story/forms-form-controls--textarea-story'
88
---
99

1010
import {Textarea} from '@primer/react'
@@ -158,10 +158,13 @@ By default, `Textarea` can be resized by the user vertically and horizontally. R
158158
elementType="textarea"
159159
refType="HTMLTextAreaElement"
160160
/>
161-
<PropsTablePassthroughPropsRow
162-
elementName="textarea"
161+
<PropsTableBasePropRows
162+
elementType="input"
163+
refType="HTMLTextareaElement"
163164
passthroughPropsLink={
164-
<Link href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea#attributes">MDN</Link>
165+
<Link href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea#attributes">
166+
MDN
167+
</Link>
165168
}
166169
/>
167170

src/Autocomplete/AutocompleteMenu.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,12 @@ function getItemById<T extends AutocompleteMenuItem>(itemId: string | number, it
4242
// eslint-disable-next-line @typescript-eslint/no-explicit-any
4343
type AutocompleteItemProps<T = Record<string, any>> = AutocompleteMenuItem & {metadata?: T}
4444

45+
// TODO: we should make `aria-labelledby` required for a11y
4546
export type AutocompleteMenuInternalProps<T extends AutocompleteItemProps> = {
4647
/**
4748
* A menu item that is used to allow users make a selection that is not available in the array passed to the `items` prop.
4849
* This menu item gets appended to the end of the list of options.
4950
*/
50-
// TODO: rethink this part of the component API. this is kind of weird and confusing to use
51-
// TODO: rethink `addNewItem` prop name
5251
addNewItem?: Omit<T, 'onAction' | 'leadingVisual' | 'id'> & {
5352
handleAddItem: (item: Omit<T, 'onAction' | 'leadingVisual'>) => void
5453
}

0 commit comments

Comments
 (0)