Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
53 changes: 53 additions & 0 deletions .changeset/radio-new-spec.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
---
"@jects/jds": minor
---
Comment on lines +1 to +3

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: `@jects/jds` 현재 버전과 changeset 정책 근거를 확인합니다.
fd -a 'package.json' . | xargs -I{} sh -c 'printf "\n--- %s ---\n" "$1"; jq -r ".name, .version" "$1" 2>/dev/null' sh {}
rg -n -C2 '"`@jects/jds`"|changeset|semver|major|minor|breaking' README.md .changeset package.json .github 2>/dev/null

Repository: JECT-Study/JECT-Official-WebSite-Client

Length of output: 6525


Breaking Change 에 대해 minor 로 설정하신 것이 적절합니다.

현재 @jects/jds0.4.0 버전 (0.x 대) 이며, 시맨틱 버저닝 표준에 따라 0.x 버전에서는 Breaking Change 를 minor 증가로 처리하는 것이 일반적입니다.

---
"`@jects/jds`": minor
---
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.changeset/radio-new-spec.md around lines 1 - 3, The changeset version bump
for `@jects/jds` should remain minor because the package is still on 0.x and
Breaking Change handling follows 0.x semver conventions; keep the frontmatter in
the changeset file as minor and do not change it to patch or major.


**Radio**

신규 디자인 스펙을 반영하면서 `Radio` 컴포넌트의 prop 이름·값과 서브 컴포넌트가 변경되고, `radioAlign` prop은 제거되었습니다.

| AS-IS | TO-BE |
| ---------------------------------------------------- | ---------------------------------- |
| `radioSize` / `radioStyle` | `size` / `variant` |
| `variant` (구 `radioStyle`) = `"empty" \| "outline"` | `variant = "hollow" \| "outlined"` |
| `radioAlign` | 제거 |
| `Radio.SubLabel` | `Radio.Helper` |

`Radio.Item`은 `<label>` 요소로 렌더되도록 변경되었습니다. 이에 따라 인디케이터뿐 아니라 라벨 및 헬퍼 텍스트를 클릭해도 해당 라디오를 선택할 수 있습니다.

아울러 disabled 상태의 색상, 커서 등 시각적 표현이 신규 디자인 스펙에 맞게 업데이트되었습니다.

**AS-IS**

```tsx
import { Radio } from "@jects/jds";

<Radio.Root
radioSize='md'
radioStyle='empty'
radioAlign='left'
value={selected}
onChange={setSelected}
name='group'
>
<Radio.Item>
<Radio.Basic value='1' />
<Radio.Label>옵션 1</Radio.Label>
<Radio.SubLabel>헬퍼 텍스트</Radio.SubLabel>
</Radio.Item>
</Radio.Root>;
```

**TO-BE**

```tsx
import { Radio } from "@jects/jds";

<Radio.Root size='md' variant='hollow' value={selected} onChange={setSelected} name='group'>
<Radio.Item>
<Radio.Basic value='1' />
<Radio.Label>옵션 1</Radio.Label>
<Radio.Helper>헬퍼 텍스트</Radio.Helper>
</Radio.Item>
</Radio.Root>;
```
285 changes: 153 additions & 132 deletions packages/jds/src/components/Radio/Radio.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { Meta, StoryObj } from "@storybook/react-vite";
import { FlexColumn, FlexRow } from "@storybook-utils/layout";
import { useState } from "react";

import { Radio } from "./Radio";

Expand All @@ -15,168 +16,188 @@ export default meta;

type Story = StoryObj<typeof Radio.Item>;

export const RadioBasicChecked: Story = {
export const RadioBasicSizes: Story = {
render: () => (
<FlexRow>
<Radio.Basic name='basicItem' value='1' />
<Radio.Basic name='basicItem' value='2' checked />
<Radio.Basic value='lg' size='lg' />
<Radio.Basic value='md' size='md' />
<Radio.Basic value='sm' size='sm' />
<Radio.Basic value='xs' size='xs' />
</FlexRow>
),
};

export const RadioBasicDisabled: Story = {
export const RadioBasicStates: Story = {
render: () => (
<FlexColumn>
<span>RadioBasic을 비활성화합니다.</span>
<FlexRow>
<Radio.Basic name='disabledItem' value='1' disabled />
<Radio.Basic name='disabledItem' value='2' checked disabled />
<Radio.Basic value='unchecked' checked={false} onChange={() => {}} />
<Radio.Basic value='checked' checked={true} onChange={() => {}} />
</FlexRow>
<span>Radio.Root를 통해 그룹 전체를 비활성화합니다.</span>
<FlexRow>
<Radio.Root disabled defaultValue='2' name='rootControl'>
<Radio.Item>
<Radio.Basic value='1' />
<Radio.Label>레이블</Radio.Label>
</Radio.Item>
<Radio.Item>
<Radio.Basic value='2' />
<Radio.Label>레이블</Radio.Label>
</Radio.Item>
</Radio.Root>
<Radio.Basic value='unchecked' checked={false} disabled onChange={() => {}} />
<Radio.Basic value='checked' checked={true} disabled onChange={() => {}} />
</FlexRow>
<span>Radio.Item을 통해 아이템을 개별적으로 비활성화합니다.</span>
<FlexRow>
<Radio.Root name='itemControl'>
<Radio.Item disabled>
<Radio.Basic value='1' />
</FlexColumn>
),
};

export const RadioItemVariant: Story = {
render: () => (
<FlexColumn>
<FlexColumn>
{(["lg", "md", "sm", "xs"] as const).map(size => (
<Radio.Item key={size} size={size} variant='hollow'>
<Radio.Basic value='item' />
<Radio.Label>레이블</Radio.Label>
</Radio.Item>
<Radio.Item disabled>
<Radio.Basic value='2' />
))}
</FlexColumn>
<FlexColumn>
{(["lg", "md", "sm", "xs"] as const).map(size => (
<Radio.Item key={size} size={size} variant='outlined'>
<Radio.Basic value='item' />
<Radio.Label>레이블</Radio.Label>
<Radio.Helper>헬퍼 텍스트</Radio.Helper>
</Radio.Item>
<Radio.Item>
<Radio.Basic value='3' />
<Radio.Label>비활성화X</Radio.Label>
</Radio.Item>
</Radio.Root>
</FlexRow>
))}
</FlexColumn>
</FlexColumn>
),
};

export const RadioBasicSizes: Story = {
render: () => {
return (
<FlexRow>
<Radio.Basic name='size' value='1' radioSize='lg' />
<Radio.Basic name='size' value='2' radioSize='md' />
<Radio.Basic name='size' value='3' radioSize='sm' />
<Radio.Basic name='size' value='4' radioSize='xs' />
</FlexRow>
);
},
export const RadioItemDisabled: Story = {
render: () => (
<FlexColumn>
<Radio.Item variant='hollow' disabled>
<Radio.Basic value='item' />
<Radio.Label>레이블</Radio.Label>
</Radio.Item>
<Radio.Item variant='outlined' disabled>
<Radio.Basic value='item' />
<Radio.Label>레이블</Radio.Label>
<Radio.Helper>헬퍼 텍스트</Radio.Helper>
</Radio.Item>
</FlexColumn>
),
};

export const RadioStyle: Story = {
export const RadioGroupUncontrolled: Story = {
render: () => (
<FlexColumn>
<FlexRow style={{ alignItems: "flex-start" }}>
<Radio.Root radioStyle='empty' radioSize='lg' radioAlign='left' name='emptyStyle'>
<Radio.Item>
<Radio.Basic value='1' />
<Radio.Label>레이블</Radio.Label>
</Radio.Item>
<Radio.Item>
<Radio.Basic value='2' />
<Radio.Label>레이블</Radio.Label>
</Radio.Item>
<Radio.Item>
<Radio.Basic value='3' />
<Radio.Label>레이블</Radio.Label>
<Radio.SubLabel>서브레이블</Radio.SubLabel>
</Radio.Item>
<Radio.Item>
<Radio.Basic value='4' />
<Radio.Label>레이블</Radio.Label>
<Radio.SubLabel>서브레이블</Radio.SubLabel>
</Radio.Item>
</Radio.Root>
</FlexRow>
<FlexRow style={{ alignItems: "flex-start" }}>
<Radio.Root radioStyle='outline' radioSize='lg' radioAlign='left' name='outlineStyle'>
<Radio.Item>
<Radio.Basic value='1' />
<Radio.Label>레이블</Radio.Label>
</Radio.Item>
<Radio.Item>
<Radio.Basic value='2' />
<Radio.Label>레이블</Radio.Label>
</Radio.Item>
<Radio.Item>
<Radio.Basic value='3' />
<Radio.Label>레이블</Radio.Label>
<Radio.SubLabel>서브레이블</Radio.SubLabel>
</Radio.Item>
<Radio.Item>
<Radio.Basic value='4' />
<Radio.Label>레이블</Radio.Label>
<Radio.SubLabel>서브레이블</Radio.SubLabel>
</Radio.Item>
</Radio.Root>
</FlexRow>
<Radio.Root defaultValue='2' name='groupUncontrolled'>
<Radio.Item>
<Radio.Basic value='1' />
<Radio.Label>레이블</Radio.Label>
</Radio.Item>
<Radio.Item>
<Radio.Basic value='2' />
<Radio.Label>레이블 (기본 선택)</Radio.Label>
</Radio.Item>
<Radio.Item>
<Radio.Basic value='3' />
<Radio.Label>레이블</Radio.Label>
</Radio.Item>
</Radio.Root>
</FlexColumn>
),
};

export const RadioAlign: Story = {
export const RadioGroupControlled: Story = {
render: () => {
const ControlledGroup = () => {
const [selected, setSelected] = useState("1");

return (
<FlexColumn>
<span>선택: {selected}</span>
<Radio.Root value={selected} onChange={setSelected} name='groupControlled'>
<Radio.Item>
<Radio.Basic value='1' />
<Radio.Label>레이블</Radio.Label>
</Radio.Item>
<Radio.Item>
<Radio.Basic value='2' />
<Radio.Label>레이블</Radio.Label>
</Radio.Item>
<Radio.Item>
<Radio.Basic value='3' />
<Radio.Label>레이블</Radio.Label>
</Radio.Item>
</Radio.Root>
</FlexColumn>
);
};

return <ControlledGroup />;
},
};

export const RadioGroupDisabled: Story = {
render: () => (
<FlexRow>
<Radio.Root disabled defaultValue='2' name='groupDisabled'>
<Radio.Item>
<Radio.Basic value='1' />
<Radio.Label>레이블</Radio.Label>
</Radio.Item>
<Radio.Item>
<Radio.Basic value='2' />
<Radio.Label>레이블</Radio.Label>
</Radio.Item>
</Radio.Root>
</FlexRow>
),
};

export const RadioComprehensiveMatrix: Story = {
render: () => (
<FlexColumn>
<FlexRow style={{ alignItems: "flex-start" }}>
<Radio.Root radioStyle='empty' radioSize='lg' radioAlign='right' name='emptyRight'>
<Radio.Item>
<Radio.Label>레이블</Radio.Label>
<Radio.Basic value='1' />
</Radio.Item>
<Radio.Item>
<Radio.Label>레이블</Radio.Label>
<Radio.Basic value='2' />
</Radio.Item>
<Radio.Item>
<Radio.Label>레이블</Radio.Label>
<Radio.Basic value='3' />
<Radio.SubLabel>서브레이블</Radio.SubLabel>
</Radio.Item>
<Radio.Item>
<Radio.Label>레이블</Radio.Label>
<Radio.Basic value='4' />
<Radio.SubLabel>서브레이블</Radio.SubLabel>
</Radio.Item>
</Radio.Root>
</FlexRow>
<FlexRow style={{ alignItems: "flex-start" }}>
<Radio.Root radioStyle='outline' radioSize='lg' radioAlign='right' name='outlineRight'>
<Radio.Item>
<Radio.Label>레이블</Radio.Label>
<Radio.Basic value='1' />
</Radio.Item>
<Radio.Item>
<Radio.Label>레이블</Radio.Label>
<Radio.Basic value='2' />
</Radio.Item>
<Radio.Item>
<Radio.Label>레이블</Radio.Label>
<Radio.Basic value='3' />
<Radio.SubLabel>서브레이블</Radio.SubLabel>
</Radio.Item>
<Radio.Item>
<Radio.Label>레이블</Radio.Label>
<Radio.Basic value='4' />
<Radio.SubLabel>서브레이블</Radio.SubLabel>
</Radio.Item>
</Radio.Root>
</FlexRow>
<FlexColumn>
{(["lg", "md", "sm", "xs"] as const).map(size => (
<FlexRow key={size}>
<Radio.Basic value='unchecked' size={size} checked={false} onChange={() => {}} />
<Radio.Basic value='checked' size={size} checked={true} onChange={() => {}} />
<Radio.Basic
value='unchecked'
size={size}
checked={false}
disabled
onChange={() => {}}
/>
<Radio.Basic
value='checked'
size={size}
checked={true}
disabled
onChange={() => {}}
/>
</FlexRow>
))}
</FlexColumn>
<FlexColumn>
{(["hollow", "outlined"] as const).map(variant => (
<FlexColumn key={variant}>
<Radio.Item variant={variant}>
<Radio.Basic value='item' defaultChecked />
<Radio.Label>레이블</Radio.Label>
<Radio.Helper>헬퍼 텍스트</Radio.Helper>
</Radio.Item>
<Radio.Item variant={variant} disabled>
<Radio.Basic value='item' />
<Radio.Label>레이블</Radio.Label>
<Radio.Helper>헬퍼 텍스트</Radio.Helper>
</Radio.Item>
</FlexColumn>
))}
</FlexColumn>
</FlexColumn>
),
parameters: {
docs: {
description: {
story: "모든 size와 상태 조합을 한눈에 확인할 수 있습니다.",
},
},
},
};
Loading
Loading