@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,372 @@
1
+ import React from 'react'
2
+ import { render, screen, fireEvent } from '@testing-library/react'
3
+ import '@testing-library/jest-dom'
4
+ import { Switch } from '../switch'
5
+
6
+ describe('Switch Component', () => {
7
+ describe('Basic Rendering', () => {
8
+ it('renders correctly with default props', () => {
9
+ render(<Switch data-testid="switch" />)
10
+ const switchElement = screen.getByTestId('switch')
11
+ expect(switchElement).toBeInTheDocument()
12
+ expect(switchElement).toHaveClass('peer')
13
+ expect(switchElement).toHaveClass('inline-flex')
14
+ expect(switchElement).toHaveClass('cursor-pointer')
15
+ expect(switchElement).toHaveClass('rounded-full')
16
+ })
17
+
18
+ it('applies custom className', () => {
19
+ render(<Switch className="custom-switch" data-testid="switch" />)
20
+ const switchElement = screen.getByTestId('switch')
21
+ expect(switchElement).toHaveClass('custom-switch')
22
+ })
23
+
24
+ it('forwards ref correctly', () => {
25
+ const ref = React.createRef<HTMLButtonElement>()
26
+ render(<Switch ref={ref} data-testid="switch" />)
27
+ expect(ref.current).toBeInstanceOf(HTMLButtonElement)
28
+ })
29
+
30
+ it('has correct role', () => {
31
+ render(<Switch data-testid="switch" />)
32
+ const switchElement = screen.getByTestId('switch')
33
+ expect(switchElement).toHaveAttribute('role', 'switch')
34
+ })
35
+
36
+ it('maintains displayName', () => {
37
+ expect(Switch.displayName).toBe('Switch')
38
+ })
39
+ })
40
+
41
+ describe('Sizes', () => {
42
+ it('renders sm size correctly', () => {
43
+ render(<Switch size="sm" data-testid="switch" />)
44
+ const switchElement = screen.getByTestId('switch')
45
+ expect(switchElement).toHaveClass('h-4', 'w-8')
46
+ })
47
+
48
+ it('renders md size correctly (default)', () => {
49
+ render(<Switch size="md" data-testid="switch" />)
50
+ const switchElement = screen.getByTestId('switch')
51
+ expect(switchElement).toHaveClass('h-6', 'w-11')
52
+ })
53
+
54
+ it('renders lg size correctly', () => {
55
+ render(<Switch size="lg" data-testid="switch" />)
56
+ const switchElement = screen.getByTestId('switch')
57
+ expect(switchElement).toHaveClass('h-7', 'w-14')
58
+ })
59
+
60
+ it('uses md size as default', () => {
61
+ render(<Switch data-testid="switch" />)
62
+ const switchElement = screen.getByTestId('switch')
63
+ expect(switchElement).toHaveClass('h-6', 'w-11')
64
+ })
65
+ })
66
+
67
+ describe('Variants', () => {
68
+ it('renders primary variant correctly (default)', () => {
69
+ render(<Switch variant="primary" data-testid="switch" />)
70
+ const switchElement = screen.getByTestId('switch')
71
+ expect(switchElement).toHaveClass('data-[state=checked]:bg-primary')
72
+ })
73
+
74
+ it('renders success variant correctly', () => {
75
+ render(<Switch variant="success" data-testid="switch" />)
76
+ const switchElement = screen.getByTestId('switch')
77
+ expect(switchElement).toHaveClass('data-[state=checked]:bg-success')
78
+ })
79
+
80
+ it('renders warning variant correctly', () => {
81
+ render(<Switch variant="warning" data-testid="switch" />)
82
+ const switchElement = screen.getByTestId('switch')
83
+ expect(switchElement).toHaveClass('data-[state=checked]:bg-warning')
84
+ })
85
+
86
+ it('renders danger variant correctly', () => {
87
+ render(<Switch variant="danger" data-testid="switch" />)
88
+ const switchElement = screen.getByTestId('switch')
89
+ expect(switchElement).toHaveClass('data-[state=checked]:bg-error')
90
+ })
91
+
92
+ it('renders secondary variant correctly', () => {
93
+ render(<Switch variant="secondary" data-testid="switch" />)
94
+ const switchElement = screen.getByTestId('switch')
95
+ expect(switchElement).toHaveClass('data-[state=checked]:bg-accent')
96
+ })
97
+
98
+ it('uses primary variant as default', () => {
99
+ render(<Switch data-testid="switch" />)
100
+ const switchElement = screen.getByTestId('switch')
101
+ expect(switchElement).toHaveClass('data-[state=checked]:bg-primary')
102
+ })
103
+ })
104
+
105
+ describe('States', () => {
106
+ it('renders unchecked state correctly', () => {
107
+ render(<Switch data-testid="switch" />)
108
+ const switchElement = screen.getByTestId('switch')
109
+ expect(switchElement).toHaveAttribute('data-state', 'unchecked')
110
+ expect(switchElement).toHaveAttribute('aria-checked', 'false')
111
+ })
112
+
113
+ it('renders checked state correctly', () => {
114
+ render(<Switch checked data-testid="switch" />)
115
+ const switchElement = screen.getByTestId('switch')
116
+ expect(switchElement).toHaveAttribute('data-state', 'checked')
117
+ expect(switchElement).toHaveAttribute('aria-checked', 'true')
118
+ })
119
+
120
+ it('renders disabled state correctly', () => {
121
+ render(<Switch disabled data-testid="switch" />)
122
+ const switchElement = screen.getByTestId('switch')
123
+ expect(switchElement).toBeDisabled()
124
+ expect(switchElement).toHaveClass('disabled:cursor-not-allowed')
125
+ expect(switchElement).toHaveClass('disabled:opacity-50')
126
+ })
127
+
128
+ it('handles controlled state', () => {
129
+ const handleChange = jest.fn()
130
+ render(<Switch checked={true} onCheckedChange={handleChange} data-testid="switch" />)
131
+ const switchElement = screen.getByTestId('switch')
132
+ expect(switchElement).toHaveAttribute('aria-checked', 'true')
133
+ })
134
+ })
135
+
136
+ describe('Loading State', () => {
137
+ it('renders loading state correctly', () => {
138
+ render(<Switch loading data-testid="switch" />)
139
+ const switchElement = screen.getByTestId('switch')
140
+ expect(switchElement).toBeDisabled()
141
+ expect(switchElement).toHaveClass('opacity-80')
142
+ expect(switchElement).toHaveClass('cursor-wait')
143
+ })
144
+
145
+ it('shows loading spinner when loading', () => {
146
+ render(<Switch loading data-testid="switch" />)
147
+ const switchElement = screen.getByTestId('switch')
148
+ const spinner = switchElement.querySelector('.animate-spin')
149
+ expect(spinner).toBeInTheDocument()
150
+ expect(spinner).toHaveClass('animate-spin')
151
+ expect(spinner).toHaveClass('rounded-full')
152
+ expect(spinner).toHaveClass('border-2')
153
+ })
154
+
155
+ it('disables switch when loading', () => {
156
+ render(<Switch loading data-testid="switch" />)
157
+ const switchElement = screen.getByTestId('switch')
158
+ expect(switchElement).toBeDisabled()
159
+ })
160
+
161
+ it('combines loading with disabled prop', () => {
162
+ render(<Switch loading disabled data-testid="switch" />)
163
+ const switchElement = screen.getByTestId('switch')
164
+ expect(switchElement).toBeDisabled()
165
+ })
166
+ })
167
+
168
+ describe('Icons and Description', () => {
169
+ it('renders left icon correctly', () => {
170
+ render(<Switch leftIcon={<span>Left</span>} data-testid="switch" />)
171
+ expect(screen.getByText('Left')).toBeInTheDocument()
172
+ const iconWrapper = screen.getByText('Left').parentElement
173
+ expect(iconWrapper).toHaveClass('text-muted-foreground')
174
+ })
175
+
176
+ it('renders right icon correctly', () => {
177
+ render(<Switch rightIcon={<span>Right</span>} data-testid="switch" />)
178
+ expect(screen.getByText('Right')).toBeInTheDocument()
179
+ const iconWrapper = screen.getByText('Right').parentElement
180
+ expect(iconWrapper).toHaveClass('text-muted-foreground')
181
+ })
182
+
183
+ it('renders description correctly', () => {
184
+ render(<Switch description="Switch description" data-testid="switch" />)
185
+ expect(screen.getByText('Switch description')).toBeInTheDocument()
186
+ expect(screen.getByText('Switch description')).toHaveClass('text-sm')
187
+ expect(screen.getByText('Switch description')).toHaveClass('text-muted-foreground')
188
+ })
189
+
190
+ it('renders both icons and description together', () => {
191
+ render(
192
+ <Switch
193
+ leftIcon={<span>Left</span>}
194
+ rightIcon={<span>Right</span>}
195
+ description="Description"
196
+ data-testid="switch"
197
+ />
198
+ )
199
+ expect(screen.getByText('Left')).toBeInTheDocument()
200
+ expect(screen.getByText('Right')).toBeInTheDocument()
201
+ expect(screen.getByText('Description')).toBeInTheDocument()
202
+ })
203
+
204
+ it('does not render icons when not provided', () => {
205
+ render(<Switch data-testid="switch" />)
206
+ expect(screen.queryByText('Left')).not.toBeInTheDocument()
207
+ expect(screen.queryByText('Right')).not.toBeInTheDocument()
208
+ })
209
+ })
210
+
211
+ describe('Thumb Element', () => {
212
+ it('renders thumb element', () => {
213
+ render(<Switch data-testid="switch" />)
214
+ const switchElement = screen.getByTestId('switch')
215
+ const thumb = switchElement.querySelector('span[data-state]')
216
+ expect(thumb).toBeInTheDocument()
217
+ })
218
+
219
+ it('applies correct thumb size classes for sm', () => {
220
+ render(<Switch size="sm" data-testid="switch" />)
221
+ const switchElement = screen.getByTestId('switch')
222
+ const thumb = switchElement.querySelector('span[data-state]')
223
+ expect(thumb).toHaveClass('h-3', 'w-3')
224
+ })
225
+
226
+ it('applies correct thumb size classes for md', () => {
227
+ render(<Switch size="md" data-testid="switch" />)
228
+ const switchElement = screen.getByTestId('switch')
229
+ const thumb = switchElement.querySelector('span[data-state]')
230
+ expect(thumb).toHaveClass('h-5', 'w-5')
231
+ })
232
+
233
+ it('applies correct thumb size classes for lg', () => {
234
+ render(<Switch size="lg" data-testid="switch" />)
235
+ const switchElement = screen.getByTestId('switch')
236
+ const thumb = switchElement.querySelector('span[data-state]')
237
+ expect(thumb).toHaveClass('h-6', 'w-6')
238
+ })
239
+ })
240
+
241
+ describe('Interactions', () => {
242
+ it('handles click events', () => {
243
+ const handleChange = jest.fn()
244
+ render(<Switch onCheckedChange={handleChange} data-testid="switch" />)
245
+ const switchElement = screen.getByTestId('switch')
246
+
247
+ fireEvent.click(switchElement)
248
+ expect(handleChange).toHaveBeenCalledWith(true)
249
+ })
250
+
251
+ it('does not trigger events when disabled', () => {
252
+ const handleChange = jest.fn()
253
+ render(<Switch disabled onCheckedChange={handleChange} data-testid="switch" />)
254
+ const switchElement = screen.getByTestId('switch')
255
+
256
+ fireEvent.click(switchElement)
257
+ expect(handleChange).not.toHaveBeenCalled()
258
+ })
259
+
260
+ it('does not trigger events when loading', () => {
261
+ const handleChange = jest.fn()
262
+ render(<Switch loading onCheckedChange={handleChange} data-testid="switch" />)
263
+ const switchElement = screen.getByTestId('switch')
264
+
265
+ fireEvent.click(switchElement)
266
+ expect(handleChange).not.toHaveBeenCalled()
267
+ })
268
+
269
+ it('is focusable for keyboard navigation', () => {
270
+ render(<Switch data-testid="switch" />)
271
+ const switchElement = screen.getByTestId('switch')
272
+
273
+ switchElement.focus()
274
+ expect(switchElement).toHaveFocus()
275
+ })
276
+ })
277
+
278
+ describe('Accessibility', () => {
279
+ it('has correct ARIA attributes', () => {
280
+ render(<Switch data-testid="switch" />)
281
+ const switchElement = screen.getByTestId('switch')
282
+ expect(switchElement).toHaveAttribute('role', 'switch')
283
+ expect(switchElement).toHaveAttribute('aria-checked', 'false')
284
+ })
285
+
286
+ it('supports custom ARIA attributes', () => {
287
+ render(<Switch aria-label="Toggle setting" data-testid="switch" />)
288
+ const switchElement = screen.getByTestId('switch')
289
+ expect(switchElement).toHaveAttribute('aria-label', 'Toggle setting')
290
+ })
291
+
292
+ it('has focus-visible styles', () => {
293
+ render(<Switch data-testid="switch" />)
294
+ const switchElement = screen.getByTestId('switch')
295
+ expect(switchElement).toHaveClass('focus-visible:outline-none')
296
+ expect(switchElement).toHaveClass('focus-visible:ring-2')
297
+ expect(switchElement).toHaveClass('focus-visible:ring-ring')
298
+ })
299
+ })
300
+
301
+ describe('Complex Combinations', () => {
302
+ it('renders with all props correctly', () => {
303
+ const handleChange = jest.fn()
304
+ render(
305
+ <Switch
306
+ size="lg"
307
+ variant="success"
308
+ checked={true}
309
+ onCheckedChange={handleChange}
310
+ leftIcon={<span>Left</span>}
311
+ rightIcon={<span>Right</span>}
312
+ description="Toggle description"
313
+ className="custom-class"
314
+ data-testid="switch"
315
+ />
316
+ )
317
+
318
+ const switchElement = screen.getByTestId('switch')
319
+ expect(switchElement).toHaveClass('h-7', 'w-14')
320
+ expect(switchElement).toHaveClass('data-[state=checked]:bg-success')
321
+ expect(switchElement).toHaveClass('custom-class')
322
+ expect(switchElement).toHaveAttribute('aria-checked', 'true')
323
+
324
+ expect(screen.getByText('Left')).toBeInTheDocument()
325
+ expect(screen.getByText('Right')).toBeInTheDocument()
326
+ expect(screen.getByText('Toggle description')).toBeInTheDocument()
327
+ })
328
+
329
+ it('handles loading with other props', () => {
330
+ render(
331
+ <Switch
332
+ loading
333
+ size="sm"
334
+ variant="danger"
335
+ leftIcon={<span>Left</span>}
336
+ description="Loading switch"
337
+ data-testid="switch"
338
+ />
339
+ )
340
+
341
+ const switchElement = screen.getByTestId('switch')
342
+ expect(switchElement).toBeDisabled()
343
+ expect(switchElement).toHaveClass('h-4', 'w-8')
344
+ expect(switchElement).toHaveClass('data-[state=checked]:bg-error')
345
+ expect(switchElement).toHaveClass('opacity-80')
346
+
347
+ expect(screen.getByText('Left')).toBeInTheDocument()
348
+ expect(screen.getByText('Loading switch')).toBeInTheDocument()
349
+ })
350
+ })
351
+
352
+ describe('Edge Cases', () => {
353
+ it('passes through HTML attributes', () => {
354
+ render(<Switch data-testid="switch-test" id="switch-1" />)
355
+ const switchElement = screen.getByTestId('switch-test')
356
+ expect(switchElement).toHaveAttribute('id', 'switch-1')
357
+ })
358
+
359
+ it('handles empty string description', () => {
360
+ render(<Switch description="" data-testid="switch" />)
361
+ const container = screen.getByTestId('switch').parentElement
362
+ const description = container?.querySelector('.text-sm.text-muted-foreground')
363
+ expect(description).not.toBeInTheDocument()
364
+ })
365
+
366
+ it('handles null icons gracefully', () => {
367
+ render(<Switch leftIcon={null} rightIcon={null} data-testid="switch" />)
368
+ const switchElement = screen.getByTestId('switch')
369
+ expect(switchElement).toBeInTheDocument()
370
+ })
371
+ })
372
+ })