From 63fdbe5f07e86d620321378fcacde3dc203a5eff Mon Sep 17 00:00:00 2001 From: onejuneee Date: Fri, 21 Nov 2025 20:31:13 +0900 Subject: [PATCH 01/14] =?UTF-8?q?feat:=20=ED=88=B4=ED=8C=81=EC=9D=98=20?= =?UTF-8?q?=EA=B8=B0=EB=B3=B8=20=EA=B5=AC=EC=A1=B0=EB=A5=BC=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=ED=95=A9=EB=8B=88=EB=8B=A4.=20-=20=EB=82=B4=EB=B6=80?= =?UTF-8?q?=20Context=EB=A1=9C=20=EC=83=81=ED=83=9C=EA=B4=80=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jds/src/components/Tooltip/Tooltip.tsx | 77 +++++++++++++++++++ .../src/components/Tooltip/tooltip.styles.ts | 46 +++++++++++ .../src/components/Tooltip/tooltip.types.ts | 19 +++++ 3 files changed, 142 insertions(+) create mode 100644 packages/jds/src/components/Tooltip/Tooltip.tsx create mode 100644 packages/jds/src/components/Tooltip/tooltip.styles.ts create mode 100644 packages/jds/src/components/Tooltip/tooltip.types.ts diff --git a/packages/jds/src/components/Tooltip/Tooltip.tsx b/packages/jds/src/components/Tooltip/Tooltip.tsx new file mode 100644 index 000000000..8324029b6 --- /dev/null +++ b/packages/jds/src/components/Tooltip/Tooltip.tsx @@ -0,0 +1,77 @@ +import * as RadixTooltip from '@radix-ui/react-tooltip'; +import { createContext, useContext } from 'react'; + +import { StyledTooltipContent } from './tooltip.styles'; +import type { + TooltipContentProps, + TooltipProps, + TooltipSide, + TooltipTriggerProps, +} from './tooltip.types'; + +interface TooltipContextValue { + side: TooltipSide; + sideOffset: number; +} + +const TooltipContext = createContext(undefined); + +const useTooltipContext = () => { + const context = useContext(TooltipContext); + if (!context) { + throw new Error('Tooltip 하위 컴포넌트는 반드시 Tooltip 컴포넌트 내부에서 사용되어야 합니다.'); + } + return context; +}; + +const TooltipRoot = ({ + children, + side = 'top', + sideOffset = 8, + delayDuration = 0, +}: TooltipProps) => { + const contextValue: TooltipContextValue = { + side, + sideOffset, + }; + + return ( + + + {children} + + + ); +}; + +TooltipRoot.displayName = 'Tooltip.Root'; + +const TooltipTrigger = ({ children, asChild = true, ...restProps }: TooltipTriggerProps) => { + return ( + + {children} + + ); +}; + +TooltipTrigger.displayName = 'Tooltip.Trigger'; + +const TooltipContent = ({ children, ...restProps }: TooltipContentProps) => { + const { side, sideOffset } = useTooltipContext(); + + return ( + + + {children} + + + ); +}; + +TooltipContent.displayName = 'Tooltip.Content'; + +export const Tooltip = { + Root: TooltipRoot, + Trigger: TooltipTrigger, + Content: TooltipContent, +}; diff --git a/packages/jds/src/components/Tooltip/tooltip.styles.ts b/packages/jds/src/components/Tooltip/tooltip.styles.ts new file mode 100644 index 000000000..c4a431922 --- /dev/null +++ b/packages/jds/src/components/Tooltip/tooltip.styles.ts @@ -0,0 +1,46 @@ +import styled from '@emotion/styled'; +import * as RadixTooltip from '@radix-ui/react-tooltip'; +import { pxToRem, shadow } from 'utils'; + +export const StyledTooltipContent = styled(RadixTooltip.Content)(({ theme }) => ({ + backgroundColor: theme.color.semantic.fill.boldest, + color: theme.color.semantic.object.inverse.boldest, + padding: `${theme.scheme.semantic.spacing[6]} ${theme.scheme.semantic.spacing[10]}`, + borderRadius: theme.scheme.semantic.radius[8], + //NOTE : 모바일 디바이스 절대 최소치 기준(토큰 breakpoint 명으로 개선되어도 됨) + maxWidth: pxToRem(320), + overflowWrap: 'break-word', + zIndex: 9999, + ...shadow(theme, 'overlay'), + ...theme.textStyle['semantic-textStyle-body-sm-normal'], + + '&[data-state="delayed-open"]': { + animation: `tooltipFadeIn ${theme.environment.semantic.duration[200]} ${theme.environment.semantic.motion.fluent}`, + }, + + '&[data-state="instant-open"]': { + animation: `tooltipFadeIn ${theme.environment.semantic.duration[200]} ${theme.environment.semantic.motion.fluent}`, + }, + + '&[data-state="closed"]': { + animation: `tooltipFadeOut ${theme.environment.semantic.duration[200]} ${theme.environment.semantic.motion.fluent}`, + }, + + '@keyframes tooltipFadeIn': { + from: { + opacity: 0, + }, + to: { + opacity: 1, + }, + }, + + '@keyframes tooltipFadeOut': { + from: { + opacity: 1, + }, + to: { + opacity: 0, + }, + }, +})); diff --git a/packages/jds/src/components/Tooltip/tooltip.types.ts b/packages/jds/src/components/Tooltip/tooltip.types.ts new file mode 100644 index 000000000..86f7618ed --- /dev/null +++ b/packages/jds/src/components/Tooltip/tooltip.types.ts @@ -0,0 +1,19 @@ +import type * as RadixTooltip from '@radix-ui/react-tooltip'; +import type { ReactNode } from 'react'; + +export type TooltipSide = 'top' | 'right' | 'bottom' | 'left'; + +export interface TooltipProps { + children: ReactNode; + side?: TooltipSide; + sideOffset?: number; + delayDuration?: number; +} + +export interface TooltipTriggerProps extends RadixTooltip.TooltipTriggerProps { + children: ReactNode; +} + +export interface TooltipContentProps extends RadixTooltip.TooltipContentProps { + children: ReactNode; +} From 5b50dbc6e2f52909ca7bf03ccba44371bd3d5c48 Mon Sep 17 00:00:00 2001 From: onejuneee Date: Sat, 22 Nov 2025 08:48:16 +0900 Subject: [PATCH 02/14] =?UTF-8?q?feat:=20=ED=88=B4=ED=8C=81=EC=9D=98=20Con?= =?UTF-8?q?text=20=EA=B5=AC=EB=8F=85=20=EB=B0=A9=EC=8B=9D=EC=9D=84=20?= =?UTF-8?q?=EC=9D=B4=EC=9B=90=ED=99=94=20=ED=95=A9=EB=8B=88=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jds/src/components/Tooltip/Tooltip.tsx | 32 ++++++++++++------- .../src/components/Tooltip/tooltip.styles.ts | 4 +-- .../src/components/Tooltip/tooltip.types.ts | 14 +++----- 3 files changed, 27 insertions(+), 23 deletions(-) diff --git a/packages/jds/src/components/Tooltip/Tooltip.tsx b/packages/jds/src/components/Tooltip/Tooltip.tsx index 8324029b6..aadf67e72 100644 --- a/packages/jds/src/components/Tooltip/Tooltip.tsx +++ b/packages/jds/src/components/Tooltip/Tooltip.tsx @@ -1,4 +1,4 @@ -import * as RadixTooltip from '@radix-ui/react-tooltip'; +import { Tooltip as TooltipPrimitive } from 'radix-ui'; import { createContext, useContext } from 'react'; import { StyledTooltipContent } from './tooltip.styles'; @@ -12,6 +12,7 @@ import type { interface TooltipContextValue { side: TooltipSide; sideOffset: number; + collisionPadding: number; } const TooltipContext = createContext(undefined); @@ -28,19 +29,20 @@ const TooltipRoot = ({ children, side = 'top', sideOffset = 8, + collisionPadding = 0, delayDuration = 0, + ...radixProps }: TooltipProps) => { const contextValue: TooltipContextValue = { side, sideOffset, + collisionPadding, }; return ( - - - {children} - - + + {children} + ); }; @@ -48,29 +50,35 @@ TooltipRoot.displayName = 'Tooltip.Root'; const TooltipTrigger = ({ children, asChild = true, ...restProps }: TooltipTriggerProps) => { return ( - + {children} - + ); }; TooltipTrigger.displayName = 'Tooltip.Trigger'; const TooltipContent = ({ children, ...restProps }: TooltipContentProps) => { - const { side, sideOffset } = useTooltipContext(); + const { side, sideOffset, collisionPadding } = useTooltipContext(); return ( - - + + {children} - + ); }; TooltipContent.displayName = 'Tooltip.Content'; export const Tooltip = { + Provider: TooltipPrimitive.Provider, Root: TooltipRoot, Trigger: TooltipTrigger, Content: TooltipContent, diff --git a/packages/jds/src/components/Tooltip/tooltip.styles.ts b/packages/jds/src/components/Tooltip/tooltip.styles.ts index c4a431922..24b0fc060 100644 --- a/packages/jds/src/components/Tooltip/tooltip.styles.ts +++ b/packages/jds/src/components/Tooltip/tooltip.styles.ts @@ -1,8 +1,8 @@ import styled from '@emotion/styled'; -import * as RadixTooltip from '@radix-ui/react-tooltip'; +import { Tooltip as TooltipPrimitive } from 'radix-ui'; import { pxToRem, shadow } from 'utils'; -export const StyledTooltipContent = styled(RadixTooltip.Content)(({ theme }) => ({ +export const StyledTooltipContent = styled(TooltipPrimitive.Content)(({ theme }) => ({ backgroundColor: theme.color.semantic.fill.boldest, color: theme.color.semantic.object.inverse.boldest, padding: `${theme.scheme.semantic.spacing[6]} ${theme.scheme.semantic.spacing[10]}`, diff --git a/packages/jds/src/components/Tooltip/tooltip.types.ts b/packages/jds/src/components/Tooltip/tooltip.types.ts index 86f7618ed..a91626650 100644 --- a/packages/jds/src/components/Tooltip/tooltip.types.ts +++ b/packages/jds/src/components/Tooltip/tooltip.types.ts @@ -1,19 +1,15 @@ -import type * as RadixTooltip from '@radix-ui/react-tooltip'; +import type { Tooltip } from 'radix-ui'; import type { ReactNode } from 'react'; export type TooltipSide = 'top' | 'right' | 'bottom' | 'left'; -export interface TooltipProps { +export interface TooltipProps extends Omit { children: ReactNode; side?: TooltipSide; sideOffset?: number; - delayDuration?: number; + collisionPadding?: number; } -export interface TooltipTriggerProps extends RadixTooltip.TooltipTriggerProps { - children: ReactNode; -} +export type TooltipTriggerProps = Tooltip.TooltipTriggerProps; -export interface TooltipContentProps extends RadixTooltip.TooltipContentProps { - children: ReactNode; -} +export type TooltipContentProps = Tooltip.TooltipContentProps; From ef5b32420629d929c8053acb06795260141eec52 Mon Sep 17 00:00:00 2001 From: onejuneee Date: Sat, 22 Nov 2025 09:12:54 +0900 Subject: [PATCH 03/14] =?UTF-8?q?test:=20=EC=8A=A4=ED=86=A0=EB=A6=AC?= =?UTF-8?q?=EB=B6=81=EC=9D=84=20=EC=9D=B4=EC=9A=A9=ED=95=B4=20=ED=88=B4?= =?UTF-8?q?=ED=8C=81=EC=9D=98=20=EC=95=A1=EC=85=98=EC=9D=84=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=ED=95=A9=EB=8B=88=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/Tooltip/Tooltip.stories.tsx | 213 ++++++++++++++++++ 1 file changed, 213 insertions(+) create mode 100644 packages/jds/src/components/Tooltip/Tooltip.stories.tsx diff --git a/packages/jds/src/components/Tooltip/Tooltip.stories.tsx b/packages/jds/src/components/Tooltip/Tooltip.stories.tsx new file mode 100644 index 000000000..79b2ae0ea --- /dev/null +++ b/packages/jds/src/components/Tooltip/Tooltip.stories.tsx @@ -0,0 +1,213 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import { FlexColumn, FlexRow, Label } from '@storybook-utils/layout'; +import { Icon, IconButton, Input, Tooltip, BlockButton } from 'components'; +import { Label as LabelComponent } from 'components'; + +const meta = { + title: 'Components/Tooltip', + component: Tooltip.Root, + decorators: [ + Story => ( + + + + ), + ], + parameters: { + layout: 'centered', + }, + argTypes: { + side: { + control: 'select', + options: ['top', 'right', 'bottom', 'left'], + description: '툴팁 표시 위치', + table: { + defaultValue: { summary: 'top' }, + }, + }, + sideOffset: { + control: 'number', + description: '트리거 요소와의 간격 (px)', + table: { + defaultValue: { summary: '8' }, + }, + }, + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + render: args => ( + + + + + 툴팁 테스트 레이블 + + ), + args: { + side: 'top', + sideOffset: 8, + children: undefined, + }, +}; + +export const AllSides: Story = { + args: { children: undefined }, + render: () => ( + + + + + + + + + 툴팁 상단 + + + + + + + + + + 툴팁 우측 + + + + + + + + + + 툴팁 아래 + + + + + + + + + + 툴팁 좌측 + + + + + ), + parameters: { + docs: { + description: { + story: + '툴팁은 네 가지 방향(top, right, bottom, left)으로 표시할 수 있습니다. 공간이 부족하면 자동으로 다른 방향으로 전환됩니다.', + }, + }, + }, +}; + +export const LongContent: Story = { + args: { children: undefined }, + render: () => ( + + + 표시되는 요소가 길 경우 + + + 아주 아주 아주 아주 긴 요소입니다. 모바일 환경에서도 정상적인 툴팁 내용이 표시되어야하기 + 때문에 이러한 처리를 하였습니다. 최대 길이는 320px이며 이 후 자동으로 줄바꿈 됩니다. + + + ), + parameters: { + docs: { + description: { + story: '긴 텍스트는 자동으로 줄바꿈되며, 최대 너비는 320px입니다.', + }, + }, + }, +}; + +export const WithCustomOffset: Story = { + args: { children: undefined }, + render: () => ( + + + + + + 기본 오프셋 + + 트리거 요소로 부터 8px + + + + + + + + 커스텀(확장) 오프셋 + + 트리거 요소로 부터 24px + + + + ), + parameters: { + docs: { + description: { + story: 'sideOffset prop으로 트리거 요소와 툴팁 사이의 간격을 조절할 수 있습니다.', + }, + }, + }, +}; + +export const CustomTrigger: Story = { + args: { children: undefined }, + render: () => ( + + + + + + 레이블입니다. + + 레이블 + + + + + + + + + + 아이콘 버튼 툴팁 + + + + + + + + e.preventDefault()} /> + + 안녕하세요? 툴팁입니다. + + + + ), + parameters: { + docs: { + description: { + story: + '툴팁은 다양한 Interactive한 요소에 적용할 수 있습니다. 다만, forwardRef 로 래핑된 요소나 Content 자체에 Html Element를 가진 요소이어야 합니다. 버튼, 텍스트, 아이콘, 입력 필드 등 어떤 요소든 트리거로 사용할 수 있습니다.', + }, + }, + }, +}; From 7327576143b71d4ba1fe340790b63c905b0727c4 Mon Sep 17 00:00:00 2001 From: onejuneee Date: Sat, 22 Nov 2025 09:34:58 +0900 Subject: [PATCH 04/14] =?UTF-8?q?refactor:=20Icon=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=EC=97=90=20ref=EB=A5=BC=20forwardRef?= =?UTF-8?q?=EB=A1=9C=20=EB=9E=98=ED=95=91=ED=95=A9=EB=8B=88=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/jds/src/components/Icon/Icon.tsx | 26 ++++++++++++++--------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/packages/jds/src/components/Icon/Icon.tsx b/packages/jds/src/components/Icon/Icon.tsx index 81a51cfd6..5cd27a53c 100644 --- a/packages/jds/src/components/Icon/Icon.tsx +++ b/packages/jds/src/components/Icon/Icon.tsx @@ -1,17 +1,23 @@ +import { forwardRef } from 'react'; + import { StyledIconWrapper } from './Icon.styles'; import type { IconProps } from './Icon.types'; import { iconMap, sizeMap } from './IconMap'; -export const Icon = ({ name, size = 'md', color = 'currentColor', ...props }: IconProps) => { - const IconComponent = iconMap[name]; +export const Icon = forwardRef( + ({ name, size = 'md', color = 'currentColor', ...props }, ref) => { + const IconComponent = iconMap[name]; + + if (!IconComponent) return null; - if (!IconComponent) return null; + const pixelSize = sizeMap[size]; - const pixelSize = sizeMap[size]; + return ( + + + + ); + }, +); - return ( - - - - ); -}; +Icon.displayName = 'Icon'; From a35876887623397708ecefe366723dab351a3787 Mon Sep 17 00:00:00 2001 From: onejuneee Date: Sat, 22 Nov 2025 09:35:41 +0900 Subject: [PATCH 05/14] =?UTF-8?q?refactor:=20Label=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=EC=97=90=20display=20=EC=86=8D=EC=84=B1?= =?UTF-8?q?=EC=9D=84=20=EC=A0=9C=EA=B1=B0=ED=95=A9=EB=8B=88=EB=8B=A4.=20-?= =?UTF-8?q?=20=EC=A3=BC=EC=96=B4=EC=A7=84=20html=20element=EC=97=90=20?= =?UTF-8?q?=EB=A7=9E=EB=8A=94=20=EA=B8=B0=EB=B3=B8=EA=B0=92=EC=9D=84=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95=ED=95=98=EA=B8=B0=20=EC=9C=84=ED=95=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/jds/src/components/Label/Label.style.ts | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/packages/jds/src/components/Label/Label.style.ts b/packages/jds/src/components/Label/Label.style.ts index ebf4c70ec..a1585fbdd 100644 --- a/packages/jds/src/components/Label/Label.style.ts +++ b/packages/jds/src/components/Label/Label.style.ts @@ -2,14 +2,8 @@ import isPropValid from '@emotion/is-prop-valid'; import type { Theme } from '@emotion/react'; import styled from '@emotion/styled'; -const TEXT_ALIGN_MAPPING = { - center: 'center', - left: 'flex-start', - right: 'flex-end', -} as const; - export type LabelSize = 'lg' | 'md' | 'sm' | 'xs'; -export type LabelTextAlign = keyof typeof TEXT_ALIGN_MAPPING; +export type LabelTextAlign = 'left' | 'center' | 'right'; export type LabelWeight = 'bold' | 'normal' | 'subtle'; interface LabelStyledProps { @@ -32,12 +26,9 @@ export const LabelStyled = styled('label', { shouldForwardProp: prop => isPropValid(prop) && !prop.startsWith('$'), })(({ theme, $size, $textAlign, $weight, $color }) => { const tokenKey = getLabelTokenKey($size, $weight); - const justifyContent = TEXT_ALIGN_MAPPING[$textAlign]; return { - display: 'flex', - justifyContent, - alignItems: 'center', + textAlign: $textAlign, color: $color ?? theme.color.semantic.object.bold, cursor: 'default', ...theme.textStyle[tokenKey], From 498929bab803c2e8847214903ed5629fab418155 Mon Sep 17 00:00:00 2001 From: onejuneee Date: Sat, 22 Nov 2025 09:36:01 +0900 Subject: [PATCH 06/14] =?UTF-8?q?feat:=20=EC=8A=A4=ED=86=A0=EB=A6=AC?= =?UTF-8?q?=EB=B6=81=EC=97=90=20GlobalStyles=EB=A5=BC=20=EC=A3=BC=EC=9E=85?= =?UTF-8?q?=ED=95=A9=EB=8B=88=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/jds/.storybook/preview.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/jds/.storybook/preview.tsx b/packages/jds/.storybook/preview.tsx index 4738622aa..d841566aa 100644 --- a/packages/jds/.storybook/preview.tsx +++ b/packages/jds/.storybook/preview.tsx @@ -3,6 +3,7 @@ import { ThemeProvider } from '@emotion/react'; import { Global } from '@emotion/react'; import { globalStyles } from '../src/tokens/globalStyles'; import { theme } from '../src/tokens/theme'; +import { GlobalStyles } from '../src/style/globalStyle'; const preview: Preview = { parameters: { @@ -41,6 +42,7 @@ const preview: Preview = { return ( +
From 2f92bb8fffdd4672a5bfb2246a4c196eaafcfe80 Mon Sep 17 00:00:00 2001 From: onejuneee Date: Sat, 22 Nov 2025 09:36:18 +0900 Subject: [PATCH 07/14] =?UTF-8?q?test:=20Tooltip=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=BC=80=EC=9D=B4=EC=8A=A4=EB=A5=BC=20=EA=B5=AC?= =?UTF-8?q?=EC=B2=B4=ED=99=94=ED=95=A9=EB=8B=88=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/Tooltip/Tooltip.stories.tsx | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/packages/jds/src/components/Tooltip/Tooltip.stories.tsx b/packages/jds/src/components/Tooltip/Tooltip.stories.tsx index 79b2ae0ea..f1f333c19 100644 --- a/packages/jds/src/components/Tooltip/Tooltip.stories.tsx +++ b/packages/jds/src/components/Tooltip/Tooltip.stories.tsx @@ -171,14 +171,23 @@ export const CustomTrigger: Story = { args: { children: undefined }, render: () => ( - - +
- 레이블입니다. + 텍스트 레이블입니다. 레이블 +
+ + + + + + + + 아이콘 툴팁 + @@ -193,7 +202,7 @@ export const CustomTrigger: Story = { - + e.preventDefault()} /> From 4ed7812a26b3565eeb6b62f6a72c1e8562b6cffd Mon Sep 17 00:00:00 2001 From: onejuneee Date: Sat, 22 Nov 2025 09:36:44 +0900 Subject: [PATCH 08/14] =?UTF-8?q?feat:=20=EA=B5=AC=ED=98=84=ED=95=9C=20?= =?UTF-8?q?=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=EB=A5=BC=20export=ED=95=A9?= =?UTF-8?q?=EB=8B=88=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/jds/src/components/Tooltip/index.ts | 2 ++ packages/jds/src/components/index.ts | 1 + 2 files changed, 3 insertions(+) create mode 100644 packages/jds/src/components/Tooltip/index.ts diff --git a/packages/jds/src/components/Tooltip/index.ts b/packages/jds/src/components/Tooltip/index.ts new file mode 100644 index 000000000..f8caba773 --- /dev/null +++ b/packages/jds/src/components/Tooltip/index.ts @@ -0,0 +1,2 @@ +export * from './Tooltip'; +export type { TooltipProps, TooltipTriggerProps, TooltipContentProps } from './tooltip.types'; diff --git a/packages/jds/src/components/index.ts b/packages/jds/src/components/index.ts index 2e775d589..95abdb6fc 100644 --- a/packages/jds/src/components/index.ts +++ b/packages/jds/src/components/index.ts @@ -16,3 +16,4 @@ export * from './Image'; export * from './Title'; export * from './Uploader'; export * from './FileItem'; +export * from './Tooltip'; From e008dd1a101dfacabc61e720177a2e2733379863 Mon Sep 17 00:00:00 2001 From: onejuneee Date: Wed, 26 Nov 2025 06:43:45 +0900 Subject: [PATCH 09/14] =?UTF-8?q?feat:=20Tooltip=EC=9D=98=20Side=EB=A5=BC?= =?UTF-8?q?=20=EA=B2=B0=EC=A0=95=EC=A7=93=EB=8A=94=20props=EB=A5=BC=20?= =?UTF-8?q?=EB=B3=84=EB=8F=84=EB=A1=9C=20=EC=84=A0=EC=96=B8=ED=95=98?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EA=B3=A0=20radix=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EC=A0=9C=EA=B3=B5=ED=95=98=EB=8A=94=20=EC=9D=B8=ED=84=B0?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=8A=A4=EB=A5=BC=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=ED=95=A9=EB=8B=88=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/jds/src/components/Tooltip/tooltip.types.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/packages/jds/src/components/Tooltip/tooltip.types.ts b/packages/jds/src/components/Tooltip/tooltip.types.ts index a91626650..cc97adcc8 100644 --- a/packages/jds/src/components/Tooltip/tooltip.types.ts +++ b/packages/jds/src/components/Tooltip/tooltip.types.ts @@ -1,15 +1,12 @@ import type { Tooltip } from 'radix-ui'; import type { ReactNode } from 'react'; -export type TooltipSide = 'top' | 'right' | 'bottom' | 'left'; - export interface TooltipProps extends Omit { children: ReactNode; - side?: TooltipSide; - sideOffset?: number; - collisionPadding?: number; } export type TooltipTriggerProps = Tooltip.TooltipTriggerProps; -export type TooltipContentProps = Tooltip.TooltipContentProps; +export interface TooltipContentProps extends Tooltip.TooltipContentProps { + children?: ReactNode; +} From 34d11d1bccd1d1fadd5f0292e63d86a80ce8cb8b Mon Sep 17 00:00:00 2001 From: onejuneee Date: Wed, 26 Nov 2025 06:44:51 +0900 Subject: [PATCH 10/14] =?UTF-8?q?feat:=20Tooltip=EC=9D=B4=20=EC=9E=90?= =?UTF-8?q?=EC=B2=B4=20Context=EB=A5=BC=20=EA=B0=80=EC=A7=80=EC=A7=80=20?= =?UTF-8?q?=EC=95=8A=EA=B3=A0=20radix=EC=9D=98=20=EC=84=A4=EA=B3=84?= =?UTF-8?q?=EB=A5=BC=20=EB=94=B0=EB=A5=B4=EB=8F=84=EB=A1=9D=20=ED=95=A9?= =?UTF-8?q?=EB=8B=88=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jds/src/components/Tooltip/Tooltip.tsx | 51 ++++--------------- 1 file changed, 10 insertions(+), 41 deletions(-) diff --git a/packages/jds/src/components/Tooltip/Tooltip.tsx b/packages/jds/src/components/Tooltip/Tooltip.tsx index aadf67e72..ed2147d02 100644 --- a/packages/jds/src/components/Tooltip/Tooltip.tsx +++ b/packages/jds/src/components/Tooltip/Tooltip.tsx @@ -1,47 +1,12 @@ import { Tooltip as TooltipPrimitive } from 'radix-ui'; -import { createContext, useContext } from 'react'; import { StyledTooltipContent } from './tooltip.styles'; -import type { - TooltipContentProps, - TooltipProps, - TooltipSide, - TooltipTriggerProps, -} from './tooltip.types'; - -interface TooltipContextValue { - side: TooltipSide; - sideOffset: number; - collisionPadding: number; -} - -const TooltipContext = createContext(undefined); - -const useTooltipContext = () => { - const context = useContext(TooltipContext); - if (!context) { - throw new Error('Tooltip 하위 컴포넌트는 반드시 Tooltip 컴포넌트 내부에서 사용되어야 합니다.'); - } - return context; -}; - -const TooltipRoot = ({ - children, - side = 'top', - sideOffset = 8, - collisionPadding = 0, - delayDuration = 0, - ...radixProps -}: TooltipProps) => { - const contextValue: TooltipContextValue = { - side, - sideOffset, - collisionPadding, - }; +import type { TooltipContentProps, TooltipProps, TooltipTriggerProps } from './tooltip.types'; +const TooltipRoot = ({ children, delayDuration = 0, ...radixProps }: TooltipProps) => { return ( - {children} + {children} ); }; @@ -58,9 +23,13 @@ const TooltipTrigger = ({ children, asChild = true, ...restProps }: TooltipTrigg TooltipTrigger.displayName = 'Tooltip.Trigger'; -const TooltipContent = ({ children, ...restProps }: TooltipContentProps) => { - const { side, sideOffset, collisionPadding } = useTooltipContext(); - +const TooltipContent = ({ + children, + side = 'top', + sideOffset = 8, + collisionPadding = 0, + ...restProps +}: TooltipContentProps) => { return ( Date: Wed, 26 Nov 2025 07:44:22 +0900 Subject: [PATCH 11/14] =?UTF-8?q?feat:=20radix=EA=B0=80=20=EC=A0=9C?= =?UTF-8?q?=EA=B3=B5=ED=95=98=EB=8A=94=20avoidCollisions=20prop=EC=9D=84?= =?UTF-8?q?=20=EC=82=AC=EC=9A=A9=ED=95=98=EC=97=AC=20=ED=88=B4=ED=8C=81=20?= =?UTF-8?q?=EC=9A=94=EC=86=8C=EC=9D=98=20=EB=B0=A9=ED=96=A5=EC=84=B1?= =?UTF-8?q?=EC=9D=84=20=EA=B2=B0=EC=A0=95=ED=95=A9=EB=8B=88=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/jds/src/components/Tooltip/Tooltip.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/jds/src/components/Tooltip/Tooltip.tsx b/packages/jds/src/components/Tooltip/Tooltip.tsx index ed2147d02..e70fc57b1 100644 --- a/packages/jds/src/components/Tooltip/Tooltip.tsx +++ b/packages/jds/src/components/Tooltip/Tooltip.tsx @@ -28,6 +28,7 @@ const TooltipContent = ({ side = 'top', sideOffset = 8, collisionPadding = 0, + avoidCollisions = true, ...restProps }: TooltipContentProps) => { return ( @@ -36,6 +37,7 @@ const TooltipContent = ({ side={side} sideOffset={sideOffset} collisionPadding={collisionPadding} + avoidCollisions={avoidCollisions} {...restProps} > {children} From bdf801195d4b26ce1e59e73e65343ad78365983c Mon Sep 17 00:00:00 2001 From: onejuneee Date: Wed, 26 Nov 2025 08:16:09 +0900 Subject: [PATCH 12/14] =?UTF-8?q?test:=20=EB=B3=80=EA=B2=BD=EB=90=9C=20Too?= =?UTF-8?q?ltip=20=EA=B5=AC=EC=A1=B0=EC=97=90=20=EB=A7=9E=EC=B6=B0=20?= =?UTF-8?q?=EC=8A=A4=ED=86=A0=EB=A6=AC=EB=B6=81=EC=9D=98=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=BC=80=EC=9D=B4=EC=8A=A4=EB=A5=BC=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=ED=95=A9=EB=8B=88=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/Tooltip/Tooltip.stories.tsx | 49 +++++++++++-------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/packages/jds/src/components/Tooltip/Tooltip.stories.tsx b/packages/jds/src/components/Tooltip/Tooltip.stories.tsx index f1f333c19..e5818619c 100644 --- a/packages/jds/src/components/Tooltip/Tooltip.stories.tsx +++ b/packages/jds/src/components/Tooltip/Tooltip.stories.tsx @@ -5,7 +5,7 @@ import { Label as LabelComponent } from 'components'; const meta = { title: 'Components/Tooltip', - component: Tooltip.Root, + component: Tooltip.Content, decorators: [ Story => ( @@ -32,25 +32,32 @@ const meta = { defaultValue: { summary: '8' }, }, }, + avoidCollisions: { + control: 'boolean', + description: '뷰포트 충돌 방지 자동 위치 조정', + table: { + defaultValue: { summary: 'true' }, + }, + }, }, -} satisfies Meta; +} satisfies Meta; export default meta; type Story = StoryObj; export const Default: Story = { render: args => ( - + - 툴팁 테스트 레이블 + 툴팁 테스트 레이블 ), args: { side: 'top', sideOffset: 8, - children: undefined, + avoidCollisions: true, }, }; @@ -61,41 +68,41 @@ export const AllSides: Story = { - + - 툴팁 상단 + 툴팁 상단 - + - 툴팁 우측 + 툴팁 우측 - + - 툴팁 아래 + 툴팁 아래 - + - 툴팁 좌측 + 툴팁 좌측 @@ -105,7 +112,7 @@ export const AllSides: Story = { docs: { description: { story: - '툴팁은 네 가지 방향(top, right, bottom, left)으로 표시할 수 있습니다. 공간이 부족하면 자동으로 다른 방향으로 전환됩니다.', + '툴팁은 네 가지 방향(top, right, bottom, left)으로 표시할 수 있습니다. avoidCollisions가 활성화되어 공간이 부족하면 자동으로 다른 방향으로 전환됩니다.', }, }, }, @@ -139,21 +146,21 @@ export const WithCustomOffset: Story = { - + 기본 오프셋 - 트리거 요소로 부터 8px + 트리거 요소로 부터 8px - + 커스텀(확장) 오프셋 - 트리거 요소로 부터 24px + 트리거 요소로 부터 24px @@ -202,11 +209,13 @@ export const CustomTrigger: Story = { - + e.preventDefault()} /> - 안녕하세요? 툴팁입니다. + + 안녕하세요? 툴팁입니다. + From cb505c4038ca387fbeecf830a06f7bbc926171e9 Mon Sep 17 00:00:00 2001 From: onejuneee Date: Wed, 26 Nov 2025 08:17:23 +0900 Subject: [PATCH 13/14] =?UTF-8?q?feat:=20=ED=88=B4=ED=8C=81=EC=9D=98=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20=EA=B5=AC=ED=98=84=20=EB=B0=A9=ED=96=A5?= =?UTF-8?q?=EC=84=B1=EC=97=90=20=EB=8C=80=ED=95=9C=20Todo=EB=A5=BC=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1=ED=95=A9=EB=8B=88=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/jds/src/components/Tooltip/Tooltip.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/jds/src/components/Tooltip/Tooltip.tsx b/packages/jds/src/components/Tooltip/Tooltip.tsx index e70fc57b1..1687784d9 100644 --- a/packages/jds/src/components/Tooltip/Tooltip.tsx +++ b/packages/jds/src/components/Tooltip/Tooltip.tsx @@ -13,6 +13,7 @@ const TooltipRoot = ({ children, delayDuration = 0, ...radixProps }: TooltipProp TooltipRoot.displayName = 'Tooltip.Root'; +//Todo: avoidCollisions로 제어되고 있는 위치 감지를 디자인 에셋에서 요구하는 감지 플로우로 변경 시 내부 Context 활용 필요 가능성 const TooltipTrigger = ({ children, asChild = true, ...restProps }: TooltipTriggerProps) => { return ( From 5e1fd341e69b4d2c9576bb0988d5380574ebdb3e Mon Sep 17 00:00:00 2001 From: onejuneee Date: Wed, 26 Nov 2025 08:24:59 +0900 Subject: [PATCH 14/14] =?UTF-8?q?refactor:=20Icon=EC=9D=98=20Wrapper?= =?UTF-8?q?=EC=9D=98=20=ED=83=80=EC=9E=85=EC=9D=84=20=EC=8B=A4=EC=A0=9C=20?= =?UTF-8?q?=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=EC=9D=98=20=ED=83=80?= =?UTF-8?q?=EC=9E=85=EC=9D=84=20=EC=A7=81=EC=A0=91=20=EC=B0=B8=EC=A1=B0?= =?UTF-8?q?=ED=95=98=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD=ED=95=A9?= =?UTF-8?q?=EB=8B=88=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/jds/src/components/Icon/Icon.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/jds/src/components/Icon/Icon.tsx b/packages/jds/src/components/Icon/Icon.tsx index 5cd27a53c..40bbd1634 100644 --- a/packages/jds/src/components/Icon/Icon.tsx +++ b/packages/jds/src/components/Icon/Icon.tsx @@ -1,10 +1,10 @@ -import { forwardRef } from 'react'; +import { type ElementRef, forwardRef } from 'react'; import { StyledIconWrapper } from './Icon.styles'; import type { IconProps } from './Icon.types'; import { iconMap, sizeMap } from './IconMap'; -export const Icon = forwardRef( +export const Icon = forwardRef, IconProps>( ({ name, size = 'md', color = 'currentColor', ...props }, ref) => { const IconComponent = iconMap[name];