@moontra/moonui 4.1.0 → 5.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.global.js +9 -9
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +21 -15
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +21 -15
- package/dist/index.mjs.map +1 -1
- package/dist/lib/theme.global.js.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/use-intersection-observer.test.tsx +18 -4
- package/src/__tests__/use-local-storage.test.tsx +34 -20
- package/src/components/ui/__tests__/avatar.test.tsx +5 -7
- package/src/components/ui/__tests__/badge.test.tsx +24 -14
- package/src/components/ui/__tests__/breadcrumb.test.tsx +4 -7
- package/src/components/ui/__tests__/button.test.tsx +6 -3
- package/src/components/ui/__tests__/card.test.tsx +4 -1
- package/src/components/ui/__tests__/checkbox.test.tsx +4 -1
- package/src/components/ui/__tests__/command.test.tsx +268 -124
- package/src/components/ui/__tests__/data-table-basic.test.tsx +271 -0
- package/src/components/ui/__tests__/date-picker.test.tsx +184 -197
- package/src/components/ui/__tests__/progress.test.tsx +0 -1
- package/src/components/ui/__tests__/table.test.tsx +8 -2
- package/src/components/ui/__tests__/tabs.test.tsx +5 -3
- package/src/components/ui/__tests__/toast.test.tsx +461 -159
- package/src/components/ui/__tests__/tooltip.test.tsx +387 -264
- package/src/components/ui/badge.tsx +4 -4
- package/src/components/ui/command.tsx +11 -7
- package/src/components/ui/date-picker.tsx +12 -6
- package/src/components/ui/toast.tsx +3 -0
- package/src/styles/tokens.css +21 -6
- package/src/use-intersection-observer.tsx +9 -0
- package/src/use-local-storage.tsx +35 -1
- package/src/components/ui/__tests__/data-table.test.tsx +0 -256
- package/src/components/ui/data-table.stories.tsx +0 -511
|
@@ -1,7 +1,25 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
|
-
import { render, screen, fireEvent, waitFor } from '@testing-library/react'
|
|
2
|
+
import { render as rtlRender, screen, fireEvent, waitFor, within } from '@testing-library/react'
|
|
3
3
|
import userEvent from '@testing-library/user-event'
|
|
4
|
-
import { Tooltip } from '../tooltip'
|
|
4
|
+
import { SimpleTooltip as Tooltip, TooltipProvider } from '../tooltip'
|
|
5
|
+
|
|
6
|
+
// GERÇEK API NOTLARI (kaynak: ../tooltip.tsx — Radix `@radix-ui/react-tooltip` üzerine kurulu)
|
|
7
|
+
//
|
|
8
|
+
// 1) Testler `<Tooltip content=... >` API'sini kullanıyor; bu `SimpleTooltip`in yüzeyi.
|
|
9
|
+
// Dışa açılan `Tooltip` ise ham `TooltipPrimitive.Root` — `content` prop'u yok.
|
|
10
|
+
// 2) Radix, Tooltip'in bir `TooltipProvider` atası altında olmasını ZORUNLU kılar
|
|
11
|
+
// ("`Tooltip` must be used within `TooltipProvider`") ve SimpleTooltip kendi
|
|
12
|
+
// sağlayıcısını taşımaz. Tüm render'lar sağlayıcıyla sarılır.
|
|
13
|
+
// 3) Radix, stilli içerik kutusunun İÇİNE `role="tooltip"` taşıyan görsel-gizli bir
|
|
14
|
+
// `span` koyar (erişilebilir isim buradan okunur). Yani `getByRole('tooltip')`
|
|
15
|
+
// sr-only span'i döndürür; CVA sınıflarını taşıyan kutu onun EBEVEYNİdir
|
|
16
|
+
// (`getTooltipContent`). İçerik metni DOM'a iki kez düşer (kutu + sr-only kopya),
|
|
17
|
+
// bu yüzden tekil `getByText` sorguları "multiple elements" ile patlar.
|
|
18
|
+
// 4) Gerçek varyant/boyut listesi `tooltipVariants` CVA'sından gelir:
|
|
19
|
+
// variant: default|secondary|success|warning|destructive|info, size: sm|md|lg.
|
|
20
|
+
// `radius`, `shadow`, `primary`, `caution` gibi prop/değerler API'de YOKTUR —
|
|
21
|
+
// yuvarlaklık ve gölge temel (base) sınıflardan gelir, `className` ile ezilir.
|
|
22
|
+
// 5) `sideOffset` Radix Popper'a gider ve konumu sarmalayıcı `transform`una yansır.
|
|
5
23
|
|
|
6
24
|
// Mock next-themes
|
|
7
25
|
jest.mock('next-themes', () => ({
|
|
@@ -10,10 +28,38 @@ jest.mock('next-themes', () => ({
|
|
|
10
28
|
}),
|
|
11
29
|
}))
|
|
12
30
|
|
|
13
|
-
|
|
31
|
+
const render: typeof rtlRender = (ui: any, options?: any) =>
|
|
32
|
+
rtlRender(<TooltipProvider>{ui}</TooltipProvider>, options)
|
|
33
|
+
|
|
34
|
+
// CVA sınıflarını taşıyan görünür içerik kutusu (sr-only tooltip span'inin ebeveyni)
|
|
35
|
+
const getTooltipContent = () => screen.getByRole('tooltip').parentElement as HTMLElement
|
|
36
|
+
|
|
37
|
+
// Radix Popper'ın konumlandırma sarmalayıcısı (transform burada uygulanır)
|
|
38
|
+
const getPopperWrapper = () => getTooltipContent().parentElement as HTMLElement
|
|
39
|
+
|
|
40
|
+
// Tooltip'i hover ile açar ve açılmasını bekler
|
|
41
|
+
const openByHover = async (
|
|
42
|
+
user: ReturnType<typeof userEvent.setup>,
|
|
43
|
+
trigger: HTMLElement
|
|
44
|
+
) => {
|
|
45
|
+
await user.hover(trigger)
|
|
46
|
+
await screen.findByRole('tooltip')
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Radix varsayılan olarak "hoverable content" kullanır: tetikleyiciden çıkmak tek
|
|
50
|
+
// başına kapatmaz, imlecin tetikleyici+içerik arasındaki grace-area'nın DIŞINA
|
|
51
|
+
// gerçekten hareket etmesi beklenir (kullanıcı tooltip'in üzerine gidebilsin diye).
|
|
52
|
+
// jsdom'da tüm rect'ler 0 olduğundan bu hareketi açıkça üretmek gerekir.
|
|
53
|
+
const movePointerAway = async (
|
|
54
|
+
user: ReturnType<typeof userEvent.setup>,
|
|
55
|
+
trigger: HTMLElement
|
|
56
|
+
) => {
|
|
57
|
+
await user.unhover(trigger)
|
|
58
|
+
fireEvent.pointerMove(document.body, { clientX: 400, clientY: 400 })
|
|
59
|
+
}
|
|
14
60
|
|
|
15
61
|
describe('Tooltip Components', () => {
|
|
16
|
-
describe('
|
|
62
|
+
describe('SimpleTooltip Component', () => {
|
|
17
63
|
describe('Basic Rendering', () => {
|
|
18
64
|
it('renders correctly with default props', () => {
|
|
19
65
|
render(
|
|
@@ -21,9 +67,11 @@ describe('Tooltip Components', () => {
|
|
|
21
67
|
<button>Hover me</button>
|
|
22
68
|
</Tooltip>
|
|
23
69
|
)
|
|
24
|
-
|
|
70
|
+
|
|
25
71
|
expect(screen.getByRole('button')).toBeInTheDocument()
|
|
26
72
|
expect(screen.getByText('Hover me')).toBeInTheDocument()
|
|
73
|
+
// Kapalıyken içerik DOM'da olmamalı
|
|
74
|
+
expect(screen.queryByRole('tooltip')).not.toBeInTheDocument()
|
|
27
75
|
})
|
|
28
76
|
|
|
29
77
|
it('shows tooltip on hover', async () => {
|
|
@@ -33,14 +81,15 @@ describe('Tooltip Components', () => {
|
|
|
33
81
|
<button>Hover me</button>
|
|
34
82
|
</Tooltip>
|
|
35
83
|
)
|
|
36
|
-
|
|
84
|
+
|
|
37
85
|
const trigger = screen.getByRole('button')
|
|
38
86
|
await user.hover(trigger)
|
|
39
|
-
|
|
87
|
+
|
|
40
88
|
await waitFor(() => {
|
|
41
89
|
expect(screen.getByRole('tooltip')).toBeInTheDocument()
|
|
42
|
-
expect(screen.getByText('Test tooltip')).toBeInTheDocument()
|
|
43
90
|
})
|
|
91
|
+
expect(screen.getByRole('tooltip')).toHaveTextContent('Test tooltip')
|
|
92
|
+
expect(getTooltipContent()).toHaveTextContent('Test tooltip')
|
|
44
93
|
})
|
|
45
94
|
|
|
46
95
|
it('hides tooltip on mouse leave', async () => {
|
|
@@ -50,131 +99,181 @@ describe('Tooltip Components', () => {
|
|
|
50
99
|
<button>Hover me</button>
|
|
51
100
|
</Tooltip>
|
|
52
101
|
)
|
|
53
|
-
|
|
102
|
+
|
|
54
103
|
const trigger = screen.getByRole('button')
|
|
55
104
|
await user.hover(trigger)
|
|
56
|
-
|
|
105
|
+
|
|
57
106
|
await waitFor(() => {
|
|
58
107
|
expect(screen.getByRole('tooltip')).toBeInTheDocument()
|
|
59
108
|
})
|
|
60
|
-
|
|
61
|
-
await user
|
|
62
|
-
|
|
109
|
+
|
|
110
|
+
await movePointerAway(user, trigger)
|
|
111
|
+
|
|
63
112
|
await waitFor(() => {
|
|
64
113
|
expect(screen.queryByRole('tooltip')).not.toBeInTheDocument()
|
|
65
114
|
})
|
|
66
115
|
})
|
|
67
116
|
|
|
68
|
-
it('
|
|
117
|
+
it('links trigger to tooltip via aria-describedby', async () => {
|
|
118
|
+
const user = userEvent.setup()
|
|
69
119
|
render(
|
|
70
|
-
<Tooltip content="Test tooltip"
|
|
120
|
+
<Tooltip content="Test tooltip" delayDuration={0}>
|
|
71
121
|
<button>Hover me</button>
|
|
72
122
|
</Tooltip>
|
|
73
123
|
)
|
|
74
|
-
|
|
75
|
-
const
|
|
76
|
-
expect(
|
|
124
|
+
|
|
125
|
+
const trigger = screen.getByRole('button')
|
|
126
|
+
expect(trigger).not.toHaveAttribute('aria-describedby')
|
|
127
|
+
|
|
128
|
+
await openByHover(user, trigger)
|
|
129
|
+
|
|
130
|
+
// Açıkken tetikleyici, sr-only tooltip düğümünü tarif eder
|
|
131
|
+
expect(trigger).toHaveAttribute('aria-describedby', screen.getByRole('tooltip').id)
|
|
132
|
+
expect(trigger).toHaveAttribute('data-state', 'delayed-open')
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
it('applies custom className', async () => {
|
|
136
|
+
const user = userEvent.setup()
|
|
137
|
+
render(
|
|
138
|
+
<Tooltip content="Test tooltip" className="custom-tooltip" delayDuration={0}>
|
|
139
|
+
<button>Hover me</button>
|
|
140
|
+
</Tooltip>
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
await openByHover(user, screen.getByRole('button'))
|
|
144
|
+
|
|
145
|
+
// className TooltipContent'e iner (SimpleTooltip → TooltipContent)
|
|
146
|
+
expect(getTooltipContent()).toHaveClass('custom-tooltip')
|
|
77
147
|
})
|
|
78
148
|
|
|
79
149
|
it('has correct displayName', () => {
|
|
80
|
-
expect(Tooltip.displayName
|
|
150
|
+
expect(Tooltip.displayName).toBe('SimpleTooltip')
|
|
81
151
|
})
|
|
82
152
|
})
|
|
83
153
|
|
|
84
154
|
describe('Variants', () => {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
155
|
+
// tooltipVariants CVA'sındaki gerçek varyantlar ve ürettikleri sınıflar
|
|
156
|
+
const variants = [
|
|
157
|
+
{ variant: 'default', className: 'bg-primary', textClassName: 'text-primary-foreground' },
|
|
158
|
+
{ variant: 'secondary', className: 'bg-secondary', textClassName: 'text-secondary-foreground' },
|
|
159
|
+
{ variant: 'success', className: 'bg-green-500', textClassName: 'text-white' },
|
|
160
|
+
{ variant: 'warning', className: 'bg-yellow-500', textClassName: 'text-white' },
|
|
161
|
+
{ variant: 'destructive', className: 'bg-destructive', textClassName: 'text-destructive-foreground' },
|
|
162
|
+
{ variant: 'info', className: 'bg-blue-500', textClassName: 'text-white' },
|
|
163
|
+
] as const
|
|
164
|
+
|
|
165
|
+
variants.forEach(({ variant, className, textClassName }) => {
|
|
88
166
|
it(`renders ${variant} variant correctly`, async () => {
|
|
89
167
|
const user = userEvent.setup()
|
|
90
168
|
render(
|
|
91
|
-
<Tooltip content="Test tooltip" variant={variant}>
|
|
169
|
+
<Tooltip content="Test tooltip" variant={variant} delayDuration={0}>
|
|
92
170
|
<button>Hover me</button>
|
|
93
171
|
</Tooltip>
|
|
94
172
|
)
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
expect(tooltip).toHaveClass(`bg-${variant}`)
|
|
103
|
-
})
|
|
173
|
+
|
|
174
|
+
await openByHover(user, screen.getByRole('button'))
|
|
175
|
+
|
|
176
|
+
const tooltip = getTooltipContent()
|
|
177
|
+
expect(tooltip).toBeInTheDocument()
|
|
178
|
+
expect(tooltip).toHaveClass(className)
|
|
179
|
+
expect(tooltip).toHaveClass(textClassName)
|
|
104
180
|
})
|
|
105
181
|
})
|
|
106
182
|
})
|
|
107
183
|
|
|
108
184
|
describe('Sizes', () => {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
185
|
+
// Boyut sınıfları tooltipVariants üzerinden gelir (twMerge base padding'i ezer)
|
|
186
|
+
const sizes = [
|
|
187
|
+
{ size: 'sm', classes: ['px-2', 'py-1', 'text-xs'] },
|
|
188
|
+
{ size: 'md', classes: ['px-3', 'py-1.5', 'text-sm'] },
|
|
189
|
+
{ size: 'lg', classes: ['px-4', 'py-2', 'text-base'] },
|
|
190
|
+
] as const
|
|
191
|
+
|
|
192
|
+
sizes.forEach(({ size, classes }) => {
|
|
112
193
|
it(`renders ${size} size correctly`, async () => {
|
|
113
194
|
const user = userEvent.setup()
|
|
114
195
|
render(
|
|
115
|
-
<Tooltip content="Test tooltip" size={size}>
|
|
196
|
+
<Tooltip content="Test tooltip" size={size} delayDuration={0}>
|
|
116
197
|
<button>Hover me</button>
|
|
117
198
|
</Tooltip>
|
|
118
199
|
)
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
expect(tooltip).toBeInTheDocument()
|
|
126
|
-
// Size classes are applied through tooltipVariants
|
|
127
|
-
expect(tooltip).toHaveAttribute('class')
|
|
128
|
-
})
|
|
200
|
+
|
|
201
|
+
await openByHover(user, screen.getByRole('button'))
|
|
202
|
+
|
|
203
|
+
const tooltip = getTooltipContent()
|
|
204
|
+
expect(tooltip).toBeInTheDocument()
|
|
205
|
+
classes.forEach((cls) => expect(tooltip).toHaveClass(cls))
|
|
129
206
|
})
|
|
130
207
|
})
|
|
131
208
|
})
|
|
132
209
|
|
|
133
|
-
describe('
|
|
134
|
-
|
|
210
|
+
describe('Base Styling', () => {
|
|
211
|
+
// `radius` / `shadow` prop'ları YOK; yuvarlaklık, kenarlık ve gölge base sınıflarda sabit
|
|
212
|
+
it('applies base radius, border, shadow and layering classes', async () => {
|
|
213
|
+
const user = userEvent.setup()
|
|
214
|
+
render(
|
|
215
|
+
<Tooltip content="Test tooltip" delayDuration={0}>
|
|
216
|
+
<button>Hover me</button>
|
|
217
|
+
</Tooltip>
|
|
218
|
+
)
|
|
135
219
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
220
|
+
await openByHover(user, screen.getByRole('button'))
|
|
221
|
+
|
|
222
|
+
const tooltip = getTooltipContent()
|
|
223
|
+
expect(tooltip).toHaveClass('rounded-md')
|
|
224
|
+
expect(tooltip).toHaveClass('border')
|
|
225
|
+
expect(tooltip).toHaveClass('shadow-md')
|
|
226
|
+
expect(tooltip).toHaveClass('z-50')
|
|
227
|
+
expect(tooltip).toHaveClass('overflow-hidden')
|
|
228
|
+
expect(tooltip).toHaveClass('moonui-theme')
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
it('lets className override radius and shadow tokens', async () => {
|
|
232
|
+
const user = userEvent.setup()
|
|
233
|
+
render(
|
|
234
|
+
<Tooltip content="Test tooltip" className="rounded-none shadow-none" delayDuration={0}>
|
|
235
|
+
<button>Hover me</button>
|
|
236
|
+
</Tooltip>
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
await openByHover(user, screen.getByRole('button'))
|
|
240
|
+
|
|
241
|
+
// cn() = twMerge → çakışan token'lar className lehine çözülür
|
|
242
|
+
const tooltip = getTooltipContent()
|
|
243
|
+
expect(tooltip).toHaveClass('rounded-none')
|
|
244
|
+
expect(tooltip).toHaveClass('shadow-none')
|
|
245
|
+
expect(tooltip).not.toHaveClass('rounded-md')
|
|
246
|
+
expect(tooltip).not.toHaveClass('shadow-md')
|
|
154
247
|
})
|
|
155
248
|
})
|
|
156
249
|
|
|
157
|
-
describe('
|
|
158
|
-
|
|
250
|
+
describe('Arrow', () => {
|
|
251
|
+
it('does not render arrow by default', async () => {
|
|
252
|
+
const user = userEvent.setup()
|
|
253
|
+
render(
|
|
254
|
+
<Tooltip content="Test tooltip" delayDuration={0}>
|
|
255
|
+
<button>Hover me</button>
|
|
256
|
+
</Tooltip>
|
|
257
|
+
)
|
|
159
258
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
259
|
+
await openByHover(user, screen.getByRole('button'))
|
|
260
|
+
|
|
261
|
+
expect(getTooltipContent().querySelector('svg')).toBeNull()
|
|
262
|
+
})
|
|
263
|
+
|
|
264
|
+
it('renders arrow when showArrow is true', async () => {
|
|
265
|
+
const user = userEvent.setup()
|
|
266
|
+
render(
|
|
267
|
+
<Tooltip content="Test tooltip" showArrow delayDuration={0}>
|
|
268
|
+
<button>Hover me</button>
|
|
269
|
+
</Tooltip>
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
await openByHover(user, screen.getByRole('button'))
|
|
273
|
+
|
|
274
|
+
const arrow = getTooltipContent().querySelector('svg')
|
|
275
|
+
expect(arrow).not.toBeNull()
|
|
276
|
+
expect(arrow).toHaveClass('fill-current')
|
|
178
277
|
})
|
|
179
278
|
})
|
|
180
279
|
|
|
@@ -185,20 +284,20 @@ describe('Tooltip Components', () => {
|
|
|
185
284
|
it(`renders ${side} position correctly`, async () => {
|
|
186
285
|
const user = userEvent.setup()
|
|
187
286
|
render(
|
|
188
|
-
|
|
287
|
+
// jsdom'da görünür alan 0×0'dır; çarpışma kaçınması açık kalırsa Radix
|
|
288
|
+
// istenen tarafı ters çevirir. İstenen tarafın gerçekten iletildiğini
|
|
289
|
+
// ölçmek için collision davranışı kapatılır.
|
|
290
|
+
<Tooltip content="Test tooltip" side={side} delayDuration={0} avoidCollisions={false}>
|
|
189
291
|
<button>Hover me</button>
|
|
190
292
|
</Tooltip>
|
|
191
293
|
)
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
// Position classes are applied through sideClasses
|
|
200
|
-
expect(tooltip).toHaveAttribute('class')
|
|
201
|
-
})
|
|
294
|
+
|
|
295
|
+
await openByHover(user, screen.getByRole('button'))
|
|
296
|
+
|
|
297
|
+
const tooltip = getTooltipContent()
|
|
298
|
+
expect(tooltip).toBeInTheDocument()
|
|
299
|
+
// Konum Radix tarafından data-side ile yayınlanır (stil sınıfları buna bağlanır)
|
|
300
|
+
expect(tooltip).toHaveAttribute('data-side', side)
|
|
202
301
|
})
|
|
203
302
|
})
|
|
204
303
|
|
|
@@ -208,20 +307,16 @@ describe('Tooltip Components', () => {
|
|
|
208
307
|
it(`renders ${align} alignment correctly`, async () => {
|
|
209
308
|
const user = userEvent.setup()
|
|
210
309
|
render(
|
|
211
|
-
<Tooltip content="Test tooltip" align={align}>
|
|
310
|
+
<Tooltip content="Test tooltip" align={align} delayDuration={0}>
|
|
212
311
|
<button>Hover me</button>
|
|
213
312
|
</Tooltip>
|
|
214
313
|
)
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
expect(tooltip).toBeInTheDocument()
|
|
222
|
-
// Alignment classes are applied through alignClasses
|
|
223
|
-
expect(tooltip).toHaveAttribute('class')
|
|
224
|
-
})
|
|
314
|
+
|
|
315
|
+
await openByHover(user, screen.getByRole('button'))
|
|
316
|
+
|
|
317
|
+
const tooltip = getTooltipContent()
|
|
318
|
+
expect(tooltip).toBeInTheDocument()
|
|
319
|
+
expect(tooltip).toHaveAttribute('data-align', align)
|
|
225
320
|
})
|
|
226
321
|
})
|
|
227
322
|
})
|
|
@@ -234,10 +329,10 @@ describe('Tooltip Components', () => {
|
|
|
234
329
|
<button>Hover me</button>
|
|
235
330
|
</Tooltip>
|
|
236
331
|
)
|
|
237
|
-
|
|
332
|
+
|
|
238
333
|
const trigger = screen.getByRole('button')
|
|
239
334
|
await user.hover(trigger)
|
|
240
|
-
|
|
335
|
+
|
|
241
336
|
// Should appear after 100ms
|
|
242
337
|
await waitFor(() => {
|
|
243
338
|
expect(screen.getByRole('tooltip')).toBeInTheDocument()
|
|
@@ -251,10 +346,10 @@ describe('Tooltip Components', () => {
|
|
|
251
346
|
<button>Hover me</button>
|
|
252
347
|
</Tooltip>
|
|
253
348
|
)
|
|
254
|
-
|
|
349
|
+
|
|
255
350
|
const trigger = screen.getByRole('button')
|
|
256
351
|
await user.hover(trigger)
|
|
257
|
-
|
|
352
|
+
|
|
258
353
|
// Should appear immediately
|
|
259
354
|
await waitFor(() => {
|
|
260
355
|
expect(screen.getByRole('tooltip')).toBeInTheDocument()
|
|
@@ -266,19 +361,28 @@ describe('Tooltip Components', () => {
|
|
|
266
361
|
it('applies custom side offset', async () => {
|
|
267
362
|
const user = userEvent.setup()
|
|
268
363
|
render(
|
|
269
|
-
<Tooltip content="Test tooltip" sideOffset={10}>
|
|
364
|
+
<Tooltip content="Test tooltip" sideOffset={10} delayDuration={0}>
|
|
270
365
|
<button>Hover me</button>
|
|
271
366
|
</Tooltip>
|
|
272
367
|
)
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
368
|
+
|
|
369
|
+
await openByHover(user, screen.getByRole('button'))
|
|
370
|
+
|
|
371
|
+
// sideOffset Popper'a gider: side="top" için içerik 10px yukarı ötelenir
|
|
372
|
+
expect(getPopperWrapper()).toHaveStyle({ transform: 'translate(0px, -10px)' })
|
|
373
|
+
})
|
|
374
|
+
|
|
375
|
+
it('applies zero side offset', async () => {
|
|
376
|
+
const user = userEvent.setup()
|
|
377
|
+
render(
|
|
378
|
+
<Tooltip content="Test tooltip" sideOffset={0} delayDuration={0}>
|
|
379
|
+
<button>Hover me</button>
|
|
380
|
+
</Tooltip>
|
|
381
|
+
)
|
|
382
|
+
|
|
383
|
+
await openByHover(user, screen.getByRole('button'))
|
|
384
|
+
|
|
385
|
+
expect(getPopperWrapper()).toHaveStyle({ transform: 'translate(0px, 0px)' })
|
|
282
386
|
})
|
|
283
387
|
})
|
|
284
388
|
|
|
@@ -286,33 +390,31 @@ describe('Tooltip Components', () => {
|
|
|
286
390
|
it('renders text content', async () => {
|
|
287
391
|
const user = userEvent.setup()
|
|
288
392
|
render(
|
|
289
|
-
<Tooltip content="Simple text">
|
|
393
|
+
<Tooltip content="Simple text" delayDuration={0}>
|
|
290
394
|
<button>Hover me</button>
|
|
291
395
|
</Tooltip>
|
|
292
396
|
)
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
expect(screen.getByText('Simple text')).toBeInTheDocument()
|
|
299
|
-
})
|
|
397
|
+
|
|
398
|
+
await openByHover(user, screen.getByRole('button'))
|
|
399
|
+
|
|
400
|
+
expect(getTooltipContent()).toHaveTextContent('Simple text')
|
|
401
|
+
expect(screen.getByRole('tooltip')).toHaveTextContent('Simple text')
|
|
300
402
|
})
|
|
301
403
|
|
|
302
404
|
it('renders JSX content', async () => {
|
|
303
405
|
const user = userEvent.setup()
|
|
304
406
|
render(
|
|
305
|
-
<Tooltip content={<div data-testid="jsx-content">JSX Content</div>}>
|
|
407
|
+
<Tooltip content={<div data-testid="jsx-content">JSX Content</div>} delayDuration={0}>
|
|
306
408
|
<button>Hover me</button>
|
|
307
409
|
</Tooltip>
|
|
308
410
|
)
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
411
|
+
|
|
412
|
+
await openByHover(user, screen.getByRole('button'))
|
|
413
|
+
|
|
414
|
+
const tooltip = getTooltipContent()
|
|
415
|
+
// Radix içeriği sr-only kopyada da render eder → ilk düğüm görünür olandır
|
|
416
|
+
expect(within(tooltip).getAllByTestId('jsx-content')[0]).toBeInTheDocument()
|
|
417
|
+
expect(tooltip).toHaveTextContent('JSX Content')
|
|
316
418
|
})
|
|
317
419
|
|
|
318
420
|
it('renders complex content', async () => {
|
|
@@ -323,20 +425,18 @@ describe('Tooltip Components', () => {
|
|
|
323
425
|
<p>Description</p>
|
|
324
426
|
</div>
|
|
325
427
|
)
|
|
326
|
-
|
|
428
|
+
|
|
327
429
|
render(
|
|
328
|
-
<Tooltip content={complexContent}>
|
|
430
|
+
<Tooltip content={complexContent} delayDuration={0}>
|
|
329
431
|
<button>Hover me</button>
|
|
330
432
|
</Tooltip>
|
|
331
433
|
)
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
expect(screen.getByText('Description')).toBeInTheDocument()
|
|
339
|
-
})
|
|
434
|
+
|
|
435
|
+
await openByHover(user, screen.getByRole('button'))
|
|
436
|
+
|
|
437
|
+
const tooltip = getTooltipContent()
|
|
438
|
+
expect(within(tooltip).getAllByText('Title')[0]).toBeInTheDocument()
|
|
439
|
+
expect(within(tooltip).getAllByText('Description')[0]).toBeInTheDocument()
|
|
340
440
|
})
|
|
341
441
|
})
|
|
342
442
|
|
|
@@ -347,10 +447,10 @@ describe('Tooltip Components', () => {
|
|
|
347
447
|
<button>Focus me</button>
|
|
348
448
|
</Tooltip>
|
|
349
449
|
)
|
|
350
|
-
|
|
450
|
+
|
|
351
451
|
const trigger = screen.getByRole('button')
|
|
352
452
|
fireEvent.focus(trigger)
|
|
353
|
-
|
|
453
|
+
|
|
354
454
|
await waitFor(() => {
|
|
355
455
|
expect(screen.getByRole('tooltip')).toBeInTheDocument()
|
|
356
456
|
})
|
|
@@ -362,87 +462,58 @@ describe('Tooltip Components', () => {
|
|
|
362
462
|
<button>Focus me</button>
|
|
363
463
|
</Tooltip>
|
|
364
464
|
)
|
|
365
|
-
|
|
465
|
+
|
|
366
466
|
const trigger = screen.getByRole('button')
|
|
367
467
|
fireEvent.focus(trigger)
|
|
368
|
-
|
|
468
|
+
|
|
369
469
|
await waitFor(() => {
|
|
370
470
|
expect(screen.getByRole('tooltip')).toBeInTheDocument()
|
|
371
471
|
})
|
|
372
|
-
|
|
472
|
+
|
|
373
473
|
fireEvent.blur(trigger)
|
|
374
|
-
|
|
474
|
+
|
|
375
475
|
await waitFor(() => {
|
|
376
476
|
expect(screen.queryByRole('tooltip')).not.toBeInTheDocument()
|
|
377
477
|
})
|
|
378
478
|
})
|
|
379
479
|
})
|
|
380
480
|
|
|
381
|
-
describe('
|
|
382
|
-
it('renders
|
|
481
|
+
describe('Controlled State', () => {
|
|
482
|
+
it('renders open when defaultOpen is set and reports changes', async () => {
|
|
483
|
+
const onOpenChange = jest.fn()
|
|
383
484
|
const user = userEvent.setup()
|
|
384
485
|
render(
|
|
385
|
-
<Tooltip
|
|
386
|
-
|
|
387
|
-
variant="success"
|
|
388
|
-
size="lg"
|
|
389
|
-
radius="lg"
|
|
390
|
-
shadow="lg"
|
|
391
|
-
side="bottom"
|
|
392
|
-
align="end"
|
|
393
|
-
delayDuration={50}
|
|
394
|
-
sideOffset={8}
|
|
395
|
-
className="custom-tooltip"
|
|
396
|
-
>
|
|
397
|
-
<button>Complex button</button>
|
|
486
|
+
<Tooltip content="Test tooltip" defaultOpen onOpenChange={onOpenChange}>
|
|
487
|
+
<button>Hover me</button>
|
|
398
488
|
</Tooltip>
|
|
399
489
|
)
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
490
|
+
|
|
491
|
+
expect(screen.getByRole('tooltip')).toBeInTheDocument()
|
|
492
|
+
|
|
493
|
+
await user.hover(screen.getByRole('button'))
|
|
494
|
+
await movePointerAway(user, screen.getByRole('button'))
|
|
495
|
+
|
|
404
496
|
await waitFor(() => {
|
|
405
|
-
|
|
406
|
-
expect(tooltip).toBeInTheDocument()
|
|
407
|
-
expect(tooltip).toHaveClass('bg-success')
|
|
408
|
-
expect(tooltip).toHaveClass('rounded-lg')
|
|
409
|
-
expect(tooltip).toHaveClass('shadow-lg')
|
|
410
|
-
expect(tooltip).toHaveStyle({ marginTop: '8px' })
|
|
497
|
+
expect(screen.queryByRole('tooltip')).not.toBeInTheDocument()
|
|
411
498
|
})
|
|
499
|
+
expect(onOpenChange).toHaveBeenCalledWith(false)
|
|
412
500
|
})
|
|
413
501
|
|
|
414
|
-
it('
|
|
502
|
+
it('stays open while controlled open is true', async () => {
|
|
415
503
|
const user = userEvent.setup()
|
|
416
504
|
render(
|
|
417
|
-
<
|
|
418
|
-
<
|
|
419
|
-
|
|
420
|
-
</Tooltip>
|
|
421
|
-
<Tooltip content="Link tooltip">
|
|
422
|
-
<a href="#" role="link">Link</a>
|
|
423
|
-
</Tooltip>
|
|
424
|
-
<Tooltip content="Div tooltip">
|
|
425
|
-
<div role="button" tabIndex={0}>Div</div>
|
|
426
|
-
</Tooltip>
|
|
427
|
-
</div>
|
|
505
|
+
<Tooltip content="Test tooltip" open>
|
|
506
|
+
<button>Hover me</button>
|
|
507
|
+
</Tooltip>
|
|
428
508
|
)
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
await
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
await waitFor(() => {
|
|
438
|
-
expect(screen.queryByText('Button tooltip')).not.toBeInTheDocument()
|
|
439
|
-
})
|
|
440
|
-
|
|
441
|
-
// Test link
|
|
442
|
-
await user.hover(screen.getByRole('link'))
|
|
443
|
-
await waitFor(() => {
|
|
444
|
-
expect(screen.getByText('Link tooltip')).toBeInTheDocument()
|
|
445
|
-
})
|
|
509
|
+
|
|
510
|
+
expect(screen.getByRole('tooltip')).toBeInTheDocument()
|
|
511
|
+
|
|
512
|
+
await user.hover(screen.getByRole('button'))
|
|
513
|
+
await movePointerAway(user, screen.getByRole('button'))
|
|
514
|
+
|
|
515
|
+
// Kontrollü `open` dışarıdan yönetilir; iç etkileşim kapatamaz
|
|
516
|
+
expect(screen.getByRole('tooltip')).toBeInTheDocument()
|
|
446
517
|
})
|
|
447
518
|
})
|
|
448
519
|
|
|
@@ -450,57 +521,46 @@ describe('Tooltip Components', () => {
|
|
|
450
521
|
it('handles empty content', async () => {
|
|
451
522
|
const user = userEvent.setup()
|
|
452
523
|
render(
|
|
453
|
-
<Tooltip content="">
|
|
524
|
+
<Tooltip content="" delayDuration={0}>
|
|
454
525
|
<button>Hover me</button>
|
|
455
526
|
</Tooltip>
|
|
456
527
|
)
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
expect(tooltip).toBeInTheDocument()
|
|
464
|
-
expect(tooltip).toBeEmptyDOMElement()
|
|
465
|
-
})
|
|
528
|
+
|
|
529
|
+
await openByHover(user, screen.getByRole('button'))
|
|
530
|
+
|
|
531
|
+
const tooltip = screen.getByRole('tooltip')
|
|
532
|
+
expect(tooltip).toBeInTheDocument()
|
|
533
|
+
expect(tooltip).toBeEmptyDOMElement()
|
|
466
534
|
})
|
|
467
535
|
|
|
468
536
|
it('handles null content', async () => {
|
|
469
537
|
const user = userEvent.setup()
|
|
470
538
|
render(
|
|
471
|
-
<Tooltip content={null}>
|
|
539
|
+
<Tooltip content={null} delayDuration={0}>
|
|
472
540
|
<button>Hover me</button>
|
|
473
541
|
</Tooltip>
|
|
474
542
|
)
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
await waitFor(() => {
|
|
480
|
-
const tooltip = screen.getByRole('tooltip')
|
|
481
|
-
expect(tooltip).toBeInTheDocument()
|
|
482
|
-
})
|
|
543
|
+
|
|
544
|
+
await openByHover(user, screen.getByRole('button'))
|
|
545
|
+
|
|
546
|
+
expect(screen.getByRole('tooltip')).toBeInTheDocument()
|
|
483
547
|
})
|
|
484
548
|
|
|
485
549
|
it('handles undefined content', async () => {
|
|
486
550
|
const user = userEvent.setup()
|
|
487
551
|
render(
|
|
488
|
-
<Tooltip content={undefined as React.ReactNode}>
|
|
552
|
+
<Tooltip content={undefined as React.ReactNode} delayDuration={0}>
|
|
489
553
|
<button>Hover me</button>
|
|
490
554
|
</Tooltip>
|
|
491
555
|
)
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
await waitFor(() => {
|
|
497
|
-
const tooltip = screen.getByRole('tooltip')
|
|
498
|
-
expect(tooltip).toBeInTheDocument()
|
|
499
|
-
})
|
|
556
|
+
|
|
557
|
+
await openByHover(user, screen.getByRole('button'))
|
|
558
|
+
|
|
559
|
+
expect(screen.getByRole('tooltip')).toBeInTheDocument()
|
|
500
560
|
})
|
|
501
561
|
|
|
502
562
|
it('handles single child requirement', () => {
|
|
503
|
-
//
|
|
563
|
+
// TooltipTrigger `asChild` ile tek bir çocuk bekler; sarmalayıcı div ile birden fazla düğüm taşınır
|
|
504
564
|
render(
|
|
505
565
|
<Tooltip content="Test tooltip">
|
|
506
566
|
<div>
|
|
@@ -509,7 +569,7 @@ describe('Tooltip Components', () => {
|
|
|
509
569
|
</div>
|
|
510
570
|
</Tooltip>
|
|
511
571
|
)
|
|
512
|
-
|
|
572
|
+
|
|
513
573
|
// Should render both buttons wrapped in div
|
|
514
574
|
expect(screen.getByText('Button 1')).toBeInTheDocument()
|
|
515
575
|
expect(screen.getByText('Button 2')).toBeInTheDocument()
|
|
@@ -518,24 +578,23 @@ describe('Tooltip Components', () => {
|
|
|
518
578
|
it('passes through HTML attributes', async () => {
|
|
519
579
|
const user = userEvent.setup()
|
|
520
580
|
render(
|
|
521
|
-
<Tooltip
|
|
522
|
-
content="Test tooltip"
|
|
523
|
-
|
|
581
|
+
<Tooltip
|
|
582
|
+
content="Test tooltip"
|
|
583
|
+
delayDuration={0}
|
|
584
|
+
data-testid="tooltip-content"
|
|
524
585
|
aria-label="Custom tooltip"
|
|
525
586
|
>
|
|
526
587
|
<button>Hover me</button>
|
|
527
588
|
</Tooltip>
|
|
528
589
|
)
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
expect(tooltip).toHaveAttribute('aria-label', 'Custom tooltip')
|
|
538
|
-
})
|
|
590
|
+
|
|
591
|
+
await openByHover(user, screen.getByRole('button'))
|
|
592
|
+
|
|
593
|
+
// Fazladan prop'lar TooltipContent'e (görünür kutuya) iner
|
|
594
|
+
const tooltip = getTooltipContent()
|
|
595
|
+
expect(tooltip).toHaveAttribute('data-testid', 'tooltip-content')
|
|
596
|
+
// `aria-label` Radix tarafından tüketilir: erişilebilir tooltip metnini o belirler
|
|
597
|
+
expect(screen.getByRole('tooltip')).toHaveTextContent('Custom tooltip')
|
|
539
598
|
})
|
|
540
599
|
|
|
541
600
|
it('handles rapid hover/unhover', async () => {
|
|
@@ -545,14 +604,14 @@ describe('Tooltip Components', () => {
|
|
|
545
604
|
<button>Hover me</button>
|
|
546
605
|
</Tooltip>
|
|
547
606
|
)
|
|
548
|
-
|
|
607
|
+
|
|
549
608
|
const trigger = screen.getByRole('button')
|
|
550
|
-
|
|
609
|
+
|
|
551
610
|
// Rapid hover/unhover
|
|
552
611
|
await user.hover(trigger)
|
|
553
612
|
await user.unhover(trigger)
|
|
554
613
|
await user.hover(trigger)
|
|
555
|
-
|
|
614
|
+
|
|
556
615
|
await waitFor(() => {
|
|
557
616
|
expect(screen.getByRole('tooltip')).toBeInTheDocument()
|
|
558
617
|
})
|
|
@@ -561,21 +620,85 @@ describe('Tooltip Components', () => {
|
|
|
561
620
|
it('handles very long content', async () => {
|
|
562
621
|
const user = userEvent.setup()
|
|
563
622
|
const longContent = 'This is a very long tooltip content that should wrap properly and not break the layout. '.repeat(10)
|
|
564
|
-
|
|
623
|
+
|
|
565
624
|
render(
|
|
566
|
-
<Tooltip content={longContent}>
|
|
625
|
+
<Tooltip content={longContent} delayDuration={0}>
|
|
567
626
|
<button>Hover me</button>
|
|
568
627
|
</Tooltip>
|
|
569
628
|
)
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
629
|
+
|
|
630
|
+
await openByHover(user, screen.getByRole('button'))
|
|
631
|
+
|
|
632
|
+
const tooltip = screen.getByRole('tooltip')
|
|
633
|
+
expect(tooltip).toBeInTheDocument()
|
|
634
|
+
// Check if content contains the expected text (ignoring whitespace differences)
|
|
635
|
+
expect(tooltip.textContent?.trim()).toContain('This is a very long tooltip content')
|
|
636
|
+
})
|
|
637
|
+
})
|
|
638
|
+
|
|
639
|
+
describe('Complex Combinations', () => {
|
|
640
|
+
it('renders with all props', async () => {
|
|
641
|
+
const user = userEvent.setup()
|
|
642
|
+
render(
|
|
643
|
+
<Tooltip
|
|
644
|
+
content="Complex tooltip"
|
|
645
|
+
variant="success"
|
|
646
|
+
size="lg"
|
|
647
|
+
side="bottom"
|
|
648
|
+
align="end"
|
|
649
|
+
delayDuration={50}
|
|
650
|
+
sideOffset={8}
|
|
651
|
+
showArrow
|
|
652
|
+
className="custom-tooltip"
|
|
653
|
+
>
|
|
654
|
+
<button>Complex button</button>
|
|
655
|
+
</Tooltip>
|
|
656
|
+
)
|
|
657
|
+
|
|
658
|
+
await openByHover(user, screen.getByRole('button'))
|
|
659
|
+
|
|
660
|
+
const tooltip = getTooltipContent()
|
|
661
|
+
expect(tooltip).toBeInTheDocument()
|
|
662
|
+
expect(tooltip).toHaveClass('bg-green-500')
|
|
663
|
+
expect(tooltip).toHaveClass('text-base')
|
|
664
|
+
expect(tooltip).toHaveClass('custom-tooltip')
|
|
665
|
+
expect(tooltip).toHaveAttribute('data-side', 'bottom')
|
|
666
|
+
expect(tooltip).toHaveAttribute('data-align', 'end')
|
|
667
|
+
expect(tooltip.querySelector('svg')).not.toBeNull()
|
|
668
|
+
expect(getPopperWrapper()).toHaveStyle({ transform: 'translate(0px, 8px)' })
|
|
669
|
+
})
|
|
670
|
+
|
|
671
|
+
it('works with different trigger elements', async () => {
|
|
672
|
+
const user = userEvent.setup()
|
|
673
|
+
render(
|
|
674
|
+
<div>
|
|
675
|
+
<Tooltip content="Button tooltip" delayDuration={0}>
|
|
676
|
+
<button data-testid="tooltip-button">Button</button>
|
|
677
|
+
</Tooltip>
|
|
678
|
+
<Tooltip content="Link tooltip" delayDuration={0}>
|
|
679
|
+
<a href="#" role="link">Link</a>
|
|
680
|
+
</Tooltip>
|
|
681
|
+
<Tooltip content="Div tooltip" delayDuration={0}>
|
|
682
|
+
<div role="button" tabIndex={0}>Div</div>
|
|
683
|
+
</Tooltip>
|
|
684
|
+
</div>
|
|
685
|
+
)
|
|
686
|
+
|
|
687
|
+
// Test button
|
|
688
|
+
await user.hover(screen.getByTestId('tooltip-button'))
|
|
574
689
|
await waitFor(() => {
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
690
|
+
expect(screen.getByRole('tooltip')).toHaveTextContent('Button tooltip')
|
|
691
|
+
})
|
|
692
|
+
|
|
693
|
+
await movePointerAway(user, screen.getByTestId('tooltip-button'))
|
|
694
|
+
await waitFor(() => {
|
|
695
|
+
expect(screen.queryByRole('tooltip')).not.toBeInTheDocument()
|
|
696
|
+
})
|
|
697
|
+
|
|
698
|
+
// Test link
|
|
699
|
+
await user.hover(screen.getByRole('link'))
|
|
700
|
+
await waitFor(() => {
|
|
701
|
+
expect(screen.getByRole('tooltip')).toHaveTextContent('Link tooltip')
|
|
579
702
|
})
|
|
580
703
|
})
|
|
581
704
|
})
|