[React] Tooltip コンポーネントの追加 - #954
Conversation
🦋 Changeset detectedLatest commit: 2d77b08 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits. |
✅ Deploy Preview for design-system-abukuma-css ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
✅ Deploy Preview for design-system-abukuma-react ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
Original prompt from yusei.kaida@giftee.co |
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
| style={{ | ||
| display: 'flex', | ||
| gap: '80px', | ||
| justifyContent: 'center', | ||
| alignItems: 'center', | ||
| padding: '80px', | ||
| }} |
There was a problem hiding this comment.
[niiiits]
この辺全部 abukuma css の util class で書いていただけると 🙏
There was a problem hiding this comment.
インラインスタイルを abukuma css の utility class (ab-flex ab-justify-center ab-items-center ab-gap-30 ab-p-30) に置き換えました。
| {children} | ||
| <span className="ab-Tooltip-description">{content}</span> |
There was a problem hiding this comment.
[imo]
Tooltip の trigger と開いた時の中身は、どちらも好きな React 要素を渡したいのだと思います。
しかし、現状は trigger が children で、中身は props の react node になっていて、若干違和感があります。
そのため、以下のような compound component にするのはいかがでしょうか?
<Tooltip>
<Tooltip.Trigger>
<button>ホバーしてください</button>
</Tooltip.Trigger>
<Tooltip.Content>
<strong>カスタム</strong>なコンテンツ
</Tooltip.Content>
</Tooltip>こうすることで、どちらも children として同じ扱いをすることが可能です。
There was a problem hiding this comment.
良さそうです!compound componentにするで問題ないです!
Co-Authored-By: yusei.kaida@giftee.co <yusei.kaida@giftee.co>
Co-Authored-By: yusei.kaida@giftee.co <yusei.kaida@giftee.co>
…irection.stories.tsx Co-Authored-By: yusei.kaida@giftee.co <yusei.kaida@giftee.co>
Co-Authored-By: yusei.kaida@giftee.co <yusei.kaida@giftee.co>
592558f to
b2434be
Compare
| export type TooltipRootProps = ComponentPropsWithoutRef<'span'> & { | ||
| /** | ||
| * ツールチップの表示位置 | ||
| * @default 'top' | ||
| */ | ||
| position?: 'top' | 'right' | 'bottom' | 'left'; | ||
| }; |
|
❌ Cannot revive Devin session - the session is too old. Please start a new session instead. |
| export const TooltipTrigger = forwardRef< | ||
| ElementRef<'span'>, | ||
| TooltipTriggerProps | ||
| >(({ children, ...rest }, forwardedRef) => ( | ||
| <span ref={forwardedRef} {...rest}> | ||
| {children} | ||
| </span> | ||
| )); |
There was a problem hiding this comment.
[ask]
TooltipTrigger を別途コンポーネント化している理由とかってあったりしますか?
class を別途付与しているわけではないので、Trigger はなしにして以下でも良いかなと思いました。
<Tooltip.Root position="top">
ホバーしてください
<Tooltip.Content>ツールチップ</Tooltip.Content>
</Tooltip.Root>There was a problem hiding this comment.
他のデザインシステムを眺めてて、トリガーがコンポーネントになってることがちらほらあったので、コンポーネントにしてみようかなくらいでした。確かにclassの付与等がないので、なしにしてサクッと書けるようにするで良いと思いました!
prettier fix
de6ac12 to
a088890
Compare
contentIdを渡してaria-describedbyでコンテキストを共有し、ツールチップとの関係性を確立するようにした
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
🔴 Tooltip content is always invisible: CSS has opacity: 0; visibility: hidden with no rule to override when open
The .ab-Tooltip-description CSS class (packages/css/src/components/tooltip/index.scss:9-10) permanently sets opacity: 0 and visibility: hidden. The old :hover rules that overrode these (setting opacity: 1; visibility: visible) were removed in this PR. The React component adds an ab-Tooltip--open class to the root when the tooltip is open (TooltipRoot.tsx:31), but no CSS rule exists for .ab-Tooltip--open anywhere in the codebase. As a result, even when TooltipContent renders (when open is true), the <span> is in the DOM but is visually hidden by the CSS — the tooltip will never be visible to users.
How the rendering path works
- User hovers trigger →
setOpen(true)→TooltipContentrenders a<span class="ab-Tooltip-description"> - CSS always applies
opacity: 0; visibility: hiddento.ab-Tooltip-description - No CSS rule overrides this when
ab-Tooltip--openis present on the parent - Tooltip is in the DOM but invisible
(Refers to lines 9-10)
Prompt for agents
The .ab-Tooltip-description class has opacity: 0 and visibility: hidden, but the :hover rules that previously overrode these were removed. The React component adds an ab-Tooltip--open class to the root element (TooltipRoot.tsx:31) when the tooltip is open, but there is no corresponding CSS rule.
Two possible approaches:
1. Add a CSS rule that makes the tooltip visible when the open class is present on the parent. For example, add to index.scss:
&.ab-Tooltip--open .ab-Tooltip-description { opacity: 1; visibility: visible; }
This would also be the place to add transitions and the arrow pseudo-elements that were removed.
2. Alternatively, since TooltipContent already conditionally renders (returns null when not open), remove the opacity: 0 and visibility: hidden from .ab-Tooltip-description entirely, since visibility is now controlled by React state (mount/unmount) rather than CSS. This is the simpler approach but means the CSS package no longer provides any show/hide behavior for non-React consumers.
Was this helpful? React with 👍 or 👎 to provide feedback.
マウスホバーはCSSの:hover、キーボードフォーカスはReactのopen state + インラインスタイルで表示を制御する構成に変更。Contentを常にDOMに保持 することでaria-describedbyの参照先が常に有効になる。 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
WAI-ARIAのtooltipパターンに従い、TriggerにonKeyDownを注入して Escape押下時にopen stateをfalseにする。 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
spanは標準でフォーカス不可のため、tabIndex={0}を付けないと
TriggerがTriggerに注入するonFocus/onBlur/onKeyDownが発火しない。
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
autodocsはRender Propsを正しく抽出できず、Triggerが空タグで 表示されてしまうため、parameters.docs.source.codeで正しいJSXを 明示する。 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
CSSの:hoverによる表示も含めてEscapeで閉じられるよう、TooltipRootに documentレベルのkeydownリスナーをopen中だけ張る。Reactで管理する dismissed stateとインラインスタイルでCSSの:hoverを上書きするため、 CSS自体には変更を加えない。 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
import順をeslint --fixで修正し、storybookの<span tabIndex={0}>には
no-noninteractive-tabindex を抑止するコメントを付与(Render Props
パターンの柔軟性をデモする意図のため)。Index.spec.tsxの整形も
prettierで反映。
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
| <Tooltip.Root {...args}> | ||
| <Tooltip.Trigger> | ||
| {(props) => ( | ||
| // eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex |
There was a problem hiding this comment.
storybookへのエラーだったので、柔軟性を示した方が良いという判断で無視するようにしました
|
Compound Component patternで全体を作りつつ、トリガー部分はRender props patternで構成するようにしました。 |
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- ComponentPropsWithoutRef<'button'>でaria-labelやclassName等を受け取れるように - 自前のイベントハンドラとユーザー指定ハンドラを合成 - ref転送のテストを追加 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
概要
Tooltip.Root/Tooltip.Trigger/Tooltip.Content)使い方
ホバー / フォーカスで表示、Escape で非表示になります。アイコン単体をトリガーにする場合は
Tooltip.Triggerにaria-labelを渡してください。追加ファイル
packages/react/src/components/tooltip/TooltipRoot.tsx— Root(position prop、ab-Tooltip/ab-Tooltip-{position}クラス付与、表示状態の管理と Escape 制御)packages/react/src/components/tooltip/TooltipTrigger.tsx— Trigger(<button>でレンダリングし、ホバー / フォーカスによる表示制御・aria-describedby付与・Escape での dismiss を担う)packages/react/src/components/tooltip/TooltipContent.tsx— Content(ab-Tooltip-descriptionクラス・role="tooltip"付与)packages/react/src/components/tooltip/TooltipContext.tsx— サブコンポーネント間で表示状態・id を共有する Contextpackages/react/src/components/tooltip/Index.tsx— Root / Trigger / Content の re-exportpackages/react/src/components/tooltip/Index.spec.tsx— テスト(13件)packages/react/src/components/tooltip/stories/— Storybook(Base / Direction)packages/react/src/index.ts—export * as Tooltip追加.changeset/add-tooltip-component.md— minor バージョンアップProps
Tooltip.Root
position'top' | 'right' | 'bottom' | 'left''top'Root/Contentは<span>の標準属性(className,ref等)を受け取ります。Triggerは<button>の標準属性(className,aria-label,ref, 各種イベントハンドラ等)を受け取ります。typeは省略時"button"。独自のイベントハンドラを渡しても内部の表示制御と両立します。アクセシビリティ
Trigger(<button>)にaria-describedby、Contentにrole="tooltip"/idを付与し、両者を紐付け<button>によりネイティブにフォーカス・キーボード操作が可能注意事項
<a>/<button>)を入れないでください。<button>の中に置くと不正な HTML(ネスト)になり a11y 違反になります。リンクやボタン自体にツールチップを付けたいケースは、現状の固定 button 実装では非対応ですレビュー時の確認ポイント
ab-Tooltip,ab-Tooltip-{position},ab-Tooltip-description)が CSS パッケージと一致しているかTriggerを<button>固定とした設計の是非(render prop ではなく children をそのまま受け取る)aria-describedby,role="tooltip")と Escape による dismiss の挙動スクリーンショット
Storybook

ユーザ影響
Link to Devin run: https://app.devin.ai/sessions/ba09baaac4c443e786ddd9d60478750e
Requested by: @aidyak