Skip to content

Commit 31519ca

Browse files
authored
Merge pull request #13559 from TylerAPfledderer/feat/shadcn-radio
feat: create ShadCN Radio Component
2 parents 5089c1c + 2bac5d6 commit 31519ca

File tree

5 files changed

+153
-5
lines changed

5 files changed

+153
-5
lines changed

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
"@radix-ui/react-checkbox": "^1.1.1",
3939
"@radix-ui/react-navigation-menu": "^1.2.0",
4040
"@radix-ui/react-popover": "^1.1.1",
41+
"@radix-ui/react-radio-group": "^1.2.0",
4142
"@radix-ui/react-slot": "^1.1.0",
4243
"@radix-ui/react-visually-hidden": "^1.1.0",
4344
"@socialgouv/matomo-next": "^1.8.0",
@@ -130,4 +131,4 @@
130131
"jackspeak": "2.1.1",
131132
"sharp": "0.32.6"
132133
}
133-
}
134+
}

tailwind/ui/Checkbox.tsx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ import * as CheckboxPrimitive from "@radix-ui/react-checkbox"
44

55
import { cn } from "@/lib/utils/cn"
66

7+
/**
8+
* Common style classes for the checkbox, radio, and switch controls
9+
*/
10+
export const commonControlClasses =
11+
"size-4 border border-body-medium text-background hover:border-primary-high-contrast hover:bg-body-light focus-visible:outline-offset-4 focus-visible:outline-primary-hover disabled:cursor-not-allowed disabled:border-disabled disabled:bg-disabled aria-[invalid]:border-error aria-[invalid]:bg-error-light data-[state='checked']:not-disabled:border-primary data-[state='checked']:not-disabled:bg-primary data-[state='checked']:hover:not-disabled:bg-primary-hover"
12+
713
export type CheckboxProps = React.ComponentPropsWithoutRef<
814
typeof CheckboxPrimitive.Root
915
>
@@ -14,10 +20,7 @@ const Checkbox = React.forwardRef<
1420
>(({ className, ...props }, ref) => (
1521
<CheckboxPrimitive.Root
1622
ref={ref}
17-
className={cn(
18-
"size-4 rounded-sm border border-body-medium text-background hover:border-primary-high-contrast hover:bg-body-light focus-visible:outline-offset-4 focus-visible:outline-primary-hover disabled:cursor-not-allowed disabled:border-disabled disabled:bg-disabled aria-[invalid]:border-error aria-[invalid]:bg-error-light data-[state='checked']:not-disabled:border-primary data-[state='checked']:not-disabled:bg-primary data-[state='checked']:hover:not-disabled:bg-primary-hover",
19-
className
20-
)}
23+
className={cn(commonControlClasses, "rounded-sm", className)}
2124
{...props}
2225
>
2326
<CheckboxPrimitive.Indicator className="flex items-center justify-center">

tailwind/ui/RadioGroup.tsx

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import * as React from "react"
2+
import { MdCircle } from "react-icons/md"
3+
import * as RadioGroupPrimitive from "@radix-ui/react-radio-group"
4+
5+
import { cn } from "@/lib/utils/cn"
6+
7+
import { commonControlClasses } from "./Checkbox"
8+
9+
const RadioGroup = React.forwardRef<
10+
React.ElementRef<typeof RadioGroupPrimitive.Root>,
11+
React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Root>
12+
>(({ className, ...props }, ref) => {
13+
return (
14+
<RadioGroupPrimitive.Root
15+
className={cn("grid gap-2", className)}
16+
{...props}
17+
ref={ref}
18+
/>
19+
)
20+
})
21+
RadioGroup.displayName = RadioGroupPrimitive.Root.displayName
22+
23+
type RadioGroupItemProps = React.ComponentPropsWithoutRef<
24+
typeof RadioGroupPrimitive.Item
25+
>
26+
27+
const RadioGroupItem = React.forwardRef<
28+
React.ElementRef<typeof RadioGroupPrimitive.Item>,
29+
RadioGroupItemProps
30+
>(({ className, ...props }, ref) => {
31+
return (
32+
<RadioGroupPrimitive.Item
33+
ref={ref}
34+
className={cn(commonControlClasses, "rounded-full", className)}
35+
{...props}
36+
>
37+
<RadioGroupPrimitive.Indicator className="flex items-center justify-center">
38+
<MdCircle className="size-2" />
39+
</RadioGroupPrimitive.Indicator>
40+
</RadioGroupPrimitive.Item>
41+
)
42+
})
43+
RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName
44+
45+
export { RadioGroup, RadioGroupItem, type RadioGroupItemProps }
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import type { Meta, StoryObj } from "@storybook/react/*"
2+
3+
import { HStack } from "../../../src/components/ui/flex"
4+
import {
5+
RadioGroup,
6+
RadioGroupItem,
7+
type RadioGroupItemProps,
8+
} from "../RadioGroup"
9+
10+
const meta = {
11+
title: "Atoms / Form / ShadCN Radio",
12+
component: RadioGroup,
13+
} satisfies Meta<typeof RadioGroup>
14+
15+
export default meta
16+
17+
const DEFAULT_CHECKED = "checked"
18+
19+
const radioSet: Array<RadioGroupItemProps & { label: string }> = [
20+
{
21+
id: "default",
22+
value: "default",
23+
label: "default",
24+
},
25+
{
26+
id: DEFAULT_CHECKED,
27+
value: DEFAULT_CHECKED,
28+
label: DEFAULT_CHECKED,
29+
},
30+
{
31+
id: "disabled",
32+
value: "disabled",
33+
disabled: true,
34+
label: "disabled",
35+
},
36+
{
37+
id: "disabled-checked",
38+
value: "disabled-checked",
39+
disabled: true,
40+
checked: true,
41+
label: "disabled-checked",
42+
},
43+
{
44+
id: "invalid",
45+
value: "invalid",
46+
"aria-invalid": true,
47+
label: "invalid",
48+
},
49+
]
50+
51+
export const Radio: StoryObj<typeof meta> = {
52+
args: {
53+
defaultValue: DEFAULT_CHECKED,
54+
className: "gap-4",
55+
},
56+
render: (args) => (
57+
<RadioGroup {...args}>
58+
{radioSet.map((radio) => (
59+
<HStack key={radio.id} asChild>
60+
<label>
61+
<RadioGroupItem {...radio} />
62+
{radio.label}
63+
</label>
64+
</HStack>
65+
))}
66+
</RadioGroup>
67+
),
68+
}

yarn.lock

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4051,6 +4051,37 @@
40514051
dependencies:
40524052
"@radix-ui/react-slot" "1.1.0"
40534053

4054+
"@radix-ui/react-radio-group@^1.2.0":
4055+
version "1.2.0"
4056+
resolved "https://registry.yarnpkg.com/@radix-ui/react-radio-group/-/react-radio-group-1.2.0.tgz#f937dd6b9436ded80c4bebdf3901c20cb8bcbb5a"
4057+
integrity sha512-yv+oiLaicYMBpqgfpSPw6q+RyXlLdIpQWDHZbUKURxe+nEh53hFXPPlfhfQQtYkS5MMK/5IWIa76SksleQZSzw==
4058+
dependencies:
4059+
"@radix-ui/primitive" "1.1.0"
4060+
"@radix-ui/react-compose-refs" "1.1.0"
4061+
"@radix-ui/react-context" "1.1.0"
4062+
"@radix-ui/react-direction" "1.1.0"
4063+
"@radix-ui/react-presence" "1.1.0"
4064+
"@radix-ui/react-primitive" "2.0.0"
4065+
"@radix-ui/react-roving-focus" "1.1.0"
4066+
"@radix-ui/react-use-controllable-state" "1.1.0"
4067+
"@radix-ui/react-use-previous" "1.1.0"
4068+
"@radix-ui/react-use-size" "1.1.0"
4069+
4070+
"@radix-ui/[email protected]":
4071+
version "1.1.0"
4072+
resolved "https://registry.yarnpkg.com/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.0.tgz#b30c59daf7e714c748805bfe11c76f96caaac35e"
4073+
integrity sha512-EA6AMGeq9AEeQDeSH0aZgG198qkfHSbvWTf1HvoDmOB5bBG/qTxjYMWUKMnYiV6J/iP/J8MEFSuB2zRU2n7ODA==
4074+
dependencies:
4075+
"@radix-ui/primitive" "1.1.0"
4076+
"@radix-ui/react-collection" "1.1.0"
4077+
"@radix-ui/react-compose-refs" "1.1.0"
4078+
"@radix-ui/react-context" "1.1.0"
4079+
"@radix-ui/react-direction" "1.1.0"
4080+
"@radix-ui/react-id" "1.1.0"
4081+
"@radix-ui/react-primitive" "2.0.0"
4082+
"@radix-ui/react-use-callback-ref" "1.1.0"
4083+
"@radix-ui/react-use-controllable-state" "1.1.0"
4084+
40544085
"@radix-ui/[email protected]", "@radix-ui/react-slot@^1.0.2":
40554086
version "1.0.2"
40564087
resolved "https://registry.yarnpkg.com/@radix-ui/react-slot/-/react-slot-1.0.2.tgz#a9ff4423eade67f501ffb32ec22064bc9d3099ab"

0 commit comments

Comments
 (0)