@moontra/moonui 2.0.8 → 2.0.9

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.
Files changed (94) hide show
  1. package/package.json +9 -2
  2. package/src/__tests__/use-intersection-observer.test.tsx +216 -0
  3. package/src/__tests__/use-local-storage.test.tsx +174 -0
  4. package/src/__tests__/use-pro-access.test.tsx +183 -0
  5. package/src/components/ui/__tests__/accordion.test.tsx +571 -0
  6. package/src/components/ui/__tests__/alert.test.tsx +484 -0
  7. package/src/components/ui/__tests__/aspect-ratio.test.tsx +492 -0
  8. package/src/components/ui/__tests__/avatar.test.tsx +382 -0
  9. package/src/components/ui/__tests__/badge.test.tsx +364 -0
  10. package/src/components/ui/__tests__/breadcrumb.test.tsx +687 -0
  11. package/src/components/ui/__tests__/button.test.tsx +91 -0
  12. package/src/components/ui/__tests__/card.test.tsx +104 -0
  13. package/src/components/ui/__tests__/checkbox.test.tsx +591 -0
  14. package/src/components/ui/__tests__/collapsible.test.tsx +592 -0
  15. package/src/components/ui/__tests__/color-picker.test.tsx +365 -0
  16. package/src/components/ui/__tests__/command.test.tsx +677 -0
  17. package/src/components/ui/__tests__/data-table.test.tsx +256 -0
  18. package/src/components/ui/__tests__/date-picker.test.tsx +320 -0
  19. package/src/components/ui/__tests__/dialog.test.tsx +764 -0
  20. package/src/components/ui/__tests__/input.test.tsx +318 -0
  21. package/src/components/ui/__tests__/label.test.tsx +103 -0
  22. package/src/components/ui/__tests__/popover.test.tsx +637 -0
  23. package/src/components/ui/__tests__/progress.test.tsx +382 -0
  24. package/src/components/ui/__tests__/radio-group.test.tsx +555 -0
  25. package/src/components/ui/__tests__/select.test.tsx +705 -0
  26. package/src/components/ui/__tests__/skeleton.test.tsx +253 -0
  27. package/src/components/ui/__tests__/slider.test.tsx +493 -0
  28. package/src/components/ui/__tests__/switch.test.tsx +372 -0
  29. package/src/components/ui/__tests__/table.test.tsx +824 -0
  30. package/src/components/ui/__tests__/tabs.test.tsx +887 -0
  31. package/src/components/ui/__tests__/toast.test.tsx +458 -0
  32. package/src/components/ui/__tests__/tooltip.test.tsx +583 -0
  33. package/src/components/ui/accordion.tsx +63 -0
  34. package/src/components/ui/alert.tsx +130 -0
  35. package/src/components/ui/aspect-ratio.tsx +72 -0
  36. package/src/components/ui/avatar.tsx +135 -0
  37. package/src/components/ui/badge.tsx +225 -0
  38. package/src/components/ui/breadcrumb.tsx +207 -0
  39. package/src/components/ui/button.stories.tsx +162 -0
  40. package/src/components/ui/button.tsx +221 -0
  41. package/src/components/ui/calendar.tsx +262 -0
  42. package/src/components/ui/card.stories.tsx +208 -0
  43. package/src/components/ui/card.tsx +141 -0
  44. package/src/components/ui/checkbox.tsx +256 -0
  45. package/src/components/ui/collapsible.tsx +129 -0
  46. package/src/components/ui/color-picker.tsx +664 -0
  47. package/src/components/ui/command.tsx +218 -0
  48. package/src/components/ui/data-table.stories.tsx +509 -0
  49. package/src/components/ui/data-table.tsx +623 -0
  50. package/src/components/ui/date-picker.stories.tsx +321 -0
  51. package/src/components/ui/date-picker.tsx +603 -0
  52. package/src/components/ui/dialog.tsx +332 -0
  53. package/src/components/ui/dropdown-menu.tsx +200 -0
  54. package/src/components/ui/file-upload.tsx +400 -0
  55. package/src/components/ui/github-stars.tsx +201 -0
  56. package/src/components/ui/index.ts +12 -0
  57. package/src/components/ui/input.stories.tsx +255 -0
  58. package/src/components/ui/input.tsx +219 -0
  59. package/src/components/ui/label.tsx +26 -0
  60. package/src/components/ui/locked-component.tsx +215 -0
  61. package/src/components/ui/moon-logo.tsx +97 -0
  62. package/src/components/ui/pagination.tsx +116 -0
  63. package/src/components/ui/popover.tsx +183 -0
  64. package/src/components/ui/progress.tsx +182 -0
  65. package/src/components/ui/radio-group.tsx +243 -0
  66. package/src/components/ui/select.tsx +273 -0
  67. package/src/components/ui/separator.tsx +140 -0
  68. package/src/components/ui/simple-editor.tsx +652 -0
  69. package/src/components/ui/skeleton.tsx +351 -0
  70. package/src/components/ui/slider.tsx +351 -0
  71. package/src/components/ui/switch.tsx +83 -0
  72. package/src/components/ui/table.tsx +322 -0
  73. package/src/components/ui/tabs.tsx +195 -0
  74. package/src/components/ui/textarea.tsx +46 -0
  75. package/src/components/ui/toast.tsx +313 -0
  76. package/src/components/ui/toggle.tsx +47 -0
  77. package/src/components/ui/tooltip.tsx +152 -0
  78. package/src/docs/theme-system.md +1194 -0
  79. package/src/index.tsx +39 -0
  80. package/src/lib/micro-interactions.ts +255 -0
  81. package/src/lib/theme.tsx +398 -0
  82. package/src/lib/utils.ts +69 -0
  83. package/src/styles/design-system.css +365 -0
  84. package/src/styles/index.css +4 -0
  85. package/src/styles/tailwind.css +6 -0
  86. package/src/styles/tokens.css +453 -0
  87. package/src/use-intersection-observer.tsx +154 -0
  88. package/src/use-local-storage.tsx +71 -0
  89. package/src/use-paddle.ts +138 -0
  90. package/src/use-performance-optimizer.ts +379 -0
  91. package/src/use-pro-access.ts +141 -0
  92. package/src/use-scroll-animation.ts +221 -0
  93. package/src/use-subscription.ts +37 -0
  94. package/src/use-toast.ts +32 -0
@@ -0,0 +1,382 @@
1
+ import React from 'react'
2
+ import { render, screen } from '@testing-library/react'
3
+ import '@testing-library/jest-dom'
4
+ import { Avatar, AvatarImage, AvatarFallback, AvatarGroup } from '../avatar'
5
+
6
+ describe('Avatar Component', () => {
7
+ describe('Avatar', () => {
8
+ it('renders correctly with default props', () => {
9
+ render(
10
+ <Avatar>
11
+ <AvatarFallback>JD</AvatarFallback>
12
+ </Avatar>
13
+ )
14
+ const avatar = screen.getByText('JD').parentElement
15
+ expect(avatar).toBeInTheDocument()
16
+ expect(avatar).toHaveClass('relative')
17
+ expect(avatar).toHaveClass('flex')
18
+ expect(avatar).toHaveClass('shrink-0')
19
+ expect(avatar).toHaveClass('overflow-hidden')
20
+ expect(avatar).toHaveClass('h-10')
21
+ expect(avatar).toHaveClass('w-10')
22
+ expect(avatar).toHaveClass('rounded-full')
23
+ })
24
+
25
+ it('applies custom className', () => {
26
+ render(
27
+ <Avatar className="custom-avatar">
28
+ <AvatarFallback>JD</AvatarFallback>
29
+ </Avatar>
30
+ )
31
+ const avatar = screen.getByText('JD').parentElement
32
+ expect(avatar).toHaveClass('custom-avatar')
33
+ })
34
+
35
+ it('forwards ref correctly', () => {
36
+ const ref = React.createRef<HTMLSpanElement>()
37
+ render(
38
+ <Avatar ref={ref}>
39
+ <AvatarFallback>JD</AvatarFallback>
40
+ </Avatar>
41
+ )
42
+ expect(ref.current).toBeInstanceOf(HTMLSpanElement)
43
+ })
44
+
45
+ describe('Sizes', () => {
46
+ it('renders xs size correctly', () => {
47
+ render(
48
+ <Avatar size="xs">
49
+ <AvatarFallback>XS</AvatarFallback>
50
+ </Avatar>
51
+ )
52
+ const avatar = screen.getByText('XS').parentElement
53
+ expect(avatar).toHaveClass('h-6')
54
+ expect(avatar).toHaveClass('w-6')
55
+ })
56
+
57
+ it('renders sm size correctly', () => {
58
+ render(
59
+ <Avatar size="sm">
60
+ <AvatarFallback>SM</AvatarFallback>
61
+ </Avatar>
62
+ )
63
+ const avatar = screen.getByText('SM').parentElement
64
+ expect(avatar).toHaveClass('h-8')
65
+ expect(avatar).toHaveClass('w-8')
66
+ })
67
+
68
+ it('renders md size correctly', () => {
69
+ render(
70
+ <Avatar size="md">
71
+ <AvatarFallback>MD</AvatarFallback>
72
+ </Avatar>
73
+ )
74
+ const avatar = screen.getByText('MD').parentElement
75
+ expect(avatar).toHaveClass('h-10')
76
+ expect(avatar).toHaveClass('w-10')
77
+ })
78
+
79
+ it('renders lg size correctly', () => {
80
+ render(
81
+ <Avatar size="lg">
82
+ <AvatarFallback>LG</AvatarFallback>
83
+ </Avatar>
84
+ )
85
+ const avatar = screen.getByText('LG').parentElement
86
+ expect(avatar).toHaveClass('h-12')
87
+ expect(avatar).toHaveClass('w-12')
88
+ })
89
+
90
+ it('renders xl size correctly', () => {
91
+ render(
92
+ <Avatar size="xl">
93
+ <AvatarFallback>XL</AvatarFallback>
94
+ </Avatar>
95
+ )
96
+ const avatar = screen.getByText('XL').parentElement
97
+ expect(avatar).toHaveClass('h-16')
98
+ expect(avatar).toHaveClass('w-16')
99
+ })
100
+
101
+ it('renders 2xl size correctly', () => {
102
+ render(
103
+ <Avatar size="2xl">
104
+ <AvatarFallback>2XL</AvatarFallback>
105
+ </Avatar>
106
+ )
107
+ const avatar = screen.getByText('2XL').parentElement
108
+ expect(avatar).toHaveClass('h-20')
109
+ expect(avatar).toHaveClass('w-20')
110
+ })
111
+ })
112
+
113
+ describe('Radius', () => {
114
+ it('renders default radius correctly', () => {
115
+ render(
116
+ <Avatar radius="default">
117
+ <AvatarFallback>DF</AvatarFallback>
118
+ </Avatar>
119
+ )
120
+ const avatar = screen.getByText('DF').parentElement
121
+ expect(avatar).toHaveClass('rounded-full')
122
+ })
123
+
124
+ it('renders sm radius correctly', () => {
125
+ render(
126
+ <Avatar radius="sm">
127
+ <AvatarFallback>SM</AvatarFallback>
128
+ </Avatar>
129
+ )
130
+ const avatar = screen.getByText('SM').parentElement
131
+ expect(avatar).toHaveClass('rounded-md')
132
+ })
133
+
134
+ it('renders lg radius correctly', () => {
135
+ render(
136
+ <Avatar radius="lg">
137
+ <AvatarFallback>LG</AvatarFallback>
138
+ </Avatar>
139
+ )
140
+ const avatar = screen.getByText('LG').parentElement
141
+ expect(avatar).toHaveClass('rounded-xl')
142
+ })
143
+
144
+ it('renders full radius correctly', () => {
145
+ render(
146
+ <Avatar radius="full">
147
+ <AvatarFallback>FL</AvatarFallback>
148
+ </Avatar>
149
+ )
150
+ const avatar = screen.getByText('FL').parentElement
151
+ expect(avatar).toHaveClass('rounded-full')
152
+ })
153
+
154
+ it('renders none radius correctly', () => {
155
+ render(
156
+ <Avatar radius="none">
157
+ <AvatarFallback>NO</AvatarFallback>
158
+ </Avatar>
159
+ )
160
+ const avatar = screen.getByText('NO').parentElement
161
+ expect(avatar).toHaveClass('rounded-none')
162
+ })
163
+ })
164
+
165
+ describe('Variants', () => {
166
+ it('renders default variant correctly', () => {
167
+ render(
168
+ <Avatar variant="default">
169
+ <AvatarFallback>DF</AvatarFallback>
170
+ </Avatar>
171
+ )
172
+ const avatar = screen.getByText('DF').parentElement
173
+ expect(avatar).toBeInTheDocument()
174
+ // Default variant has no additional classes
175
+ })
176
+
177
+ it('renders ring variant correctly', () => {
178
+ render(
179
+ <Avatar variant="ring">
180
+ <AvatarFallback>RG</AvatarFallback>
181
+ </Avatar>
182
+ )
183
+ const avatar = screen.getByText('RG').parentElement
184
+ expect(avatar).toHaveClass('ring-2')
185
+ expect(avatar).toHaveClass('ring-gray-300')
186
+ expect(avatar).toHaveClass('dark:ring-gray-600')
187
+ })
188
+
189
+ it('renders ringOffset variant correctly', () => {
190
+ render(
191
+ <Avatar variant="ringOffset">
192
+ <AvatarFallback>RO</AvatarFallback>
193
+ </Avatar>
194
+ )
195
+ const avatar = screen.getByText('RO').parentElement
196
+ expect(avatar).toHaveClass('ring-2')
197
+ expect(avatar).toHaveClass('ring-gray-300')
198
+ expect(avatar).toHaveClass('dark:ring-gray-600')
199
+ expect(avatar).toHaveClass('ring-offset-2')
200
+ expect(avatar).toHaveClass('ring-offset-background')
201
+ expect(avatar).toHaveClass('dark:ring-offset-gray-950')
202
+ })
203
+
204
+ it('renders border variant correctly', () => {
205
+ render(
206
+ <Avatar variant="border">
207
+ <AvatarFallback>BD</AvatarFallback>
208
+ </Avatar>
209
+ )
210
+ const avatar = screen.getByText('BD').parentElement
211
+ expect(avatar).toHaveClass('border-2')
212
+ expect(avatar).toHaveClass('border-gray-200')
213
+ expect(avatar).toHaveClass('dark:border-gray-800')
214
+ })
215
+ })
216
+ })
217
+
218
+ describe('AvatarImage', () => {
219
+ it('renders image correctly', () => {
220
+ render(
221
+ <Avatar>
222
+ <AvatarImage src="/test-image.jpg" alt="Test User" />
223
+ <AvatarFallback>TU</AvatarFallback>
224
+ </Avatar>
225
+ )
226
+ // AvatarImage falls back to AvatarFallback when image fails to load in test environment
227
+ const fallback = screen.getByText('TU')
228
+ expect(fallback).toBeInTheDocument()
229
+ })
230
+
231
+ it('applies custom className to image', () => {
232
+ render(
233
+ <Avatar>
234
+ <AvatarImage src="/test.jpg" className="custom-image" />
235
+ <AvatarFallback>TU</AvatarFallback>
236
+ </Avatar>
237
+ )
238
+ // AvatarImage falls back to AvatarFallback when image fails to load in test environment
239
+ const fallback = screen.getByText('TU')
240
+ expect(fallback).toBeInTheDocument()
241
+ })
242
+
243
+ it('forwards ref correctly', () => {
244
+ const ref = React.createRef<HTMLImageElement>()
245
+ render(
246
+ <Avatar>
247
+ <AvatarImage ref={ref} src="/test.jpg" />
248
+ <AvatarFallback>TU</AvatarFallback>
249
+ </Avatar>
250
+ )
251
+ // In test environment, ref may be null due to image loading behavior
252
+ expect(ref.current).toBeDefined()
253
+ })
254
+ })
255
+
256
+ describe('AvatarFallback', () => {
257
+ it('renders fallback correctly', () => {
258
+ render(
259
+ <Avatar>
260
+ <AvatarFallback>FB</AvatarFallback>
261
+ </Avatar>
262
+ )
263
+ const fallback = screen.getByText('FB')
264
+ expect(fallback).toBeInTheDocument()
265
+ expect(fallback).toHaveClass('flex')
266
+ expect(fallback).toHaveClass('h-full')
267
+ expect(fallback).toHaveClass('w-full')
268
+ expect(fallback).toHaveClass('items-center')
269
+ expect(fallback).toHaveClass('justify-center')
270
+ expect(fallback).toHaveClass('bg-muted')
271
+ })
272
+
273
+ it('applies custom className to fallback', () => {
274
+ render(
275
+ <Avatar>
276
+ <AvatarFallback className="custom-fallback">FB</AvatarFallback>
277
+ </Avatar>
278
+ )
279
+ const fallback = screen.getByText('FB')
280
+ expect(fallback).toHaveClass('custom-fallback')
281
+ })
282
+
283
+ it('forwards ref correctly', () => {
284
+ const ref = React.createRef<HTMLSpanElement>()
285
+ render(
286
+ <Avatar>
287
+ <AvatarFallback ref={ref}>FB</AvatarFallback>
288
+ </Avatar>
289
+ )
290
+ expect(ref.current).toBeInstanceOf(HTMLSpanElement)
291
+ })
292
+ })
293
+
294
+ describe('AvatarGroup', () => {
295
+ const mockAvatars = [
296
+ <Avatar key="1"><AvatarFallback>A1</AvatarFallback></Avatar>,
297
+ <Avatar key="2"><AvatarFallback>A2</AvatarFallback></Avatar>,
298
+ <Avatar key="3"><AvatarFallback>A3</AvatarFallback></Avatar>,
299
+ <Avatar key="4"><AvatarFallback>A4</AvatarFallback></Avatar>,
300
+ <Avatar key="5"><AvatarFallback>A5</AvatarFallback></Avatar>,
301
+ ]
302
+
303
+ it('renders all avatars when no limit is set', () => {
304
+ render(<AvatarGroup avatars={mockAvatars} />)
305
+ expect(screen.getByText('A1')).toBeInTheDocument()
306
+ expect(screen.getByText('A2')).toBeInTheDocument()
307
+ expect(screen.getByText('A3')).toBeInTheDocument()
308
+ expect(screen.getByText('A4')).toBeInTheDocument()
309
+ expect(screen.getByText('A5')).toBeInTheDocument()
310
+ })
311
+
312
+ it('limits avatars when limit is set', () => {
313
+ render(<AvatarGroup avatars={mockAvatars} limit={3} />)
314
+ expect(screen.getByText('A1')).toBeInTheDocument()
315
+ expect(screen.getByText('A2')).toBeInTheDocument()
316
+ expect(screen.getByText('A3')).toBeInTheDocument()
317
+ expect(screen.queryByText('A4')).not.toBeInTheDocument()
318
+ expect(screen.queryByText('A5')).not.toBeInTheDocument()
319
+ })
320
+
321
+ it('shows remaining count when limit is exceeded', () => {
322
+ render(<AvatarGroup avatars={mockAvatars} limit={3} />)
323
+ expect(screen.getByText('+2')).toBeInTheDocument()
324
+ })
325
+
326
+ it('applies custom className', () => {
327
+ render(<AvatarGroup avatars={mockAvatars} className="custom-group" />)
328
+ const group = screen.getByText('A1').closest('.custom-group')
329
+ expect(group).toBeInTheDocument()
330
+ })
331
+
332
+ it('forwards ref correctly', () => {
333
+ const ref = React.createRef<HTMLDivElement>()
334
+ render(<AvatarGroup ref={ref} avatars={mockAvatars} />)
335
+ expect(ref.current).toBeInstanceOf(HTMLDivElement)
336
+ })
337
+
338
+ it('applies custom overlap offset', () => {
339
+ render(<AvatarGroup avatars={mockAvatars} overlapOffset={-12} />)
340
+ // This test verifies the component renders without error with custom offset
341
+ expect(screen.getByText('A1')).toBeInTheDocument()
342
+ })
343
+
344
+ it('handles empty avatars array', () => {
345
+ render(<AvatarGroup avatars={[]} data-testid="avatar-group" />)
346
+ const group = screen.getByTestId('avatar-group')
347
+ expect(group).toBeInTheDocument()
348
+ expect(group).toHaveClass('flex')
349
+ expect(group).toHaveClass('items-center')
350
+ })
351
+
352
+ it('handles single avatar', () => {
353
+ const singleAvatar = [<Avatar key="1"><AvatarFallback>SA</AvatarFallback></Avatar>]
354
+ render(<AvatarGroup avatars={singleAvatar} />)
355
+ expect(screen.getByText('SA')).toBeInTheDocument()
356
+ expect(screen.queryByText('+')).not.toBeInTheDocument()
357
+ })
358
+ })
359
+
360
+ describe('Complex Combinations', () => {
361
+ it('renders avatar with image and fallback', () => {
362
+ render(
363
+ <Avatar size="lg" variant="ring" radius="sm">
364
+ <AvatarImage src="/test.jpg" alt="Test" />
365
+ <AvatarFallback>TU</AvatarFallback>
366
+ </Avatar>
367
+ )
368
+ const avatar = screen.getByText('TU').parentElement
369
+ expect(avatar).toHaveClass('h-12')
370
+ expect(avatar).toHaveClass('w-12')
371
+ expect(avatar).toHaveClass('ring-2')
372
+ expect(avatar).toHaveClass('rounded-md')
373
+ })
374
+
375
+ it('maintains displayName for all components', () => {
376
+ expect(Avatar.displayName).toBeDefined()
377
+ expect(AvatarImage.displayName).toBeDefined()
378
+ expect(AvatarFallback.displayName).toBeDefined()
379
+ expect(AvatarGroup.displayName).toBe('AvatarGroup')
380
+ })
381
+ })
382
+ })