@moontra/moonui 2.0.7 → 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 +26 -19
  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,591 @@
1
+ import React from 'react'
2
+ import { render, screen, fireEvent } from '@testing-library/react'
3
+ import '@testing-library/jest-dom'
4
+ import { Checkbox, CheckboxGroup, CheckboxLabel, CheckboxWithLabel } from '../checkbox'
5
+
6
+ describe('Checkbox Components', () => {
7
+ describe('Checkbox Component', () => {
8
+ describe('Basic Rendering', () => {
9
+ it('renders correctly with default props', () => {
10
+ render(<Checkbox data-testid="checkbox" />)
11
+ const checkbox = screen.getByTestId('checkbox')
12
+ expect(checkbox).toBeInTheDocument()
13
+ expect(checkbox).toHaveAttribute('role', 'checkbox')
14
+ expect(checkbox).toHaveAttribute('aria-checked', 'false')
15
+ })
16
+
17
+ it('applies custom className', () => {
18
+ render(<Checkbox className="custom-checkbox" data-testid="checkbox" />)
19
+ const checkbox = screen.getByTestId('checkbox')
20
+ expect(checkbox).toHaveClass('custom-checkbox')
21
+ })
22
+
23
+ it('forwards ref correctly', () => {
24
+ const ref = React.createRef<HTMLButtonElement>()
25
+ render(<Checkbox ref={ref} data-testid="checkbox" />)
26
+ expect(ref.current).toBeInstanceOf(HTMLButtonElement)
27
+ })
28
+
29
+ it('maintains displayName', () => {
30
+ expect(Checkbox.displayName).toBe('Checkbox')
31
+ })
32
+ })
33
+
34
+ describe('Variants', () => {
35
+ it('renders default variant correctly', () => {
36
+ render(<Checkbox variant="default" data-testid="checkbox" />)
37
+ const checkbox = screen.getByTestId('checkbox')
38
+ expect(checkbox).toHaveClass('border-border')
39
+ expect(checkbox).toHaveClass('bg-background')
40
+ })
41
+
42
+ it('renders outline variant correctly', () => {
43
+ render(<Checkbox variant="outline" data-testid="checkbox" />)
44
+ const checkbox = screen.getByTestId('checkbox')
45
+ expect(checkbox).toHaveClass('border-border')
46
+ expect(checkbox).toHaveClass('bg-transparent')
47
+ })
48
+
49
+ it('renders muted variant correctly', () => {
50
+ render(<Checkbox variant="muted" data-testid="checkbox" />)
51
+ const checkbox = screen.getByTestId('checkbox')
52
+ expect(checkbox).toHaveClass('border-border')
53
+ expect(checkbox).toHaveClass('bg-accent')
54
+ })
55
+
56
+ it('renders ghost variant correctly', () => {
57
+ render(<Checkbox variant="ghost" data-testid="checkbox" />)
58
+ const checkbox = screen.getByTestId('checkbox')
59
+ expect(checkbox).toHaveClass('border-transparent')
60
+ expect(checkbox).toHaveClass('bg-transparent')
61
+ })
62
+
63
+ it('uses default variant as default', () => {
64
+ render(<Checkbox data-testid="checkbox" />)
65
+ const checkbox = screen.getByTestId('checkbox')
66
+ expect(checkbox).toHaveClass('border-border')
67
+ expect(checkbox).toHaveClass('bg-background')
68
+ })
69
+ })
70
+
71
+ describe('Sizes', () => {
72
+ it('renders sm size correctly', () => {
73
+ render(<Checkbox size="sm" data-testid="checkbox" />)
74
+ const checkbox = screen.getByTestId('checkbox')
75
+ expect(checkbox).toHaveClass('h-3.5', 'w-3.5')
76
+ })
77
+
78
+ it('renders default size correctly', () => {
79
+ render(<Checkbox size="default" data-testid="checkbox" />)
80
+ const checkbox = screen.getByTestId('checkbox')
81
+ expect(checkbox).toHaveClass('h-4', 'w-4')
82
+ })
83
+
84
+ it('renders md size correctly', () => {
85
+ render(<Checkbox size="md" data-testid="checkbox" />)
86
+ const checkbox = screen.getByTestId('checkbox')
87
+ expect(checkbox).toHaveClass('h-5', 'w-5')
88
+ })
89
+
90
+ it('renders lg size correctly', () => {
91
+ render(<Checkbox size="lg" data-testid="checkbox" />)
92
+ const checkbox = screen.getByTestId('checkbox')
93
+ expect(checkbox).toHaveClass('h-6', 'w-6')
94
+ })
95
+
96
+ it('uses default size as default', () => {
97
+ render(<Checkbox data-testid="checkbox" />)
98
+ const checkbox = screen.getByTestId('checkbox')
99
+ expect(checkbox).toHaveClass('h-4', 'w-4')
100
+ })
101
+ })
102
+
103
+ describe('Radius', () => {
104
+ it('renders none radius correctly', () => {
105
+ render(<Checkbox radius="none" data-testid="checkbox" />)
106
+ const checkbox = screen.getByTestId('checkbox')
107
+ expect(checkbox).toHaveClass('rounded-none')
108
+ })
109
+
110
+ it('renders sm radius correctly', () => {
111
+ render(<Checkbox radius="sm" data-testid="checkbox" />)
112
+ const checkbox = screen.getByTestId('checkbox')
113
+ expect(checkbox).toHaveClass('rounded-sm')
114
+ })
115
+
116
+ it('renders default radius correctly', () => {
117
+ render(<Checkbox radius="default" data-testid="checkbox" />)
118
+ const checkbox = screen.getByTestId('checkbox')
119
+ expect(checkbox).toHaveClass('rounded-sm')
120
+ })
121
+
122
+ it('renders md radius correctly', () => {
123
+ render(<Checkbox radius="md" data-testid="checkbox" />)
124
+ const checkbox = screen.getByTestId('checkbox')
125
+ expect(checkbox).toHaveClass('rounded-md')
126
+ })
127
+
128
+ it('renders full radius correctly', () => {
129
+ render(<Checkbox radius="full" data-testid="checkbox" />)
130
+ const checkbox = screen.getByTestId('checkbox')
131
+ expect(checkbox).toHaveClass('rounded-full')
132
+ })
133
+ })
134
+
135
+ describe('Animation', () => {
136
+ it('renders none animation correctly', () => {
137
+ render(<Checkbox animation="none" data-testid="checkbox" />)
138
+ const checkbox = screen.getByTestId('checkbox')
139
+ expect(checkbox).not.toHaveClass('transition-all')
140
+ })
141
+
142
+ it('renders subtle animation correctly', () => {
143
+ render(<Checkbox animation="subtle" data-testid="checkbox" />)
144
+ const checkbox = screen.getByTestId('checkbox')
145
+ expect(checkbox).toHaveClass('transition-all')
146
+ })
147
+
148
+ it('renders default animation correctly', () => {
149
+ render(<Checkbox animation="default" data-testid="checkbox" />)
150
+ const checkbox = screen.getByTestId('checkbox')
151
+ expect(checkbox).toHaveClass('transition-all')
152
+ })
153
+
154
+ it('renders bounce animation correctly', () => {
155
+ render(<Checkbox animation="bounce" data-testid="checkbox" />)
156
+ const checkbox = screen.getByTestId('checkbox')
157
+ expect(checkbox).toHaveClass('transition-all')
158
+ })
159
+ })
160
+
161
+ describe('States', () => {
162
+ it('renders unchecked state correctly', () => {
163
+ render(<Checkbox data-testid="checkbox" />)
164
+ const checkbox = screen.getByTestId('checkbox')
165
+ expect(checkbox).toHaveAttribute('aria-checked', 'false')
166
+ expect(checkbox).toHaveAttribute('data-state', 'unchecked')
167
+ })
168
+
169
+ it('renders checked state correctly', () => {
170
+ render(<Checkbox checked data-testid="checkbox" />)
171
+ const checkbox = screen.getByTestId('checkbox')
172
+ expect(checkbox).toHaveAttribute('aria-checked', 'true')
173
+ expect(checkbox).toHaveAttribute('data-state', 'checked')
174
+ })
175
+
176
+ it('renders disabled state correctly', () => {
177
+ render(<Checkbox disabled data-testid="checkbox" />)
178
+ const checkbox = screen.getByTestId('checkbox')
179
+ expect(checkbox).toBeDisabled()
180
+ expect(checkbox).toHaveClass('disabled:cursor-not-allowed')
181
+ expect(checkbox).toHaveClass('disabled:opacity-50')
182
+ })
183
+
184
+ it('handles controlled state', () => {
185
+ const handleChange = jest.fn()
186
+ render(<Checkbox checked={true} onCheckedChange={handleChange} data-testid="checkbox" />)
187
+ const checkbox = screen.getByTestId('checkbox')
188
+ expect(checkbox).toHaveAttribute('aria-checked', 'true')
189
+ })
190
+ })
191
+
192
+ describe('Indeterminate State', () => {
193
+ it('renders indeterminate state correctly', () => {
194
+ render(<Checkbox indeterminate data-testid="checkbox" />)
195
+ const checkbox = screen.getByTestId('checkbox')
196
+ expect(checkbox).toHaveAttribute('aria-checked', 'false')
197
+ expect(checkbox).toHaveAttribute('data-state', 'unchecked')
198
+ })
199
+
200
+ it('shows minus icon when indeterminate and checked', () => {
201
+ render(<Checkbox indeterminate checked data-testid="checkbox" />)
202
+ const checkbox = screen.getByTestId('checkbox')
203
+ // Even with checked=true, indeterminate overrides to false
204
+ expect(checkbox).toHaveAttribute('aria-checked', 'false')
205
+ expect(checkbox).toHaveAttribute('data-state', 'unchecked')
206
+ })
207
+
208
+ it('overrides checked state when indeterminate', () => {
209
+ render(<Checkbox checked indeterminate data-testid="checkbox" />)
210
+ const checkbox = screen.getByTestId('checkbox')
211
+ expect(checkbox).toHaveAttribute('aria-checked', 'false')
212
+ })
213
+
214
+ it('updates indeterminate state via prop', () => {
215
+ const { rerender } = render(<Checkbox indeterminate={false} data-testid="checkbox" />)
216
+ let checkbox = screen.getByTestId('checkbox')
217
+ expect(checkbox).toHaveAttribute('aria-checked', 'false')
218
+
219
+ rerender(<Checkbox indeterminate={true} data-testid="checkbox" />)
220
+ checkbox = screen.getByTestId('checkbox')
221
+ expect(checkbox).toHaveAttribute('aria-checked', 'false')
222
+ })
223
+ })
224
+
225
+ describe('Custom Icon', () => {
226
+ it('renders custom icon when provided', () => {
227
+ render(<Checkbox icon={<span data-testid="custom-icon">★</span>} checked data-testid="checkbox" />)
228
+ expect(screen.getByTestId('custom-icon')).toBeInTheDocument()
229
+ })
230
+
231
+ it('uses default check icon when no custom icon', () => {
232
+ render(<Checkbox checked data-testid="checkbox" />)
233
+ const checkbox = screen.getByTestId('checkbox')
234
+ const checkIcon = checkbox.querySelector('svg')
235
+ expect(checkIcon).toBeInTheDocument()
236
+ })
237
+
238
+ it('prioritizes indeterminate over custom icon', () => {
239
+ render(
240
+ <Checkbox
241
+ indeterminate
242
+ icon={<span data-testid="custom-icon">★</span>}
243
+ data-testid="checkbox"
244
+ />
245
+ )
246
+ expect(screen.queryByTestId('custom-icon')).not.toBeInTheDocument()
247
+
248
+ const checkbox = screen.getByTestId('checkbox')
249
+ // Check that custom icon is not rendered
250
+ expect(screen.queryByTestId('custom-icon')).not.toBeInTheDocument()
251
+ // Indeterminate state should show as unchecked
252
+ expect(checkbox).toHaveAttribute('aria-checked', 'false')
253
+ })
254
+ })
255
+
256
+ describe('Interactions', () => {
257
+ it('handles click events', () => {
258
+ const handleChange = jest.fn()
259
+ render(<Checkbox onCheckedChange={handleChange} data-testid="checkbox" />)
260
+ const checkbox = screen.getByTestId('checkbox')
261
+
262
+ fireEvent.click(checkbox)
263
+ expect(handleChange).toHaveBeenCalledWith(true)
264
+ })
265
+
266
+ it('does not trigger events when disabled', () => {
267
+ const handleChange = jest.fn()
268
+ render(<Checkbox disabled onCheckedChange={handleChange} data-testid="checkbox" />)
269
+ const checkbox = screen.getByTestId('checkbox')
270
+
271
+ fireEvent.click(checkbox)
272
+ expect(handleChange).not.toHaveBeenCalled()
273
+ })
274
+
275
+ it('is focusable for keyboard navigation', () => {
276
+ render(<Checkbox data-testid="checkbox" />)
277
+ const checkbox = screen.getByTestId('checkbox')
278
+
279
+ checkbox.focus()
280
+ expect(checkbox).toHaveFocus()
281
+ })
282
+ })
283
+
284
+ describe('Accessibility', () => {
285
+ it('has correct ARIA attributes', () => {
286
+ render(<Checkbox data-testid="checkbox" />)
287
+ const checkbox = screen.getByTestId('checkbox')
288
+ expect(checkbox).toHaveAttribute('role', 'checkbox')
289
+ expect(checkbox).toHaveAttribute('aria-checked', 'false')
290
+ })
291
+
292
+ it('supports custom ARIA attributes', () => {
293
+ render(<Checkbox aria-label="Accept terms" data-testid="checkbox" />)
294
+ const checkbox = screen.getByTestId('checkbox')
295
+ expect(checkbox).toHaveAttribute('aria-label', 'Accept terms')
296
+ })
297
+
298
+ it('has focus-visible styles', () => {
299
+ render(<Checkbox data-testid="checkbox" />)
300
+ const checkbox = screen.getByTestId('checkbox')
301
+ expect(checkbox).toHaveClass('focus-visible:outline-none')
302
+ expect(checkbox).toHaveClass('focus-visible:ring-2')
303
+ })
304
+ })
305
+ })
306
+
307
+ describe('CheckboxGroup Component', () => {
308
+ it('renders correctly with default props', () => {
309
+ render(
310
+ <CheckboxGroup data-testid="checkbox-group">
311
+ <Checkbox />
312
+ <Checkbox />
313
+ </CheckboxGroup>
314
+ )
315
+ const group = screen.getByTestId('checkbox-group')
316
+ expect(group).toBeInTheDocument()
317
+ expect(group).toHaveAttribute('role', 'group')
318
+ expect(group).toHaveClass('flex', 'flex-col')
319
+ })
320
+
321
+ it('renders horizontal orientation correctly', () => {
322
+ render(
323
+ <CheckboxGroup orientation="horizontal" data-testid="checkbox-group">
324
+ <Checkbox />
325
+ </CheckboxGroup>
326
+ )
327
+ const group = screen.getByTestId('checkbox-group')
328
+ expect(group).toHaveClass('flex-row', 'flex-wrap')
329
+ })
330
+
331
+ it('renders vertical orientation correctly', () => {
332
+ render(
333
+ <CheckboxGroup orientation="vertical" data-testid="checkbox-group">
334
+ <Checkbox />
335
+ </CheckboxGroup>
336
+ )
337
+ const group = screen.getByTestId('checkbox-group')
338
+ expect(group).toHaveClass('flex-col')
339
+ })
340
+
341
+ it('applies custom spacing', () => {
342
+ render(
343
+ <CheckboxGroup spacing="2rem" data-testid="checkbox-group">
344
+ <Checkbox />
345
+ </CheckboxGroup>
346
+ )
347
+ const group = screen.getByTestId('checkbox-group')
348
+ expect(group).toHaveStyle({ gap: '2rem' })
349
+ })
350
+
351
+ it('applies custom spacing as number', () => {
352
+ render(
353
+ <CheckboxGroup spacing={24} data-testid="checkbox-group">
354
+ <Checkbox />
355
+ </CheckboxGroup>
356
+ )
357
+ const group = screen.getByTestId('checkbox-group')
358
+ expect(group).toHaveStyle({ gap: '24px' })
359
+ })
360
+
361
+ it('forwards ref correctly', () => {
362
+ const ref = React.createRef<HTMLDivElement>()
363
+ render(
364
+ <CheckboxGroup ref={ref} data-testid="checkbox-group">
365
+ <Checkbox />
366
+ </CheckboxGroup>
367
+ )
368
+ expect(ref.current).toBeInstanceOf(HTMLDivElement)
369
+ })
370
+
371
+ it('maintains displayName', () => {
372
+ expect(CheckboxGroup.displayName).toBe('CheckboxGroup')
373
+ })
374
+ })
375
+
376
+ describe('CheckboxLabel Component', () => {
377
+ it('renders correctly with default props', () => {
378
+ render(<CheckboxLabel data-testid="checkbox-label">Label text</CheckboxLabel>)
379
+ const label = screen.getByTestId('checkbox-label')
380
+ expect(label).toBeInTheDocument()
381
+ expect(label.tagName).toBe('LABEL')
382
+ expect(label).toHaveTextContent('Label text')
383
+ })
384
+
385
+ it('applies htmlFor attribute', () => {
386
+ render(<CheckboxLabel htmlFor="checkbox-1" data-testid="checkbox-label">Label</CheckboxLabel>)
387
+ const label = screen.getByTestId('checkbox-label')
388
+ expect(label).toHaveAttribute('for', 'checkbox-1')
389
+ })
390
+
391
+ it('renders start position correctly', () => {
392
+ render(<CheckboxLabel position="start" data-testid="checkbox-label">Label</CheckboxLabel>)
393
+ const label = screen.getByTestId('checkbox-label')
394
+ expect(label).toHaveClass('mr-2')
395
+ })
396
+
397
+ it('renders end position correctly', () => {
398
+ render(<CheckboxLabel position="end" data-testid="checkbox-label">Label</CheckboxLabel>)
399
+ const label = screen.getByTestId('checkbox-label')
400
+ expect(label).toHaveClass('ml-2')
401
+ })
402
+
403
+ it('renders disabled state correctly', () => {
404
+ render(<CheckboxLabel disabled data-testid="checkbox-label">Label</CheckboxLabel>)
405
+ const label = screen.getByTestId('checkbox-label')
406
+ expect(label).toHaveClass('cursor-not-allowed', 'opacity-70')
407
+ })
408
+
409
+ it('forwards ref correctly', () => {
410
+ const ref = React.createRef<HTMLLabelElement>()
411
+ render(<CheckboxLabel ref={ref} data-testid="checkbox-label">Label</CheckboxLabel>)
412
+ expect(ref.current).toBeInstanceOf(HTMLLabelElement)
413
+ })
414
+
415
+ it('maintains displayName', () => {
416
+ expect(CheckboxLabel.displayName).toBe('CheckboxLabel')
417
+ })
418
+ })
419
+
420
+ describe('CheckboxWithLabel Component', () => {
421
+ it('renders correctly with default props', () => {
422
+ render(<CheckboxWithLabel label="Accept terms" data-testid="checkbox-with-label" />)
423
+ const container = screen.getByTestId('checkbox-with-label').parentElement
424
+ expect(container).toHaveClass('flex', 'items-center')
425
+ expect(screen.getByText('Accept terms')).toBeInTheDocument()
426
+ })
427
+
428
+ it('renders label at start position', () => {
429
+ render(<CheckboxWithLabel label="Accept terms" labelPosition="start" data-testid="checkbox-with-label" />)
430
+ const container = screen.getByTestId('checkbox-with-label').parentElement
431
+ const label = container?.querySelector('label')
432
+ const checkbox = container?.querySelector('[role="checkbox"]')
433
+
434
+ expect(label).toBeInTheDocument()
435
+ expect(checkbox).toBeInTheDocument()
436
+
437
+ // Label should come before checkbox in DOM
438
+ const elements = Array.from(container?.children || [])
439
+ const labelIndex = elements.indexOf(label!)
440
+ const checkboxIndex = elements.indexOf(checkbox!)
441
+ expect(labelIndex).toBeLessThan(checkboxIndex)
442
+ })
443
+
444
+ it('renders label at end position', () => {
445
+ render(<CheckboxWithLabel label="Accept terms" labelPosition="end" data-testid="checkbox-with-label" />)
446
+ const container = screen.getByTestId('checkbox-with-label').parentElement
447
+ const label = container?.querySelector('label')
448
+ const checkbox = container?.querySelector('[role="checkbox"]')
449
+
450
+ expect(label).toBeInTheDocument()
451
+ expect(checkbox).toBeInTheDocument()
452
+
453
+ // Checkbox should come before label in DOM
454
+ const elements = Array.from(container?.children || [])
455
+ const labelIndex = elements.indexOf(label!)
456
+ const checkboxIndex = elements.indexOf(checkbox!)
457
+ expect(checkboxIndex).toBeLessThan(labelIndex)
458
+ })
459
+
460
+ it('applies custom label className', () => {
461
+ render(
462
+ <CheckboxWithLabel
463
+ label="Accept terms"
464
+ labelClassName="custom-label"
465
+ data-testid="checkbox-with-label"
466
+ />
467
+ )
468
+ const label = screen.getByText('Accept terms')
469
+ expect(label).toHaveClass('custom-label')
470
+ })
471
+
472
+ it('generates unique id when not provided', () => {
473
+ render(<CheckboxWithLabel label="Accept terms" data-testid="checkbox-with-label" />)
474
+ const checkbox = screen.getByTestId('checkbox-with-label')
475
+ const id = checkbox.getAttribute('id')
476
+ expect(id).toMatch(/^checkbox-[a-z0-9]+$/)
477
+ })
478
+
479
+ it('uses provided id', () => {
480
+ render(<CheckboxWithLabel id="custom-id" label="Accept terms" data-testid="checkbox-with-label" />)
481
+ const checkbox = screen.getByTestId('checkbox-with-label')
482
+ expect(checkbox).toHaveAttribute('id', 'custom-id')
483
+
484
+ const label = screen.getByText('Accept terms')
485
+ expect(label).toHaveAttribute('for', 'custom-id')
486
+ })
487
+
488
+ it('forwards ref correctly', () => {
489
+ const ref = React.createRef<HTMLButtonElement>()
490
+ render(<CheckboxWithLabel ref={ref} label="Accept terms" data-testid="checkbox-with-label" />)
491
+ expect(ref.current).toBeInstanceOf(HTMLButtonElement)
492
+ })
493
+
494
+ it('passes checkbox props correctly', () => {
495
+ render(
496
+ <CheckboxWithLabel
497
+ label="Accept terms"
498
+ checked
499
+ disabled
500
+ variant="outline"
501
+ data-testid="checkbox-with-label"
502
+ />
503
+ )
504
+ const checkbox = screen.getByTestId('checkbox-with-label')
505
+ expect(checkbox).toHaveAttribute('aria-checked', 'true')
506
+ expect(checkbox).toBeDisabled()
507
+ expect(checkbox).toHaveClass('bg-transparent')
508
+ })
509
+
510
+ it('maintains displayName', () => {
511
+ expect(CheckboxWithLabel.displayName).toBe('CheckboxWithLabel')
512
+ })
513
+ })
514
+
515
+ describe('Complex Combinations', () => {
516
+ it('renders checkbox with all props correctly', () => {
517
+ const handleChange = jest.fn()
518
+ render(
519
+ <Checkbox
520
+ variant="outline"
521
+ size="lg"
522
+ radius="full"
523
+ animation="bounce"
524
+ checked={true}
525
+ indeterminate={false}
526
+ onCheckedChange={handleChange}
527
+ className="custom-class"
528
+ data-testid="checkbox"
529
+ />
530
+ )
531
+
532
+ const checkbox = screen.getByTestId('checkbox')
533
+ expect(checkbox).toHaveClass('bg-transparent')
534
+ expect(checkbox).toHaveClass('h-6', 'w-6')
535
+ expect(checkbox).toHaveClass('rounded-full')
536
+ expect(checkbox).toHaveClass('transition-all')
537
+ expect(checkbox).toHaveClass('custom-class')
538
+ expect(checkbox).toHaveAttribute('aria-checked', 'true')
539
+ })
540
+
541
+ it('renders checkbox group with multiple checkboxes', () => {
542
+ render(
543
+ <CheckboxGroup orientation="horizontal" spacing="1.5rem" data-testid="checkbox-group">
544
+ <CheckboxWithLabel label="Option 1" />
545
+ <CheckboxWithLabel label="Option 2" />
546
+ <CheckboxWithLabel label="Option 3" />
547
+ </CheckboxGroup>
548
+ )
549
+
550
+ const group = screen.getByTestId('checkbox-group')
551
+ expect(group).toHaveClass('flex-row')
552
+ expect(group).toHaveStyle({ gap: '1.5rem' })
553
+
554
+ expect(screen.getByText('Option 1')).toBeInTheDocument()
555
+ expect(screen.getByText('Option 2')).toBeInTheDocument()
556
+ expect(screen.getByText('Option 3')).toBeInTheDocument()
557
+ })
558
+ })
559
+
560
+ describe('Edge Cases', () => {
561
+ it('passes through HTML attributes', () => {
562
+ render(<Checkbox data-testid="checkbox-test" id="checkbox-1" />)
563
+ const checkbox = screen.getByTestId('checkbox-test')
564
+ expect(checkbox).toHaveAttribute('id', 'checkbox-1')
565
+ })
566
+
567
+ it('handles null icon gracefully', () => {
568
+ render(<Checkbox icon={null} checked data-testid="checkbox" />)
569
+ const checkbox = screen.getByTestId('checkbox')
570
+ expect(checkbox).toBeInTheDocument()
571
+ })
572
+
573
+ it('handles empty label in CheckboxWithLabel', () => {
574
+ render(<CheckboxWithLabel label="" data-testid="checkbox-with-label" />)
575
+ const container = screen.getByTestId('checkbox-with-label').parentElement
576
+ const label = container?.querySelector('label')
577
+ expect(label).toBeInTheDocument()
578
+ expect(label).toHaveTextContent('')
579
+ })
580
+
581
+ it('handles zero spacing in CheckboxGroup', () => {
582
+ render(
583
+ <CheckboxGroup spacing={0} data-testid="checkbox-group">
584
+ <Checkbox />
585
+ </CheckboxGroup>
586
+ )
587
+ const group = screen.getByTestId('checkbox-group')
588
+ expect(group).toHaveStyle({ gap: 0 })
589
+ })
590
+ })
591
+ })