Skip to content

[✨ Feat/#49] Surface 공통 컴포넌트 구현#50

Merged
Lseojeong merged 28 commits into
devfrom
feat/#49
Jun 29, 2026
Merged

[✨ Feat/#49] Surface 공통 컴포넌트 구현#50
Lseojeong merged 28 commits into
devfrom
feat/#49

Conversation

@Lseojeong

@Lseojeong Lseojeong commented Jun 29, 2026

Copy link
Copy Markdown
Member

#️⃣연관된 이슈

체크 사항

  • UI 동작 및 레이아웃 확인
  • 불필요한 console 제거
  • 기능 정상 동작
  • 팀 컨벤션에 맞게 구현했는지
  • 관련 문서 또는 주석을 갱신했는지

📝작업 내용

Surface 공통 컴포넌트 구현

  • 모달과 패널을 동일한 인터페이스로 사용할 수 있는 Surface compound component를 추가했습니다.
  • variant="modal" | "panel"로 중앙 모달과 우측 패널 표현을 선택하도록 구성했습니다.
  • Surface.Trigger, Surface.Portal, Surface.Overlay, Surface.Content, Surface.Header, Surface.Body, Surface.Footer, Surface.Close를 구현했습니다.
  • Trigger, Close는 공통 Button과 조합할 수 있도록 asChild 패턴을 지원합니다.
  • compound 하위 컴포넌트를 Surface 밖에서 사용하면 명확한 에러가 발생하도록 Context hook을 구성했습니다.

Open / Close 상태 및 인터랙션 처리

  • controlled / uncontrolled open 상태를 지원하는 useControllableState 훅을 추가했습니다.
  • ESC 닫기, outside click 닫기, focus trap, focus restore, scroll lock 관련 공통 hook을 추가했습니다.
  • closeOnEscape, closeOnOutsideClick, canClose 옵션으로 dismiss 닫기 정책을 제어할 수 있도록 구현했습니다.
  • Surface.Close는 정책과 무관하게 항상 닫히도록 정리했습니다.
  • 여러 Surface가 동시에 열렸을 때 최상단 Surface만 ESC, outside click, focus trap을 처리하도록 SurfaceStack을 추가했습니다.

접근성 및 스타일

  • Surface.Contentrole="dialog"를 적용했습니다.
  • modal variant에서는 aria-modal을 적용합니다.
  • Surface.Contentaria-label 또는 aria-labelledby 중 하나를 타입으로 필수화했습니다.
  • Trigger에 aria-controls, aria-expanded를 자동 연결했습니다.
  • 중앙 modal 스타일과 우측 panel 스타일을 추가했습니다.
  • z-index.css를 추가해 surface overlay/content와 form floating z-index를 토큰으로 관리하도록 정리했습니다.

스크린샷 (선택)

추가한 라이브러리 (선택)

없음

💬리뷰 요구사항(선택)

Summary by CodeRabbit

  • New Features
    • 모달/패널 형태의 새로운 Surface 컴포넌트(Trigger/Overlay/Portal/헤더·본문·푸터/닫기)가 추가되었습니다.
    • ESC, 바깥 클릭, 포커스 트랩·복원, 스크롤 잠금, 제어형 상태 훅을 제공하며 중첩 화면 동작도 포함됩니다.
  • Bug Fixes
    • 포커스 이동·복원, 외부 클릭, 중첩 우선순위가 더 안정적으로 개선되었습니다.
    • AccordionTextAreaField의 상태 동기화가 개선되었습니다.
  • Chores
    • 관련 단위 테스트와 z-index 스타일 레이어가 추가되었습니다.

@Lseojeong Lseojeong self-assigned this Jun 29, 2026
@Lseojeong Lseojeong added the ✨Feature 새로운 기능 구현 label Jun 29, 2026
@Lseojeong Lseojeong linked an issue Jun 29, 2026 that may be closed by this pull request
39 tasks
@vercel

vercel Bot commented Jun 29, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
rewrite Ready Ready Preview, Comment Jun 29, 2026 10:42am

@github-actions

Copy link
Copy Markdown

🧪 테스트 결과

항목 결과
✅ Jest 테스트 success

@github-actions

Copy link
Copy Markdown

🚦 CI 검증 결과

항목 결과
🧠 TypeScript 타입 체크 success
🧹 ESLint 검사 success
🎨 Prettier 검사 success
🏗️ Build 검증 success

@coderabbitai

coderabbitai Bot commented Jun 29, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

공통 훅과 Surface 컴파운드 컴포넌트가 추가되었습니다. Surface는 modal/panel 변형, 닫기 정책, 포커스·스크롤 상호작용, asChild 지원 서브컴포넌트, 스타일 토큰, 스토리북 예시를 포함합니다. AccordionTextAreaField는 새 상태 훅을 사용하도록 변경되었습니다.

Changes

공통 훅 및 Surface

Layer / File(s) Summary
상태 훅과 배럴 export
src/shared/hooks/useControllableState.ts, src/shared/hooks/useControllableState.test.ts, src/shared/hooks/useEscapeKey.ts, src/shared/hooks/useFocusRestore.ts, src/shared/hooks/useFocusTrap.ts, src/shared/hooks/useFocusTrap.test.tsx, src/shared/hooks/useOutsideClick.ts, src/shared/hooks/useScrollLock.ts, src/shared/hooks/index.ts
controlled/uncontrolled 상태 훅과 Escape, 포커스, 외부 클릭, 스크롤 잠금 훅이 추가되고, 훅 배럴에서 재export됩니다.
Surface 타입, 컨텍스트, 루트
src/shared/ui/surface/Surface.types.ts, src/shared/ui/surface/SurfaceContext.ts, src/shared/ui/surface/hooks/useSurfaceStack.ts, src/shared/ui/surface/Surface.tsx, src/shared/ui/surface/index.ts, src/shared/styles/base/z-index.css, src/shared/styles/globals.css, src/shared/ui/textarea/TextAreaField.tsx, src/shared/ui/accordion/Accordion.tsx, src/shared/ui/accordion/AccordionLabel.tsx
Surface 공개 타입, 컨텍스트, 최상단 스택 판별, 루트 상태 관리와 닫기 정책, z-index 토큰, 기존 컴포넌트 적용이 반영됩니다.
Surface 서브컴포넌트
src/shared/ui/surface/SurfaceTrigger.tsx, src/shared/ui/surface/SurfaceClose.tsx, src/shared/ui/surface/SurfacePortal.tsx, src/shared/ui/surface/SurfaceOverlay.tsx, src/shared/ui/surface/SurfaceSections.tsx, src/shared/ui/surface/SurfaceContent.tsx, src/shared/ui/surface/hooks/useSurfaceInteractions.ts, src/shared/ui/utils/Slot.ts
Trigger, Close, Portal, Overlay, Header, Body, Footer, Content가 각각 asChild, 포탈, 오버레이, 섹션, 접근성, 포커스/스크롤 동작으로 추가됩니다.
스토리북 예시
src/shared/ui/surface/Surface.stories.tsx
Modal, Panel, PreventCloseWhenDirty 예시와 Storybook 메타 export가 추가됩니다.

Possibly related PRs

  • Rewrite-Team/Rewrite-FE#48: Accordion의 controlled/uncontrolled 상태를 useControllableState로 바꾸는 흐름이 이 변경과 직접 연결됩니다.
  • Rewrite-Team/Rewrite-FE#44: TextAreaField의 글자 수 상태와 표시 로직이 같은 TextArea 계열 구현과 연결됩니다.

🎯 4 (Complex) | ⏱️ ~60 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목이 Surface 공통 컴포넌트 구현이라는 핵심 변경을 정확히 요약합니다.
Linked Issues check ✅ Passed Surface 컴파운드 패턴, 상호작용, 접근성, 스타일, 문서화 요구가 요약상 모두 구현되어 있습니다.
Out of Scope Changes check ✅ Passed 표면적으로는 Surface 기능과 이를 뒷받침하는 훅·테스트·스타일만 보여 명백한 범위 외 변경은 없습니다.
Docstring Coverage ✅ Passed Docstring coverage is 84.62% which is sufficient. The required threshold is 80.00%.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/#49

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 11

🤖 Prompt for all review comments with 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.

Inline comments:
In `@src/shared/hooks/useControllableState.test.ts`:
- Around line 53-68: The current functional update test in useControllableState
only verifies a single updater call, so it misses stale-state regressions when
updates are chained in the same act. Extend the supports functional updates case
to call result.current[1] twice with updater functions starting from
defaultValue and assert the final value is 3, and keep the onChange expectation
aligned with the latest resolved value from useControllableState.

In `@src/shared/hooks/useControllableState.ts`:
- Around line 41-58: The functional updater in useControllableState is
calculated from the render-time currentValue, so repeated setValue(updater)
calls can read the same stale state. Update the setValue callback in
useControllableState to apply updater calls against the latest uncontrolled
state, while still honoring the controlled path and onChange behavior, and keep
the Object.is guard intact. Add a regression test covering back-to-back
functional updates to verify consecutive calls accumulate correctly.

In `@src/shared/hooks/useFocusTrap.ts`:
- Around line 53-78: The focus trap logic in handleKeyDown only wraps when
document.activeElement is the first or last focusable element, so focus can
escape when it is already outside the container or on the container itself.
Update useFocusTrap to first check whether the current active element is inside
the container, and if not, intercept Tab to move focus back to firstElement or
lastElement depending on event.shiftKey before allowing navigation to continue.

In `@src/shared/ui/surface/Surface.stories.tsx`:
- Around line 132-193: The Storybook controls defined in the Surface.stories
meta are not wired into the previews because the story render functions ignore
args, so changes in the controls panel do not affect the rendered UI. Update the
Surface story entries such as ModalVariant, PanelVariant, and
PreventCloseWhenDirty to pass the story args through to the example components
or render Surface directly with those args; if these are meant to be static
demos, remove the unused controls from meta.args and argTypes instead.
- Around line 84-125: The PreventCloseWhenDirtyExample in Surface.stories.tsx
keeps the `message` state after the form becomes clean or the Surface is closed,
so the dirty warning can linger incorrectly. Update the example so `message` is
reset whenever `isDirty` becomes false and/or when the Surface closes, using the
existing `useState`, `isDirty`, `onClosePrevented`, and `Surface` example setup
to keep the warning aligned with the current dirty state.

In `@src/shared/ui/surface/Surface.types.ts`:
- Around line 123-127: SurfaceCloseButtonProps currently allows icon-only
Surface.Close usage without guaranteeing an accessible name because aria-label
is optional. Update the Surface.Close contract so the icon-only path cannot omit
an accessible label: either make aria-label required on SurfaceCloseButtonProps
or split the prop types in Surface.types.ts and enforce aria-label as mandatory
for the icon-only branch. Use the SurfaceCloseButtonProps symbol to locate and
adjust the public API accordingly.

In `@src/shared/ui/surface/SurfaceClose.tsx`:
- Around line 62-80: The click/close contract in SurfaceClose’s
createHandleClick is inconsistent with the Surface.Close documentation because
actions.close('close-button') is skipped when either childOnClick or onClick
calls preventDefault. Decide whether vetoing close is intended: if yes, update
the Surface.Close JSDoc to say the close request can be prevented; if no, adjust
createHandleClick so actions.close('close-button') is invoked outside the
preventDefault checks while still preserving the callbacks on
SurfaceCloseChildProps.

In `@src/shared/ui/surface/SurfacePortal.tsx`:
- Around line 21-29: `SurfacePortal` is treating an explicit `container={null}`
the same as “no container” and falling back to `document.body`. Update the
`portalContainer` selection logic in `SurfacePortal` so only an `undefined`
container uses the body fallback, while an explicit `null` remains `null` and
prevents rendering. Keep the existing `useSurfaceContext`, `SurfacePortalProps`,
and `createPortal` flow intact.

In `@src/shared/ui/surface/SurfaceSections.tsx`:
- Around line 25-31: The SurfaceSections layout currently renders these clearly
labeled header/footer areas with a generic div, so update the shared section
component(s) to use semantic header and footer elements instead of div while
preserving the existing props, ref forwarding, and className merging via cn. Use
the relevant SurfaceSections/section wrapper components to map the header-like
and footer-like slots to the proper HTML elements so the document structure and
accessibility tree reflect their meaning.

In `@src/shared/ui/surface/SurfaceStack.ts`:
- Around line 81-88: Surface registration in useSurfaceStackItem currently
happens in useEffect, which is too late for stacking before the first paint.
Move the registration logic to useLayoutEffect so registerSurface runs
synchronously for the new Surface as soon as it mounts, preventing the previous
Surface from handling ESC or outside clicks during overlap transitions.

In `@src/shared/ui/surface/SurfaceTrigger.tsx`:
- Around line 21-32: `asChild` currently accepts `React.Fragment` because
`isValidElement()` passes it, which prevents `cloneElement()` props like
`className`, `onClick`, and `aria-*` from reaching a real interactive node.
Update `getSingleTriggerChild` in `SurfaceTrigger` to explicitly reject
`Fragment` and only allow DOM-like element children before cloning, and apply
the same restriction to the matching child-validation logic in `SurfaceClose` so
both components enforce the same safe `asChild` behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 7e1188be-d19d-46b5-9ecb-1e3353714d30

📥 Commits

Reviewing files that changed from the base of the PR and between 23a294d and 5dd92e7.

📒 Files selected for processing (23)
  • src/shared/hooks/index.ts
  • src/shared/hooks/useControllableState.test.ts
  • src/shared/hooks/useControllableState.ts
  • src/shared/hooks/useEscapeKey.ts
  • src/shared/hooks/useFocusRestore.ts
  • src/shared/hooks/useFocusTrap.ts
  • src/shared/hooks/useOutsideClick.ts
  • src/shared/hooks/useScrollLock.ts
  • src/shared/styles/base/z-index.css
  • src/shared/styles/globals.css
  • src/shared/ui/surface/Surface.stories.tsx
  • src/shared/ui/surface/Surface.tsx
  • src/shared/ui/surface/Surface.types.ts
  • src/shared/ui/surface/SurfaceClose.tsx
  • src/shared/ui/surface/SurfaceContent.tsx
  • src/shared/ui/surface/SurfaceContext.ts
  • src/shared/ui/surface/SurfaceOverlay.tsx
  • src/shared/ui/surface/SurfacePortal.tsx
  • src/shared/ui/surface/SurfaceSections.tsx
  • src/shared/ui/surface/SurfaceStack.ts
  • src/shared/ui/surface/SurfaceTrigger.tsx
  • src/shared/ui/surface/index.ts
  • src/shared/ui/textarea/TextAreaField.tsx

Comment thread src/shared/hooks/useControllableState.test.ts
Comment thread src/shared/hooks/useControllableState.ts
Comment thread src/shared/hooks/useFocusTrap.ts
Comment thread src/shared/ui/surface/Surface.stories.tsx Outdated
Comment thread src/shared/ui/surface/Surface.stories.tsx
Comment thread src/shared/ui/surface/SurfaceClose.tsx
Comment thread src/shared/ui/surface/SurfacePortal.tsx
Comment thread src/shared/ui/surface/SurfaceSections.tsx Outdated
Comment thread src/shared/ui/surface/SurfaceStack.ts Outdated
Comment thread src/shared/ui/surface/SurfaceTrigger.tsx Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/shared/ui/accordion/Accordion.tsx (1)

122-128: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

토글은 함수형 업데이트로 바꾸는 게 안전합니다.

setIsOpen(!isOpen)는 같은 렌더 안에서 연속 토글이 들어오면 같은 값을 기준으로 계산될 수 있습니다. useControllableState가 updater를 받으니 이 경로를 쓰는 편이 맞습니다.

수정 예시
-        setIsOpen(!isOpen);
+        setIsOpen((previousOpen) => !previousOpen);
🤖 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 `@src/shared/ui/accordion/Accordion.tsx` around lines 122 - 128, The Accordion
toggleOpen handler currently uses a stale state read via setIsOpen(!isOpen),
which can break when multiple toggles happen in the same render. Update the
toggle logic in Accordion.tsx’s toggleOpen implementation to use the functional
updater form supported by useControllableState so the next state is derived from
the previous value. Keep the existing disabled and collapsible guard conditions,
but change the state update path to compute from the latest open state instead
of the captured isOpen value.
🧹 Nitpick comments (1)
src/shared/hooks/useFocusTrap.test.tsx (1)

1-12: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

테스트 픽스처에서는 createRef() 대신 useRef()를 쓰세요.

함수 컴포넌트에서 createRef()는 렌더마다 새 ref 객체를 만들어서, 픽스처가 rerender되면 포커스 트랩 대상이 흔들릴 수 있습니다. 여기서는 고정 ref가 맞습니다.

수정 예시
-import { createRef } from 'react';
+import { useRef } from 'react';
@@
 function FocusTrapFixture() {
-  const containerRef = createRef<HTMLDivElement>();
+  const containerRef = useRef<HTMLDivElement>(null);
🤖 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 `@src/shared/hooks/useFocusTrap.test.tsx` around lines 1 - 12, The test fixture
in FocusTrapFixture is using createRef() inside a function component, which
recreates the ref on every render and can make the focus trap target unstable
across rerenders. Update the fixture to use useRef() instead, keeping the same
ref instance for useFocusTrap and any assertions in useFocusTrap.test.tsx.

Source: Linters/SAST tools

🤖 Prompt for all review comments with 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.

Outside diff comments:
In `@src/shared/ui/accordion/Accordion.tsx`:
- Around line 122-128: The Accordion toggleOpen handler currently uses a stale
state read via setIsOpen(!isOpen), which can break when multiple toggles happen
in the same render. Update the toggle logic in Accordion.tsx’s toggleOpen
implementation to use the functional updater form supported by
useControllableState so the next state is derived from the previous value. Keep
the existing disabled and collapsible guard conditions, but change the state
update path to compute from the latest open state instead of the captured isOpen
value.

---

Nitpick comments:
In `@src/shared/hooks/useFocusTrap.test.tsx`:
- Around line 1-12: The test fixture in FocusTrapFixture is using createRef()
inside a function component, which recreates the ref on every render and can
make the focus trap target unstable across rerenders. Update the fixture to use
useRef() instead, keeping the same ref instance for useFocusTrap and any
assertions in useFocusTrap.test.tsx.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 36edab16-e6e8-43e1-b736-135475082a51

📥 Commits

Reviewing files that changed from the base of the PR and between 5dd92e7 and 90e8e7b.

📒 Files selected for processing (14)
  • src/shared/hooks/useControllableState.test.ts
  • src/shared/hooks/useControllableState.ts
  • src/shared/hooks/useFocusTrap.test.tsx
  • src/shared/hooks/useFocusTrap.ts
  • src/shared/ui/accordion/Accordion.tsx
  • src/shared/ui/accordion/AccordionLabel.tsx
  • src/shared/ui/surface/Surface.stories.tsx
  • src/shared/ui/surface/Surface.tsx
  • src/shared/ui/surface/Surface.types.ts
  • src/shared/ui/surface/SurfaceClose.tsx
  • src/shared/ui/surface/SurfaceContent.tsx
  • src/shared/ui/surface/SurfaceTrigger.tsx
  • src/shared/ui/surface/hooks/useSurfaceStack.ts
  • src/shared/ui/textarea/TextAreaField.tsx
🚧 Files skipped from review as they are similar to previous changes (9)
  • src/shared/ui/textarea/TextAreaField.tsx
  • src/shared/hooks/useFocusTrap.ts
  • src/shared/hooks/useControllableState.ts
  • src/shared/ui/surface/Surface.tsx
  • src/shared/ui/surface/SurfaceContent.tsx
  • src/shared/ui/surface/SurfaceClose.tsx
  • src/shared/ui/surface/SurfaceTrigger.tsx
  • src/shared/ui/surface/Surface.stories.tsx
  • src/shared/ui/surface/Surface.types.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with 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.

Inline comments:
In `@src/shared/ui/surface/SurfaceClose.tsx`:
- Around line 58-65: The `SurfaceClose` `asChild` path is dropping most of the
close-button props, so the slot branch should preserve the same API as the
default button branch. Update the `SurfaceClose` component to pass through the
remaining props when calling `cloneSlot`, using the same prop set handled for
the native `<button>` path, while still merging `aria-label`, `className`, and
`onClick` via `createHandleClick` and `getSingleSlotChild`.

In `@src/shared/ui/surface/SurfaceTrigger.tsx`:
- Around line 74-81: The asChild branch in SurfaceTrigger is dropping any
remaining props passed to Surface.Trigger, so it should forward the full trigger
props set instead of only className, onClick, and auto aria attributes. Update
the SurfaceTrigger asChild path to merge and pass through the same remaining
props used by the non-asChild button path, ensuring values like disabled, id,
data-*, and explicit type are preserved when calling cloneSlot and
createHandleClick.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 526d48d5-1221-4969-9f2f-6524c257dbe2

📥 Commits

Reviewing files that changed from the base of the PR and between d31353a and 5ea98a8.

📒 Files selected for processing (10)
  • src/shared/ui/accordion/AccordionLabel.tsx
  • src/shared/ui/surface/Surface.stories.tsx
  • src/shared/ui/surface/Surface.tsx
  • src/shared/ui/surface/Surface.types.ts
  • src/shared/ui/surface/SurfaceClose.tsx
  • src/shared/ui/surface/SurfaceContent.tsx
  • src/shared/ui/surface/SurfaceSections.tsx
  • src/shared/ui/surface/SurfaceTrigger.tsx
  • src/shared/ui/surface/hooks/useSurfaceInteractions.ts
  • src/shared/ui/utils/Slot.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/shared/ui/surface/Surface.types.ts
  • src/shared/ui/surface/Surface.tsx
  • src/shared/ui/surface/Surface.stories.tsx

Comment thread src/shared/ui/surface/SurfaceClose.tsx
Comment thread src/shared/ui/surface/SurfaceTrigger.tsx
@Lseojeong Lseojeong merged commit f883ac2 into dev Jun 29, 2026
5 checks passed
@Lseojeong Lseojeong deleted the feat/#49 branch June 29, 2026 10:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✨Feature 새로운 기능 구현

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[✨ Feature] 모달 Surface 공통 컴포넌트 구현

1 participant