Skip to content

Commit eb23dab

Browse files
committed
Refactor component API
1 parent 7a46b2d commit eb23dab

3 files changed

Lines changed: 42 additions & 81 deletions

File tree

src/components/ui/sidebar.tsx

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ import { Input } from './input'
1010
import { Separator } from './separator'
1111
import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle } from './sheet'
1212
import { Skeleton } from './skeleton'
13-
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from './tooltip'
13+
import { Tooltip } from './tooltip'
14+
import { TooltipProvider, TooltipContent } from '@radix-ui/react-tooltip'
1415

1516
const SIDEBAR_COOKIE_NAME = 'sidebar_state'
1617
const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7
@@ -480,7 +481,6 @@ function SidebarMenuButton({
480481
tooltip?: string | React.ComponentProps<typeof TooltipContent>
481482
} & VariantProps<typeof sidebarMenuButtonVariants>) {
482483
const Comp = asChild ? Slot : 'button'
483-
const { isMobile, state } = useSidebar()
484484

485485
const button = (
486486
<Comp
@@ -504,9 +504,8 @@ function SidebarMenuButton({
504504
}
505505

506506
return (
507-
<Tooltip>
508-
<TooltipTrigger asChild>{button}</TooltipTrigger>
509-
<TooltipContent side="right" align="center" hidden={state !== 'collapsed' || isMobile} {...tooltip} />
507+
<Tooltip side="right" title={tooltip.children}>
508+
{button}
510509
</Tooltip>
511510
)
512511
}

src/components/ui/tooltip.tsx

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,31 +3,41 @@ import * as TooltipPrimitive from '@radix-ui/react-tooltip'
33

44
import { cn } from '../../lib/utils'
55

6-
function TooltipProvider({
6+
const TooltipProvider = ({
77
delayDuration = 0,
88
...props
9-
}: React.ComponentProps<typeof TooltipPrimitive.Provider>) {
9+
}: React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Provider>) => {
1010
return <TooltipPrimitive.Provider data-slot="tooltip-provider" delayDuration={delayDuration} {...props} />
1111
}
1212

13-
function Tooltip({ ...props }: React.ComponentProps<typeof TooltipPrimitive.Root>) {
13+
type TooltipProps = React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Root> & {
14+
title: React.ReactNode
15+
side?: 'top' | 'right' | 'bottom' | 'left'
16+
children: React.ReactNode
17+
}
18+
19+
const Tooltip: React.FC<TooltipProps> = ({ title, side = 'top', children, ...props }) => {
1420
return (
1521
<TooltipProvider>
16-
<TooltipPrimitive.Root data-slot="tooltip" {...props} />
22+
<TooltipPrimitive.Root data-slot="tooltip" {...props}>
23+
<TooltipTrigger asChild>{children}</TooltipTrigger>
24+
<TooltipContent side={side}>{title}</TooltipContent>
25+
</TooltipPrimitive.Root>
1726
</TooltipProvider>
1827
)
1928
}
2029

21-
function TooltipTrigger({ ...props }: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {
22-
return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />
23-
}
30+
const TooltipTrigger = React.forwardRef<
31+
React.ElementRef<typeof TooltipPrimitive.Trigger>,
32+
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Trigger>
33+
>(({ asChild = true, ...props }, ref) => {
34+
return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" asChild={asChild} {...props} ref={ref} />
35+
})
2436

25-
function TooltipContent({
26-
className,
27-
sideOffset = 0,
28-
children,
29-
...props
30-
}: React.ComponentProps<typeof TooltipPrimitive.Content>) {
37+
const TooltipContent = React.forwardRef<
38+
React.ElementRef<typeof TooltipPrimitive.Content>,
39+
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
40+
>(({ className, sideOffset = 0, children, ...props }, ref) => {
3141
return (
3242
<TooltipPrimitive.Portal>
3343
<TooltipPrimitive.Content
@@ -37,13 +47,14 @@ function TooltipContent({
3747
'bg-primary text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-md px-3 py-1.5 text-xs text-balance',
3848
className
3949
)}
50+
ref={ref}
4051
{...props}
4152
>
4253
{children}
4354
<TooltipPrimitive.Arrow className="bg-primary fill-primary z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px]" />
4455
</TooltipPrimitive.Content>
4556
</TooltipPrimitive.Portal>
4657
)
47-
}
58+
})
4859

49-
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }
60+
export { Tooltip }
Lines changed: 12 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { Meta, StoryObj } from '@storybook/react-vite'
2-
import { Tooltip, TooltipContent, TooltipTrigger } from '../../components/ui/tooltip.tsx'
2+
import { Tooltip } from '../../components/ui/tooltip.tsx'
33
import { Button } from '../../components/ui/button.tsx'
44
import { expect, within } from 'storybook/test'
55

@@ -21,6 +21,15 @@ const meta: Meta<typeof Tooltip> = {
2121
},
2222
tags: ['autodocs'],
2323
argTypes: {
24+
title: {
25+
control: 'text',
26+
description: 'The content of the tooltip',
27+
},
28+
side: {
29+
control: { type: 'radio' },
30+
options: ['top', 'right', 'bottom', 'left'],
31+
description: 'The preferred side of the trigger to render against',
32+
},
2433
defaultOpen: {
2534
control: 'boolean',
2635
},
@@ -38,70 +47,12 @@ type Story = StoryObj<typeof meta>
3847

3948
export const Default: Story = {
4049
args: {
41-
children: (
42-
<>
43-
<TooltipTrigger>
44-
<Button variant="outline">Hover me</Button>
45-
</TooltipTrigger>
46-
<TooltipContent>
47-
<p>Tooltip content</p>
48-
</TooltipContent>
49-
</>
50-
),
50+
title: 'Tooltip content',
51+
children: <Button variant="outline">Hover me</Button>,
5152
},
5253
play: async ({ canvasElement }) => {
5354
const canvas = within(canvasElement)
5455
const button = canvas.getAllByRole('button', { name: 'Hover me' })[0]
5556
await expect(button).toBeInTheDocument()
5657
},
5758
}
58-
59-
export const Variants: Story = {
60-
args: {
61-
children: (
62-
<div className="flex flex-col items-center gap-4">
63-
<div className="flex justify-center gap-4">
64-
<Tooltip>
65-
<TooltipTrigger>
66-
<Button variant="outline">Top</Button>
67-
</TooltipTrigger>
68-
<TooltipContent side="top">
69-
<p>Tooltip on top</p>
70-
</TooltipContent>
71-
</Tooltip>
72-
</div>
73-
74-
<div className="flex justify-between gap-16">
75-
<Tooltip>
76-
<TooltipTrigger>
77-
<Button variant="outline">Left</Button>
78-
</TooltipTrigger>
79-
<TooltipContent side="left">
80-
<p>Tooltip on left</p>
81-
</TooltipContent>
82-
</Tooltip>
83-
84-
<Tooltip>
85-
<TooltipTrigger>
86-
<Button variant="outline">Right</Button>
87-
</TooltipTrigger>
88-
<TooltipContent side="right">
89-
<p>Tooltip on right</p>
90-
</TooltipContent>
91-
</Tooltip>
92-
</div>
93-
94-
<div className="flex justify-center gap-4">
95-
<Tooltip>
96-
<TooltipTrigger>
97-
<Button variant="outline">Bottom</Button>
98-
</TooltipTrigger>
99-
<TooltipContent side="bottom">
100-
<p>Tooltip on bottom</p>
101-
</TooltipContent>
102-
</Tooltip>
103-
</div>
104-
</div>
105-
),
106-
},
107-
}

0 commit comments

Comments
 (0)