@copilotkit/react-core 1.54.1-next.6 → 1.55.0-next.7

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 (183) hide show
  1. package/CHANGELOG.md +121 -106
  2. package/dist/copilotkit-B3Mb1yVE.cjs +7975 -0
  3. package/dist/copilotkit-B3Mb1yVE.cjs.map +1 -0
  4. package/dist/copilotkit-DBzgOMby.d.cts +2182 -0
  5. package/dist/copilotkit-DBzgOMby.d.cts.map +1 -0
  6. package/dist/copilotkit-DNYSFuz5.mjs +7562 -0
  7. package/dist/copilotkit-DNYSFuz5.mjs.map +1 -0
  8. package/dist/copilotkit-Dy5w3qEV.d.mts +2182 -0
  9. package/dist/copilotkit-Dy5w3qEV.d.mts.map +1 -0
  10. package/dist/index.cjs +27 -28
  11. package/dist/index.cjs.map +1 -1
  12. package/dist/index.d.cts +3 -3
  13. package/dist/index.d.cts.map +1 -1
  14. package/dist/index.d.mts +3 -3
  15. package/dist/index.d.mts.map +1 -1
  16. package/dist/index.mjs +4 -5
  17. package/dist/index.mjs.map +1 -1
  18. package/dist/index.umd.js +1941 -35
  19. package/dist/index.umd.js.map +1 -1
  20. package/dist/v2/index.cjs +77 -7
  21. package/dist/v2/index.css +1 -2
  22. package/dist/v2/index.d.cts +6 -4
  23. package/dist/v2/index.d.mts +6 -4
  24. package/dist/v2/index.mjs +7 -4
  25. package/dist/v2/index.umd.js +5725 -24
  26. package/dist/v2/index.umd.js.map +1 -1
  27. package/package.json +35 -7
  28. package/scripts/scope-preflight.mjs +101 -0
  29. package/src/components/CopilotListeners.tsx +2 -6
  30. package/src/components/copilot-provider/copilot-messages.tsx +1 -1
  31. package/src/components/copilot-provider/copilotkit-props.tsx +1 -1
  32. package/src/components/copilot-provider/copilotkit.tsx +4 -4
  33. package/src/context/copilot-messages-context.tsx +1 -1
  34. package/src/hooks/__tests__/use-coagent-config.test.ts +2 -2
  35. package/src/hooks/__tests__/use-coagent-state-render.e2e.test.tsx +2 -2
  36. package/src/hooks/__tests__/use-copilot-chat-internal-connect.test.tsx +3 -7
  37. package/src/hooks/__tests__/use-frontend-tool-available.test.tsx +1 -1
  38. package/src/hooks/__tests__/use-frontend-tool-remount.e2e.test.tsx +4 -4
  39. package/src/hooks/use-agent-nodename.ts +1 -1
  40. package/src/hooks/use-coagent-state-render-bridge.tsx +1 -4
  41. package/src/hooks/use-coagent.ts +1 -1
  42. package/src/hooks/use-configure-chat-suggestions.tsx +2 -2
  43. package/src/hooks/use-copilot-chat-suggestions.tsx +2 -2
  44. package/src/hooks/use-copilot-chat_internal.ts +2 -2
  45. package/src/hooks/use-copilot-readable.ts +1 -1
  46. package/src/hooks/use-frontend-tool.ts +2 -2
  47. package/src/hooks/use-human-in-the-loop.ts +2 -2
  48. package/src/hooks/use-langgraph-interrupt.ts +2 -5
  49. package/src/hooks/use-lazy-tool-renderer.tsx +1 -1
  50. package/src/hooks/use-render-tool-call.ts +1 -1
  51. package/src/lib/copilot-task.ts +1 -1
  52. package/src/setupTests.ts +18 -14
  53. package/src/v2/__tests__/A2UIMessageRenderer.test.tsx +176 -0
  54. package/src/v2/__tests__/globalSetup.ts +14 -0
  55. package/src/v2/__tests__/setup.ts +93 -0
  56. package/src/v2/__tests__/utils/test-helpers.tsx +470 -0
  57. package/src/v2/a2ui/A2UIMessageRenderer.tsx +206 -0
  58. package/src/v2/components/CopilotKitInspector.tsx +50 -0
  59. package/src/v2/components/MCPAppsActivityRenderer.tsx +785 -0
  60. package/src/v2/components/WildcardToolCallRender.tsx +86 -0
  61. package/src/v2/components/__tests__/license-warning-banner.test.tsx +46 -0
  62. package/src/v2/components/chat/CopilotChat.tsx +431 -0
  63. package/src/v2/components/chat/CopilotChatAssistantMessage.tsx +375 -0
  64. package/src/v2/components/chat/CopilotChatAudioRecorder.tsx +350 -0
  65. package/src/v2/components/chat/CopilotChatInput.tsx +1302 -0
  66. package/src/v2/components/chat/CopilotChatMessageView.tsx +556 -0
  67. package/src/v2/components/chat/CopilotChatReasoningMessage.tsx +252 -0
  68. package/src/v2/components/chat/CopilotChatSuggestionPill.tsx +59 -0
  69. package/src/v2/components/chat/CopilotChatSuggestionView.tsx +133 -0
  70. package/src/v2/components/chat/CopilotChatToggleButton.tsx +171 -0
  71. package/src/v2/components/chat/CopilotChatToolCallsView.tsx +40 -0
  72. package/src/v2/components/chat/CopilotChatUserMessage.tsx +388 -0
  73. package/src/v2/components/chat/CopilotChatView.tsx +598 -0
  74. package/src/v2/components/chat/CopilotModalHeader.tsx +129 -0
  75. package/src/v2/components/chat/CopilotPopup.tsx +81 -0
  76. package/src/v2/components/chat/CopilotPopupView.tsx +317 -0
  77. package/src/v2/components/chat/CopilotSidebar.tsx +76 -0
  78. package/src/v2/components/chat/CopilotSidebarView.tsx +255 -0
  79. package/src/v2/components/chat/__tests__/CopilotChat.e2e.test.tsx +1113 -0
  80. package/src/v2/components/chat/__tests__/CopilotChat.onError.test.tsx +73 -0
  81. package/src/v2/components/chat/__tests__/CopilotChat.slots.e2e.test.tsx +432 -0
  82. package/src/v2/components/chat/__tests__/CopilotChatActivityRendering.e2e.test.tsx +150 -0
  83. package/src/v2/components/chat/__tests__/CopilotChatAssistantMessage.slots.e2e.test.tsx +624 -0
  84. package/src/v2/components/chat/__tests__/CopilotChatAssistantMessage.test.tsx +702 -0
  85. package/src/v2/components/chat/__tests__/CopilotChatCssClasses.test.tsx +107 -0
  86. package/src/v2/components/chat/__tests__/CopilotChatInput.slots.e2e.test.tsx +929 -0
  87. package/src/v2/components/chat/__tests__/CopilotChatInput.test.tsx +986 -0
  88. package/src/v2/components/chat/__tests__/CopilotChatMessageView.slots.e2e.test.tsx +1004 -0
  89. package/src/v2/components/chat/__tests__/CopilotChatMessageView.test.tsx +169 -0
  90. package/src/v2/components/chat/__tests__/CopilotChatSuggestionView.slots.e2e.test.tsx +530 -0
  91. package/src/v2/components/chat/__tests__/CopilotChatToolRendering.e2e.test.tsx +782 -0
  92. package/src/v2/components/chat/__tests__/CopilotChatToolRerenders.e2e.test.tsx +2413 -0
  93. package/src/v2/components/chat/__tests__/CopilotChatUserMessage.slots.e2e.test.tsx +621 -0
  94. package/src/v2/components/chat/__tests__/CopilotChatView.onClick.e2e.test.tsx +853 -0
  95. package/src/v2/components/chat/__tests__/CopilotChatView.slots.e2e.test.tsx +1050 -0
  96. package/src/v2/components/chat/__tests__/CopilotModalHeader.slots.e2e.test.tsx +484 -0
  97. package/src/v2/components/chat/__tests__/CopilotPopupView.slots.e2e.test.tsx +612 -0
  98. package/src/v2/components/chat/__tests__/CopilotSidebarView.slots.e2e.test.tsx +502 -0
  99. package/src/v2/components/chat/__tests__/MCPAppsActivityRenderer.e2e.test.tsx +1011 -0
  100. package/src/v2/components/chat/__tests__/setup.ts +1 -0
  101. package/src/v2/components/chat/index.ts +79 -0
  102. package/src/v2/components/index.ts +7 -0
  103. package/src/v2/components/license-warning-banner.tsx +198 -0
  104. package/src/v2/components/ui/button.tsx +123 -0
  105. package/src/v2/components/ui/dropdown-menu.tsx +258 -0
  106. package/src/v2/components/ui/tooltip.tsx +60 -0
  107. package/src/v2/hooks/__tests__/standard-schema-types.test.tsx +152 -0
  108. package/src/v2/hooks/__tests__/standard-schema.test.tsx +282 -0
  109. package/src/v2/hooks/__tests__/use-agent-context-timing.e2e.test.tsx +132 -0
  110. package/src/v2/hooks/__tests__/use-agent-context.test.tsx +401 -0
  111. package/src/v2/hooks/__tests__/use-agent-error-state.test.tsx +44 -0
  112. package/src/v2/hooks/__tests__/use-agent-stability.test.tsx +205 -0
  113. package/src/v2/hooks/__tests__/use-agent.e2e.test.tsx +148 -0
  114. package/src/v2/hooks/__tests__/use-component.test.tsx +123 -0
  115. package/src/v2/hooks/__tests__/use-configure-suggestions.e2e.test.tsx +696 -0
  116. package/src/v2/hooks/__tests__/use-default-render-tool.test.tsx +153 -0
  117. package/src/v2/hooks/__tests__/use-frontend-tool-available.test.tsx +167 -0
  118. package/src/v2/hooks/__tests__/use-frontend-tool.e2e.test.tsx +2129 -0
  119. package/src/v2/hooks/__tests__/use-human-in-the-loop.e2e.test.tsx +1261 -0
  120. package/src/v2/hooks/__tests__/use-interrupt.test.tsx +397 -0
  121. package/src/v2/hooks/__tests__/use-katex-styles.test.tsx +56 -0
  122. package/src/v2/hooks/__tests__/use-keyboard-height.test.tsx +192 -0
  123. package/src/v2/hooks/__tests__/use-render-tool.test.tsx +259 -0
  124. package/src/v2/hooks/__tests__/use-suggestions.e2e.test.tsx +524 -0
  125. package/src/v2/hooks/__tests__/use-threads.test.tsx +433 -0
  126. package/src/v2/hooks/__tests__/zod-regression.test.tsx +311 -0
  127. package/src/v2/hooks/index.ts +18 -0
  128. package/src/v2/hooks/use-agent-context.tsx +45 -0
  129. package/src/v2/hooks/use-agent.tsx +155 -0
  130. package/src/v2/hooks/use-component.tsx +89 -0
  131. package/src/v2/hooks/use-configure-suggestions.tsx +187 -0
  132. package/src/v2/hooks/use-default-render-tool.tsx +254 -0
  133. package/src/v2/hooks/use-frontend-tool.tsx +43 -0
  134. package/src/v2/hooks/use-human-in-the-loop.tsx +81 -0
  135. package/src/v2/hooks/use-interrupt.tsx +305 -0
  136. package/src/v2/hooks/use-keyboard-height.tsx +67 -0
  137. package/src/v2/hooks/use-render-activity-message.tsx +73 -0
  138. package/src/v2/hooks/use-render-custom-messages.tsx +93 -0
  139. package/src/v2/hooks/use-render-tool-call.tsx +175 -0
  140. package/src/v2/hooks/use-render-tool.tsx +181 -0
  141. package/src/v2/hooks/use-suggestions.tsx +91 -0
  142. package/src/v2/hooks/use-threads.tsx +256 -0
  143. package/src/v2/hooks/useKatexStyles.ts +27 -0
  144. package/src/v2/index.css +1 -1
  145. package/src/v2/index.ts +18 -2
  146. package/src/v2/lib/__tests__/completePartialMarkdown.test.ts +495 -0
  147. package/src/v2/lib/__tests__/renderSlot.test.tsx +588 -0
  148. package/src/v2/lib/react-core.ts +156 -0
  149. package/src/v2/lib/slots.tsx +143 -0
  150. package/src/v2/lib/transcription-client.ts +184 -0
  151. package/src/v2/lib/utils.ts +8 -0
  152. package/src/v2/providers/CopilotChatConfigurationProvider.tsx +162 -0
  153. package/src/v2/providers/CopilotKitProvider.tsx +600 -0
  154. package/src/v2/providers/__tests__/CopilotChatConfigurationProvider.test.tsx +546 -0
  155. package/src/v2/providers/__tests__/CopilotKitProvider.license.test.tsx +101 -0
  156. package/src/v2/providers/__tests__/CopilotKitProvider.onError.test.tsx +69 -0
  157. package/src/v2/providers/__tests__/CopilotKitProvider.renderCustomMessages.e2e.test.tsx +881 -0
  158. package/src/v2/providers/__tests__/CopilotKitProvider.stability.test.tsx +740 -0
  159. package/src/v2/providers/__tests__/CopilotKitProvider.test.tsx +642 -0
  160. package/src/v2/providers/__tests__/CopilotKitProvider.wildcard.test.tsx +294 -0
  161. package/src/v2/providers/index.ts +14 -0
  162. package/src/v2/styles/globals.css +230 -0
  163. package/src/v2/types/__tests__/defineToolCallRenderer.test.tsx +525 -0
  164. package/src/v2/types/defineToolCallRenderer.ts +65 -0
  165. package/src/v2/types/frontend-tool.ts +8 -0
  166. package/src/v2/types/human-in-the-loop.ts +33 -0
  167. package/src/v2/types/index.ts +7 -0
  168. package/src/v2/types/interrupt.ts +15 -0
  169. package/src/v2/types/react-activity-message-renderer.ts +27 -0
  170. package/src/v2/types/react-custom-message-renderer.ts +17 -0
  171. package/src/v2/types/react-tool-call-renderer.ts +32 -0
  172. package/tsdown.config.ts +34 -10
  173. package/vitest.config.mjs +4 -3
  174. package/LICENSE +0 -21
  175. package/dist/copilotkit-BRPQ2sqS.d.cts +0 -670
  176. package/dist/copilotkit-BRPQ2sqS.d.cts.map +0 -1
  177. package/dist/copilotkit-C94ayZbs.cjs +0 -2161
  178. package/dist/copilotkit-C94ayZbs.cjs.map +0 -1
  179. package/dist/copilotkit-CwZMFmSK.d.mts +0 -670
  180. package/dist/copilotkit-CwZMFmSK.d.mts.map +0 -1
  181. package/dist/copilotkit-Yh_Ld_FX.mjs +0 -2031
  182. package/dist/copilotkit-Yh_Ld_FX.mjs.map +0 -1
  183. package/dist/v2/index.css.map +0 -1
@@ -0,0 +1,502 @@
1
+ import React from "react";
2
+ import { render, screen, fireEvent } from "@testing-library/react";
3
+ import { describe, it, expect, vi } from "vitest";
4
+ import { CopilotSidebarView } from "../CopilotSidebarView";
5
+ import { CopilotKitProvider } from "../../../providers/CopilotKitProvider";
6
+ import { CopilotChatConfigurationProvider } from "../../../providers/CopilotChatConfigurationProvider";
7
+
8
+ // Wrapper to provide required context
9
+ const TestWrapper: React.FC<{ children: React.ReactNode }> = ({ children }) => (
10
+ <CopilotKitProvider>
11
+ <CopilotChatConfigurationProvider threadId="test-thread">
12
+ {children}
13
+ </CopilotChatConfigurationProvider>
14
+ </CopilotKitProvider>
15
+ );
16
+
17
+ const sampleMessages = [
18
+ { id: "1", role: "user" as const, content: "Hello" },
19
+ { id: "2", role: "assistant" as const, content: "Hi there!" },
20
+ ];
21
+
22
+ describe("CopilotSidebarView Slot System E2E Tests", () => {
23
+ // ============================================================================
24
+ // 1. TAILWIND CLASS TESTS - HEADER SLOT (UNIQUE TO SIDEBAR)
25
+ // ============================================================================
26
+ describe("1. Tailwind Class Slot Override - Header Slot", () => {
27
+ describe("header slot", () => {
28
+ it("should apply tailwind class string to header", () => {
29
+ const { container } = render(
30
+ <TestWrapper>
31
+ <CopilotSidebarView
32
+ messages={sampleMessages}
33
+ header="bg-gradient-to-r from-blue-500 to-purple-500 text-white"
34
+ />
35
+ </TestWrapper>,
36
+ );
37
+
38
+ const header = container.querySelector(".bg-gradient-to-r");
39
+ if (header) {
40
+ expect(header.classList.contains("from-blue-500")).toBe(true);
41
+ expect(header.classList.contains("text-white")).toBe(true);
42
+ }
43
+ });
44
+
45
+ it("should override default header border with custom styles", () => {
46
+ const { container } = render(
47
+ <TestWrapper>
48
+ <CopilotSidebarView
49
+ messages={sampleMessages}
50
+ header="border-b-2 border-blue-500"
51
+ />
52
+ </TestWrapper>,
53
+ );
54
+
55
+ const header = container.querySelector(".border-b-2");
56
+ expect(header).toBeDefined();
57
+ });
58
+ });
59
+ });
60
+
61
+ // ============================================================================
62
+ // 2. PROPERTY PASSING TESTS - HEADER SLOT
63
+ // ============================================================================
64
+ describe("2. Property Passing - Header Slot", () => {
65
+ describe("header slot", () => {
66
+ it("should pass custom props to header", () => {
67
+ const { container } = render(
68
+ <TestWrapper>
69
+ <CopilotSidebarView
70
+ messages={sampleMessages}
71
+ header={{ "data-testid": "custom-sidebar-header" }}
72
+ />
73
+ </TestWrapper>,
74
+ );
75
+
76
+ const header = screen.queryByTestId("custom-sidebar-header");
77
+ expect(header).toBeDefined();
78
+ });
79
+
80
+ it("should pass title prop through to header", () => {
81
+ render(
82
+ <TestWrapper>
83
+ <CopilotSidebarView
84
+ messages={sampleMessages}
85
+ header={{ title: "My Sidebar Chat" }}
86
+ />
87
+ </TestWrapper>,
88
+ );
89
+
90
+ // The title should be rendered in the header
91
+ expect(screen.queryByText("My Sidebar Chat")).toBeDefined();
92
+ });
93
+ });
94
+ });
95
+
96
+ // ============================================================================
97
+ // 3. CUSTOM COMPONENT TESTS - HEADER SLOT
98
+ // ============================================================================
99
+ describe("3. Custom Component - Header Slot", () => {
100
+ it("should allow custom component for header", () => {
101
+ const CustomHeader: React.FC = () => (
102
+ <header
103
+ data-testid="custom-header-component"
104
+ className="custom-sidebar-header"
105
+ >
106
+ <span>Custom Sidebar Header</span>
107
+ <button>Custom Close</button>
108
+ </header>
109
+ );
110
+
111
+ render(
112
+ <TestWrapper>
113
+ <CopilotSidebarView
114
+ messages={sampleMessages}
115
+ header={CustomHeader as any}
116
+ />
117
+ </TestWrapper>,
118
+ );
119
+
120
+ const custom = screen.queryByTestId("custom-header-component");
121
+ expect(custom).toBeDefined();
122
+ expect(custom?.textContent).toContain("Custom Sidebar Header");
123
+ });
124
+
125
+ it("should allow passing header props for customization", () => {
126
+ const { container } = render(
127
+ <TestWrapper>
128
+ <CopilotSidebarView
129
+ messages={sampleMessages}
130
+ header={{
131
+ title: "Customized Header",
132
+ titleContent: "text-xl font-bold",
133
+ closeButton: "bg-red-500",
134
+ }}
135
+ />
136
+ </TestWrapper>,
137
+ );
138
+
139
+ expect(screen.queryByText("Customized Header")).toBeDefined();
140
+ expect(container.querySelector(".text-xl")).toBeDefined();
141
+ });
142
+ });
143
+
144
+ // ============================================================================
145
+ // 4. INHERITED COPILOTCHATVIEW SLOTS
146
+ // ============================================================================
147
+ describe("4. Inherited CopilotChatView Slots", () => {
148
+ describe("messageView slot (inherited)", () => {
149
+ it("should apply tailwind class string to inherited messageView", () => {
150
+ const { container } = render(
151
+ <TestWrapper>
152
+ <CopilotSidebarView
153
+ messages={sampleMessages}
154
+ messageView="bg-gray-50 rounded-lg"
155
+ />
156
+ </TestWrapper>,
157
+ );
158
+
159
+ const messageView = container.querySelector(".bg-gray-50");
160
+ expect(messageView).toBeDefined();
161
+ });
162
+ });
163
+
164
+ describe("input slot (inherited)", () => {
165
+ it("should apply tailwind class string to inherited input", () => {
166
+ const { container } = render(
167
+ <TestWrapper>
168
+ <CopilotSidebarView
169
+ messages={sampleMessages}
170
+ input="border-2 border-blue-400"
171
+ />
172
+ </TestWrapper>,
173
+ );
174
+
175
+ const input = container.querySelector(".border-blue-400");
176
+ expect(input).toBeDefined();
177
+ });
178
+ });
179
+
180
+ describe("scrollView slot (inherited)", () => {
181
+ it("should apply tailwind class string to inherited scrollView", () => {
182
+ const { container } = render(
183
+ <TestWrapper>
184
+ <CopilotSidebarView
185
+ messages={sampleMessages}
186
+ scrollView="overflow-y-scroll bg-white"
187
+ />
188
+ </TestWrapper>,
189
+ );
190
+
191
+ const scrollView = container.querySelector(".overflow-y-scroll");
192
+ expect(scrollView).toBeDefined();
193
+ });
194
+ });
195
+
196
+ describe("suggestionView slot (inherited)", () => {
197
+ it("should apply tailwind class string to inherited suggestionView", () => {
198
+ const suggestions = [
199
+ { title: "Test", message: "Test message", isLoading: false },
200
+ ];
201
+
202
+ const { container } = render(
203
+ <TestWrapper>
204
+ <CopilotSidebarView
205
+ messages={sampleMessages}
206
+ suggestions={suggestions}
207
+ suggestionView="gap-4 p-2"
208
+ />
209
+ </TestWrapper>,
210
+ );
211
+
212
+ const suggestionView = container.querySelector(".gap-4");
213
+ if (suggestionView) {
214
+ expect(suggestionView.classList.contains("p-2")).toBe(true);
215
+ }
216
+ });
217
+ });
218
+ });
219
+
220
+ // ============================================================================
221
+ // 5. DRILL-DOWN INTO HEADER SUB-SLOTS
222
+ // ============================================================================
223
+ describe("5. Drill-down into Header Sub-slots", () => {
224
+ it("should allow customizing header titleContent through props object", () => {
225
+ const { container } = render(
226
+ <TestWrapper>
227
+ <CopilotSidebarView
228
+ messages={sampleMessages}
229
+ header={{
230
+ title: "Sidebar Chat",
231
+ titleContent: "text-2xl text-purple-600 font-extrabold",
232
+ }}
233
+ />
234
+ </TestWrapper>,
235
+ );
236
+
237
+ const titleContent = container.querySelector(".text-2xl");
238
+ expect(titleContent).toBeDefined();
239
+ expect(titleContent?.classList.contains("text-purple-600")).toBe(true);
240
+ });
241
+
242
+ it("should allow customizing header closeButton through props object", () => {
243
+ const { container } = render(
244
+ <TestWrapper>
245
+ <CopilotSidebarView
246
+ messages={sampleMessages}
247
+ header={{
248
+ title: "Sidebar",
249
+ closeButton: "custom-close-btn",
250
+ }}
251
+ />
252
+ </TestWrapper>,
253
+ );
254
+
255
+ const closeBtn = container.querySelector(".custom-close-btn");
256
+ expect(closeBtn).toBeDefined();
257
+ });
258
+
259
+ it("should allow custom component for header via component slot", () => {
260
+ const CustomHeader: React.FC = () => (
261
+ <header data-testid="full-custom-header">
262
+ <span>Custom Header</span>
263
+ <button data-testid="sidebar-custom-close">← Back</button>
264
+ </header>
265
+ );
266
+
267
+ render(
268
+ <TestWrapper>
269
+ <CopilotSidebarView
270
+ messages={sampleMessages}
271
+ header={CustomHeader as any}
272
+ />
273
+ </TestWrapper>,
274
+ );
275
+
276
+ const customClose = screen.queryByTestId("sidebar-custom-close");
277
+ expect(customClose).toBeDefined();
278
+ expect(customClose?.textContent).toBe("← Back");
279
+ });
280
+ });
281
+
282
+ // ============================================================================
283
+ // 6. CLASSNAME AND MIXED CUSTOMIZATION
284
+ // ============================================================================
285
+ describe("6. className Override and Mixed Customization", () => {
286
+ it("should merge multiple slot classNames correctly", () => {
287
+ const { container } = render(
288
+ <TestWrapper>
289
+ <CopilotSidebarView
290
+ messages={sampleMessages}
291
+ header="header-style"
292
+ messageView="message-style"
293
+ input="input-style"
294
+ />
295
+ </TestWrapper>,
296
+ );
297
+
298
+ expect(container.querySelector(".header-style")).toBeDefined();
299
+ expect(container.querySelector(".message-style")).toBeDefined();
300
+ expect(container.querySelector(".input-style")).toBeDefined();
301
+ });
302
+
303
+ it("should work with property objects and class strings mixed", () => {
304
+ const onClick = vi.fn();
305
+ const { container } = render(
306
+ <TestWrapper>
307
+ <CopilotSidebarView
308
+ messages={sampleMessages}
309
+ header={{ onClick, className: "clickable-header" }}
310
+ input="styled-input"
311
+ />
312
+ </TestWrapper>,
313
+ );
314
+
315
+ expect(container.querySelector(".styled-input")).toBeDefined();
316
+
317
+ const header = container.querySelector(".clickable-header");
318
+ if (header) {
319
+ fireEvent.click(header);
320
+ expect(onClick).toHaveBeenCalled();
321
+ }
322
+ });
323
+
324
+ it("should support custom width prop", () => {
325
+ const { container } = render(
326
+ <TestWrapper>
327
+ <CopilotSidebarView messages={sampleMessages} width={600} />
328
+ </TestWrapper>,
329
+ );
330
+
331
+ const sidebar = container.querySelector("[data-copilot-sidebar]");
332
+ expect(sidebar).toBeDefined();
333
+ // Width should be applied via CSS custom property
334
+ expect(sidebar?.getAttribute("style")).toContain("--sidebar-width");
335
+ });
336
+
337
+ it("should support string width prop", () => {
338
+ const { container } = render(
339
+ <TestWrapper>
340
+ <CopilotSidebarView messages={sampleMessages} width="50vw" />
341
+ </TestWrapper>,
342
+ );
343
+
344
+ const sidebar = container.querySelector("[data-copilot-sidebar]");
345
+ expect(sidebar).toBeDefined();
346
+ });
347
+ });
348
+
349
+ // ============================================================================
350
+ // 7. INTEGRATION TESTS
351
+ // ============================================================================
352
+ describe("7. Integration Tests", () => {
353
+ it("should render sidebar with all default components", () => {
354
+ const { container } = render(
355
+ <TestWrapper>
356
+ <CopilotSidebarView messages={sampleMessages} />
357
+ </TestWrapper>,
358
+ );
359
+
360
+ const sidebar = container.querySelector("[data-copilot-sidebar]");
361
+ expect(sidebar).toBeDefined();
362
+ // Should have header
363
+ expect(
364
+ container.querySelector('[data-slot="copilot-modal-header"]'),
365
+ ).toBeDefined();
366
+ });
367
+
368
+ it("should render messages in sidebar", () => {
369
+ render(
370
+ <TestWrapper>
371
+ <CopilotSidebarView messages={sampleMessages} />
372
+ </TestWrapper>,
373
+ );
374
+
375
+ expect(screen.queryByText("Hello")).toBeDefined();
376
+ expect(screen.queryByText("Hi there!")).toBeDefined();
377
+ });
378
+
379
+ it("should handle empty messages array", () => {
380
+ const { container } = render(
381
+ <TestWrapper>
382
+ <CopilotSidebarView messages={[]} />
383
+ </TestWrapper>,
384
+ );
385
+
386
+ const sidebar = container.querySelector("[data-copilot-sidebar]");
387
+ expect(sidebar).toBeDefined();
388
+ });
389
+
390
+ it("should combine header customization with inherited slot customization", () => {
391
+ const { container } = render(
392
+ <TestWrapper>
393
+ <CopilotSidebarView
394
+ messages={sampleMessages}
395
+ header={{
396
+ title: "Full Custom",
397
+ className: "custom-header-root",
398
+ titleContent: "custom-title",
399
+ }}
400
+ messageView="custom-message"
401
+ input="custom-input"
402
+ scrollView="custom-scroll"
403
+ />
404
+ </TestWrapper>,
405
+ );
406
+
407
+ expect(container.querySelector(".custom-header-root")).toBeDefined();
408
+ expect(container.querySelector(".custom-title")).toBeDefined();
409
+ expect(container.querySelector(".custom-message")).toBeDefined();
410
+ expect(container.querySelector(".custom-input")).toBeDefined();
411
+ expect(container.querySelector(".custom-scroll")).toBeDefined();
412
+ });
413
+ });
414
+
415
+ // ============================================================================
416
+ // 8. TOGGLE BUTTON SLOT TESTS
417
+ // ============================================================================
418
+ describe("8. Toggle Button Slot", () => {
419
+ describe("toggleButton slot - Tailwind class string", () => {
420
+ it("should apply tailwind class string to toggle button", () => {
421
+ const { container } = render(
422
+ <TestWrapper>
423
+ <CopilotSidebarView
424
+ messages={sampleMessages}
425
+ toggleButton="bg-red-500 hover:bg-red-600"
426
+ />
427
+ </TestWrapper>,
428
+ );
429
+
430
+ const toggleButton = container.querySelector(".bg-red-500");
431
+ expect(toggleButton).toBeDefined();
432
+ expect(toggleButton?.classList.contains("hover:bg-red-600")).toBe(true);
433
+ });
434
+ });
435
+
436
+ describe("toggleButton slot - Props object", () => {
437
+ it("should pass custom props to toggle button", () => {
438
+ const { container } = render(
439
+ <TestWrapper>
440
+ <CopilotSidebarView
441
+ messages={sampleMessages}
442
+ toggleButton={{ "data-testid": "custom-toggle-button" }}
443
+ />
444
+ </TestWrapper>,
445
+ );
446
+
447
+ const toggleButton = screen.queryByTestId("custom-toggle-button");
448
+ expect(toggleButton).toBeDefined();
449
+ });
450
+
451
+ it("should pass openIcon and closeIcon sub-slot props", () => {
452
+ const { container } = render(
453
+ <TestWrapper>
454
+ <CopilotSidebarView
455
+ messages={sampleMessages}
456
+ toggleButton={{
457
+ openIcon: "text-green-500",
458
+ closeIcon: "text-red-500",
459
+ }}
460
+ />
461
+ </TestWrapper>,
462
+ );
463
+
464
+ // The icons should have custom classes applied
465
+ const openIconSlot = container.querySelector(
466
+ '[data-slot="chat-toggle-button-open-icon"]',
467
+ );
468
+ const closeIconSlot = container.querySelector(
469
+ '[data-slot="chat-toggle-button-close-icon"]',
470
+ );
471
+ expect(openIconSlot).toBeDefined();
472
+ expect(closeIconSlot).toBeDefined();
473
+ });
474
+ });
475
+
476
+ describe("toggleButton slot - Custom component", () => {
477
+ it("should allow custom component for toggle button", () => {
478
+ const CustomToggleButton: React.FC = () => (
479
+ <button
480
+ data-testid="custom-toggle-component"
481
+ className="custom-toggle"
482
+ >
483
+ Toggle Chat
484
+ </button>
485
+ );
486
+
487
+ render(
488
+ <TestWrapper>
489
+ <CopilotSidebarView
490
+ messages={sampleMessages}
491
+ toggleButton={CustomToggleButton as any}
492
+ />
493
+ </TestWrapper>,
494
+ );
495
+
496
+ const custom = screen.queryByTestId("custom-toggle-component");
497
+ expect(custom).toBeDefined();
498
+ expect(custom?.textContent).toBe("Toggle Chat");
499
+ });
500
+ });
501
+ });
502
+ });