Skip to content

Commit b20f863

Browse files
feat: create radio group for ShadCN
1 parent 1a91d09 commit b20f863

File tree

5 files changed

+154
-4
lines changed

5 files changed

+154
-4
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
"@radix-ui/react-accordion": "^1.2.0",
3838
"@radix-ui/react-checkbox": "^1.1.1",
3939
"@radix-ui/react-navigation-menu": "^1.2.0",
40+
"@radix-ui/react-radio-group": "^1.2.0",
4041
"@radix-ui/react-slot": "^1.1.0",
4142
"@socialgouv/matomo-next": "^1.8.0",
4243
"chart.js": "^4.4.2",

src/components/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 "../../../tailwind/ui/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: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import type { Meta, StoryObj } from "@storybook/react/*"
2+
3+
import { HStack } from "../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+
// TODO: Waiting for PR #13434 to be merged
45+
// {
46+
// id: "invalid",
47+
// value: "invalid",
48+
// "aria-invalid": true,
49+
// label: "invalid",
50+
// },
51+
]
52+
53+
export const Radio: StoryObj<typeof meta> = {
54+
args: {
55+
defaultValue: DEFAULT_CHECKED,
56+
className: "gap-4",
57+
},
58+
render: (args) => (
59+
<RadioGroup {...args}>
60+
{radioSet.map((radio) => (
61+
<HStack key={radio.id} asChild>
62+
<label>
63+
<RadioGroupItem {...radio} />
64+
{radio.label}
65+
</label>
66+
</HStack>
67+
))}
68+
</RadioGroup>
69+
),
70+
}

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 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 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>

yarn.lock

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3965,6 +3965,37 @@
39653965
dependencies:
39663966
"@radix-ui/react-slot" "1.1.0"
39673967

3968+
"@radix-ui/react-radio-group@^1.2.0":
3969+
version "1.2.0"
3970+
resolved "https://registry.yarnpkg.com/@radix-ui/react-radio-group/-/react-radio-group-1.2.0.tgz#f937dd6b9436ded80c4bebdf3901c20cb8bcbb5a"
3971+
integrity sha512-yv+oiLaicYMBpqgfpSPw6q+RyXlLdIpQWDHZbUKURxe+nEh53hFXPPlfhfQQtYkS5MMK/5IWIa76SksleQZSzw==
3972+
dependencies:
3973+
"@radix-ui/primitive" "1.1.0"
3974+
"@radix-ui/react-compose-refs" "1.1.0"
3975+
"@radix-ui/react-context" "1.1.0"
3976+
"@radix-ui/react-direction" "1.1.0"
3977+
"@radix-ui/react-presence" "1.1.0"
3978+
"@radix-ui/react-primitive" "2.0.0"
3979+
"@radix-ui/react-roving-focus" "1.1.0"
3980+
"@radix-ui/react-use-controllable-state" "1.1.0"
3981+
"@radix-ui/react-use-previous" "1.1.0"
3982+
"@radix-ui/react-use-size" "1.1.0"
3983+
3984+
"@radix-ui/[email protected]":
3985+
version "1.1.0"
3986+
resolved "https://registry.yarnpkg.com/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.0.tgz#b30c59daf7e714c748805bfe11c76f96caaac35e"
3987+
integrity sha512-EA6AMGeq9AEeQDeSH0aZgG198qkfHSbvWTf1HvoDmOB5bBG/qTxjYMWUKMnYiV6J/iP/J8MEFSuB2zRU2n7ODA==
3988+
dependencies:
3989+
"@radix-ui/primitive" "1.1.0"
3990+
"@radix-ui/react-collection" "1.1.0"
3991+
"@radix-ui/react-compose-refs" "1.1.0"
3992+
"@radix-ui/react-context" "1.1.0"
3993+
"@radix-ui/react-direction" "1.1.0"
3994+
"@radix-ui/react-id" "1.1.0"
3995+
"@radix-ui/react-primitive" "2.0.0"
3996+
"@radix-ui/react-use-callback-ref" "1.1.0"
3997+
"@radix-ui/react-use-controllable-state" "1.1.0"
3998+
39683999
"@radix-ui/[email protected]", "@radix-ui/react-slot@^1.0.2":
39694000
version "1.0.2"
39704001
resolved "https://registry.yarnpkg.com/@radix-ui/react-slot/-/react-slot-1.0.2.tgz#a9ff4423eade67f501ffb32ec22064bc9d3099ab"

0 commit comments

Comments
 (0)