Skip to content

Commit 3103a10

Browse files
te6-inclaude
andcommitted
feat(react): add HelpBubbleTooltip (#1635)
* docs: add ComponentExample `isolate` option * feat(react-headless): add floating and tooltip packages Extract the shared floating-positioning logic into @seed-design/react-floating and refactor popover to consume it. Add @seed-design/react-tooltip: a hover/focus tooltip headless with fixed open/close delays, delay grouping (NextFloatingDelayGroup), and an optional interactive content mode. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(react): add HelpBubbleTooltip Add the styled HelpBubbleTooltip (Root/Trigger/Positioner/Content/Arrow/Body/ Title/Description) and HelpBubbleTooltipDelayGroup. The help-bubble recipe skips its enter/exit animation while a delay group switches between tooltips (data-instant) so the swap reads as instant. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(react): add HelpBubbleTooltip documentation, examples, and stories Add the HelpBubbleTooltip docs page, examples, and Storybook story, and move the HelpBubble page into the shared (help-bubble) route group. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(stackflow-spa): add HelpBubbleTooltip example Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(changeset): include @seed-design/css patch in HelpBubbleTooltip changeset The HelpBubble recipe gained a `[data-instant]` rule that compiles into the published @seed-design/css package, so add a css patch bump. Also reword the summary to describe HelpBubbleTooltip as a separate component instead of a mode added to HelpBubble. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs * docs * docs: make variant=ghost action buttons use consistent icon styles across examples * chore: generate * feat: add snippet component `HelpBubbleTooltipTriggerPortal` * docs * fix(help-bubble-tooltip): add displayName to snippet trigger components Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(tooltip): cover content pointer-events and state data-attributes Lock the glue useTooltip owns on top of floating-ui: the default pointer-events block on content (and its keepOpenOnContentHover escape hatch), and the placement/transition-status to data-* mapping. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(help-bubble): add displayName to snippet trigger and anchor components Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 723e40b commit 3103a10

63 files changed

Lines changed: 1917 additions & 37 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.changeset/help-bubble-tooltip.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
"@seed-design/react-floating": minor
3+
"@seed-design/react-tooltip": minor
4+
"@seed-design/react": minor
5+
"@seed-design/react-popover": minor
6+
"@seed-design/css": minor
7+
---
8+
9+
Help Bubble Tooltip 컴포넌트를 추가합니다.

bun.lock

Lines changed: 43 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/components/component-example.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,19 @@ import ErrorBoundary from "./error-boundary";
77
interface ComponentExampleProps {
88
name: string;
99

10+
isolate?: boolean;
11+
1012
children?: React.ReactNode;
1113
}
1214

1315
export function ComponentExample(props: ComponentExampleProps) {
14-
const { name, children } = props;
16+
const { name, isolate, children } = props;
1517

1618
if (!children) {
1719
return (
1820
<React.Suspense fallback={null}>
1921
<div className="flex min-h-80">
20-
<ComponentPreview name={name} />
22+
<ComponentPreview name={name} isolate={isolate} />
2123
</div>
2224
</React.Suspense>
2325
);
@@ -28,7 +30,7 @@ export function ComponentExample(props: ComponentExampleProps) {
2830
<Tabs items={["미리보기", "코드"]}>
2931
<Tab value="미리보기">
3032
<div className="flex min-h-80">
31-
<ComponentPreview name={name} />
33+
<ComponentPreview name={name} isolate={isolate} />
3234
</div>
3335
</Tab>
3436
<Tab value="코드">{children}</Tab>

docs/components/component-preview.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
"use client";
22

33
import * as React from "react";
4+
import { twMerge as cn } from "tailwind-merge";
45

56
interface ComponentPreviewProps {
67
name: string;
8+
isolate?: boolean;
79
}
810

911
export function ComponentPreview(props: ComponentPreviewProps) {
10-
const { name } = props;
12+
const { name, isolate } = props;
1113

1214
const Preview = React.useMemo(() => {
1315
const Component = React.lazy(() => import(`../examples/${name}.tsx`));
@@ -22,7 +24,10 @@ export function ComponentPreview(props: ComponentPreviewProps) {
2224
return (
2325
<React.Suspense fallback={null}>
2426
<div
25-
className="not-prose example-reset w-full flex flex-col justify-center items-center"
27+
className={cn(
28+
"not-prose example-reset w-full flex flex-col justify-center items-center",
29+
isolate && "isolate",
30+
)}
2631
style={{
2732
backgroundColor: "var(--seed-color-bg-layer-default)",
2833
}}
Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
---
2+
title: Help Bubble Tooltip
3+
description: 포인터를 올리거나 포커스했을 때 보조 정보를 보여주는 툴팁 형태의 Help Bubble입니다.
4+
---
5+
6+
<ComponentExample name="react/help-bubble-tooltip/preview" isolate>
7+
```json doc-gen:file
8+
{
9+
"file": "examples/react/help-bubble-tooltip/preview.tsx",
10+
"codeblock": true
11+
}
12+
```
13+
</ComponentExample>
14+
15+
## Installation
16+
17+
```package-install
18+
npx @seed-design/cli@latest add ui:help-bubble-tooltip
19+
```
20+
21+
<ManualInstallation name="help-bubble-tooltip" />
22+
23+
## Props
24+
25+
### `HelpBubbleTooltipTrigger`
26+
27+
<react-type-table
28+
path="./registry/react/ui/help-bubble-tooltip.tsx"
29+
name="HelpBubbleTooltipTriggerProps"
30+
/>
31+
32+
### `HelpBubbleTooltipDelayGroup`
33+
34+
<react-type-table
35+
path="./registry/react/ui/help-bubble-tooltip.tsx"
36+
name="HelpBubbleTooltipDelayGroupProps"
37+
/>
38+
39+
## Examples
40+
41+
### Hover & Focus
42+
43+
`HelpBubbleTooltipTrigger``children`에 포인터를 올리거나(hover) 키보드로 포커스(focus)하면 Help Bubble Tooltip을 엽니다.
44+
45+
`children`으로는 `button` 등 포커스 가능한 요소를 넣어야 합니다.
46+
47+
Help Bubble Tooltip은 Close Button이나 내부 인터랙션 요소를 포함하지 않습니다. 클릭으로 열고 닫거나([Toggletip](https://inclusive-components.design/tooltips-toggletips/)), Close Button이 필요하다면 [Help Bubble](/react/components/help-bubble)을 사용하세요.
48+
49+
<ComponentExample name="react/help-bubble-tooltip/hover" isolate>
50+
```json doc-gen:file
51+
{
52+
"file": "examples/react/help-bubble-tooltip/hover.tsx",
53+
"codeblock": true
54+
}
55+
```
56+
</ComponentExample>
57+
58+
### Delay
59+
60+
`openDelay`로 포인터가 올라온 뒤 열리기까지, `closeDelay`로 포인터가 벗어난 뒤 닫히기까지의 시간을 조절합니다. 기본값은 각각 `200ms`, `100ms`이며, focus로 열 때는 두 delay 모두 적용되지 않습니다.
61+
62+
<ComponentExample name="react/help-bubble-tooltip/delay" isolate>
63+
```json doc-gen:file
64+
{
65+
"file": "examples/react/help-bubble-tooltip/delay.tsx",
66+
"codeblock": true
67+
}
68+
```
69+
</ComponentExample>
70+
71+
### Delay Group
72+
73+
같은 `HelpBubbleTooltipDelayGroup`에 속한 Help Bubble Trigger 하나가 열린 뒤에는, 같은 그룹의 다른 트리거로 포인터를 옮길 때 `openDelay` 없이 즉시 열리며, `HelpBubbleTooltipDelayGroup``openDelay` 또는 `closeDelay`를 지정하여 그룹 내 트리거들의 지연을 일괄적으로 조절할 수 있습니다.
74+
75+
그룹 내 `HelpBubbleTooltipTrigger``openDelay` 또는 `closeDelay`를 직접 설정하는 경우 해당 값이 그룹 delay보다 우선합니다.
76+
77+
<ComponentExample name="react/help-bubble-tooltip/delay-group" isolate>
78+
```json doc-gen:file
79+
{
80+
"file": "examples/react/help-bubble-tooltip/delay-group.tsx",
81+
"codeblock": true
82+
}
83+
```
84+
</ComponentExample>
85+
86+
### Keep Open on Content Hover
87+
88+
기본적으로 포인터를 Help Bubble Tooltip 위로 옮기면 트리거에서 벗어난 것으로 간주되어 닫힙니다. `keepOpenOnContentHover`를 주면 Help Bubble Tooltip 위로 포인터를 옮겨도 닫히지 않고, Help Bubble Tooltip 안의 텍스트를 드래그해 선택하는 등 내용과 상호작용할 수 있습니다.
89+
90+
<ComponentExample name="react/help-bubble-tooltip/keep-open-on-content-hover" isolate>
91+
```json doc-gen:file
92+
{
93+
"file": "examples/react/help-bubble-tooltip/keep-open-on-content-hover.tsx",
94+
"codeblock": true
95+
}
96+
```
97+
</ComponentExample>
98+
99+
### Placement
100+
101+
<ComponentExample name="react/help-bubble-tooltip/placement" isolate>
102+
```json doc-gen:file
103+
{
104+
"file": "examples/react/help-bubble-tooltip/placement.tsx",
105+
"codeblock": true
106+
}
107+
```
108+
</ComponentExample>
109+
110+
### Flip
111+
112+
<ComponentExample name="react/help-bubble-tooltip/flip" isolate>
113+
```json doc-gen:file
114+
{
115+
"file": "examples/react/help-bubble-tooltip/flip.tsx",
116+
"codeblock": true
117+
}
118+
```
119+
</ComponentExample>
120+
121+
### Description
122+
123+
`description`을 사용하여 `title` 아래에 설명을 추가할 수 있습니다.
124+
125+
<ComponentExample name="react/help-bubble-tooltip/description" isolate>
126+
```json doc-gen:file
127+
{
128+
"file": "examples/react/help-bubble-tooltip/description.tsx",
129+
"codeblock": true
130+
}
131+
```
132+
</ComponentExample>
133+
134+
### Title Only
135+
136+
<ComponentExample name="react/help-bubble-tooltip/title-only" isolate>
137+
```json doc-gen:file
138+
{
139+
"file": "examples/react/help-bubble-tooltip/title-only.tsx",
140+
"codeblock": true
141+
}
142+
```
143+
</ComponentExample>
144+
145+
### Setting Width Manually
146+
147+
`contentProps`에 인라인 스타일로 `maxWidth``width`를 설정하여 너비를 조절할 수 있습니다.
148+
149+
<ComponentExample name="react/help-bubble-tooltip/width" isolate>
150+
```json doc-gen:file
151+
{
152+
"file": "examples/react/help-bubble-tooltip/width.tsx",
153+
"codeblock": true
154+
}
155+
```
156+
</ComponentExample>
157+
158+
### Line Breaks
159+
160+
<ComponentExample name="react/help-bubble-tooltip/line-breaks" isolate>
161+
```json doc-gen:file
162+
{
163+
"file": "examples/react/help-bubble-tooltip/line-breaks.tsx",
164+
"codeblock": true
165+
}
166+
```
167+
</ComponentExample>
168+
169+
### `z-index` Offset
170+
171+
<ComponentExample name="react/help-bubble-tooltip/z-index-offset" isolate>
172+
```json doc-gen:file
173+
{
174+
"file": "examples/react/help-bubble-tooltip/z-index-offset.tsx",
175+
"codeblock": true
176+
}
177+
```
178+
</ComponentExample>
179+
180+
### Accessibility
181+
182+
#### Icon-only Button
183+
184+
Help Bubble Tooltip의 내용은 트리거의 설명으로서 작동하며, 트리거의 이름을 대신하는 것이 아닙니다. Help Bubble Tooltip 사용 여부와 무관하게, 텍스트 없이 아이콘만 있는 버튼에는 항상 `aria-label`을 지정하세요.
185+
186+
```tsx
187+
<HelpBubbleTooltipTrigger title="비밀번호는 영문, 숫자, 특수문자를 포함해 8자 이상이어야 해요.">
188+
{/* iconOnly ActionButton에 aria-label을 지정합니다. */}
189+
{/* [!code highlight] */}
190+
<ActionButton layout="iconOnly" aria-label="비밀번호 규칙">
191+
<Icon svg={<IconILowercaseSerifCircleFill />} />
192+
</ActionButton>
193+
</HelpBubbleTooltipTrigger>
194+
```
195+
196+
#### Disabled Triggers
197+
198+
기본적으로 `<button disabled>` 요소는 키보드로 포커스할 수 없어, 해당 요소를 트리거로 사용하는 경우 키보드를 통해 Help Bubble Tooltip이 열리지 않습니다.
199+
200+
버튼이 비활성화된 이유를 안내해야 한다면, 비활성화된 버튼에 Help Bubble Tooltip을 붙이는 것보다는 그 이유를 가까운 텍스트로 함께 표시하는 구조를 권장합니다. `disabled` 버튼에 Help Bubble Tooltip을 붙여야 한다면, 버튼을 포커스 가능한 `span`(`tabIndex={0}`)으로 감싸 `children`으로 사용하세요.
201+
202+
<ComponentExample name="react/help-bubble-tooltip/disabled" isolate>
203+
```json doc-gen:file
204+
{
205+
"file": "examples/react/help-bubble-tooltip/disabled.tsx",
206+
"codeblock": true
207+
}
208+
```
209+
</ComponentExample>

0 commit comments

Comments
 (0)