@elizaos/client 1.5.5-alpha.10

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 (209) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +350 -0
  3. package/dist/assets/empty-module-CLMscLYw.js +1 -0
  4. package/dist/assets/main-BBZ_3lkn.css +5999 -0
  5. package/dist/assets/main-C5zNUkXH.js +7 -0
  6. package/dist/assets/main-Dz64ENQg.js +614 -0
  7. package/dist/assets/react-vendor-DM5m98rr.js +545 -0
  8. package/dist/assets/ui-vendor-BQCqNqg0.js +1 -0
  9. package/dist/elizaos-avatar.png +0 -0
  10. package/dist/elizaos-icon.png +0 -0
  11. package/dist/elizaos-logo-light.png +0 -0
  12. package/dist/elizaos.webp +0 -0
  13. package/dist/favicon.ico +0 -0
  14. package/dist/images/agents/agent1.png +0 -0
  15. package/dist/images/agents/agent2.png +0 -0
  16. package/dist/images/agents/agent3.png +0 -0
  17. package/dist/images/agents/agent4.png +0 -0
  18. package/dist/images/agents/agent5.png +0 -0
  19. package/dist/index.html +14 -0
  20. package/index.html +24 -0
  21. package/package.json +159 -0
  22. package/postcss.config.js +3 -0
  23. package/public/elizaos-avatar.png +0 -0
  24. package/public/elizaos-icon.png +0 -0
  25. package/public/elizaos-logo-light.png +0 -0
  26. package/public/elizaos.webp +0 -0
  27. package/public/favicon.ico +0 -0
  28. package/public/images/agents/agent1.png +0 -0
  29. package/public/images/agents/agent2.png +0 -0
  30. package/public/images/agents/agent3.png +0 -0
  31. package/public/images/agents/agent4.png +0 -0
  32. package/public/images/agents/agent5.png +0 -0
  33. package/src/App.tsx +222 -0
  34. package/src/components/AgentDetailsPanel.tsx +147 -0
  35. package/src/components/ChatInputArea.tsx +196 -0
  36. package/src/components/ChatMessageListComponent.tsx +139 -0
  37. package/src/components/actionTool.tsx +186 -0
  38. package/src/components/add-agent-card.tsx +77 -0
  39. package/src/components/agent-action-viewer.tsx +816 -0
  40. package/src/components/agent-avatar-stack.tsx +121 -0
  41. package/src/components/agent-card.cy.tsx +259 -0
  42. package/src/components/agent-card.tsx +177 -0
  43. package/src/components/agent-creator.tsx +142 -0
  44. package/src/components/agent-log-viewer.tsx +645 -0
  45. package/src/components/agent-memory-edit-overlay.tsx +461 -0
  46. package/src/components/agent-memory-viewer.tsx +504 -0
  47. package/src/components/agent-settings.tsx +270 -0
  48. package/src/components/agent-sidebar.tsx +178 -0
  49. package/src/components/api-key-dialog.tsx +113 -0
  50. package/src/components/app-sidebar.tsx +685 -0
  51. package/src/components/array-input.tsx +116 -0
  52. package/src/components/audio-recorder.tsx +292 -0
  53. package/src/components/avatar-panel.tsx +141 -0
  54. package/src/components/character-form.tsx +1138 -0
  55. package/src/components/chat.tsx +1813 -0
  56. package/src/components/combobox.tsx +187 -0
  57. package/src/components/confirmation-dialog.tsx +59 -0
  58. package/src/components/connection-error-banner.tsx +101 -0
  59. package/src/components/connection-status.cy.tsx +73 -0
  60. package/src/components/connection-status.tsx +155 -0
  61. package/src/components/copy-button.tsx +35 -0
  62. package/src/components/delete-button.tsx +24 -0
  63. package/src/components/env-settings.tsx +261 -0
  64. package/src/components/group-card.tsx +160 -0
  65. package/src/components/group-panel.tsx +543 -0
  66. package/src/components/input-copy.tsx +21 -0
  67. package/src/components/logs-page.tsx +41 -0
  68. package/src/components/media-content.tsx +385 -0
  69. package/src/components/memory-graph.tsx +170 -0
  70. package/src/components/missing-secrets-dialog.tsx +72 -0
  71. package/src/components/onboarding-tour.tsx +247 -0
  72. package/src/components/page-title.tsx +8 -0
  73. package/src/components/plugins-panel.tsx +383 -0
  74. package/src/components/profile-card.tsx +66 -0
  75. package/src/components/profile-overlay.tsx +283 -0
  76. package/src/components/retry-button.tsx +28 -0
  77. package/src/components/secret-panel.tsx +1505 -0
  78. package/src/components/server-management.tsx +264 -0
  79. package/src/components/split-button.tsx +148 -0
  80. package/src/components/stop-agent-button.tsx +99 -0
  81. package/src/components/ui/alert-dialog.cy.tsx +333 -0
  82. package/src/components/ui/alert-dialog.tsx +115 -0
  83. package/src/components/ui/alert.tsx +49 -0
  84. package/src/components/ui/avatar.cy.tsx +180 -0
  85. package/src/components/ui/avatar.tsx +57 -0
  86. package/src/components/ui/badge.cy.tsx +146 -0
  87. package/src/components/ui/badge.tsx +43 -0
  88. package/src/components/ui/button.cy.tsx +177 -0
  89. package/src/components/ui/button.tsx +56 -0
  90. package/src/components/ui/card.cy.tsx +160 -0
  91. package/src/components/ui/card.tsx +73 -0
  92. package/src/components/ui/chat/animated-markdown.tsx +59 -0
  93. package/src/components/ui/chat/chat-bubble.tsx +178 -0
  94. package/src/components/ui/chat/chat-container.tsx +51 -0
  95. package/src/components/ui/chat/chat-input.cy.tsx +169 -0
  96. package/src/components/ui/chat/chat-input.tsx +47 -0
  97. package/src/components/ui/chat/chat-message-list.tsx +61 -0
  98. package/src/components/ui/chat/chat-tts-button.tsx +199 -0
  99. package/src/components/ui/chat/code-block.tsx +79 -0
  100. package/src/components/ui/chat/expandable-chat.tsx +131 -0
  101. package/src/components/ui/chat/hooks/useAutoScroll.ts +86 -0
  102. package/src/components/ui/chat/markdown.tsx +209 -0
  103. package/src/components/ui/chat/message-loading.tsx +48 -0
  104. package/src/components/ui/checkbox.cy.tsx +170 -0
  105. package/src/components/ui/checkbox.tsx +30 -0
  106. package/src/components/ui/collapsible.cy.tsx +283 -0
  107. package/src/components/ui/collapsible.tsx +9 -0
  108. package/src/components/ui/command.cy.tsx +313 -0
  109. package/src/components/ui/command.tsx +143 -0
  110. package/src/components/ui/dialog.cy.tsx +279 -0
  111. package/src/components/ui/dialog.tsx +104 -0
  112. package/src/components/ui/dropdown-menu.cy.tsx +273 -0
  113. package/src/components/ui/dropdown-menu.tsx +281 -0
  114. package/src/components/ui/input.cy.tsx +82 -0
  115. package/src/components/ui/input.tsx +27 -0
  116. package/src/components/ui/label.cy.tsx +157 -0
  117. package/src/components/ui/label.tsx +19 -0
  118. package/src/components/ui/resizable.tsx +42 -0
  119. package/src/components/ui/scroll-area.cy.tsx +242 -0
  120. package/src/components/ui/scroll-area.tsx +46 -0
  121. package/src/components/ui/select.cy.tsx +277 -0
  122. package/src/components/ui/select.tsx +155 -0
  123. package/src/components/ui/separator.cy.tsx +145 -0
  124. package/src/components/ui/separator.tsx +29 -0
  125. package/src/components/ui/sheet.cy.tsx +324 -0
  126. package/src/components/ui/sheet.tsx +119 -0
  127. package/src/components/ui/sidebar.tsx +734 -0
  128. package/src/components/ui/skeleton.cy.tsx +149 -0
  129. package/src/components/ui/skeleton.tsx +17 -0
  130. package/src/components/ui/split-button.cy.tsx +274 -0
  131. package/src/components/ui/split-button.tsx +112 -0
  132. package/src/components/ui/switch.tsx +28 -0
  133. package/src/components/ui/tabs.cy.tsx +271 -0
  134. package/src/components/ui/tabs.tsx +53 -0
  135. package/src/components/ui/textarea.cy.tsx +136 -0
  136. package/src/components/ui/textarea.tsx +26 -0
  137. package/src/components/ui/toast.cy.tsx +209 -0
  138. package/src/components/ui/toast.tsx +126 -0
  139. package/src/components/ui/toaster.tsx +29 -0
  140. package/src/components/ui/tooltip.cy.tsx +244 -0
  141. package/src/components/ui/tooltip.tsx +30 -0
  142. package/src/config/agent-templates.ts +349 -0
  143. package/src/config/voice-models.ts +181 -0
  144. package/src/constants.ts +23 -0
  145. package/src/context/AuthContext.tsx +44 -0
  146. package/src/context/ConnectionContext.tsx +194 -0
  147. package/src/entry.tsx +9 -0
  148. package/src/hooks/__tests__/use-agent-tab-state.test.ts +137 -0
  149. package/src/hooks/__tests__/use-agent-update.test.tsx +250 -0
  150. package/src/hooks/__tests__/use-character-convert.test.ts +102 -0
  151. package/src/hooks/__tests__/use-panel-width-state.test.ts +243 -0
  152. package/src/hooks/__tests__/use-sidebar-state.test.ts +117 -0
  153. package/src/hooks/use-agent-management.ts +130 -0
  154. package/src/hooks/use-agent-tab-state.ts +74 -0
  155. package/src/hooks/use-agent-update.ts +469 -0
  156. package/src/hooks/use-character-convert.ts +138 -0
  157. package/src/hooks/use-confirmation.ts +55 -0
  158. package/src/hooks/use-delete-agent.ts +123 -0
  159. package/src/hooks/use-dm-channels.ts +198 -0
  160. package/src/hooks/use-elevenlabs-voices.ts +83 -0
  161. package/src/hooks/use-file-upload.ts +224 -0
  162. package/src/hooks/use-mobile.tsx +19 -0
  163. package/src/hooks/use-onboarding.tsx +49 -0
  164. package/src/hooks/use-panel-width-state.ts +147 -0
  165. package/src/hooks/use-partial-update.ts +288 -0
  166. package/src/hooks/use-plugin-details.ts +462 -0
  167. package/src/hooks/use-plugins.ts +119 -0
  168. package/src/hooks/use-query-hooks.ts +1263 -0
  169. package/src/hooks/use-server-agents.ts +62 -0
  170. package/src/hooks/use-server-version.tsx +47 -0
  171. package/src/hooks/use-sidebar-state.ts +50 -0
  172. package/src/hooks/use-socket-chat.ts +264 -0
  173. package/src/hooks/use-toast.ts +260 -0
  174. package/src/hooks/use-version.tsx +64 -0
  175. package/src/index.css +146 -0
  176. package/src/lib/api-client-config.ts +53 -0
  177. package/src/lib/api-type-mappers.ts +196 -0
  178. package/src/lib/export-utils.ts +123 -0
  179. package/src/lib/logger.ts +19 -0
  180. package/src/lib/media-utils.ts +170 -0
  181. package/src/lib/pca.test.ts +17 -0
  182. package/src/lib/pca.ts +52 -0
  183. package/src/lib/socketio-manager.ts +664 -0
  184. package/src/lib/utils.ts +168 -0
  185. package/src/main.tsx +16 -0
  186. package/src/mocks/empty-module.ts +12 -0
  187. package/src/mocks/node-module.ts +57 -0
  188. package/src/polyfills.ts +37 -0
  189. package/src/routes/agent-detail.tsx +30 -0
  190. package/src/routes/agent-list.tsx +27 -0
  191. package/src/routes/agent-settings.tsx +48 -0
  192. package/src/routes/character-detail.tsx +52 -0
  193. package/src/routes/character-form.tsx +79 -0
  194. package/src/routes/character-list.tsx +38 -0
  195. package/src/routes/chat.tsx +128 -0
  196. package/src/routes/createAgent.tsx +13 -0
  197. package/src/routes/group-new.tsx +50 -0
  198. package/src/routes/group.tsx +29 -0
  199. package/src/routes/home.tsx +218 -0
  200. package/src/routes/not-found.tsx +71 -0
  201. package/src/test/setup.ts +154 -0
  202. package/src/types/crypto-browserify.d.ts +4 -0
  203. package/src/types/index.ts +13 -0
  204. package/src/types/rooms.ts +8 -0
  205. package/src/types.ts +84 -0
  206. package/src/vite-env.d.ts +40 -0
  207. package/tailwind.config.ts +90 -0
  208. package/tsconfig.json +10 -0
  209. package/vite.config.ts +102 -0
@@ -0,0 +1,242 @@
1
+ /// <reference types="cypress" />
2
+ /// <reference path="../../../cypress/support/types.d.ts" />
3
+
4
+ import React from 'react';
5
+ import { ScrollArea, ScrollBar } from './scroll-area';
6
+
7
+ describe('ScrollArea Component', () => {
8
+ it('renders basic scroll area', () => {
9
+ cy.mountRadix(
10
+ <ScrollArea className="h-[200px] w-[350px] rounded-md border p-4">
11
+ {Array.from({ length: 20 }).map((_, i) => (
12
+ <div key={i} className="text-sm">
13
+ Item {i + 1}
14
+ </div>
15
+ ))}
16
+ </ScrollArea>
17
+ );
18
+
19
+ cy.get('[data-radix-scroll-area-viewport]').should('exist');
20
+ cy.contains('Item 1').should('be.visible');
21
+ });
22
+
23
+ it('handles vertical scrolling', () => {
24
+ cy.mountRadix(
25
+ <ScrollArea className="h-[200px] w-[350px] rounded-md border">
26
+ <div className="p-4">
27
+ {Array.from({ length: 50 }).map((_, i) => (
28
+ <div key={i} className="py-2">
29
+ Long content item {i + 1}
30
+ </div>
31
+ ))}
32
+ </div>
33
+ </ScrollArea>
34
+ );
35
+
36
+ cy.contains('Long content item 1').should('be.visible');
37
+ cy.contains('Long content item 50').should('not.be.visible');
38
+
39
+ // Scroll to bottom
40
+ cy.get('[data-radix-scroll-area-viewport]').scrollTo('bottom');
41
+ cy.contains('Long content item 50').should('be.visible');
42
+ });
43
+
44
+ it('handles horizontal scrolling', () => {
45
+ cy.mountRadix(
46
+ <ScrollArea className="w-[300px] rounded-md border">
47
+ <div className="flex w-max space-x-4 p-4">
48
+ {Array.from({ length: 20 }).map((_, i) => (
49
+ <div
50
+ key={i}
51
+ className="flex h-20 w-[150px] shrink-0 items-center justify-center rounded-md border"
52
+ >
53
+ Card {i + 1}
54
+ </div>
55
+ ))}
56
+ </div>
57
+ <ScrollBar orientation="horizontal" />
58
+ </ScrollArea>
59
+ );
60
+
61
+ cy.contains('Card 1').should('be.visible');
62
+
63
+ // Scroll horizontally
64
+ cy.get('[data-radix-scroll-area-viewport]').scrollTo('right');
65
+ });
66
+
67
+ it('renders with custom ScrollBar', () => {
68
+ cy.mountRadix(
69
+ <ScrollArea className="h-72 w-48 rounded-md border">
70
+ <div className="p-4">
71
+ <h4 className="mb-4 text-sm font-medium leading-none">Tags</h4>
72
+ {Array.from({ length: 50 }).map((_, i) => (
73
+ <div key={i} className="text-sm">
74
+ Tag {i + 1}
75
+ </div>
76
+ ))}
77
+ </div>
78
+ <ScrollBar className="bg-red-500" />
79
+ </ScrollArea>
80
+ );
81
+
82
+ cy.get('[data-radix-scroll-area-viewport]').should('exist');
83
+ cy.contains('Tags').should('be.visible');
84
+ });
85
+
86
+ it('applies custom className', () => {
87
+ cy.mountRadix(
88
+ <ScrollArea className="custom-scroll-area h-[200px] w-[200px]">
89
+ <div className="p-4">Content</div>
90
+ </ScrollArea>
91
+ );
92
+
93
+ cy.get('[data-radix-scroll-area-viewport]').should('exist');
94
+ });
95
+
96
+ it('handles dynamic content updates', () => {
97
+ const TestComponent = () => {
98
+ const [items, setItems] = React.useState(5);
99
+
100
+ return (
101
+ <div>
102
+ <button onClick={() => setItems(items + 5)}>Add More</button>
103
+ <ScrollArea className="h-[200px] w-[300px] rounded-md border">
104
+ <div className="p-4">
105
+ {Array.from({ length: items }).map((_, i) => (
106
+ <div key={i} className="py-1">
107
+ Item {i + 1}
108
+ </div>
109
+ ))}
110
+ </div>
111
+ </ScrollArea>
112
+ </div>
113
+ );
114
+ };
115
+
116
+ cy.mountRadix(<TestComponent />);
117
+
118
+ cy.contains('Item 5').should('be.visible');
119
+ cy.contains('Item 10').should('not.exist');
120
+
121
+ // Add more items
122
+ cy.contains('button', 'Add More').click();
123
+ cy.contains('Item 10').should('exist');
124
+ });
125
+
126
+ it('works with nested scroll areas', () => {
127
+ cy.mountRadix(
128
+ <ScrollArea className="h-[400px] w-[600px] rounded-md border">
129
+ <div className="p-4">
130
+ <h2 className="mb-4 text-xl font-bold">Outer Scroll Area</h2>
131
+ {Array.from({ length: 5 }).map((_, i) => (
132
+ <div key={i} className="mb-4">
133
+ <h3 className="mb-2 font-medium">Section {i + 1}</h3>
134
+ <ScrollArea className="h-[100px] rounded-md border bg-muted">
135
+ <div className="p-2">
136
+ {Array.from({ length: 10 }).map((_, j) => (
137
+ <div key={j} className="text-sm">
138
+ Nested item {j + 1}
139
+ </div>
140
+ ))}
141
+ </div>
142
+ </ScrollArea>
143
+ </div>
144
+ ))}
145
+ </div>
146
+ </ScrollArea>
147
+ );
148
+
149
+ cy.contains('Outer Scroll Area').should('be.visible');
150
+ cy.get('[data-radix-scroll-area-viewport]').should('have.length.at.least', 5);
151
+ });
152
+
153
+ it('preserves scroll position', () => {
154
+ const TestComponent = () => {
155
+ const [show, setShow] = React.useState(true);
156
+
157
+ return (
158
+ <div>
159
+ <button onClick={() => setShow(!show)}>Toggle</button>
160
+ {show && (
161
+ <ScrollArea className="h-[200px] w-[300px] rounded-md border">
162
+ <div className="p-4">
163
+ {Array.from({ length: 50 }).map((_, i) => (
164
+ <div key={i} className="py-1" data-index={i}>
165
+ Item {i + 1}
166
+ </div>
167
+ ))}
168
+ </div>
169
+ </ScrollArea>
170
+ )}
171
+ </div>
172
+ );
173
+ };
174
+
175
+ cy.mountRadix(<TestComponent />);
176
+
177
+ // Scroll to middle
178
+ cy.get('[data-radix-scroll-area-viewport]').scrollTo(0, 500);
179
+
180
+ // Check that scroll position is maintained
181
+ cy.get('[data-radix-scroll-area-viewport]').should('exist');
182
+ });
183
+
184
+ it('handles content with images', () => {
185
+ cy.mountRadix(
186
+ <ScrollArea className="h-[300px] w-[400px] rounded-md border">
187
+ <div className="p-4">
188
+ {Array.from({ length: 10 }).map((_, i) => (
189
+ <div key={i} className="mb-4 flex items-center space-x-4">
190
+ <img
191
+ src={`https://via.placeholder.com/100x100?text=${i + 1}`}
192
+ alt={`Image ${i + 1}`}
193
+ className="h-24 w-24 rounded-md object-cover"
194
+ />
195
+ <div>
196
+ <h4 className="font-medium">Image {i + 1}</h4>
197
+ <p className="text-sm text-muted-foreground">Description for image {i + 1}</p>
198
+ </div>
199
+ </div>
200
+ ))}
201
+ </div>
202
+ </ScrollArea>
203
+ );
204
+
205
+ cy.contains('Image 1').should('be.visible');
206
+ cy.get('img').should('have.length', 10);
207
+ });
208
+
209
+ it('works with tables', () => {
210
+ cy.mountRadix(
211
+ <ScrollArea className="h-[300px] w-full rounded-md border">
212
+ <table className="w-full">
213
+ <thead>
214
+ <tr className="border-b">
215
+ <th className="p-2 text-left">ID</th>
216
+ <th className="p-2 text-left">Name</th>
217
+ <th className="p-2 text-left">Status</th>
218
+ <th className="p-2 text-left">Date</th>
219
+ </tr>
220
+ </thead>
221
+ <tbody>
222
+ {Array.from({ length: 50 }).map((_, i) => (
223
+ <tr key={i} className="border-b">
224
+ <td className="p-2">{i + 1}</td>
225
+ <td className="p-2">Item {i + 1}</td>
226
+ <td className="p-2">Active</td>
227
+ <td className="p-2">2024-01-{String(i + 1).padStart(2, '0')}</td>
228
+ </tr>
229
+ ))}
230
+ </tbody>
231
+ </table>
232
+ </ScrollArea>
233
+ );
234
+
235
+ cy.get('table').should('be.visible');
236
+ cy.contains('Item 1').should('be.visible');
237
+
238
+ // Scroll to see more items
239
+ cy.get('[data-radix-scroll-area-viewport]').scrollTo('bottom');
240
+ cy.contains('Item 50').should('be.visible');
241
+ });
242
+ });
@@ -0,0 +1,46 @@
1
+ 'use client';
2
+
3
+ import * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area';
4
+ import * as React from 'react';
5
+
6
+ import { cn } from '@/lib/utils';
7
+
8
+ const ScrollArea = React.forwardRef<
9
+ React.ElementRef<typeof ScrollAreaPrimitive.Root>,
10
+ React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root>
11
+ >(({ className, children, ...props }, ref) => (
12
+ <ScrollAreaPrimitive.Root
13
+ ref={ref}
14
+ className={cn('relative overflow-hidden', className)}
15
+ {...props}
16
+ >
17
+ <ScrollAreaPrimitive.Viewport className="h-full w-full rounded-[inherit]">
18
+ {children}
19
+ </ScrollAreaPrimitive.Viewport>
20
+ <ScrollBar />
21
+ <ScrollAreaPrimitive.Corner />
22
+ </ScrollAreaPrimitive.Root>
23
+ ));
24
+ ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName;
25
+
26
+ const ScrollBar = React.forwardRef<
27
+ React.ElementRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>,
28
+ React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>
29
+ >(({ className, orientation = 'vertical', ...props }, ref) => (
30
+ <ScrollAreaPrimitive.ScrollAreaScrollbar
31
+ ref={ref}
32
+ orientation={orientation}
33
+ className={cn(
34
+ 'flex touch-none select-none transition-colors',
35
+ orientation === 'vertical' && 'h-full w-2.5 border-l border-l-transparent p-[1px]',
36
+ orientation === 'horizontal' && 'h-2.5 flex-col border-t border-t-transparent p-[1px]',
37
+ className
38
+ )}
39
+ {...props}
40
+ >
41
+ <ScrollAreaPrimitive.ScrollAreaThumb className="relative flex-1 rounded-full bg-border" />
42
+ </ScrollAreaPrimitive.ScrollAreaScrollbar>
43
+ ));
44
+ ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName;
45
+
46
+ export { ScrollArea, ScrollBar };
@@ -0,0 +1,277 @@
1
+ /// <reference types="cypress" />
2
+ /// <reference path="../../../cypress/support/types.d.ts" />
3
+
4
+ import React from 'react';
5
+ import {
6
+ Select,
7
+ SelectContent,
8
+ SelectGroup,
9
+ SelectItem,
10
+ SelectLabel,
11
+ SelectTrigger,
12
+ SelectValue,
13
+ SelectSeparator,
14
+ } from './select';
15
+
16
+ describe('Select Component', () => {
17
+ it('renders correctly with default props', () => {
18
+ cy.mount(
19
+ <Select>
20
+ <SelectTrigger>
21
+ <SelectValue placeholder="Select an option" />
22
+ </SelectTrigger>
23
+ <SelectContent>
24
+ <SelectItem value="option1">Option 1</SelectItem>
25
+ <SelectItem value="option2">Option 2</SelectItem>
26
+ <SelectItem value="option3">Option 3</SelectItem>
27
+ </SelectContent>
28
+ </Select>
29
+ );
30
+
31
+ cy.get('[role="combobox"]').should('exist');
32
+ cy.get('[role="combobox"]').should('contain', 'Select an option');
33
+ });
34
+
35
+ it('opens and closes dropdown', () => {
36
+ cy.mount(
37
+ <Select>
38
+ <SelectTrigger>
39
+ <SelectValue placeholder="Choose..." />
40
+ </SelectTrigger>
41
+ <SelectContent>
42
+ <SelectItem value="apple">Apple</SelectItem>
43
+ <SelectItem value="banana">Banana</SelectItem>
44
+ <SelectItem value="orange">Orange</SelectItem>
45
+ </SelectContent>
46
+ </Select>
47
+ );
48
+
49
+ // Initially closed
50
+ cy.contains('Apple').should('not.exist');
51
+
52
+ // Click to open
53
+ cy.get('[role="combobox"]').click();
54
+ cy.contains('Apple').should('be.visible');
55
+ cy.contains('Banana').should('be.visible');
56
+ cy.contains('Orange').should('be.visible');
57
+ });
58
+
59
+ it('selects an option', () => {
60
+ cy.mount(
61
+ <Select>
62
+ <SelectTrigger>
63
+ <SelectValue placeholder="Select a fruit" />
64
+ </SelectTrigger>
65
+ <SelectContent>
66
+ <SelectItem value="apple">Apple</SelectItem>
67
+ <SelectItem value="banana">Banana</SelectItem>
68
+ <SelectItem value="orange">Orange</SelectItem>
69
+ </SelectContent>
70
+ </Select>
71
+ );
72
+
73
+ cy.get('[role="combobox"]').click();
74
+ cy.contains('Banana').click();
75
+ cy.get('[role="combobox"]').should('contain', 'Banana');
76
+ });
77
+
78
+ it('works as controlled component', () => {
79
+ const TestComponent = () => {
80
+ const [value, setValue] = React.useState('');
81
+
82
+ return (
83
+ <div>
84
+ <Select value={value} onValueChange={setValue}>
85
+ <SelectTrigger>
86
+ <SelectValue placeholder="Select..." />
87
+ </SelectTrigger>
88
+ <SelectContent>
89
+ <SelectItem value="1">One</SelectItem>
90
+ <SelectItem value="2">Two</SelectItem>
91
+ <SelectItem value="3">Three</SelectItem>
92
+ </SelectContent>
93
+ </Select>
94
+ <p>Selected: {value || 'none'}</p>
95
+ </div>
96
+ );
97
+ };
98
+
99
+ cy.mount(<TestComponent />);
100
+
101
+ cy.contains('Selected: none').should('exist');
102
+ cy.get('[role="combobox"]').click();
103
+ cy.contains('Two').click();
104
+ cy.contains('Selected: 2').should('exist');
105
+ });
106
+
107
+ it('can be disabled', () => {
108
+ cy.mount(
109
+ <Select disabled>
110
+ <SelectTrigger>
111
+ <SelectValue placeholder="Disabled select" />
112
+ </SelectTrigger>
113
+ <SelectContent>
114
+ <SelectItem value="1">Option 1</SelectItem>
115
+ </SelectContent>
116
+ </Select>
117
+ );
118
+
119
+ cy.get('[role="combobox"]').should('have.attr', 'data-disabled');
120
+ cy.get('[role="combobox"]').click({ force: true });
121
+ cy.contains('Option 1').should('not.exist');
122
+ });
123
+
124
+ it('supports grouped options', () => {
125
+ cy.mount(
126
+ <Select>
127
+ <SelectTrigger>
128
+ <SelectValue placeholder="Select..." />
129
+ </SelectTrigger>
130
+ <SelectContent>
131
+ <SelectGroup>
132
+ <SelectLabel>Fruits</SelectLabel>
133
+ <SelectItem value="apple">Apple</SelectItem>
134
+ <SelectItem value="banana">Banana</SelectItem>
135
+ </SelectGroup>
136
+ <SelectSeparator />
137
+ <SelectGroup>
138
+ <SelectLabel>Vegetables</SelectLabel>
139
+ <SelectItem value="carrot">Carrot</SelectItem>
140
+ <SelectItem value="lettuce">Lettuce</SelectItem>
141
+ </SelectGroup>
142
+ </SelectContent>
143
+ </Select>
144
+ );
145
+
146
+ cy.get('[role="combobox"]').click();
147
+ cy.contains('Fruits').should('be.visible');
148
+ cy.contains('Vegetables').should('be.visible');
149
+ cy.contains('Apple').should('be.visible');
150
+ cy.contains('Carrot').should('be.visible');
151
+ });
152
+
153
+ it('supports custom className', () => {
154
+ cy.mount(
155
+ <Select>
156
+ <SelectTrigger className="w-[200px] custom-select">
157
+ <SelectValue placeholder="Custom styled" />
158
+ </SelectTrigger>
159
+ <SelectContent>
160
+ <SelectItem value="1">Option 1</SelectItem>
161
+ </SelectContent>
162
+ </Select>
163
+ );
164
+
165
+ cy.get('[role="combobox"]').should('have.class', 'w-[200px]');
166
+ cy.get('[role="combobox"]').should('have.class', 'custom-select');
167
+ });
168
+
169
+ it('handles keyboard navigation', () => {
170
+ cy.mount(
171
+ <Select>
172
+ <SelectTrigger>
173
+ <SelectValue placeholder="Navigate with keyboard" />
174
+ </SelectTrigger>
175
+ <SelectContent>
176
+ <SelectItem value="1">First</SelectItem>
177
+ <SelectItem value="2">Second</SelectItem>
178
+ <SelectItem value="3">Third</SelectItem>
179
+ </SelectContent>
180
+ </Select>
181
+ );
182
+
183
+ // Open with Enter key
184
+ cy.get('[role="combobox"]').focus();
185
+ cy.get('[role="combobox"]').type('{enter}');
186
+ cy.contains('First').should('be.visible');
187
+
188
+ // Navigate with arrow keys
189
+ cy.get('body').type('{downarrow}');
190
+ cy.get('body').type('{downarrow}');
191
+ cy.get('body').type('{enter}');
192
+
193
+ cy.get('[role="combobox"]').should('contain', 'Third');
194
+ });
195
+
196
+ it('supports disabled items', () => {
197
+ cy.mount(
198
+ <Select>
199
+ <SelectTrigger>
200
+ <SelectValue placeholder="Some options disabled" />
201
+ </SelectTrigger>
202
+ <SelectContent>
203
+ <SelectItem value="1">Available</SelectItem>
204
+ <SelectItem value="2" disabled>
205
+ Not Available
206
+ </SelectItem>
207
+ <SelectItem value="3">Also Available</SelectItem>
208
+ </SelectContent>
209
+ </Select>
210
+ );
211
+
212
+ cy.get('[role="combobox"]').click();
213
+ cy.get('[data-disabled]').should('contain', 'Not Available');
214
+ });
215
+
216
+ it('displays long option text properly', () => {
217
+ cy.mount(
218
+ <Select>
219
+ <SelectTrigger className="w-[300px]">
220
+ <SelectValue placeholder="Long options" />
221
+ </SelectTrigger>
222
+ <SelectContent>
223
+ <SelectItem value="1">
224
+ This is a very long option text that should be handled properly
225
+ </SelectItem>
226
+ <SelectItem value="2">Short</SelectItem>
227
+ </SelectContent>
228
+ </Select>
229
+ );
230
+
231
+ cy.get('[role="combobox"]').click();
232
+ cy.contains('This is a very long option text').should('be.visible');
233
+ });
234
+
235
+ it('works in forms', () => {
236
+ cy.mount(
237
+ <form>
238
+ <label htmlFor="country">Country</label>
239
+ <Select name="country">
240
+ <SelectTrigger id="country">
241
+ <SelectValue placeholder="Select country" />
242
+ </SelectTrigger>
243
+ <SelectContent>
244
+ <SelectItem value="us">United States</SelectItem>
245
+ <SelectItem value="uk">United Kingdom</SelectItem>
246
+ <SelectItem value="ca">Canada</SelectItem>
247
+ </SelectContent>
248
+ </Select>
249
+ </form>
250
+ );
251
+
252
+ cy.get('[role="combobox"]').click();
253
+ cy.get('[role="listbox"]').within(() => {
254
+ cy.contains('United Kingdom').click({ force: true });
255
+ });
256
+ cy.get('[role="combobox"]').should('contain', 'United Kingdom');
257
+ });
258
+
259
+ it('maintains focus after selection', () => {
260
+ cy.mount(
261
+ <Select>
262
+ <SelectTrigger>
263
+ <SelectValue placeholder="Test focus" />
264
+ </SelectTrigger>
265
+ <SelectContent>
266
+ <SelectItem value="1">Option 1</SelectItem>
267
+ <SelectItem value="2">Option 2</SelectItem>
268
+ </SelectContent>
269
+ </Select>
270
+ );
271
+
272
+ cy.get('[role="combobox"]').focus();
273
+ cy.get('[role="combobox"]').click();
274
+ cy.contains('Option 1').click();
275
+ cy.get('[role="combobox"]').should('have.focus');
276
+ });
277
+ });