@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,677 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import React from 'react'
3
+ import { render, screen, fireEvent } from '@testing-library/react'
4
+ import '@testing-library/jest-dom'
5
+ import {
6
+ Command,
7
+ CommandDialog,
8
+ CommandInput,
9
+ CommandList,
10
+ CommandEmpty,
11
+ CommandGroup,
12
+ CommandItem,
13
+ CommandShortcut,
14
+ CommandSeparator,
15
+ commandVariants,
16
+ commandInputVariants,
17
+ commandListVariants,
18
+ commandGroupVariants,
19
+ commandItemVariants,
20
+ } from '../command'
21
+
22
+ // Mock Dialog component
23
+ jest.mock('./dialog', () => ({
24
+ Dialog: ({ children, open, ...props }: any) => (
25
+ <div data-testid="dialog" data-open={open ? 'true' : 'false'} {...props}>{children}</div>
26
+ ),
27
+ DialogContent: ({ children, className, ...props }: React.ComponentProps<'div'>) => (
28
+ <div data-testid="dialog-content" className={className} {...props}>{children}</div>
29
+ ),
30
+ }))
31
+
32
+ describe('Command Components', () => {
33
+ describe('Command Component', () => {
34
+ it('renders correctly with default props', () => {
35
+ render(<Command data-testid="command">Content</Command>)
36
+
37
+ const command = screen.getByTestId('command')
38
+ expect(command).toBeInTheDocument()
39
+ expect(command).toHaveClass('flex h-full w-full flex-col')
40
+ })
41
+
42
+ it('applies custom className', () => {
43
+ render(<Command className="custom-class" data-testid="command">Content</Command>)
44
+
45
+ const command = screen.getByTestId('command')
46
+ expect(command).toHaveClass('custom-class')
47
+ })
48
+
49
+ it('forwards ref correctly', () => {
50
+ const ref = React.createRef<HTMLDivElement>()
51
+ render(<Command ref={ref} data-testid="command">Content</Command>)
52
+
53
+ expect(ref.current).toBeInstanceOf(HTMLDivElement)
54
+ })
55
+
56
+ it('maintains displayName', () => {
57
+ expect(Command.displayName).toBe('Command')
58
+ })
59
+
60
+ it('renders default variant correctly', () => {
61
+ render(<Command data-testid="command">Content</Command>)
62
+
63
+ const command = screen.getByTestId('command')
64
+ expect(command).toHaveClass('bg-popover text-popover-foreground')
65
+ })
66
+
67
+ it('renders glass variant correctly', () => {
68
+ render(<Command variant="glass" data-testid="command">Content</Command>)
69
+
70
+ const command = screen.getByTestId('command')
71
+ expect(command).toHaveClass('bg-background/80 backdrop-blur-sm')
72
+ })
73
+
74
+ it('renders bordered variant correctly', () => {
75
+ render(<Command variant="bordered" data-testid="command">Content</Command>)
76
+
77
+ const command = screen.getByTestId('command')
78
+ expect(command).toHaveClass('border border-border')
79
+ })
80
+
81
+ it('renders sm size correctly', () => {
82
+ render(<Command size="sm" data-testid="command">Content</Command>)
83
+
84
+ const command = screen.getByTestId('command')
85
+ expect(command).toHaveClass('p-2')
86
+ })
87
+
88
+ it('renders default size correctly', () => {
89
+ render(<Command data-testid="command">Content</Command>)
90
+
91
+ const command = screen.getByTestId('command')
92
+ expect(command).toHaveClass('p-4')
93
+ })
94
+
95
+ it('renders lg size correctly', () => {
96
+ render(<Command size="lg" data-testid="command">Content</Command>)
97
+
98
+ const command = screen.getByTestId('command')
99
+ expect(command).toHaveClass('p-6')
100
+ })
101
+
102
+ it('passes through HTML attributes', () => {
103
+ render(<Command data-testid="command" id="command-1">Content</Command>)
104
+
105
+ const command = screen.getByTestId('command')
106
+ expect(command).toHaveAttribute('id', 'command-1')
107
+ })
108
+ })
109
+
110
+ describe('CommandDialog Component', () => {
111
+ it('renders correctly with default props', () => {
112
+ render(
113
+ <CommandDialog open={true}>
114
+ <div>Dialog Content</div>
115
+ </CommandDialog>
116
+ )
117
+
118
+ const dialog = screen.getByTestId('dialog')
119
+ const dialogContent = screen.getByTestId('dialog-content')
120
+ expect(dialog).toBeInTheDocument()
121
+ expect(dialogContent).toBeInTheDocument()
122
+ })
123
+
124
+ it('applies custom commandClassName', () => {
125
+ render(
126
+ <CommandDialog open={true} commandClassName="custom-command">
127
+ <div>Dialog Content</div>
128
+ </CommandDialog>
129
+ )
130
+
131
+ const dialogContent = screen.getByTestId('dialog-content')
132
+ expect(dialogContent.querySelector('[class*="custom-command"]')).toBeInTheDocument()
133
+ })
134
+
135
+ it('passes through dialog props', () => {
136
+ const onOpenChange = jest.fn()
137
+ render(
138
+ <CommandDialog open={true} onOpenChange={onOpenChange}>
139
+ <div>Dialog Content</div>
140
+ </CommandDialog>
141
+ )
142
+
143
+ const dialog = screen.getByTestId('dialog')
144
+ expect(dialog).toHaveAttribute('data-open', 'true')
145
+ })
146
+ })
147
+
148
+ describe('CommandInput Component', () => {
149
+ it('renders correctly with default props', () => {
150
+ render(<CommandInput data-testid="command-input" />)
151
+
152
+ const input = screen.getByTestId('command-input')
153
+ expect(input).toBeInTheDocument()
154
+ expect(input).toHaveClass('flex')
155
+ expect(input).toHaveClass('h-11')
156
+ expect(input).toHaveClass('w-full')
157
+ })
158
+
159
+ it('renders search icon', () => {
160
+ render(<CommandInput />)
161
+
162
+ const searchIcon = document.querySelector('svg')
163
+ expect(searchIcon).toBeInTheDocument()
164
+ expect(searchIcon).toHaveClass('h-4 w-4 shrink-0 opacity-50')
165
+ })
166
+
167
+ it('applies custom className', () => {
168
+ render(<CommandInput className="custom-input" data-testid="command-input" />)
169
+
170
+ const input = screen.getByTestId('command-input')
171
+ expect(input).toHaveClass('custom-input')
172
+ })
173
+
174
+ it('forwards ref correctly', () => {
175
+ const ref = React.createRef<HTMLInputElement>()
176
+ render(<CommandInput ref={ref} />)
177
+
178
+ expect(ref.current).toBeInstanceOf(HTMLInputElement)
179
+ })
180
+
181
+ it('maintains displayName', () => {
182
+ expect(CommandInput.displayName).toBe('CommandInput')
183
+ })
184
+
185
+ it('renders minimal variant correctly', () => {
186
+ render(<CommandInput variant="minimal" data-testid="command-input" />)
187
+
188
+ const input = screen.getByTestId('command-input')
189
+ expect(input).toHaveClass('h-9')
190
+ })
191
+
192
+ it('renders bordered variant correctly', () => {
193
+ render(<CommandInput variant="bordered" data-testid="command-input" />)
194
+
195
+ const input = screen.getByTestId('command-input')
196
+ expect(input).toHaveClass('border-b border-border')
197
+ })
198
+
199
+ it('handles input events', () => {
200
+ const onChange = jest.fn()
201
+ render(<CommandInput onChange={onChange} />)
202
+
203
+ const input = screen.getByRole('textbox')
204
+ fireEvent.change(input, { target: { value: 'test' } })
205
+ expect(onChange).toHaveBeenCalled()
206
+ })
207
+
208
+ it('passes through HTML attributes', () => {
209
+ render(<CommandInput placeholder="Search..." id="search-input" />)
210
+
211
+ const input = screen.getByRole('textbox')
212
+ expect(input).toHaveAttribute('placeholder', 'Search...')
213
+ expect(input).toHaveAttribute('id', 'search-input')
214
+ })
215
+ })
216
+
217
+ describe('CommandList Component', () => {
218
+ it('renders correctly with default props', () => {
219
+ render(<CommandList data-testid="command-list">List Content</CommandList>)
220
+
221
+ const list = screen.getByTestId('command-list')
222
+ expect(list).toBeInTheDocument()
223
+ expect(list).toHaveClass('max-h-[300px] overflow-y-auto')
224
+ expect(list).toHaveAttribute('role', 'listbox')
225
+ })
226
+
227
+ it('applies custom className', () => {
228
+ render(<CommandList className="custom-list" data-testid="command-list">Content</CommandList>)
229
+
230
+ const list = screen.getByTestId('command-list')
231
+ expect(list).toHaveClass('custom-list')
232
+ })
233
+
234
+ it('forwards ref correctly', () => {
235
+ const ref = React.createRef<HTMLDivElement>()
236
+ render(<CommandList ref={ref}>Content</CommandList>)
237
+
238
+ expect(ref.current).toBeInstanceOf(HTMLDivElement)
239
+ })
240
+
241
+ it('maintains displayName', () => {
242
+ expect(CommandList.displayName).toBe('CommandList')
243
+ })
244
+
245
+ it('renders scrollable variant correctly', () => {
246
+ render(<CommandList variant="scrollable" data-testid="command-list">Content</CommandList>)
247
+
248
+ const list = screen.getByTestId('command-list')
249
+ expect(list).toHaveClass('max-h-[400px]')
250
+ })
251
+
252
+ it('renders compact variant correctly', () => {
253
+ render(<CommandList variant="compact" data-testid="command-list">Content</CommandList>)
254
+
255
+ const list = screen.getByTestId('command-list')
256
+ expect(list).toHaveClass('max-h-[200px]')
257
+ })
258
+
259
+ it('passes through HTML attributes', () => {
260
+ render(<CommandList data-testid="command-list" id="list-1">Content</CommandList>)
261
+
262
+ const list = screen.getByTestId('command-list')
263
+ expect(list).toHaveAttribute('id', 'list-1')
264
+ })
265
+ })
266
+
267
+ describe('CommandEmpty Component', () => {
268
+ it('renders correctly with default props', () => {
269
+ render(<CommandEmpty data-testid="command-empty">No results</CommandEmpty>)
270
+
271
+ const empty = screen.getByTestId('command-empty')
272
+ expect(empty).toBeInTheDocument()
273
+ expect(empty).toHaveClass('py-6 text-center text-sm text-muted-foreground')
274
+ })
275
+
276
+ it('applies custom className', () => {
277
+ render(<CommandEmpty className="custom-empty" data-testid="command-empty">No results</CommandEmpty>)
278
+
279
+ const empty = screen.getByTestId('command-empty')
280
+ expect(empty).toHaveClass('custom-empty')
281
+ })
282
+
283
+ it('forwards ref correctly', () => {
284
+ const ref = React.createRef<HTMLDivElement>()
285
+ render(<CommandEmpty ref={ref}>No results</CommandEmpty>)
286
+
287
+ expect(ref.current).toBeInstanceOf(HTMLDivElement)
288
+ })
289
+
290
+ it('maintains displayName', () => {
291
+ expect(CommandEmpty.displayName).toBe('CommandEmpty')
292
+ })
293
+
294
+ it('passes through HTML attributes', () => {
295
+ render(<CommandEmpty data-testid="command-empty" id="empty-1">No results</CommandEmpty>)
296
+
297
+ const empty = screen.getByTestId('command-empty')
298
+ expect(empty).toHaveAttribute('id', 'empty-1')
299
+ })
300
+ })
301
+
302
+ describe('CommandGroup Component', () => {
303
+ it('renders correctly with default props', () => {
304
+ render(<CommandGroup data-testid="command-group">Group Content</CommandGroup>)
305
+
306
+ const group = screen.getByTestId('command-group')
307
+ expect(group).toBeInTheDocument()
308
+ expect(group).toHaveClass('overflow-hidden p-1 text-foreground')
309
+ })
310
+
311
+ it('applies custom className', () => {
312
+ render(<CommandGroup className="custom-group" data-testid="command-group">Content</CommandGroup>)
313
+
314
+ const group = screen.getByTestId('command-group')
315
+ expect(group).toHaveClass('custom-group')
316
+ })
317
+
318
+ it('forwards ref correctly', () => {
319
+ const ref = React.createRef<HTMLDivElement>()
320
+ render(<CommandGroup ref={ref}>Content</CommandGroup>)
321
+
322
+ expect(ref.current).toBeInstanceOf(HTMLDivElement)
323
+ })
324
+
325
+ it('maintains displayName', () => {
326
+ expect(CommandGroup.displayName).toBe('CommandGroup')
327
+ })
328
+
329
+ it('renders heading when provided', () => {
330
+ render(
331
+ <CommandGroup heading="Group Title" data-testid="command-group">
332
+ Group Content
333
+ </CommandGroup>
334
+ )
335
+
336
+ expect(screen.getByText('Group Title')).toBeInTheDocument()
337
+ const heading = screen.getByText('Group Title')
338
+ expect(heading).toHaveClass('px-2 py-1.5 text-xs font-medium text-muted-foreground')
339
+ })
340
+
341
+ it('renders without heading', () => {
342
+ render(<CommandGroup data-testid="command-group">Group Content</CommandGroup>)
343
+
344
+ const group = screen.getByTestId('command-group')
345
+ expect(group.querySelector('.px-2.py-1\\.5')).not.toBeInTheDocument()
346
+ })
347
+
348
+ it('renders separated variant correctly', () => {
349
+ render(<CommandGroup variant="separated" data-testid="command-group">Content</CommandGroup>)
350
+
351
+ const group = screen.getByTestId('command-group')
352
+ expect(group).toHaveClass('mt-2 border-t border-border pt-2')
353
+ })
354
+
355
+ it('renders indented variant correctly', () => {
356
+ render(<CommandGroup variant="indented" data-testid="command-group">Content</CommandGroup>)
357
+
358
+ const group = screen.getByTestId('command-group')
359
+ expect(group).toHaveClass('pl-4')
360
+ })
361
+
362
+ it('passes through HTML attributes', () => {
363
+ render(<CommandGroup data-testid="command-group" id="group-1">Content</CommandGroup>)
364
+
365
+ const group = screen.getByTestId('command-group')
366
+ expect(group).toHaveAttribute('id', 'group-1')
367
+ })
368
+ })
369
+
370
+ describe('CommandItem Component', () => {
371
+ it('renders correctly with default props', () => {
372
+ render(<CommandItem data-testid="command-item">Item Content</CommandItem>)
373
+
374
+ const item = screen.getByTestId('command-item')
375
+ expect(item).toBeInTheDocument()
376
+ expect(item).toHaveClass('relative flex cursor-default select-none')
377
+ expect(item).toHaveAttribute('role', 'option')
378
+ })
379
+
380
+ it('applies custom className', () => {
381
+ render(<CommandItem className="custom-item" data-testid="command-item">Content</CommandItem>)
382
+
383
+ const item = screen.getByTestId('command-item')
384
+ expect(item).toHaveClass('custom-item')
385
+ })
386
+
387
+ it('forwards ref correctly', () => {
388
+ const ref = React.createRef<HTMLDivElement>()
389
+ render(<CommandItem ref={ref}>Content</CommandItem>)
390
+
391
+ expect(ref.current).toBeInstanceOf(HTMLDivElement)
392
+ })
393
+
394
+ it('maintains displayName', () => {
395
+ expect(CommandItem.displayName).toBe('CommandItem')
396
+ })
397
+
398
+ it('handles selected state', () => {
399
+ render(<CommandItem selected={true} data-testid="command-item">Content</CommandItem>)
400
+
401
+ const item = screen.getByTestId('command-item')
402
+ expect(item).toHaveAttribute('aria-selected', 'true')
403
+ })
404
+
405
+ it('handles disabled state', () => {
406
+ render(<CommandItem disabled={true} data-testid="command-item">Content</CommandItem>)
407
+
408
+ const item = screen.getByTestId('command-item')
409
+ expect(item).toHaveAttribute('data-disabled', '')
410
+ })
411
+
412
+ it('handles click events with onSelect', () => {
413
+ const onSelect = jest.fn()
414
+ render(
415
+ <CommandItem onSelect={onSelect} value="test-value" data-testid="command-item">
416
+ Content
417
+ </CommandItem>
418
+ )
419
+
420
+ const item = screen.getByTestId('command-item')
421
+ fireEvent.click(item)
422
+ expect(onSelect).toHaveBeenCalledWith('test-value')
423
+ })
424
+
425
+ it('does not call onSelect when disabled', () => {
426
+ const onSelect = jest.fn()
427
+ render(
428
+ <CommandItem onSelect={onSelect} disabled={true} value="test-value" data-testid="command-item">
429
+ Content
430
+ </CommandItem>
431
+ )
432
+
433
+ const item = screen.getByTestId('command-item')
434
+ fireEvent.click(item)
435
+ expect(onSelect).not.toHaveBeenCalled()
436
+ })
437
+
438
+ it('renders subtle variant correctly', () => {
439
+ render(<CommandItem variant="subtle" data-testid="command-item">Content</CommandItem>)
440
+
441
+ const item = screen.getByTestId('command-item')
442
+ expect(item).toHaveClass('aria-selected:bg-accent/10')
443
+ })
444
+
445
+ it('renders ghost variant correctly', () => {
446
+ render(<CommandItem variant="ghost" data-testid="command-item">Content</CommandItem>)
447
+
448
+ const item = screen.getByTestId('command-item')
449
+ expect(item).toHaveClass('hover:bg-muted/10')
450
+ })
451
+
452
+ it('passes through HTML attributes', () => {
453
+ render(<CommandItem data-testid="command-item" id="item-1">Content</CommandItem>)
454
+
455
+ const item = screen.getByTestId('command-item')
456
+ expect(item).toHaveAttribute('id', 'item-1')
457
+ })
458
+ })
459
+
460
+ describe('CommandShortcut Component', () => {
461
+ it('renders correctly with default props', () => {
462
+ render(<CommandShortcut data-testid="command-shortcut">⌘K</CommandShortcut>)
463
+
464
+ const shortcut = screen.getByTestId('command-shortcut')
465
+ expect(shortcut).toBeInTheDocument()
466
+ expect(shortcut).toHaveClass('ml-auto text-xs tracking-widest text-muted-foreground')
467
+ })
468
+
469
+ it('applies custom className', () => {
470
+ render(<CommandShortcut className="custom-shortcut" data-testid="command-shortcut">⌘K</CommandShortcut>)
471
+
472
+ const shortcut = screen.getByTestId('command-shortcut')
473
+ expect(shortcut).toHaveClass('custom-shortcut')
474
+ })
475
+
476
+ it('maintains displayName', () => {
477
+ expect(CommandShortcut.displayName).toBe('CommandShortcut')
478
+ })
479
+
480
+ it('passes through HTML attributes', () => {
481
+ render(<CommandShortcut data-testid="command-shortcut" id="shortcut-1">⌘K</CommandShortcut>)
482
+
483
+ const shortcut = screen.getByTestId('command-shortcut')
484
+ expect(shortcut).toHaveAttribute('id', 'shortcut-1')
485
+ })
486
+ })
487
+
488
+ describe('CommandSeparator Component', () => {
489
+ it('renders correctly with default props', () => {
490
+ render(<CommandSeparator data-testid="command-separator" />)
491
+
492
+ const separator = screen.getByTestId('command-separator')
493
+ expect(separator).toBeInTheDocument()
494
+ expect(separator).toHaveClass('-mx-1 h-px bg-border')
495
+ })
496
+
497
+ it('applies custom className', () => {
498
+ render(<CommandSeparator className="custom-separator" data-testid="command-separator" />)
499
+
500
+ const separator = screen.getByTestId('command-separator')
501
+ expect(separator).toHaveClass('custom-separator')
502
+ })
503
+
504
+ it('forwards ref correctly', () => {
505
+ const ref = React.createRef<HTMLDivElement>()
506
+ render(<CommandSeparator ref={ref} />)
507
+
508
+ expect(ref.current).toBeInstanceOf(HTMLDivElement)
509
+ })
510
+
511
+ it('maintains displayName', () => {
512
+ expect(CommandSeparator.displayName).toBe('CommandSeparator')
513
+ })
514
+
515
+ it('passes through HTML attributes', () => {
516
+ render(<CommandSeparator data-testid="command-separator" id="separator-1" />)
517
+
518
+ const separator = screen.getByTestId('command-separator')
519
+ expect(separator).toHaveAttribute('id', 'separator-1')
520
+ })
521
+ })
522
+
523
+ describe('Variant Functions', () => {
524
+ it('commandVariants generates correct classes', () => {
525
+ expect(commandVariants()).toContain('flex h-full w-full flex-col')
526
+ expect(commandVariants({ variant: 'glass' })).toContain('bg-background/80')
527
+ expect(commandVariants({ size: 'lg' })).toContain('p-6')
528
+ })
529
+
530
+ it('commandInputVariants generates correct classes', () => {
531
+ expect(commandInputVariants()).toContain('flex h-11 w-full')
532
+ expect(commandInputVariants({ variant: 'minimal' })).toContain('h-9')
533
+ expect(commandInputVariants({ variant: 'bordered' })).toContain('border-b')
534
+ })
535
+
536
+ it('commandListVariants generates correct classes', () => {
537
+ expect(commandListVariants()).toContain('max-h-[300px]')
538
+ expect(commandListVariants({ variant: 'scrollable' })).toContain('max-h-[400px]')
539
+ expect(commandListVariants({ variant: 'compact' })).toContain('max-h-[200px]')
540
+ })
541
+
542
+ it('commandGroupVariants generates correct classes', () => {
543
+ expect(commandGroupVariants()).toContain('overflow-hidden p-1')
544
+ expect(commandGroupVariants({ variant: 'separated' })).toContain('border-t')
545
+ expect(commandGroupVariants({ variant: 'indented' })).toContain('pl-4')
546
+ })
547
+
548
+ it('commandItemVariants generates correct classes', () => {
549
+ expect(commandItemVariants()).toContain('relative flex cursor-default')
550
+ expect(commandItemVariants({ variant: 'subtle' })).toContain('aria-selected:bg-accent/10')
551
+ expect(commandItemVariants({ variant: 'ghost' })).toContain('hover:bg-muted/10')
552
+ })
553
+ })
554
+
555
+ describe('Complex Combinations', () => {
556
+ it('renders complete command palette', () => {
557
+ render(
558
+ <Command data-testid="command">
559
+ <CommandInput placeholder="Type a command..." />
560
+ <CommandList>
561
+ <CommandEmpty>No results found.</CommandEmpty>
562
+ <CommandGroup heading="Suggestions">
563
+ <CommandItem value="calendar">
564
+ Calendar
565
+ <CommandShortcut>⌘C</CommandShortcut>
566
+ </CommandItem>
567
+ <CommandItem value="search">
568
+ Search
569
+ <CommandShortcut>⌘S</CommandShortcut>
570
+ </CommandItem>
571
+ </CommandGroup>
572
+ <CommandSeparator />
573
+ <CommandGroup heading="Settings">
574
+ <CommandItem value="profile">Profile</CommandItem>
575
+ <CommandItem value="billing">Billing</CommandItem>
576
+ </CommandGroup>
577
+ </CommandList>
578
+ </Command>
579
+ )
580
+
581
+ expect(screen.getByTestId('command')).toBeInTheDocument()
582
+ expect(screen.getByPlaceholderText('Type a command...')).toBeInTheDocument()
583
+ expect(screen.getByText('Suggestions')).toBeInTheDocument()
584
+ expect(screen.getByText('Calendar')).toBeInTheDocument()
585
+ expect(screen.getByText('⌘C')).toBeInTheDocument()
586
+ })
587
+
588
+ it('renders command dialog with all components', () => {
589
+ render(
590
+ <CommandDialog open={true}>
591
+ <CommandInput placeholder="Search..." />
592
+ <CommandList>
593
+ <CommandEmpty>No results.</CommandEmpty>
594
+ <CommandGroup heading="Actions">
595
+ <CommandItem>Action 1</CommandItem>
596
+ <CommandItem>Action 2</CommandItem>
597
+ </CommandGroup>
598
+ </CommandList>
599
+ </CommandDialog>
600
+ )
601
+
602
+ expect(screen.getByTestId('dialog')).toBeInTheDocument()
603
+ expect(screen.getByPlaceholderText('Search...')).toBeInTheDocument()
604
+ expect(screen.getByText('Actions')).toBeInTheDocument()
605
+ })
606
+
607
+ it('handles all props and variants together', () => {
608
+ render(
609
+ <Command variant="bordered" size="lg" className="custom-command" data-testid="command">
610
+ <CommandInput variant="bordered" className="custom-input" />
611
+ <CommandList variant="scrollable" className="custom-list">
612
+ <CommandGroup variant="separated" heading="Test Group">
613
+ <CommandItem variant="subtle" selected={true} value="test">
614
+ Test Item
615
+ <CommandShortcut className="custom-shortcut">⌘T</CommandShortcut>
616
+ </CommandItem>
617
+ </CommandGroup>
618
+ <CommandSeparator className="custom-separator" />
619
+ </CommandList>
620
+ </Command>
621
+ )
622
+
623
+ const command = screen.getByTestId('command')
624
+ expect(command).toHaveClass('custom-command border p-6')
625
+ })
626
+ })
627
+
628
+ describe('Edge Cases', () => {
629
+ it('handles empty command', () => {
630
+ render(<Command data-testid="command"></Command>)
631
+
632
+ const command = screen.getByTestId('command')
633
+ expect(command).toBeInTheDocument()
634
+ })
635
+
636
+ it('handles command item without value', () => {
637
+ const onSelect = jest.fn()
638
+ render(<CommandItem onSelect={onSelect} data-testid="command-item">Item</CommandItem>)
639
+
640
+ const item = screen.getByTestId('command-item')
641
+ fireEvent.click(item)
642
+ expect(onSelect).not.toHaveBeenCalled()
643
+ })
644
+
645
+ it('handles command group with complex heading', () => {
646
+ render(
647
+ <CommandGroup heading={<span className="custom-heading">Complex Heading</span>}>
648
+ Content
649
+ </CommandGroup>
650
+ )
651
+
652
+ expect(screen.getByText('Complex Heading')).toBeInTheDocument()
653
+ expect(document.querySelector('.custom-heading')).toBeInTheDocument()
654
+ })
655
+
656
+ it('handles null and undefined children', () => {
657
+ render(
658
+ <Command>
659
+ {null}
660
+ {undefined}
661
+ <CommandInput />
662
+ {null}
663
+ </Command>
664
+ )
665
+
666
+ expect(screen.getByRole('textbox')).toBeInTheDocument()
667
+ })
668
+
669
+ it('handles disabled input', () => {
670
+ render(<CommandInput disabled placeholder="Disabled input" />)
671
+
672
+ const input = screen.getByRole('textbox')
673
+ expect(input).toBeDisabled()
674
+ expect(input).toHaveClass('disabled:cursor-not-allowed disabled:opacity-50')
675
+ })
676
+ })
677
+ })