Skip to content

Feat(design-system): progress bar 구현#25

Merged
jjangminii merged 10 commits intodevelopfrom
feat/#24/progress-bar-componemts
Aug 25, 2025
Merged

Feat(design-system): progress bar 구현#25
jjangminii merged 10 commits intodevelopfrom
feat/#24/progress-bar-componemts

Conversation

@jjangminii
Copy link
Collaborator

@jjangminii jjangminii commented Aug 24, 2025

📌 Related Issues

관련된 Issue를 태그해주세요. (e.g. - close #25)

✅ 체크 리스트

  • PR 제목의 형식을 잘 작성했나요? e.g. Feat(client): PR 템플릿 작성
  • 빌드가 성공했나요? (pnpm build)

📄 Tasks

  • progress bar 구현
  • 스타일 분기처리

⭐ PR Point (To Reviewer)

역할 분리 확정

  • Progress 컴포넌트는 표시만 담당(밖에서 value 전달시 시각화).
  • variant 2종으로 분기
    사용처에서 클래스 반복을 피하고 일관성을 보장하기 위해 스타일 주입/합성 대신 variant="profile" | "tree" 두 가지만 공식 지원하도록 했습니다.

분기처리 선택이유

  • “레이아웃만 제공 + 사용처 스타일 주입”은 케이스마다 클래스가 반복되고 프리셋과 헬퍼가 추가로 필요해 오히려 관리 비용이 커질 수 있다고 판단했습니다. 확실히 2가지 스타일만 쓰는 현재 단계에선 분기 방식이 좋다고 판단했습니다.
  • 추후 프로그래스바 디자인이 추가된다면 높이와 컬러 입력받는 형식으로 변경하는 것도 좋을거라 생각했습니다

사용방법

      <Progress value={progressPercent} variant="profile" />
      <Progress value={progressPercent} variant="tree" />

📷 Screenshot

Summary by CodeRabbit

  • 신규 기능
    • Progress 컴포넌트 추가(프로필/트리 변형 지원, 값 0–100 자동 제한, 접근성 속성 포함).
  • 스타일
    • 스위치 높이 조정으로 시각적 일관성 개선.
  • 문서
    • Progress용 스토리북 스토리·데모·인터랙션 테스트 추가(자동 진행, 동기화 등).
  • 작업
    • 디자인 시스템에 런타임 의존성 추가(@radix-ui/react-progress).

@jjangminii jjangminii linked an issue Aug 24, 2025 that may be closed by this pull request
@coderabbitai
Copy link

coderabbitai bot commented Aug 24, 2025

Walkthrough

디자인 시스템에 Radix 기반 Progress 컴포넌트를 추가하고 Storybook 스토리를 도입했으며, 패키지 의존성에 @radix-ui/react-progress를 추가하고 컴포넌트 인덱스에 Progress를 재수출했습니다. Switch 컴포넌트의 높이 클래스도 조정되었습니다.

Changes

Cohort / File(s) Change Summary
Dependency update
packages/design-system/package.json
런타임 의존성 추가: @radix-ui/react-progress@^1.1.7
Component exports
packages/design-system/src/components/index.ts
Switch 재수출 경로 대소문자 수정(./switch/Switch), Progress 재수출 추가(./progress/Progress)
New Progress component
packages/design-system/src/components/progress/Progress.tsx
Radix 기반 Progress 컴포넌트 추가: value 클램프(0–100), variant(profile/tree), 트랙/인디케이터 스타일 분기, ProgressProps 공개
Storybook for Progress
packages/design-system/src/components/progress/Progress.stories.tsx
Progress 스토리·데모·플레이 테스트 추가: 기본/0/100/자동 진행/동기화 인터랙션, 컨트롤/문서 메타 구성
Switch style tweak
packages/design-system/src/components/switch/Switch.tsx
SwitchPrimitive.Root의 높이 유틸에서 h-[1.15rem] 제거, h-[2rem] 유지 (시각적 변경)

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant C as Consumer (App)
  participant P as DesignSystem.Progress
  participant R as Radix.ProgressPrimitive
  participant D as Browser DOM

  C->>P: render <Progress value, variant, className?>
  note right of P: value를 0–100으로 클램프\nvariant에 따라 클래스 선택
  P->>R: <Root max=100 value=clamped ...props>
  P->>R: <Indicator style=width:clamped% class=variant>
  R-->>D: role="progressbar" aria-valuenow=clamped
  D-->>C: 시각적 트랙/인디케이터 렌더링
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Assessment against linked issues

Objective Addressed Explanation
Progress 컴포넌트 구현 및 공개 API 제공 (#24, #25)
variant 2종(profile/tree) 제공 및 스타일 분기 (#25)
입력값 0–100 범위 시각화(클램프) 구현 (#25)
Storybook 예시·인터랙션 추가로 문서화 (#25)

Assessment against linked issues: Out-of-scope changes

Code Change Explanation
Switch 높이 스타일 변경 (h-[1.15rem] 제거, h-[2rem] 유지) (packages/design-system/src/components/switch/Switch.tsx) 진행률 바 구현 목적(#24, #25)과 직접 관련 없는 시각적 스타일 조정입니다.

Possibly related PRs

Suggested labels

🛠️ Feature

Suggested reviewers

  • jllee000
  • karnelll
  • constantly-dev

Poem

"당근 향 가득, 퍼센트가 톡톡 뛰어요,
프로필엔 짧게, 나무엔 길게 자라요.
Radix 톱니에 맞춰 빛나며 가는 길 —
토끼가 두 발로 밀어 올려요, 위로 위로! 🐇🥕"

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.


📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 6d68dc1 and 809ac5d.

📒 Files selected for processing (1)
  • packages/design-system/src/components/index.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/design-system/src/components/index.ts
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/#24/progress-bar-componemts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@github-actions github-actions bot requested a review from jllee000 August 24, 2025 11:13
@github-actions github-actions bot added the feat 기능 개발하라 개발 달려라 달려 label Aug 24, 2025
@github-actions
Copy link

github-actions bot commented Aug 24, 2025

✅ Storybook chromatic 배포 확인:
🐿️ storybook

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (7)
packages/design-system/package.json (1)

57-57: @radix-ui/react-progress 의존성 관리 권장

v1.1.7의 peerDependencies를 확인한 결과, React 및 React DOM에 대해 ^16.8 || ^17.0 || ^18.0 || ^19.0 || 19.0.0-rc 범위로 지정되어 있어 React 19.1.0과도 호환됩니다. 디자인 시스템 패키지 특성상, 소비 애플리케이션에서 중복 번들을 방지하려면 아래와 같이 dependencies가 아닌 peerDependencies(+ devDependencies)로 옮기는 방식을 고려해 주세요.

  • 위치: packages/design-system/package.json (현재 57행)

제안하는 변경 예시:

 {
   "dependencies": {
     "@pivanov/vite-plugin-svg-sprite": "^3.1.3",
-    "@radix-ui/react-progress": "^1.1.7",
     "@radix-ui/react-slot": "^1.2.3",
     "@radix-ui/react-switch": "^1.2.6",
     ...
   },
+  "peerDependencies": {
+    "@radix-ui/react-progress": "^1.1.7"
+  },
+  "devDependencies": {
+    "@radix-ui/react-progress": "^1.1.7"
+  }
 }

이렇게 구성하면:

  • 소비 애플리케이션이 자신의 React 버전에 맞춰 @radix-ui/react-progress를 설치하도록 유도
  • 디자인 시스템 패키지 번들 크기 최소화

위 변경을 검토해 보시길 권장드립니다.

packages/design-system/src/components/progress/progress.tsx (3)

16-27: 작은 스타일 중복 제거: tree variant에서 indicator 높이 중복

indicator 기본 클래스에 h-full이 있고, track(tree)에서 이미 h-[1.2rem]을 지정합니다. indicator(tree)의 h-[1.2rem]는 중복/덮어쓰기라 제거 가능합니다. 가독성↑, 유지보수 비용↓.

제안 수정:

 const indicator = cva(
   'h-full rounded-full transition-[width] duration-300 ease-out',
   {
     variants: {
       variant: {
         profile: 'bg-main400',
-        tree: 'h-[1.2rem] bg-gradient-to-r from-gradient-start to-gradient-end',
+        tree: 'bg-gradient-to-r from-gradient-start to-gradient-end',
       },
     },

35-53: ref 전달 지원으로 조합성 향상

라이브러리 컴포넌트는 ref 전달을 지원하는 편이 좋습니다(포커스 관리, 측정 등). Radix Root에 ref를 연결하도록 forwardRef 전환을 권장합니다. 외부 API 표면은 그대로 유지됩니다.

예시 수정:

-type RootProps = React.ComponentProps<typeof ProgressPrimitive.Root>;
+type RootProps = React.ComponentProps<typeof ProgressPrimitive.Root>;
+type RootRef = React.ElementRef<typeof ProgressPrimitive.Root>;

-export function Progress({ className, variant, value, ...props }: Props) {
+export const Progress = React.forwardRef<RootRef, Props>(function Progress(
+  { className, variant, value, ...props },
+  ref
+) {
   const raw =
     typeof value === 'string' ? Number.parseFloat(value) : value;
   const safe = Number.isFinite(raw) ? raw : 0;
   const v = Math.max(0, Math.min(100, safe));

   return (
     <ProgressPrimitive.Root
+      ref={ref}
       data-slot="progress"
       className={cn(track({ variant }), className)}
       value={v}
       max={100}
       {...props}
     >
       <ProgressPrimitive.Indicator
         data-slot="progress-indicator"
         className={indicator({ variant })}
         style={{ width: `${v}%` }}
       />
     </ProgressPrimitive.Root>
   );
-}
+});

6-14: 사소한 포맷 통일 제안

형식 일관성을 위해 packages/design-system/src/components/progress/progress.tsxtree 변형에서

- 'h-[1.2rem]  bg-gray100'
+ 'h-[1.2rem] bg-gray100'

처럼 이중 공백을 단일 공백으로 수정해 주세요.

나머지 색상 토큰( bg-gray100·bg-main400·from-gradient-start·to-gradient-end )은
packages/tailwind-config/shared-styles.css와 디자인 시스템 유틸(utils.ts, icon.tsx 등)에서 정의되어 있어 문제없음을 확인했습니다.

packages/design-system/src/components/progress/progress.stories.tsx (3)

71-80: Chromatic 스냅샷/플레이 환경에서 애니메이션 플리키 완화

setInterval 기반 자동 진행은 스냅샷 타이밍에 따라 변동이 생길 수 있습니다. 해당 스토리는 상호작용 테스트(play)를 수행하지 않으므로 괜찮을 수 있으나, 안정성을 높이려면 캡처 지연/정지 옵션을 고려해 주세요.

옵션 예시:

  • parameters: { chromatic: { pauseAnimationAtEnd: true } } 또는
  • mount 시 최초 1초 정지 후 진행, 혹은 스토리 이름에 “demo-only” 명시하여 비시각 스냅샷 제외

127-140: 플레이 단언 강화: DOM 업데이트 동기화와 접근성 쿼리 활용

현재 getAttribute 비교는 충분히 동작하지만, 테스트 의도를 더 명확히 하려면 접근성 쿼리와 jest-dom 매처를 사용하는 편이 읽기 쉽습니다.

예시:

-    const roots = canvasElement.querySelectorAll('[data-slot="progress"]');
-    roots.forEach((el) => expect(el.getAttribute('aria-valuenow')).toBe('40'));
+    const roots = await canvas.findAllByRole('progressbar');
+    roots.forEach((el) => expect(el).toHaveAttribute('aria-valuenow', '40'));

또는 비동기 반영 보장을 위해 await를 한 번 더 양념하는 것도 방법입니다.


51-69: 경계값 시나리오 추가 제안

Zero/Full 스토리는 좋습니다. “입력 초과/미만 → 클램프”를 보여주는 스토리도 있으면 API 보증을 문서로 남기기에 좋습니다.

예시:

export const Clamped: Story = {
  args: { value: 120 }, // 또는 -10
  render: (args) => <Frame {...args} />,
};
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 3cbf52e and d4b2b27.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (3)
  • packages/design-system/package.json (1 hunks)
  • packages/design-system/src/components/progress/progress.stories.tsx (1 hunks)
  • packages/design-system/src/components/progress/progress.tsx (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
packages/design-system/src/components/progress/progress.tsx (1)
packages/design-system/src/lib/utils.ts (1)
  • cn (108-110)
packages/design-system/src/components/progress/progress.stories.tsx (1)
packages/design-system/src/components/progress/progress.tsx (1)
  • Progress (35-53)

};

export function Progress({ className, variant, value, ...props }: Props) {
const v = Math.max(0, Math.min(100, Number(value)));
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

NaN 전파 가능성: 문자열 입력 시 width/aria-valuenow가 깨질 수 있습니다

Number(value)NaN이면 Math.max/min 결과도 NaN이 되어 style={{ width: 'NaN%' }}aria-valuenow="NaN"이 발생할 수 있습니다. 스토리에서는 숫자를 쓰고 있으나, 공개 API가 string을 허용하므로 실제 사용처에서 쉽게 재현됩니다. 안전 가드가 필요합니다.

권장 수정:

-export function Progress({ className, variant, value, ...props }: Props) {
-  const v = Math.max(0, Math.min(100, Number(value)));
+export function Progress({ className, variant, value, ...props }: Props) {
+  const raw =
+    typeof value === 'string' ? Number.parseFloat(value) : value;
+  const safe = Number.isFinite(raw) ? raw : 0;
+  const v = Math.max(0, Math.min(100, safe));
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const v = Math.max(0, Math.min(100, Number(value)));
export function Progress({ className, variant, value, ...props }: Props) {
const raw =
typeof value === 'string' ? Number.parseFloat(value) : value;
const safe = Number.isFinite(raw) ? raw : 0;
const v = Math.max(0, Math.min(100, safe));
🤖 Prompt for AI Agents
In packages/design-system/src/components/progress/progress.tsx around line 36,
Number(value) can produce NaN for string/invalid inputs which then yields style
width: 'NaN%' and aria-valuenow="NaN"; replace the direct Number(value) usage
with a safe parse that defaults to 0 when the result is NaN (e.g., const raw =
Number(value); const num = Number.isFinite(raw) ? raw : 0), then clamp num to
the 0–100 range with Math.max/Math.min and use that safe numeric value for both
style width and aria-valuenow so invalid inputs no longer propagate NaN.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (4)
packages/design-system/src/components/progress/progress.stories.tsx (4)

129-137: play 함수의 비동기 업데이트 안정화 및 쿼리 방식 개선.

클릭 직후의 React 상태 반영 타이밍에 따라 드물게 레이스가 생길 수 있습니다. 또한 DOM 셀렉터 대신 접근성 역할(Role) 기반 쿼리를 쓰면 구조 변경에도 강인해집니다.

다음과 같이 waitFor를 도입하고, getAllByRole('progressbar')toHaveAttribute 매처를 사용해 주세요.

-import { within, userEvent, expect } from '@storybook/test';
+import { within, userEvent, expect, waitFor } from '@storybook/test';
@@
-  play: async ({ canvasElement }) => {
-    const canvas = within(canvasElement);
-    const btn = await canvas.findByRole('button', { name: /\+20% \(both\)/i });
-    await userEvent.click(btn);
-    await userEvent.click(btn);
-
-    const roots = canvasElement.querySelectorAll('[data-slot="progress"]');
-    roots.forEach((el) => expect(el.getAttribute('aria-valuenow')).toBe('40'));
-  },
+  play: async ({ canvasElement }) => {
+    const canvas = within(canvasElement);
+    const btn = await canvas.findByRole('button', { name: /\+20% \(both\)/i });
+    await userEvent.click(btn);
+    await userEvent.click(btn);
+
+    await waitFor(() => {
+      const roots = canvas.getAllByRole('progressbar');
+      roots.forEach((el) => expect(el).toHaveAttribute('aria-valuenow', '40'));
+    });
+  },

Also applies to: 3-3


15-17: Docs에 값 클램핑(0–100) 명시 추가 제안.

구현(Progress.tsx)에서 value가 0–100으로 클램핑되는 점을 문서에 명시하면 사용자가 범위를 오해하지 않습니다.

아래처럼 한 줄을 보강해 주세요.

           '단일 로직을 공유하고 **variant**로 스타일만 분기하는 progress입니다.\n' +
           '- `variant="profile"`: 얇은 트랙 + 단색 인디케이터 (기본)\n' +
-          '- `variant="tree"`: 두꺼운 트랙 + 그라데이션 인디케이터\n',
+          '- `variant="tree"`: 두꺼운 트랙 + 그라데이션 인디케이터\n' +
+          '- 모든 value는 컴포넌트 내부에서 0–100으로 클램핑됩니다.\n',

81-101: Auto 데모의 타이머 관리 적절함(LGTM) + 미세 개선 아이디어.

setInterval과 클린업 처리가 적절합니다. 추가로, 크로스탭 집중 손실 시 CPU 점유를 더 줄이고 싶다면 setTimeout 루프로 전환하거나 간격을 조금 늘리는 것도 고려 가능합니다(선택 사항).


44-48: 접근성 이름 부여 고려(선택).

현재 role="progressbar" 요소에 접근성 이름이 없어 스크린리더에서 식별이 모호할 수 있습니다. 스토리 레벨에서만이라도 aria-label을 부여하면 데모와 테스트 모두 더 명확해집니다.

예시:

-const Frame: React.FC<React.ComponentProps<typeof Progress>> = (props) => (
+const Frame: React.FC<React.ComponentProps<typeof Progress>> = (props) => (
   <div style={{ width: 320 }}>
-    <Progress {...props} />
+    <Progress aria-label={props.variant ?? 'profile'} {...props} />
   </div>
 );
@@
-  render: (args) => <Frame {...args} />,
+  render: (args) => <Frame {...args} aria-label="profile" />,
@@
-  render: (args) => <Frame {...args} />,
+  render: (args) => <Frame {...args} aria-label="tree" />,
@@
-      <Progress value={v} variant="profile" />
-      <Progress value={v} variant="tree" />
+      <Progress value={v} variant="profile" aria-label="profile" />
+      <Progress value={v} variant="tree" aria-label="tree" />

Also applies to: 50-68, 102-108

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between d4b2b27 and c3ac10d.

📒 Files selected for processing (1)
  • packages/design-system/src/components/progress/progress.stories.tsx (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
packages/design-system/src/components/progress/progress.stories.tsx (1)
packages/design-system/src/components/progress/progress.tsx (1)
  • Progress (35-53)
🔇 Additional comments (1)
packages/design-system/src/components/progress/progress.stories.tsx (1)

102-124: 상호작용 스토리 구성 훌륭함(LGTM).

두 Progress를 동일 상태로 동기화하고 조작 단추로 값을 변화시키는 데모는 사용성/명세 검증에 매우 유용합니다. 불필요한 복잡성 없이 목적에 부합합니다.

@@ -0,0 +1,138 @@
import type { Meta, StoryObj } from '@storybook/react-vite';
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Storybook 타입 import 경로 및 Meta 타이핑 방식 수정 권장(빌드 실패 가능성).

Meta, StoryObj 타입은 통상 @storybook/react에서 가져옵니다. @storybook/react-vite로부터 타입을 import하면 환경에 따라 트랜스파일/타입체크가 실패할 수 있습니다. 또한 Storybook 8 권장 패턴인 satisfies Meta<typeof ...>를 사용하면 메타 객체 검증이 더 견고해집니다.

아래처럼 변경해 주세요:

-import type { Meta, StoryObj } from '@storybook/react-vite';
+import type { Meta, StoryObj } from '@storybook/react';
@@
-const meta: Meta<typeof Progress> = {
+const meta = {
   title: 'Components/Progress',
   component: Progress,
   tags: ['autodocs'],
   parameters: {
@@
-};
+} satisfies Meta<typeof Progress>;

다른 스토리 파일에서도 동일한 경로를 사용 중인지 확인해 주세요:

Also applies to: 6-6, 39-39


🏁 Script executed:

#!/bin/bash
# storybook 타입 import 경로 점검
rg -nP "from\s+'@storybook/react-vite'" --type ts --type tsx -C2

Length of output: 100


다른 .ts/.tsx 스토리 파일에서 @storybook/react-vite를 import 하고 있는지 재확인하기 위해 아래 스크립트를 실행해주세요:


🏁 Script executed:

#!/bin/bash
# storybook 타입 import 경로 점검 (ts / tsx 파일 대상)
rg -nP "from\s+['\"]@storybook/react-vite['\"]" -g '*.ts' -g '*.tsx' -C2

Length of output: 1122


스토리북 타입 import 경로 및 Meta 타이핑 방식 수정 필요

아래 스토리 파일들이 현재 @storybook/react-vite로부터 Meta, StoryObj를 import 하고 있어, 빌드 실패나 타입체크 오류가 발생할 수 있습니다. Storybook 8 권장 패턴인 satisfies Meta<typeof …>를 사용하고, 타입은 @storybook/react에서 가져오도록 변경해주세요.

• packages/design-system/src/components/progress/progress.stories.tsx
• packages/design-system/src/components/input/Input.stories.tsx
• packages/design-system/src/components/button/Button.stories.tsx

예시(diff):

-import type { Meta, StoryObj } from '@storybook/react-vite';
+import type { Meta, StoryObj } from '@storybook/react';
@@
-const meta: Meta<typeof Progress> = {
+const meta = {
   title: 'Components/Progress',
   component: Progress,
   tags: ['autodocs'],
   parameters: {
@@
-};
+} satisfies Meta<typeof Progress>;

위와 동일한 방식으로 세 파일 모두 적용해 주세요.

🤖 Prompt for AI Agents
In packages/design-system/src/components/progress/progress.stories.tsx (and also
apply the same change to
packages/design-system/src/components/input/Input.stories.tsx and
packages/design-system/src/components/button/Button.stories.tsx), replace the
Storybook type imports from '@storybook/react-vite' with imports from
'@storybook/react' and change the default export typing to use the Storybook 8
pattern: export default ... satisfies Meta<typeof Component>; keep StoryObj
usage for individual stories but import it from '@storybook/react' as well;
update any existing Meta/StoryObj references accordingly and remove the
'@storybook/react-vite' import to fix type errors.

@constantly-dev
Copy link
Member

  • variant 2종으로 분기
    사용처에서 클래스 반복을 피하고 일관성을 보장하기 위해 스타일 주입/합성 대신 variant="profile" | "tree" 두 가지만 공식 지원하도록 했습니다.

좋은 접근 같아요 👍

Copy link
Member

@constantly-dev constantly-dev left a comment

Choose a reason for hiding this comment

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

수고하셨습니다 👍 깔끔하게 구현 잘 해주셨네요.
코멘트 몇 개 달았으니 확인 부탁드려요~~

그리고 추가로 지금은 단순 넘겨주는 value에 따라 정적으로 progress 진행도/바가 보일 수도 있지만 추후 애니메이션 형태로 값이 늘어나거나 줄어드는 것이 추가로 고려해야 한다면 최적화가 필요할 수 있을 것 같아요!

브라우저 렌더링 과정에서 리플로우, 리페인트 과정이 성능에 영향이 많이 가는데 transition등으로 width를 조절해서 애니메이션을 구현하면 2개 과정을 사용해서 뚝뚝 끊기는 경우가 생길 수 있을 것 같아요.

bandicam.2025-08-24.22-31-34-160.mp4

따라서 transform과 같이 GPU를 사용하는 등의 애니메이션 최적화 관련해서 공부해보고 적용해보는 것도 좋을 것 같아요 👍 굿굿~~~~

애니메이션 관련 참고 자료

Comment on lines 29 to 33
type RootProps = React.ComponentProps<typeof ProgressPrimitive.Root>;
type Props = Omit<RootProps, 'value' | 'max'> &
VariantProps<typeof track> & {
value: number | string;
};
Copy link
Member

Choose a reason for hiding this comment

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

textarea PR 리뷰 참고하셔서 컨벤션 맞게 변경해주세요!

type RootProps = React.ComponentProps<typeof ProgressPrimitive.Root>;
type Props = Omit<RootProps, 'value' | 'max'> &
VariantProps<typeof track> & {
value: number | string;
Copy link
Member

Choose a reason for hiding this comment

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

value를 어떻게 받을지 정하셔서 타입을 더 구체화하는 것이 좋을 것 같아요! number 혹은 string 중 하나로!
특별한 이유가 있으시다면 궁금합니다!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

string을 받게 했던 이유는 간혹 문자열이 들어오는 경우를 예방하려했는데 사용처에서 숫자로 보정하는 게 더 깔끔할것같네요-!! 수정하겠습니다

};

export function Progress({ className, variant, value, ...props }: Props) {
const v = Math.max(0, Math.min(100, Number(value)));
Copy link
Member

Choose a reason for hiding this comment

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

조금 더 의미 있는 네이밍으로 바꾸면 어떨까요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

이 부분은 확실히 수정하는편이 좋을거 같아요-!! shadecn을 사용해서 만들다보니 이런 부분은 넘긴 부분이 있는거같아요 앞으로 더 주의하겠습니다

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (2)
packages/design-system/src/components/progress/progress.tsx (2)

29-36: valuenumber로 한정한 결정 굿 — API 명확성/오용 방지에 효과적입니다

이전 리뷰에서 권장되던 타입 축소가 반영되어, 사용처에서의 파싱 책임이 명확해졌고 컴포넌트의 역할(시각화 전담)이 분리되었습니다. 향후 불확정(indeterminate) 상태 지원이 필요하면 value?: number로 확장하는 방향을 고려해도 됩니다.


44-45: NaN/Infinity 입력 시 NaN% 스타일·aria-valuenow="NaN" 가능성 — 안전 가드 추가 필요

현재 클램핑은 유효 숫자라는 전제하에 동작합니다. 그러나 valueNaN/Infinity이면 Math.min/max 결과도 NaN이 되어 인라인 스타일이 깨지며 접근성 속성도 비정상이 됩니다. 방어적으로 유한수만 허용하고, 그렇지 않으면 0으로 폴백하세요.

권장 수정:

-  const progressPercent = Math.max(0, Math.min(100, value));
+  const raw = Number.isFinite(value) ? value : 0;
+  const progressPercent = Math.min(100, Math.max(0, raw));
🧹 Nitpick comments (3)
packages/design-system/src/components/progress/progress.tsx (3)

38-61: ref 전달 누락 — Radix 컴포넌트 패턴에 맞춰 forwardRef로 래핑 권장

테스트 및 포커스/측정(예: IntersectionObserver, scrollIntoView) 용이성과 Radix 컴포넌트 일관성을 위해 ref를 루트에 전달하는 것이 일반적입니다. 또한 DevTools 가독성을 위해 displayName도 지정하세요.

-export function Progress({
-  className,
-  variant,
-  value,
-  ...props
-}: ProgressProps) {
-  const progressPercent = Math.max(0, Math.min(100, value));
-
-  return (
-    <ProgressPrimitive.Root
+export const Progress = React.forwardRef<
+  React.ElementRef<typeof ProgressPrimitive.Root>,
+  ProgressProps
+>(({ className, variant, value, ...props }, ref) => {
+  const progressPercent = Math.max(0, Math.min(100, value));
+
+  return (
+    <ProgressPrimitive.Root
+      ref={ref}
       data-slot="progress"
       className={cn(track({ variant }), className)}
       value={progressPercent}
       max={100}
       {...props}
     >
       <ProgressPrimitive.Indicator
         data-slot="progress-indicator"
         className={indicator({ variant })}
         style={{ width: `${progressPercent}%` }}
       />
     </ProgressPrimitive.Root>
-  );
-}
+  );
+});
+
+Progress.displayName = 'Progress';

16-27: 움직임 최소화 접근성 지원: prefers-reduced-motion 대응

모션 민감 사용자를 위해 폭 애니메이션을 비활성화하는 유틸리티를 추가하는 것을 권장합니다.

-const indicator = cva(
-  'h-full rounded-full transition-[width] duration-300 ease-out',
+const indicator = cva(
+  'h-full rounded-full transition-[width] motion-reduce:transition-none duration-300 ease-out',

6-14: variant 값(profile/tree)이 사용처 맥락 종속 — 의미 기반 네이밍/별칭 검토 제안

팀 러닝(semantic union 타입 선호)에 비춰보면, 현재 값은 “어디서 쓰이는가”를 드러내지만 “어떻게 보이는가”는 드러내지 않습니다. 디자인 변경/재사용 시 인지 부하가 커질 수 있어 다음 중 하나를 고려해보세요:

  • 의미 기반으로 재명명: 예) variant: 'thin' | 'gradient'
  • 기존 값 유지 + 별칭 도입: 'profile' | 'tree' | 'thin' | 'gradient'를 같은 스타일에 매핑
  • 최소한 JSDoc로 각 variant의 시각적 의도를 명시

이 변경은 API 안정성 요구도에 따라 추후 리팩터 우선순위를 결정하시면 됩니다.

Also applies to: 16-27

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between c3ac10d and 22907bb.

📒 Files selected for processing (1)
  • packages/design-system/src/components/progress/progress.tsx (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-07-08T11:47:27.279Z
Learnt from: constantly-dev
PR: Pinback-Team/pinback-client#30
File: apps/extension/src/components/InfoBox/InfoBox.tsx:10-11
Timestamp: 2025-07-08T11:47:27.279Z
Learning: In React TypeScript components, prefer semantic prop names with union types over abstract version numbers. For example, use `size: 'small' | 'large'` instead of `version: 1 | 2` for better code readability, type safety, and extensibility.

Applied to files:

  • packages/design-system/src/components/progress/progress.tsx
🧬 Code graph analysis (1)
packages/design-system/src/components/progress/progress.tsx (1)
packages/design-system/src/lib/utils.ts (1)
  • cn (108-110)

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/design-system/src/components/index.ts (1)

4-4: DX 개선: 컴포넌트 타입도 배럴에서 재노출

소비 측에서 ProgressProps(및 필요한 경우 variant 타입)를 함께 가져올 수 있도록 타입 재노출을 권장합니다. 트리쉐이킹에 영향 없고 사용성이 좋아집니다.

 export { default as Input } from './input/Input';
-export { Progress } from './progress/Progress';
+export { Progress } from './progress/progress';
+export type { ProgressProps } from './progress/progress';
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 22907bb and 77c8dd8.

📒 Files selected for processing (2)
  • packages/design-system/src/components/index.ts (1 hunks)
  • packages/design-system/src/components/progress/progress.stories.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/design-system/src/components/progress/progress.stories.tsx
🔇 Additional comments (1)
packages/design-system/src/components/index.ts (1)

4-4: 내보내기 방식(named vs default) 확인 완료 — 변경 불필요

Progress 컴포넌트는 packages/design-system/src/components/progress/progress.tsx 파일에서 named export(export const Progress 등)로 내보내지고 있어, 배럴(index.ts)의

export { Progress } from './progress/Progress';

구문을 그대로 유지하시면 됩니다.

export { default as Button } from './button/Button';
export { Switch } from './switch/switch';
export { default as Input } from './input/Input';
export { Progress } from './progress/Progress';
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Linux/CI에서 모듈 해석 실패 가능성: import 경로 대소문자 불일치

AI 요약에 따르면 구현 파일은 packages/design-system/src/components/progress/progress.tsx(소문자)인데, 현재 배럴에서는 ./progress/Progress(대문자)로 참조합니다. 케이스-센서티브 파일시스템(대부분의 CI/Linux)에서는 Module not found로 빌드가 깨질 수 있습니다. 우선 경로를 소문자로 맞추는 게 안전합니다.

적용 제안:

-export { Progress } from './progress/Progress';
+export { Progress } from './progress/progress';
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export { Progress } from './progress/Progress';
export { Progress } from './progress/progress';
🤖 Prompt for AI Agents
In packages/design-system/src/components/index.ts around line 4, the export path
uses './progress/Progress' while the actual implementation file is lowercase
(progress/progress.tsx); on case-sensitive filesystems this causes Module not
found errors. Update the export to match the real file path casing (use
'./progress/progress') so the import exactly matches the filesystem, and run a
local build or CI to verify the module resolves.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (5)
packages/design-system/src/components/switch/Switch.tsx (1)

14-14: 매직 넘버(치수/이동 거리) 제거: CSS 변수로 일반화 제안

현재 고정값 h-[2rem] w-[4rem] 및 Thumb의 translate-x 수치가 사용처의 임의 사이즈 오버라이드와 충돌할 수 있습니다. Root에 CSS 변수로 기본값을 선언하고, Thumb 이동 거리를 calc(var(--..))로 계산하면 재사용성과 안정성이 올라갑니다. 사용처는 className에서 변수만 덮어쓰면 됩니다. (예: [--sw-h:2.5rem] [--sw-w:5rem] [--sw-thumb:2rem] [--sw-gap:0.25rem])

변경 제안(해당 라인 내 수정):

-          'data-[state=checked]:bg-main400 data-[state=unchecked]:bg-gray200 focus-visible:border-ring focus-visible:ring-ring/50 dark:data-[state=unchecked]:bg-input/80 shadow-xs peer inline-flex h-[2rem] w-[4rem] shrink-0 items-center rounded-full border border-transparent outline-none transition-all focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50',
+          'data-[state=checked]:bg-main400 data-[state=unchecked]:bg-gray200 focus-visible:border-ring focus-visible:ring-ring/50 dark:data-[state=unchecked]:bg-input/80 shadow-xs peer inline-flex h-[var(--sw-h)] w-[var(--sw-w)] [--sw-h:2rem] [--sw-w:4rem] [--sw-thumb:1.6rem] [--sw-gap:0.2rem] shrink-0 items-center rounded-full border border-transparent outline-none transition-all focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50',

Thumb 쪽 보완(참고 코드 — 라인 22 수정 필요):

// line 22 className 제안
'bg-background dark:data-[state=unchecked]:bg-foreground dark:data-[state=checked]:bg-primary-foreground pointer-events-none block size-[var(--sw-thumb)] rounded-full ring-0 transition-transform data-[state=checked]:translate-x-[calc(var(--sw-w)-var(--sw-thumb)-var(--sw-gap))] data-[state=unchecked]:translate-x-[var(--sw-gap)]'
packages/design-system/src/components/progress/Progress.tsx (2)

49-49: variant 강제력을 높이려면 className 병합 순서를 바꾸세요

현재 순서에서는 소비자가 전달한 className이 Tailwind Merge 규칙상 마지막 승리(last-wins)로 variant의 배경색/높이를 쉽게 덮어쓸 수 있습니다. 디자인 일관성을 지키려면 variant 클래스를 마지막에 두세요.

적용 diff:

-      className={cn(track({ variant }), className)}
+      className={cn(className, track({ variant }))}

16-27: 애니메이션 비용 최적화: width → transform 전환 제안

width 트랜지션은 레이아웃 계산을 유발합니다. transform 기반으로 바꾸면 GPU 가속 경로를 타서 부드럽고 효율적입니다. 또한 사용자 환경설정(모션 축소)을 존중해 transition을 끌 수 있게 하세요.

권장 diff(Indicator 클래스/스타일 조정):

-const indicator = cva(
-  'h-full rounded-full transition-[width] duration-300 ease-out',
+const indicator = cva(
+  'h-full w-full rounded-full ease-out transition-transform motion-reduce:transition-none will-change-transform',
   {
     variants: {
       variant: {
         profile: 'bg-main400',
         tree: 'bg-gradient-to-r from-gradient-start to-gradient-end',
       },
     },
     defaultVariants: { variant: 'profile' },
   }
 );
-        style={{ width: `${progressPercent}%` }}
+        style={{ transform: `translateX(-${100 - progressPercent}%)` }}

트랙에 overflow-hidden이 이미 적용되어 있어 시각적 동작은 동일합니다.

Also applies to: 55-58

packages/design-system/src/components/progress/Progress.stories.tsx (2)

70-79: 데모 자동 진행 인터벌 완화 제안(선택)

250ms 간격은 스토리북/테스트 러너 환경에서 그려지지 않은 프레임이 생길 수 있습니다. 400–500ms로 완화하면 CPU 부하와 깜빡임이 줄어듭니다.

예:

-    const id = setInterval(() => setV((p) => (p >= 100 ? 0 : p + 10)), 250);
+    const id = setInterval(() => setV((p) => (p >= 100 ? 0 : p + 10)), 500);

126-137: play 함수의 쿼리 안정성 개선(역할 기반 쿼리 사용)

내부 구현 의존적인 data-attribute 대신 ARIA 역할 기반 쿼리가 더 견고합니다.

적용 예:

-    const roots = canvasElement.querySelectorAll('[data-slot="progress"]');
-    roots.forEach((el) => expect(el.getAttribute('aria-valuenow')).toBe('40'));
+    const roots = await canvas.findAllByRole('progressbar');
+    roots.forEach((el) => expect(el.getAttribute('aria-valuenow')).toBe('40'));

현재 구현도 동작하지만, 역할 기반이 변경에 강합니다.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 77c8dd8 and 6d68dc1.

📒 Files selected for processing (4)
  • packages/design-system/src/components/index.ts (1 hunks)
  • packages/design-system/src/components/progress/Progress.stories.tsx (1 hunks)
  • packages/design-system/src/components/progress/Progress.tsx (1 hunks)
  • packages/design-system/src/components/switch/Switch.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/design-system/src/components/index.ts
🧰 Additional context used
🧬 Code graph analysis (2)
packages/design-system/src/components/progress/Progress.tsx (1)
packages/design-system/src/lib/utils.ts (1)
  • cn (108-110)
packages/design-system/src/components/progress/Progress.stories.tsx (1)
packages/design-system/src/components/progress/Progress.tsx (1)
  • Progress (38-61)
🔇 Additional comments (7)
packages/design-system/src/components/switch/Switch.tsx (2)

14-14: 높이 2rem로 통일한 변경 좋아요

컨테이너 h-[2rem]과 Thumb size-[1.6rem] 조합이 자연스러운 여백(각 0.2rem)을 만들어 이전 크기 불일치(overflow 가능성) 우려를 해소합니다. 접근성 측면에서도 터치 타깃이 커져 이점 있습니다.


14-14: 스위치 높이 변경에 따른 사용처 검증 필요

전체 코드베이스에서 자동 스크립트로는 Switch 사용처를 확인할 수 없으므로, 아래 항목을 수동으로 점검해 주세요.

  • 디자인 시스템 외 앱/패키지 레벨에서 Switch 컴포넌트 import 및 사용 여부
  • className으로 h-/w-/size- 유틸을 오버라이드한 케이스 유무
  • 텍스트 라인높이(line-height)나 아이콘과 병렬 배치된 곳의 vertical align(align-middle, leading) 및 간격(ml, mr, gap) 조정 필요성
  • 고정된 translate-x 값(Thumb)이 오프셋을 유발하는지 여부

위 점검 후 레이아웃/정렬 문제 발생 시 추가 조율이 필요합니다.

packages/design-system/src/components/progress/Progress.tsx (3)

44-45: 입력값 클램핑으로 안전성 확보 — LGTM

0–100 범위로 고정해 예기치 않은 입력에도 안전합니다. 그대로 가시죠.


6-14: CVA 설계 깔끔 — 확장 포인트 명확

track의 높이/배경, indicator의 색·그라데이션을 variant로만 분기하는 구조가 명확합니다. 추후 디자인 추가도 용이해 보입니다.


38-61: forwardRef 적용으로 사용성 개선 및 일관성 확보

ref 전달을 지원하면 포커스/측정/스크롤 연동 등 활용 폭이 넓어집니다.

적용 diff:

-export function Progress({
-  className,
-  variant,
-  value,
-  ...props
-}: ProgressProps) {
-  const progressPercent = Math.max(0, Math.min(100, value));
-
-  return (
-    <ProgressPrimitive.Root
+export const Progress = React.forwardRef<ProgressElement, ProgressProps>(
+  function Progress({ className, variant, value, ...props }, ref) {
+    const progressPercent = Math.max(0, Math.min(100, value));
+
+    return (
+      <ProgressPrimitive.Root
+        ref={ref}
         data-slot="progress"
-      className={cn(track({ variant }), className)}
+        className={cn(className, track({ variant }))}
         value={progressPercent}
         max={100}
         {...props}
       >
         <ProgressPrimitive.Indicator
           data-slot="progress-indicator"
           className={indicator({ variant })}
           style={{ width: `${progressPercent}%` }}
         />
       </ProgressPrimitive.Root>
-  );
-}
+    );
+  }
+);
⛔ Skipped due to learnings
Learnt from: jjangminii
PR: Pinback-Team/pinback-client#22
File: packages/design-system/src/components/input/Input.tsx:5-9
Timestamp: 2025-08-23T18:37:59.985Z
Learning: React 19에서는 함수 컴포넌트가 ref를 일반 prop으로 받을 수 있다. interface에서 ref: Ref<HTMLInputElement>로 정의하고 함수 컴포넌트에서 ({ ref, ...props }) => 형태로 받을 수 있어 forwardRef가 불필요하다. React 19.1.1+ 프로젝트에서는 이 방식을 사용해야 한다.
Learnt from: jjangminii
PR: Pinback-Team/pinback-client#22
File: packages/design-system/src/components/input/Input.tsx:5-9
Timestamp: 2025-08-23T18:37:59.985Z
Learning: React 19부터 함수 컴포넌트에서 ref를 일반 prop으로 받을 수 있어 forwardRef가 더 이상 필요하지 않다. InputProps interface에서 ref: Ref<HTMLInputElement>로 정의하고 함수 컴포넌트에서 ({ ref, ...props }) => 형태로 받을 수 있다.
packages/design-system/src/components/progress/Progress.stories.tsx (2)

6-39: 문서/컨트롤/기본 args 구성 좋습니다 — LGTM

value 범위를 0–100으로 한정하고 variant를 라디오로 노출한 점이 사용 경험에 적합합니다.


1-1: Storybook Vite 환경에서는 @storybook/react-vite에서 타입을 가져오는 것이 맞습니다

현재 프로젝트는 Vite 기반으로 Storybook을 구성하고 있으며, @storybook/react-vite 패키지에서 MetaStoryObj 타입을 제공하고 있습니다. 다른 Stories 파일에서도 동일한 import 경로를 사용하고 있어 일관성을 유지하는 것이 바람직합니다.
(참고: Storybook 패키지별 릴리스 주기가 다르며, react-vite 패키지는 9.x 라인에서 안정화되어 있습니다.)

Likely an incorrect or invalid review comment.

Comment on lines +29 to +36
export interface ProgressProps
extends Omit<
React.ComponentProps<typeof ProgressPrimitive.Root>,
'value' | 'max'
>,
VariantProps<typeof track> {
value: number;
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

ref 전달 누락: Radix Root의 ref를 노출하세요

디자인 시스템 컴포넌트는 보통 루트 요소 ref 전달을 지원합니다. forwardRef로 노출하고 타입도 With/WithoutRef로 정리하면 좋습니다. 또한 구조 고정 의도가 있다면 children을 받아들이지 않도록 차단해두는 편이 안전합니다.

권장 diff(타입 정리):

+type ProgressElement = React.ElementRef<typeof ProgressPrimitive.Root>;
+type ProgressRootProps = React.ComponentPropsWithoutRef<
+  typeof ProgressPrimitive.Root
+>;
+
-export interface ProgressProps
-  extends Omit<
-      React.ComponentProps<typeof ProgressPrimitive.Root>,
-      'value' | 'max'
-    >,
-    VariantProps<typeof track> {
+export interface ProgressProps
+  extends Omit<ProgressRootProps, 'value' | 'max' | 'children'>,
+    VariantProps<typeof track> {
   value: number;
 }

이 변경에 맞춰 컴포넌트 본문도 forwardRef로 교체해야 합니다(아래 코멘트 참조).

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In packages/design-system/src/components/progress/Progress.tsx around lines
29-36, the ProgressProps currently omit the Radix Root ref and don't prevent
children; update the prop types to support forwarding the Root ref (e.g., use a
WithRef/WithoutRef pattern or include React.ComponentPropsWithRef<typeof
ProgressPrimitive.Root> and remove direct 'value'/'max' conflicts), mark
children as never to prevent passing children, and change the component
implementation to use React.forwardRef to pass the ref to
ProgressPrimitive.Root. Ensure the exported types and component signature align
(props type accepts the ref and the forwardRef generic matches the Root element
type) and update all usages to receive the forwarded ref.

Copy link
Collaborator

@jllee000 jllee000 left a comment

Choose a reason for hiding this comment

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

그라데이션 바!! 고생하셨습니다 :) 트리랑 프로필 상황에 맞게 바로 꺼내쓸 수 있어서 직관적이고 좋네요! 어차피 고정값일 max={100}을 props로 넣는 이유? 대한 개인 질문만 확인해주세요!
(shadcn에서 max prop 전달이 필수 사항이여서 그렇게 하신거 같기도하고...)

Comment on lines +4 to +7
import { cn } from '@/lib/utils';

const track = cva('relative w-full overflow-hidden rounded-full', {
variants: {
Copy link
Collaborator

Choose a reason for hiding this comment

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

w-를 full로 주면서, 추후 화면 영역에 맞게 반응형으로 맞출 수 있게 관리 가능할 것 같아서 좋네용

Comment on lines +1 to +2
import * as React from 'react';
import * as ProgressPrimitive from '@radix-ui/react-progress';
Copy link
Collaborator

Choose a reason for hiding this comment

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

progressPrimitive은 shadCn에서 불러온 요소일까요??

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

네넵

Comment on lines +49 to +51
className={cn(track({ variant }), className)}
value={progressPercent}
max={100}
Copy link
Collaborator

Choose a reason for hiding this comment

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

just 질문! 여기서 max가 필요한 이유가 있을까요? max의미가 100점만점 이런 의미인 것 같은데, 어차피 최대 100 기준이 고정인거면 그냥 value 값 그대로가 곧 막대기 width이랑 직결되어서 max값이 크게 상관 없어 보여서요!

만약에 max가 100고정이 아니라, 포인트 20개 만점~ 이런 식이면 progressPercent를 (포인트 개수 / max) * 100 이런 식으로 따로 계산해야하니까 유동적인 max가 의미 있지만, max가 100인 게 고정일거라면 이 max 속성의 의미가 있는지?에 대한 궁금증이 있습니다!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

사실 시각적 요소는 width: ${progressPercent}%로 결정되어 max값은 크게 상관 없어요-! progressbar는 value와 함께 max가 있으면 스크린리더가 “40%” 또는 “40/100”처럼 맥락 있는 값을 읽어 주어 접근성에 있어 더 좋다고 판단했어요

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feat 기능 개발하라 개발 달려라 달려

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feat] Progress bar 공통컴포넌트 제작

3 participants