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

[RadioGroup] Create new RadioGroup component #487

Merged
merged 47 commits into from
Sep 4, 2024
Merged
Show file tree
Hide file tree
Changes from 44 commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
36da8e2
Merge
atomiks Aug 7, 2024
1e5e9c2
Fix form submission data
atomiks Jul 24, 2024
d71f411
Fix demo
atomiks Jul 24, 2024
a2a1dcc
Add initial tests
atomiks Jul 24, 2024
8d53200
Port Composite tests
atomiks Jul 24, 2024
aeab5fc
Remove passing of non-DOM props
atomiks Jul 24, 2024
8318f6d
Switch to name prop
atomiks Jul 25, 2024
d2f6e55
Fix docs lint
atomiks Jul 25, 2024
69d5760
Use span indicator
atomiks Jul 25, 2024
cd6f54d
Add autoselect
atomiks Jul 25, 2024
4df6bd2
Remove unrelated changes
atomiks Jul 25, 2024
a1db842
use client
atomiks Jul 25, 2024
d1a2124
Refactor touched state
atomiks Jul 25, 2024
3f04ebe
Remove orientation
atomiks Jul 25, 2024
4fcc06d
Remove elementsRef
atomiks Jul 25, 2024
775fb8c
use client
atomiks Jul 25, 2024
6e436bf
Fix test
atomiks Jul 25, 2024
fd76688
Remove unused hook
atomiks Jul 25, 2024
750c032
Codegen
atomiks Jul 25, 2024
0e8df0a
name -> value
atomiks Jul 25, 2024
1d080e1
Update docs
atomiks Jul 25, 2024
1f5acb8
Docs
atomiks Jul 25, 2024
9d6a51b
Update
atomiks Aug 7, 2024
35f51c5
Docs
atomiks Aug 7, 2024
86bde81
value: string | number
atomiks Aug 7, 2024
63ee5d3
Rename state
atomiks Aug 7, 2024
c4898d4
Update demo
atomiks Aug 7, 2024
a128655
Update Radio APIs
atomiks Aug 13, 2024
905e76c
Add styling
atomiks Aug 13, 2024
8cc8016
Fix test linting
atomiks Aug 13, 2024
6de3e0f
use client
atomiks Aug 14, 2024
ccb26ae
Update tests
atomiks Aug 14, 2024
9293a81
Delete tests
atomiks Aug 14, 2024
ab42aa5
Update
atomiks Aug 14, 2024
4dd8ea8
Fix error throw
atomiks Aug 14, 2024
eff152f
Update types
atomiks Aug 14, 2024
f60251e
Comment out all Radio tests
atomiks Aug 14, 2024
4c070a1
Comment out Composite tests
atomiks Aug 14, 2024
2221643
Add Radio tests back
atomiks Aug 14, 2024
7d051ad
Update APIs
atomiks Aug 14, 2024
be34d0c
Test composite navigation directly
atomiks Aug 14, 2024
94fcde0
Fix form test
atomiks Aug 14, 2024
e866d9d
Integrate with Field
atomiks Aug 20, 2024
7019e20
Update
atomiks Aug 23, 2024
97ec4de
Add keepMounted prop
atomiks Aug 30, 2024
4d3be6d
Allow unknown value type
atomiks Sep 3, 2024
ed766a2
Omit button prop value
atomiks Sep 3, 2024
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import * as React from 'react';
import * as RadioGroup from '@base_ui/react/RadioGroup';
import * as Radio from '@base_ui/react/Radio';
import { styled } from '@mui/system';

export default function UnstyledRadioGroupIntroduction() {
return (
<RadioGroup.Root name="root" style={{ display: 'flex', gap: 8 }}>
<RadioItem value="light">
<Indicator />
Light
</RadioItem>
<RadioItem value="medium">
<Indicator />
Medium
</RadioItem>
<RadioItem value="heavy">
<Indicator />
Heavy
</RadioItem>
</RadioGroup.Root>
);
}

const grey = {
100: '#E5EAF2',
200: '#D8E0E9',
300: '#CBD4E2',
};

const blue = {
400: '#3399FF',
600: '#0072E6',
800: '#004C99',
};

const RadioItem = styled(Radio.Root)`
display: flex;
align-items: center;
padding: 8px 16px;
border-radius: 4px;
border: none;
background-color: ${grey[100]};
color: black;
outline: none;
font-size: 16px;
cursor: default;
&:hover {
background-color: ${grey[100]};
}
&:focus-visible {
outline: 2px solid ${blue[400]};
outline-offset: 2px;
}
&[data-radio='checked'] {
background-color: ${blue[600]};
color: white;
}
`;

const Indicator = styled(Radio.Indicator)`
border-radius: 50%;
width: 8px;
height: 8px;
margin-right: 8px;
outline: 1px solid black;
&[data-radio='checked'] {
background-color: white;
border: none;
outline: none;
}
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import * as React from 'react';
import * as RadioGroup from '@base_ui/react/RadioGroup';
import * as Radio from '@base_ui/react/Radio';
import { styled } from '@mui/system';

export default function UnstyledRadioGroupIntroduction() {
return (
<RadioGroup.Root name="root" style={{ display: 'flex', gap: 8 }}>
<RadioItem value="light">
<Indicator />
Light
</RadioItem>
<RadioItem value="medium">
<Indicator />
Medium
</RadioItem>
<RadioItem value="heavy">
<Indicator />
Heavy
</RadioItem>
</RadioGroup.Root>
);
}

const grey = {
100: '#E5EAF2',
200: '#D8E0E9',
300: '#CBD4E2',
};

const blue = {
400: '#3399FF',
600: '#0072E6',
800: '#004C99',
};

const RadioItem = styled(Radio.Root)`
display: flex;
align-items: center;
padding: 8px 16px;
border-radius: 4px;
border: none;
background-color: ${grey[100]};
color: black;
outline: none;
font-size: 16px;
cursor: default;
&:hover {
background-color: ${grey[100]};
}
&:focus-visible {
outline: 2px solid ${blue[400]};
outline-offset: 2px;
}
&[data-radio='checked'] {
background-color: ${blue[600]};
color: white;
}
`;

const Indicator = styled(Radio.Indicator)`
border-radius: 50%;
width: 8px;
height: 8px;
margin-right: 8px;
outline: 1px solid black;
&[data-radio='checked'] {
background-color: white;
border: none;
outline: none;
}
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<RadioGroup.Root name="root" style={{ display: 'flex', gap: 8 }}>
<RadioItem value="light">
<Indicator />
Light
</RadioItem>
<RadioItem value="medium">
<Indicator />
Medium
</RadioItem>
<RadioItem value="heavy">
<Indicator />
Heavy
</RadioItem>
</RadioGroup.Root>
135 changes: 135 additions & 0 deletions docs/data/base/components/radio-group/radio-group.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
---
productId: base-ui
title: React Radio Group component
components: RadioGroupRoot, RadioRoot, RadioIndicator
githubLabel: 'component: radio'
waiAria: https://www.w3.org/WAI/ARIA/apg/patterns/radio/
---

# Radio Group

<p class="description">Radio Groups contain a set of checkable buttons where only one of the buttons can be checked at a time.</p>

{{"component": "@mui/docs/ComponentLinkHeader", "design": false}}

{{"component": "modules/components/ComponentPageTabs.js"}}

## Introduction

{{"demo": "UnstyledRadioGroupIntroduction", "defaultCodeOpen": false, "bg": "gradient"}}

## Installation

Base UI components are all available as a single package.

<codeblock storageKey="package-manager">

```bash npm
npm install @base_ui/react
```

```bash yarn
yarn add @base_ui/react
```

```bash pnpm
pnpm add @base_ui/react
```

</codeblock>

Once you have the package installed, import the components.

```ts
import * as RadioGroup from '@base_ui/react/RadioGroup';
import * as Radio from '@base_ui/react/Radio';
```

## Anatomy

Radio Group is composed of a `Root` and `Radio` components:

- `<RadioGroup.Root />` is a top-level element that wraps the other components.
- `<Radio.Root />` renders an individual `<button>` radio item.
- `<Radio.Indicator />` renders a `<span>` for providing a visual indicator. You can style this itself and/or place an icon inside.

```jsx
<RadioGroup.Root>
<Radio.Root>
<Radio.Indicator />
</Radio.Root>
</RadioGroup.Root>
```

## Identifying items

The `value` prop is required on `Radio.Root` to identify it in the Radio Group:

```jsx
<RadioGroup.Root>
<Radio.Root value="a">
<Radio.Indicator />
</Radio.Root>
<Radio.Root value="b">
<Radio.Indicator />
</Radio.Root>
</RadioGroup.Root>
```

## Default value

The `defaultValue` prop determines the initial value of the component when uncontrolled, linked to the `value` prop on an individual Radio item:

```jsx
<RadioGroup.Root defaultValue="a">
<Radio.Root value="a" />
<Radio.Root value="b" />
</RadioGroup.Root>
```

## Controlled

The `value` and `onValueChange` props contain the `value` string of the currently selected Radio item in the Radio Group:

```jsx
const [value, setValue] = React.useState('a');

return (
<RadioGroup.Root value={value} onValueChange={setValue}>
<Radio.Root value="a" />
<Radio.Root value="b" />
</RadioGroup.Root>
);
```

## Styling

The `Radio` components have a `[data-radio]` attribute with values `"checked"` or `"unchecked"` to style based on the checked state:

```jsx
<Radio.Root className="Radio">
<Radio.Indicator className="RadioIndicator" />
</Radio.Root>
```

```css
.Radio {
border: 1px solid black;
}

.RadioIndicator {
width: 20px;
height: 20px;
border-radius: 50%;
border: 1px solid black;
}

.Radio[data-radio='checked'] {
background: black;
color: white;
}

.RadioIndicator[data-radio='checked'] {
background: white;
}
```
2 changes: 1 addition & 1 deletion docs/data/base/pages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const pages: readonly MuiPage[] = [
// { pathname: '/base-ui/react-autocomplete', title: 'Autocomplete' },
{ pathname: '/base-ui/react-checkbox', title: 'Checkbox' },
{ pathname: '/base-ui/react-number-field', title: 'Number Field' },
// { pathname: '/base-ui/react-radio-group', title: 'Radio Group', planned: true },
{ pathname: '/base-ui/react-radio-group', title: 'Radio Group' },
// { pathname: '/base-ui/react-select', title: 'Select' },
{ pathname: '/base-ui/react-slider', title: 'Slider' },
{ pathname: '/base-ui/react-switch', title: 'Switch' },
Expand Down
12 changes: 12 additions & 0 deletions docs/data/base/pagesApi.js
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,18 @@ module.exports = [
pathname: '/base-ui/react-progress/components-api/#progress-track',
title: 'ProgressTrack',
},
{
pathname: '/base-ui/react-radio-group/components-api/#radio-group-root',
title: 'RadioGroupRoot',
},
{
pathname: '/base-ui/react-radio-group/components-api/#radio-indicator',
title: 'RadioIndicator',
},
{
pathname: '/base-ui/react-radio-group/components-api/#radio-root',
title: 'RadioRoot',
},
{ pathname: '/base-ui/react-select/components-api/#select', title: 'Select' },
{
pathname: '/base-ui/react-slider/components-api/#slider-control',
Expand Down
26 changes: 26 additions & 0 deletions docs/pages/base-ui/api/radio-group-root.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"props": {
"className": { "type": { "name": "union", "description": "func<br>&#124;&nbsp;string" } },
"defaultValue": { "type": { "name": "union", "description": "number<br>&#124;&nbsp;string" } },
"disabled": { "type": { "name": "bool" }, "default": "false" },
"name": { "type": { "name": "string" } },
"onValueChange": { "type": { "name": "func" } },
"readOnly": { "type": { "name": "bool" }, "default": "false" },
"render": { "type": { "name": "union", "description": "element<br>&#124;&nbsp;func" } },
"required": { "type": { "name": "bool" }, "default": "false" },
"value": { "type": { "name": "union", "description": "number<br>&#124;&nbsp;string" } }
},
"name": "RadioGroupRoot",
"imports": [
"import * as RadioGroup from '@base_ui/react/RadioGroup';\nconst RadioGroupRoot = RadioGroup.Root;"
],
"classes": [],
"spread": true,
"themeDefaultProps": true,
"muiName": "RadioGroupRoot",
"forwardsRefTo": "HTMLDivElement",
"filename": "/packages/mui-base/src/RadioGroup/Root/RadioGroupRoot.tsx",
"inheritance": null,
"demos": "<ul><li><a href=\"/base-ui/react-radio-group/\">Radio Group</a></li></ul>",
"cssComponent": false
}
19 changes: 19 additions & 0 deletions docs/pages/base-ui/api/radio-indicator.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"props": {
"className": { "type": { "name": "union", "description": "func<br>&#124;&nbsp;string" } },
"render": { "type": { "name": "union", "description": "element<br>&#124;&nbsp;func" } }
},
"name": "RadioIndicator",
"imports": [
"import * as Radio from '@base_ui/react/Radio';\nconst RadioIndicator = Radio.Indicator;"
],
"classes": [],
"spread": true,
"themeDefaultProps": true,
"muiName": "RadioIndicator",
"forwardsRefTo": "HTMLSpanElement",
"filename": "/packages/mui-base/src/Radio/Indicator/RadioIndicator.tsx",
"inheritance": null,
"demos": "<ul><li><a href=\"/base-ui/react-radio-group/\">Radio Group</a></li></ul>",
"cssComponent": false
}
Loading