Skip to content
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

[Joy UI] Add FormControl component #34187

Merged
merged 32 commits into from Sep 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
a8aa5b4
bind controls id
siriwatknp Sep 3, 2022
44e606b
adjust Input to use values from form control
siriwatknp Sep 3, 2022
aeccd76
add classes to form control
siriwatknp Sep 3, 2022
c231fcb
use context for input
siriwatknp Sep 3, 2022
ed7dcc1
use context for Textarea
siriwatknp Sep 3, 2022
6d2826e
fix color
siriwatknp Sep 3, 2022
d1d61cc
add composition demo to textfield
siriwatknp Sep 3, 2022
60cd8e5
use FormControl
siriwatknp Sep 3, 2022
2023929
move to accessibility section
siriwatknp Sep 3, 2022
9a3593a
add registerEffect
siriwatknp Sep 3, 2022
f31452e
add tests
siriwatknp Sep 3, 2022
012636f
run proptypes
siriwatknp Sep 3, 2022
00b9cfa
run docs:formatted
siriwatknp Sep 3, 2022
514dbd7
fix id
siriwatknp Sep 3, 2022
f428ff7
Merge branch 'master' of https://github.com/mui/material-ui into joy/…
siriwatknp Sep 7, 2022
a132c5d
fix comment
siriwatknp Sep 7, 2022
bcd6431
link checkbox with form control
siriwatknp Sep 7, 2022
0ae09e2
add label id and linked to radio group
siriwatknp Sep 7, 2022
c0386e8
List and RadioGroup integration
siriwatknp Sep 7, 2022
b198da9
simplify label
siriwatknp Sep 7, 2022
6d56271
fix content
siriwatknp Sep 7, 2022
c89191c
minor text field doc tweak
danilo-leal Sep 8, 2022
1957939
run proptypes
siriwatknp Sep 8, 2022
cc4e165
Merge branch 'master' of https://github.com/mui/material-ui into joy/…
siriwatknp Sep 8, 2022
ffe27cd
Merge branch 'joy/form-control' of github.com:siriwatknp/material-ui …
siriwatknp Sep 8, 2022
699770d
apply id to RadioGroup for completeness
siriwatknp Sep 8, 2022
dcdbef9
adjust helper text margin
siriwatknp Sep 8, 2022
ddcd4e5
integrate FormControl with Switch
siriwatknp Sep 8, 2022
9c4f069
add label example
siriwatknp Sep 9, 2022
d0bc61a
proptypes
siriwatknp Sep 9, 2022
eaa60a8
remove custom margin
siriwatknp Sep 9, 2022
b4d53bb
update demo usage
siriwatknp Sep 9, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
13 changes: 13 additions & 0 deletions docs/data/joy/components/checkbox/HelperTextCheckbox.js
@@ -0,0 +1,13 @@
import * as React from 'react';
import Checkbox from '@mui/joy/Checkbox';
import FormControl from '@mui/joy/FormControl';
import FormHelperText from '@mui/joy/FormHelperText';

export default function HelperTextCheckbox() {
return (
<FormControl>
<Checkbox label="Label" />
<FormHelperText>A description for the checkbox.</FormHelperText>
</FormControl>
);
}
6 changes: 6 additions & 0 deletions docs/data/joy/components/checkbox/checkbox.md
Expand Up @@ -87,6 +87,12 @@ It has no accessibility or UX implications.

{{"demo": "IndeterminateCheckbox.js"}}

### Helper text

To add a description to the checkbox, use `FormControl` and `FormHelperText`. The checkbox will be linked to the helper text via `aria-describedby` attribute.

{{"demo": "HelperTextCheckbox.js"}}

### Group

To group multiple checkboxes, use `role="group"` on the wrapper component.
Expand Down
21 changes: 6 additions & 15 deletions docs/data/joy/components/radio/ControlledRadioButtonsGroup.js
@@ -1,8 +1,8 @@
import * as React from 'react';
import Box from '@mui/joy/Box';
import FormControl from '@mui/joy/FormControl';
import FormLabel from '@mui/joy/FormLabel';
import Radio from '@mui/joy/Radio';
import RadioGroup from '@mui/joy/RadioGroup';
import Typography from '@mui/joy/Typography';

export default function ControlledRadioButtonsGroup() {
const [value, setValue] = React.useState('female');
Expand All @@ -12,28 +12,19 @@ export default function ControlledRadioButtonsGroup() {
};

return (
<Box>
<Typography
id="demo-controlled-radio-buttons-group"
level="body3"
textTransform="uppercase"
fontWeight="xl"
sx={{ letterSpacing: '0.15rem' }}
mb={2}
>
Gender
</Typography>
<FormControl>
<FormLabel>Gender</FormLabel>
<RadioGroup
aria-labelledby="demo-controlled-radio-buttons-group"
defaultValue="female"
name="controlled-radio-buttons-group"
value={value}
onChange={handleChange}
sx={{ my: 1 }}
>
<Radio value="female" label="Female" />
<Radio value="male" label="Male" />
<Radio value="other" label="Other" />
</RadioGroup>
</Box>
</FormControl>
);
}
2 changes: 2 additions & 0 deletions docs/data/joy/components/radio/ExamplePaymentChannels.js
Expand Up @@ -30,6 +30,7 @@ export default function ExamplePaymentChannels() {
Pay with
</Typography>
<Switch
component="label"
size="sm"
endDecorator="Row view"
checked={row}
Expand All @@ -49,6 +50,7 @@ export default function ExamplePaymentChannels() {
defaultValue="Paypal"
>
<List
component="div"
variant="outlined"
row={row}
sx={{
Expand Down
28 changes: 9 additions & 19 deletions docs/data/joy/components/radio/OverlayRadio.js
@@ -1,35 +1,26 @@
import * as React from 'react';
import Avatar from '@mui/joy/Avatar';
import Box from '@mui/joy/Box';
import FormControl from '@mui/joy/FormControl';
import FormLabel from '@mui/joy/FormLabel';
import Radio from '@mui/joy/Radio';
import RadioGroup from '@mui/joy/RadioGroup';
import Typography from '@mui/joy/Typography';
import Sheet from '@mui/joy/Sheet';
import Typography from '@mui/joy/Typography';

export default function OverlayRadio() {
return (
<Box>
<Typography
id="member"
mb={2}
level="body3"
textTransform="uppercase"
fontWeight="xl"
sx={{ letterSpacing: '0.15rem' }}
>
Members
</Typography>
<FormControl>
<FormLabel>Members</FormLabel>
<RadioGroup
overlay
name="member"
aria-labelledby="member"
defaultValue="person1"
row
sx={{ gap: 2 }}
sx={{ gap: 2, mt: 1 }}
>
{[1, 2, 3].map((num) => (
<Sheet
component="label"
key={num}
variant="outlined"
sx={{
Expand All @@ -43,7 +34,6 @@ export default function OverlayRadio() {
}}
>
<Radio
id={`person${num}`}
value={`person${num}`}
sx={{
mt: -1,
Expand All @@ -53,11 +43,11 @@ export default function OverlayRadio() {
'--Radio-action-radius': (theme) => theme.vars.radius.md,
}}
/>
<Avatar src={`/static/images/avatar/${num}.jpg`} />
<FormLabel htmlFor={`person${num}`}>Person {num}</FormLabel>
<Avatar alt={`person${num}`} src={`/static/images/avatar/${num}.jpg`} />
<Typography level="body2">Person {num}</Typography>
</Sheet>
))}
</RadioGroup>
</Box>
</FormControl>
);
}
27 changes: 8 additions & 19 deletions docs/data/joy/components/radio/RadioButtonsGroup.js
@@ -1,31 +1,20 @@
import * as React from 'react';
import Box from '@mui/joy/Box';
import FormControl from '@mui/joy/FormControl';
import FormLabel from '@mui/joy/FormLabel';
import FormHelperText from '@mui/joy/FormHelperText';
import Radio from '@mui/joy/Radio';
import RadioGroup from '@mui/joy/RadioGroup';
import Typography from '@mui/joy/Typography';

export default function RadioButtonsGroup() {
return (
<Box>
<Typography
id="demo-radio-buttons-group-label"
level="body3"
textTransform="uppercase"
fontWeight="xl"
sx={{ letterSpacing: '0.15rem' }}
mb={2}
>
Gender
</Typography>
<RadioGroup
aria-labelledby="demo-radio-buttons-group-label"
defaultValue="female"
name="radio-buttons-group"
>
<FormControl>
<FormLabel>Gender</FormLabel>
<RadioGroup defaultValue="female" name="radio-buttons-group" sx={{ my: 1 }}>
<Radio value="female" label="Female" />
<Radio value="male" label="Male" />
<Radio value="other" label="Other" />
</RadioGroup>
</Box>
<FormHelperText>This is a helper text.</FormHelperText>
</FormControl>
);
}
34 changes: 9 additions & 25 deletions docs/data/joy/components/radio/RadioFocus.js
@@ -1,41 +1,25 @@
import * as React from 'react';
import Box from '@mui/joy/Box';
import FormControl from '@mui/joy/FormControl';
import FormLabel from '@mui/joy/FormLabel';
import FormHelperText from '@mui/joy/FormHelperText';
import Radio, { radioClasses } from '@mui/joy/Radio';
import RadioGroup from '@mui/joy/RadioGroup';
import Typography from '@mui/joy/Typography';

export default function RadioFocus() {
return (
<Box>
<Typography
id="demo-radio-buttons-group-focus"
level="body3"
textTransform="uppercase"
fontWeight="xl"
sx={{ letterSpacing: '0.15rem' }}
mb={2}
>
Focus
</Typography>
<RadioGroup
aria-labelledby="demo-radio-buttons-group-focus"
aria-describedby="demo-radio-buttons-group-focus-description"
name="radio-buttons-group-focus"
>
<FormControl>
<FormLabel>Focus</FormLabel>
<RadioGroup name="radio-buttons-group-focus" sx={{ my: 1 }}>
<Radio value="default" label="Default" />
<Radio
value="relative"
label="Position relative"
sx={{ [`& .${radioClasses.radio}`]: { position: 'relative' } }}
/>
</RadioGroup>
<Typography
level="body3"
mt={2}
id="demo-radio-buttons-group-focus-description"
>
<FormHelperText>
Select an option and use keyboard ↑↓ to see the focus outline
</Typography>
</Box>
</FormHelperText>
</FormControl>
);
}
2 changes: 1 addition & 1 deletion docs/data/joy/components/radio/RadioPositionEnd.js
Expand Up @@ -10,7 +10,7 @@ import Apartment from '@mui/icons-material/Apartment';

export default function RadioPositionEnd() {
return (
<RadioGroup name="people" defaultValue="Individual">
<RadioGroup aria-label="Your plan" name="people" defaultValue="Individual">
<List
sx={{
minWidth: 240,
Expand Down
21 changes: 5 additions & 16 deletions docs/data/joy/components/radio/RadioUsage.js
Expand Up @@ -2,6 +2,7 @@ import * as React from 'react';
import JoyUsageDemo, {
prependLinesSpace,
} from 'docs/src/modules/components/JoyUsageDemo';
import FormControl from '@mui/joy/FormControl';
import FormLabel from '@mui/joy/FormLabel';
import RadioGroup from '@mui/joy/RadioGroup';
import Radio from '@mui/joy/Radio';
Expand Down Expand Up @@ -39,31 +40,19 @@ export default function RadioUsage() {
${prependLinesSpace(code, 2)}
</RadioGroup>`}
renderDemo={({ row, ...props }) => (
<div>
<FormLabel
id="radio-button-usage-label"
sx={{
mb: 2,
fontWeight: 'xl',
textTransform: 'uppercase',
fontSize: 'xs',
letterSpacing: '0.15rem',
color: 'text.secondary',
}}
>
Pizza crust
</FormLabel>
<FormControl>
<FormLabel>Pizza crust</FormLabel>
<RadioGroup
row={row}
defaultValue="1"
name="radio-button-usage"
aria-labelledby="radio-button-usage-label"
sx={{ mt: 1 }}
>
<Radio label="Regular crust" value="1" {...props} />
<Radio label="Deep dish" value="2" {...props} />
<Radio label="Thin crust" value="3" {...props} disabled />
</RadioGroup>
</div>
</FormControl>
)}
/>
);
Expand Down
2 changes: 1 addition & 1 deletion docs/data/joy/components/radio/radio.md
Expand Up @@ -102,7 +102,7 @@ Here are a few tips to make sure you have an accessible radio button component:

- Every form control should have proper labels.
This includes radio buttons, checkboxes, and switches.
In most cases, this is done by using the `<label>` element (see Material UI's [`FormControlLabel`](/material-ui/api/form-control-label/) as reference).
In most cases, this is done by using the `FormControl` and `FormLabel` element.
- When a label can't be used, make sure to add an attribute, such as `aria-label`, `aria-labelledby`, and/or `title`, directly on the input component.
You can also use the `inputProps` prop to add them.

Expand Down
31 changes: 10 additions & 21 deletions docs/data/joy/components/select/SelectFieldDemo.js
@@ -1,5 +1,5 @@
import * as React from 'react';
import Box from '@mui/joy/Box';
import FormControl from '@mui/joy/FormControl';
import FormLabel from '@mui/joy/FormLabel';
import FormHelperText from '@mui/joy/FormHelperText';
import Select from '@mui/joy/Select';
Expand All @@ -8,31 +8,20 @@ import Option from '@mui/joy/Option';
export default function SelectFieldDemo() {
const [value, setValue] = React.useState('dog');
return (
<Box sx={{ width: 240 }}>
<FormLabel htmlFor="select-field-pet">Favorite pet</FormLabel>
<Select
id="select-field-pet"
defaultValue="dog"
componentsProps={{
button: {
// screen readers will announce "Favorite pet, dog selected" when the select is focused.
'aria-label': `Favorite pet, ${value} selected.`,
// and this in the next sentence.
'aria-describedby': 'select-field-pet-helper',
},
}}
value={value}
onChange={setValue}
sx={{ mt: 0.25 }}
<FormControl sx={{ width: 240 }}>
<FormLabel
// screen readers will announce "Favorite pet, dog selected" when the select is focused.
aria-label={`Favorite pet, ${value} selected.`}
>
Favorite pet
</FormLabel>
<Select defaultValue="dog" value={value} onChange={setValue}>
<Option value="dog">Dog</Option>
<Option value="cat">Cat</Option>
<Option value="fish">Fish</Option>
<Option value="bird">Bird</Option>
</Select>
<FormHelperText id="select-field-pet-helper">
This is a helper text.
</FormHelperText>
</Box>
<FormHelperText>This is a helper text.</FormHelperText>
</FormControl>
);
}
31 changes: 24 additions & 7 deletions docs/data/joy/components/select/select.md
Expand Up @@ -45,13 +45,6 @@ The `Select` component is similar to the native HTML's `<select>` and `<option>`

{{"demo": "SelectBasic.js"}}

### Field

Use the `FormLabel` component to add a label to the select component.
Make sure to provide an appropriate `aria-label` and an id to the button's `aria-describedby`.

{{"demo": "SelectFieldDemo.js"}}

### Decorators

Use the `startDecorator` and/or `endDecorator` props to add supporting icons or elements to the select.
Expand Down Expand Up @@ -132,6 +125,30 @@ That way, you'll have a consistent height and will be able to leverage nested CS

:::

## Accessibility

In order for the select to be accessible, **it should be linked to a label**.

The `FormControl` automatically generates a unique id that links the select with the `FormLabel` component:

{{"demo": "SelectFieldDemo.js"}}

Alternatively, you can do it manually by targeting the button slot:

```jsx
<label htmlFor="unique-id">Label</label>
<Select
componentsProps={{
button: {
id: 'unique-id',
}
}}
>
<Option value="option1">Option I</Option>
<Option value="option2">Option II</Option>
</Select>
```

## Common examples

### Clear action
Expand Down