@seed-ship/mcp-ui-solid 2.2.11 → 2.4.0

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 (52) hide show
  1. package/dist/components/ChatPrompt.cjs +271 -0
  2. package/dist/components/ChatPrompt.cjs.map +1 -0
  3. package/dist/components/ChatPrompt.d.ts +33 -0
  4. package/dist/components/ChatPrompt.d.ts.map +1 -0
  5. package/dist/components/ChatPrompt.js +271 -0
  6. package/dist/components/ChatPrompt.js.map +1 -0
  7. package/dist/components/UIResourceRenderer.cjs +29 -27
  8. package/dist/components/UIResourceRenderer.cjs.map +1 -1
  9. package/dist/components/UIResourceRenderer.js +30 -28
  10. package/dist/components/UIResourceRenderer.js.map +1 -1
  11. package/dist/hooks/useChatBus.cjs +28 -0
  12. package/dist/hooks/useChatBus.cjs.map +1 -0
  13. package/dist/hooks/useChatBus.d.ts +56 -0
  14. package/dist/hooks/useChatBus.d.ts.map +1 -0
  15. package/dist/hooks/useChatBus.js +28 -0
  16. package/dist/hooks/useChatBus.js.map +1 -0
  17. package/dist/index.cjs +11 -0
  18. package/dist/index.cjs.map +1 -1
  19. package/dist/index.d.cts +5 -1
  20. package/dist/index.d.ts +5 -1
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +12 -1
  23. package/dist/index.js.map +1 -1
  24. package/dist/services/chat-bus.cjs +118 -0
  25. package/dist/services/chat-bus.cjs.map +1 -0
  26. package/dist/services/chat-bus.d.ts +43 -0
  27. package/dist/services/chat-bus.d.ts.map +1 -0
  28. package/dist/services/chat-bus.js +118 -0
  29. package/dist/services/chat-bus.js.map +1 -0
  30. package/dist/services/index.d.ts +2 -1
  31. package/dist/services/index.d.ts.map +1 -1
  32. package/dist/services/validation.cjs +71 -1
  33. package/dist/services/validation.cjs.map +1 -1
  34. package/dist/services/validation.d.ts +21 -0
  35. package/dist/services/validation.d.ts.map +1 -1
  36. package/dist/services/validation.js +71 -1
  37. package/dist/services/validation.js.map +1 -1
  38. package/dist/types/chat-bus.d.ts +286 -0
  39. package/dist/types/chat-bus.d.ts.map +1 -0
  40. package/package.json +1 -1
  41. package/src/components/ChatPrompt.test.tsx +280 -0
  42. package/src/components/ChatPrompt.tsx +263 -0
  43. package/src/components/UIResourceRenderer.tsx +2 -2
  44. package/src/hooks/useChatBus.tsx +81 -0
  45. package/src/index.ts +36 -0
  46. package/src/services/chat-bus.test.ts +306 -0
  47. package/src/services/chat-bus.ts +183 -0
  48. package/src/services/index.ts +4 -0
  49. package/src/services/validation.test.ts +56 -1
  50. package/src/services/validation.ts +100 -0
  51. package/src/types/chat-bus.ts +320 -0
  52. package/tsconfig.tsbuildinfo +1 -1
@@ -0,0 +1,286 @@
1
+ /**
2
+ * Chat Bus — Type Definitions
3
+ * v2.4.0: Event-driven chat toolkit for agent interactions
4
+ *
5
+ * @experimental — These types may change without major bump until stabilized in v2.5.0.
6
+ * See CHANGELOG for breaking changes on experimental types.
7
+ */
8
+ import type { UIComponent, UILayout } from './index';
9
+ /**
10
+ * @experimental
11
+ * Base for all chat events — identifies the source stream (C2).
12
+ * Enables multi-stream support (Deposium supports 3 concurrent streams).
13
+ */
14
+ export interface ChatEventBase {
15
+ /** Unique key identifying the active stream */
16
+ streamKey: string;
17
+ /** Conversation ID (if available) */
18
+ conversationId?: string;
19
+ /** Space/context ID */
20
+ spaceId?: string;
21
+ /** Correlation ID linking a sendPrompt command to its resulting stream (C6) */
22
+ correlationId?: string;
23
+ }
24
+ /**
25
+ * @experimental
26
+ * Events emitted by the chat. The host app connects its SSE stream
27
+ * to these callbacks. Agents consume events to react.
28
+ *
29
+ * `onToken` is a hot path (C3) — subscribe with throttle option.
30
+ * Most agents only need `onStreamEnd`.
31
+ */
32
+ export interface ChatEvents {
33
+ onToken: (event: ChatEventBase & {
34
+ token: string;
35
+ }) => void;
36
+ onStreamStart: (event: ChatEventBase) => void;
37
+ onStreamEnd: (event: ChatEventBase & {
38
+ metadata: StreamDoneMetadata;
39
+ }) => void;
40
+ onError: (event: ChatEventBase & {
41
+ error: ChatError;
42
+ }) => void;
43
+ onUILayout: (event: ChatEventBase & {
44
+ layout: UILayout;
45
+ }) => void;
46
+ onCitation: (event: ChatEventBase & {
47
+ citation: Citation;
48
+ }) => void;
49
+ onToolCall: (event: ChatEventBase & {
50
+ tool: ToolCallEvent;
51
+ }) => void;
52
+ onSuggestions: (event: ChatEventBase & {
53
+ items: string[];
54
+ }) => void;
55
+ onChatPromptResponse: (event: ChatEventBase & {
56
+ response: ChatPromptResponse;
57
+ }) => void;
58
+ onClarificationNeeded: (event: ChatEventBase & {
59
+ clarification: ClarificationEvent;
60
+ }) => void;
61
+ onAgentSwitch: (event: ChatEventBase & {
62
+ agent: AgentContext;
63
+ }) => void;
64
+ onBriefing: (event: ChatEventBase & {
65
+ briefing: BriefingEvent;
66
+ }) => void;
67
+ onCapabilityChange: (event: ChatEventBase & {
68
+ capabilities: string[];
69
+ }) => void;
70
+ onCustomEvent: (type: string, event: ChatEventBase & {
71
+ data: unknown;
72
+ }) => void;
73
+ }
74
+ /**
75
+ * @experimental
76
+ * Subscription options for event listeners (C3).
77
+ */
78
+ export interface EventSubscribeOptions {
79
+ /** Throttle in ms — recommended 100ms for onToken */
80
+ throttle?: number;
81
+ /** Filter events by streamKey */
82
+ streamKey?: string;
83
+ }
84
+ /**
85
+ * @experimental
86
+ * Commands that agents send to the chat. The host app implements
87
+ * these commands on its UI (maps to existing signals).
88
+ */
89
+ export interface ChatCommands {
90
+ /** Fill the input field without sending */
91
+ injectPrompt: (text: string) => void;
92
+ /** Fill the input and send immediately. Returns correlationId (C6). */
93
+ sendPrompt: (text: string, metadata?: Record<string, unknown>) => string;
94
+ /** Append text to the current input value */
95
+ appendPrompt: (text: string) => void;
96
+ /** Show a ChatPrompt (choice, confirm, form) above the input (C4) */
97
+ showChatPrompt: (config: ChatPromptConfig, signal?: AbortSignal) => Promise<ChatPromptResponse>;
98
+ /** Dismiss the active ChatPrompt */
99
+ dismissChatPrompt: () => void;
100
+ /** Show suggestion chips */
101
+ showSuggestions: (items: SuggestionItem[]) => void;
102
+ /** Toggle a connector on/off */
103
+ toggleConnector: (connectorId: string, enabled: boolean) => void;
104
+ /** Change the chat mode */
105
+ setMode: (mode: string) => void;
106
+ /** Scroll to a specific message */
107
+ scrollToMessage: (messageId: string) => void;
108
+ /** Show a notification in the chat context */
109
+ notify: (message: string, type?: 'info' | 'success' | 'warning' | 'error') => void;
110
+ }
111
+ /**
112
+ * @experimental
113
+ * The combined events + commands bus.
114
+ */
115
+ export interface ChatBus {
116
+ events: ChatEventEmitter;
117
+ commands: ChatCommandHandler;
118
+ }
119
+ /**
120
+ * @experimental
121
+ * Typed event emitter for ChatEvents.
122
+ */
123
+ export interface ChatEventEmitter {
124
+ /** Subscribe to an event */
125
+ on<K extends keyof ChatEvents>(event: K, handler: ChatEvents[K], options?: EventSubscribeOptions): () => void;
126
+ /** Emit an event to all subscribers */
127
+ emit<K extends keyof ChatEvents>(event: K, ...args: Parameters<ChatEvents[K]>): void;
128
+ /** Remove all listeners (cleanup) */
129
+ clear(): void;
130
+ }
131
+ /**
132
+ * @experimental
133
+ * Typed command handler for ChatCommands.
134
+ */
135
+ export interface ChatCommandHandler {
136
+ /** Register a command handler (app-side) */
137
+ handle<K extends keyof ChatCommands>(command: K, handler: ChatCommands[K]): void;
138
+ /** Execute a command (agent-side) */
139
+ exec<K extends keyof ChatCommands>(command: K, ...args: Parameters<ChatCommands[K]>): ReturnType<ChatCommands[K]>;
140
+ }
141
+ /**
142
+ * @experimental
143
+ * Configuration for a ChatPrompt interaction.
144
+ */
145
+ export interface ChatPromptConfig {
146
+ /** Prompt type */
147
+ type: 'choice' | 'confirm' | 'form' | 'select';
148
+ /** Title / question displayed */
149
+ title: string;
150
+ /** Type-specific configuration */
151
+ config: ChoicePromptConfig | ConfirmPromptConfig | FormPromptConfig | SelectPromptConfig;
152
+ }
153
+ export interface ChoicePromptConfig {
154
+ options: Array<{
155
+ value: string;
156
+ label: string;
157
+ icon?: string;
158
+ description?: string;
159
+ }>;
160
+ layout?: 'horizontal' | 'vertical' | 'grid';
161
+ }
162
+ export interface ConfirmPromptConfig {
163
+ message?: string;
164
+ confirmLabel?: string;
165
+ cancelLabel?: string;
166
+ variant?: 'default' | 'danger';
167
+ }
168
+ export interface FormPromptConfig {
169
+ fields: Array<{
170
+ name: string;
171
+ label: string;
172
+ type: 'text' | 'number' | 'select' | 'textarea';
173
+ required?: boolean;
174
+ placeholder?: string;
175
+ options?: Array<{
176
+ label: string;
177
+ value: string;
178
+ }>;
179
+ }>;
180
+ submitLabel?: string;
181
+ }
182
+ export interface SelectPromptConfig {
183
+ options: Array<{
184
+ value: string;
185
+ label: string;
186
+ group?: string;
187
+ }>;
188
+ placeholder?: string;
189
+ searchable?: boolean;
190
+ }
191
+ /**
192
+ * @experimental
193
+ * Structured response from a ChatPrompt.
194
+ */
195
+ export interface ChatPromptResponse {
196
+ type: ChatPromptConfig['type'];
197
+ /** The selected value or form data */
198
+ value: string | Record<string, unknown>;
199
+ /** Human-readable label (for display in chat as user message) */
200
+ label: string;
201
+ /** Whether the user dismissed without answering */
202
+ dismissed?: boolean;
203
+ }
204
+ export interface SuggestionItem {
205
+ /** Text to inject when clicked */
206
+ text: string;
207
+ /** Display label (defaults to text) */
208
+ label?: string;
209
+ /** Icon */
210
+ icon?: string;
211
+ }
212
+ /**
213
+ * @experimental
214
+ * Agent context — who is the active agent?
215
+ */
216
+ export interface AgentContext {
217
+ id: string;
218
+ name: string;
219
+ persona?: string;
220
+ avatar?: string;
221
+ capabilities?: string[];
222
+ metadata?: Record<string, unknown>;
223
+ }
224
+ /**
225
+ * @experimental
226
+ * Briefing event — update the briefings tab.
227
+ */
228
+ export interface BriefingEvent {
229
+ id: string;
230
+ action: 'create' | 'update' | 'complete' | 'archive';
231
+ title: string;
232
+ sections?: BriefingSection[];
233
+ status?: 'draft' | 'in_progress' | 'complete';
234
+ agent?: string;
235
+ components?: UIComponent[];
236
+ /** true = do not persist (tooltip, preview). false/absent = app decides storage. */
237
+ ephemeral?: boolean;
238
+ }
239
+ export interface BriefingSection {
240
+ title: string;
241
+ content: string;
242
+ components?: UIComponent[];
243
+ }
244
+ export interface StreamDoneMetadata {
245
+ message_hash?: string;
246
+ intent?: string;
247
+ model?: string;
248
+ tokens?: {
249
+ input: number;
250
+ output: number;
251
+ };
252
+ cost_usd?: number;
253
+ suggestions?: string[];
254
+ extracted_charts?: unknown[];
255
+ timing_breakdown?: Record<string, number>;
256
+ [key: string]: unknown;
257
+ }
258
+ export interface ChatError {
259
+ message: string;
260
+ code?: string;
261
+ recoverable?: boolean;
262
+ }
263
+ export interface Citation {
264
+ page?: number;
265
+ document_id?: string;
266
+ document_name?: string;
267
+ snippet?: string;
268
+ score?: number;
269
+ }
270
+ export interface ToolCallEvent {
271
+ tool: string;
272
+ status: 'running' | 'completed' | 'failed';
273
+ params?: Record<string, unknown>;
274
+ results?: unknown;
275
+ duration_ms?: number;
276
+ }
277
+ export interface ClarificationEvent {
278
+ question: string;
279
+ options: Array<{
280
+ value: string;
281
+ label: string;
282
+ file_id?: number;
283
+ }>;
284
+ original_message?: string;
285
+ }
286
+ //# sourceMappingURL=chat-bus.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat-bus.d.ts","sourceRoot":"","sources":["../../src/types/chat-bus.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seed-ship/mcp-ui-solid",
3
- "version": "2.2.11",
3
+ "version": "2.4.0",
4
4
  "description": "SolidJS components for rendering MCP-generated UI resources",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -0,0 +1,280 @@
1
+ /**
2
+ * Tests for ChatPrompt component
3
+ * Phase 2: choice, confirm, form subtypes
4
+ */
5
+
6
+ import { describe, it, expect, vi, beforeEach } from 'vitest'
7
+ import { render, fireEvent, cleanup } from '@solidjs/testing-library'
8
+ import { ChatPrompt } from './ChatPrompt'
9
+ import type { ChatPromptConfig, ChatPromptResponse } from '../types/chat-bus'
10
+
11
+ describe('ChatPrompt', () => {
12
+ beforeEach(() => cleanup())
13
+
14
+ // ─── Choice ──────────────────────────────────────────
15
+
16
+ describe('type: choice', () => {
17
+ const choiceConfig: ChatPromptConfig = {
18
+ type: 'choice',
19
+ title: 'Select format',
20
+ config: {
21
+ options: [
22
+ { value: 'pdf', label: 'PDF', icon: '📄' },
23
+ { value: 'csv', label: 'CSV' },
24
+ { value: 'json', label: 'JSON', description: 'Raw data' },
25
+ ],
26
+ },
27
+ }
28
+
29
+ it('renders title and all options', () => {
30
+ const { getByText } = render(() => (
31
+ <ChatPrompt config={choiceConfig} onSubmit={() => {}} />
32
+ ))
33
+
34
+ expect(getByText('Select format')).toBeDefined()
35
+ expect(getByText('PDF')).toBeDefined()
36
+ expect(getByText('CSV')).toBeDefined()
37
+ expect(getByText('JSON')).toBeDefined()
38
+ })
39
+
40
+ it('renders option icons and descriptions', () => {
41
+ const { getByText } = render(() => (
42
+ <ChatPrompt config={choiceConfig} onSubmit={() => {}} />
43
+ ))
44
+
45
+ expect(getByText('📄')).toBeDefined()
46
+ expect(getByText('Raw data')).toBeDefined()
47
+ })
48
+
49
+ it('calls onSubmit with value and label when option clicked', () => {
50
+ const onSubmit = vi.fn()
51
+ const { getByText } = render(() => (
52
+ <ChatPrompt config={choiceConfig} onSubmit={onSubmit} />
53
+ ))
54
+
55
+ fireEvent.click(getByText('CSV'))
56
+
57
+ expect(onSubmit).toHaveBeenCalledWith({
58
+ type: 'choice',
59
+ value: 'csv',
60
+ label: 'CSV',
61
+ })
62
+ })
63
+
64
+ it('supports vertical layout', () => {
65
+ const verticalConfig: ChatPromptConfig = {
66
+ ...choiceConfig,
67
+ config: { ...choiceConfig.config as any, layout: 'vertical' },
68
+ }
69
+ const { container } = render(() => (
70
+ <ChatPrompt config={verticalConfig} onSubmit={() => {}} />
71
+ ))
72
+
73
+ const body = container.querySelector('.flex-col')
74
+ expect(body).not.toBeNull()
75
+ })
76
+ })
77
+
78
+ // ─── Confirm ─────────────────────────────────────────
79
+
80
+ describe('type: confirm', () => {
81
+ const confirmConfig: ChatPromptConfig = {
82
+ type: 'confirm',
83
+ title: 'Delete 47 documents?',
84
+ config: {
85
+ message: 'This action cannot be undone.',
86
+ confirmLabel: 'Delete',
87
+ cancelLabel: 'Keep',
88
+ variant: 'danger',
89
+ },
90
+ }
91
+
92
+ it('renders title, message, and buttons', () => {
93
+ const { getByText } = render(() => (
94
+ <ChatPrompt config={confirmConfig} onSubmit={() => {}} />
95
+ ))
96
+
97
+ expect(getByText('Delete 47 documents?')).toBeDefined()
98
+ expect(getByText('This action cannot be undone.')).toBeDefined()
99
+ expect(getByText('Delete')).toBeDefined()
100
+ expect(getByText('Keep')).toBeDefined()
101
+ })
102
+
103
+ it('calls onSubmit with confirmed on confirm click', () => {
104
+ const onSubmit = vi.fn()
105
+ const { getByText } = render(() => (
106
+ <ChatPrompt config={confirmConfig} onSubmit={onSubmit} />
107
+ ))
108
+
109
+ fireEvent.click(getByText('Delete'))
110
+
111
+ expect(onSubmit).toHaveBeenCalledWith({
112
+ type: 'confirm',
113
+ value: 'confirmed',
114
+ label: 'Delete',
115
+ })
116
+ })
117
+
118
+ it('calls onSubmit with dismissed on cancel click', () => {
119
+ const onSubmit = vi.fn()
120
+ const onDismiss = vi.fn()
121
+ const { getByText } = render(() => (
122
+ <ChatPrompt config={confirmConfig} onSubmit={onSubmit} onDismiss={onDismiss} />
123
+ ))
124
+
125
+ fireEvent.click(getByText('Keep'))
126
+
127
+ expect(onSubmit).toHaveBeenCalledWith({
128
+ type: 'confirm',
129
+ value: 'cancelled',
130
+ label: 'Keep',
131
+ dismissed: true,
132
+ })
133
+ expect(onDismiss).toHaveBeenCalled()
134
+ })
135
+
136
+ it('uses default labels when not provided', () => {
137
+ const simpleConfig: ChatPromptConfig = {
138
+ type: 'confirm',
139
+ title: 'Are you sure?',
140
+ config: {},
141
+ }
142
+ const { getByText } = render(() => (
143
+ <ChatPrompt config={simpleConfig} onSubmit={() => {}} />
144
+ ))
145
+
146
+ expect(getByText('Confirm')).toBeDefined()
147
+ expect(getByText('Cancel')).toBeDefined()
148
+ })
149
+
150
+ it('uses danger styling for danger variant', () => {
151
+ const { getByText } = render(() => (
152
+ <ChatPrompt config={confirmConfig} onSubmit={() => {}} />
153
+ ))
154
+
155
+ const deleteBtn = getByText('Delete')
156
+ expect(deleteBtn.className).toContain('bg-red-600')
157
+ })
158
+ })
159
+
160
+ // ─── Form ────────────────────────────────────────────
161
+
162
+ describe('type: form', () => {
163
+ const formConfig: ChatPromptConfig = {
164
+ type: 'form',
165
+ title: 'Additional info',
166
+ config: {
167
+ fields: [
168
+ { name: 'title', label: 'Title', type: 'text', required: true, placeholder: 'Enter title' },
169
+ { name: 'category', label: 'Category', type: 'select', options: [{ label: 'Report', value: 'report' }, { label: 'Invoice', value: 'invoice' }] },
170
+ { name: 'notes', label: 'Notes', type: 'textarea' },
171
+ ],
172
+ submitLabel: 'Send',
173
+ },
174
+ }
175
+
176
+ it('renders all fields', () => {
177
+ const { getByText, getByPlaceholderText } = render(() => (
178
+ <ChatPrompt config={formConfig} onSubmit={() => {}} />
179
+ ))
180
+
181
+ expect(getByText('Title')).toBeDefined()
182
+ expect(getByText('Category')).toBeDefined()
183
+ expect(getByText('Notes')).toBeDefined()
184
+ expect(getByPlaceholderText('Enter title')).toBeDefined()
185
+ })
186
+
187
+ it('submit button is disabled when required fields empty', () => {
188
+ const { getByText } = render(() => (
189
+ <ChatPrompt config={formConfig} onSubmit={() => {}} />
190
+ ))
191
+
192
+ const submitBtn = getByText('Send')
193
+ expect(submitBtn.hasAttribute('disabled')).toBe(true)
194
+ })
195
+
196
+ it('submit button enables when required fields filled', async () => {
197
+ const { getByText, getByPlaceholderText } = render(() => (
198
+ <ChatPrompt config={formConfig} onSubmit={() => {}} />
199
+ ))
200
+
201
+ const titleInput = getByPlaceholderText('Enter title')
202
+ fireEvent.input(titleInput, { target: { value: 'My Report' } })
203
+
204
+ const submitBtn = getByText('Send')
205
+ expect(submitBtn.hasAttribute('disabled')).toBe(false)
206
+ })
207
+
208
+ it('calls onSubmit with form data on submit', async () => {
209
+ const onSubmit = vi.fn()
210
+ const { getByText, getByPlaceholderText } = render(() => (
211
+ <ChatPrompt config={formConfig} onSubmit={onSubmit} />
212
+ ))
213
+
214
+ fireEvent.input(getByPlaceholderText('Enter title'), { target: { value: 'Q4 Report' } })
215
+ fireEvent.click(getByText('Send'))
216
+
217
+ expect(onSubmit).toHaveBeenCalledWith({
218
+ type: 'form',
219
+ value: { title: 'Q4 Report' },
220
+ label: 'title: Q4 Report',
221
+ })
222
+ })
223
+
224
+ it('shows required indicator on required fields', () => {
225
+ const { container } = render(() => (
226
+ <ChatPrompt config={formConfig} onSubmit={() => {}} />
227
+ ))
228
+
229
+ const asterisks = container.querySelectorAll('.text-red-500')
230
+ expect(asterisks.length).toBeGreaterThan(0)
231
+ })
232
+ })
233
+
234
+ // ─── Dismiss ─────────────────────────────────────────
235
+
236
+ describe('dismiss', () => {
237
+ it('calls onSubmit with dismissed:true and onDismiss when X clicked', () => {
238
+ const onSubmit = vi.fn()
239
+ const onDismiss = vi.fn()
240
+ const config: ChatPromptConfig = {
241
+ type: 'choice',
242
+ title: 'Test',
243
+ config: { options: [{ value: 'a', label: 'A' }] },
244
+ }
245
+
246
+ const { getByLabelText } = render(() => (
247
+ <ChatPrompt config={config} onSubmit={onSubmit} onDismiss={onDismiss} />
248
+ ))
249
+
250
+ fireEvent.click(getByLabelText('Dismiss'))
251
+
252
+ expect(onDismiss).toHaveBeenCalled()
253
+ expect(onSubmit).toHaveBeenCalledWith({
254
+ type: 'choice',
255
+ value: '',
256
+ label: '',
257
+ dismissed: true,
258
+ })
259
+ })
260
+ })
261
+
262
+ // ─── Accessibility ───────────────────────────────────
263
+
264
+ describe('accessibility', () => {
265
+ it('has role="dialog" with title as label', () => {
266
+ const config: ChatPromptConfig = {
267
+ type: 'choice',
268
+ title: 'Pick one',
269
+ config: { options: [{ value: 'a', label: 'A' }] },
270
+ }
271
+
272
+ const { getByRole } = render(() => (
273
+ <ChatPrompt config={config} onSubmit={() => {}} />
274
+ ))
275
+
276
+ const dialog = getByRole('dialog')
277
+ expect(dialog.getAttribute('aria-label')).toBe('Pick one')
278
+ })
279
+ })
280
+ })