diff --git a/apps/web/package.json b/apps/web/package.json index a9f2945..56fbbda 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -17,6 +17,7 @@ "dependencies": { "@radix-ui/react-switch": "^1.2.6", "@radix-ui/react-tabs": "^1.1.13", + "@radix-ui/react-radio-group": "^1.3.8", "@tanstack/react-query": "^5.80.7", "@tanstack/react-query-devtools": "^5.80.7", "date-fns": "^4.1.0", diff --git a/apps/web/src/shared/components/Radio/Radio.stories.tsx b/apps/web/src/shared/components/Radio/Radio.stories.tsx new file mode 100644 index 0000000..280da9d --- /dev/null +++ b/apps/web/src/shared/components/Radio/Radio.stories.tsx @@ -0,0 +1,61 @@ +import type { Meta, StoryObj } from "@storybook/nextjs-vite"; +import { Radio } from "./Radio"; +import type { RadioProps } from "./Radio.type"; + +const meta = { + title: "v2/Components/Radio", + component: Radio, + parameters: { + layout: "centered", + }, + tags: ["autodocs"], + argTypes: { + options: { + control: "select", + options: [ + [ + { label: "프로젝트1", value: "프로젝트1" }, + { label: "프로젝트2", value: "프로젝트2" }, + { label: "프로젝트3", value: "프로젝트3" }, + ], + ], + description: "radio의 옵션을 지정", + }, + disabled: { + control: "boolean", + description: "radio의 비활성화 상태를 지정", + }, + defaultValue: { + control: "select", + options: ["프로젝트1", "프로젝트2", "프로젝트3"], + description: "radio의 기본 값을 지정", + }, + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Base: Story = { + args: { + defaultValue: "프로젝트1", + options: [ + { label: "프로젝트1", value: "프로젝트1" }, + { label: "프로젝트2", value: "프로젝트2" }, + { label: "프로젝트3", value: "프로젝트3" }, + ], + }, + render: args => , +}; + +export const Disabled: Story = { + args: { + options: [ + { label: "프로젝트1", value: "프로젝트1" }, + { label: "프로젝트2", value: "프로젝트2" }, + { label: "프로젝트3", value: "프로젝트3" }, + ], + disabled: true, + }, + render: (args: RadioProps) => , +}; diff --git a/apps/web/src/shared/components/Radio/Radio.tsx b/apps/web/src/shared/components/Radio/Radio.tsx new file mode 100644 index 0000000..85a54b8 --- /dev/null +++ b/apps/web/src/shared/components/Radio/Radio.tsx @@ -0,0 +1,77 @@ +import * as RadioGroupBase from "@radix-ui/react-radio-group"; +import { css } from "../../../../styled-system/css"; +import type { RadioProps } from "./Radio.type"; + +export function Radio({ options, defaultValue, onChange, disabled = false }: RadioProps) { + return ( + + {options.map(option => ( +
+ +
+ + + +
+ ))} + + ); +} diff --git a/apps/web/src/shared/components/Radio/Radio.type.ts b/apps/web/src/shared/components/Radio/Radio.type.ts new file mode 100644 index 0000000..3eb1afb --- /dev/null +++ b/apps/web/src/shared/components/Radio/Radio.type.ts @@ -0,0 +1,11 @@ +import type { ReactNode } from "react"; + +export type RadioProps = { + options: { + label: ReactNode; + value: string; + }[]; + defaultValue?: string; + onChange?: (value: string) => void; + disabled?: boolean; +}; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d31754c..d425a89 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -132,6 +132,9 @@ importers: apps/web: dependencies: + '@radix-ui/react-radio-group': + specifier: ^1.3.8 + version: 1.3.8(@types/react-dom@19.1.1(@types/react@19.1.0))(@types/react@19.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-switch': specifier: ^1.2.6 version: 1.2.6(@types/react-dom@19.1.1(@types/react@19.1.0))(@types/react@19.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) @@ -2356,6 +2359,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-radio-group@1.3.8': + resolution: {integrity: sha512-VBKYIYImA5zsxACdisNQ3BjCBfmbGH3kQlnFVqlWU4tXwjy7cGX8ta80BcrO+WJXIn5iBylEH3K6ZTlee//lgQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-roving-focus@1.1.11': resolution: {integrity: sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==} peerDependencies: @@ -10804,6 +10820,24 @@ snapshots: '@types/react': 19.1.0 '@types/react-dom': 19.1.1(@types/react@19.1.0) + '@radix-ui/react-radio-group@1.3.8(@types/react-dom@19.1.1(@types/react@19.1.0))(@types/react@19.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.0)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.0)(react@19.1.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.1.0)(react@19.1.0) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.1.1(@types/react@19.1.0))(@types/react@19.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.1(@types/react@19.1.0))(@types/react@19.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.1.1(@types/react@19.1.0))(@types/react@19.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.0)(react@19.1.0) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.1.0)(react@19.1.0) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.1.0)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.1.0 + '@types/react-dom': 19.1.1(@types/react@19.1.0) + '@radix-ui/react-roving-focus@1.1.11(@types/react-dom@19.1.1(@types/react@19.1.0))(@types/react@19.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.3