@copilotkitnext/angular 0.0.2 → 0.0.5

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 (173) hide show
  1. package/README.md +3 -3
  2. package/dist/README.md +3 -3
  3. package/dist/components/chat/copilot-chat-assistant-message.component.d.ts +10 -10
  4. package/dist/components/chat/copilot-chat-message-view.component.d.ts +42 -42
  5. package/dist/components/chat/copilot-chat-view.component.d.ts +14 -14
  6. package/dist/core/copilotkit.providers.d.ts +1 -1
  7. package/dist/core/copilotkit.service.d.ts +5 -5
  8. package/dist/core/copilotkit.types.d.ts +8 -10
  9. package/dist/directives/copilotkit-frontend-tool.directive.d.ts +1 -1
  10. package/dist/esm2022/components/chat/copilot-chat-assistant-message-buttons.component.mjs +384 -0
  11. package/dist/esm2022/components/chat/copilot-chat-assistant-message-renderer.component.mjs +286 -0
  12. package/dist/esm2022/components/chat/copilot-chat-assistant-message-toolbar.component.mjs +27 -0
  13. package/dist/esm2022/components/chat/copilot-chat-assistant-message.component.mjs +433 -0
  14. package/dist/esm2022/components/chat/copilot-chat-assistant-message.types.mjs +2 -0
  15. package/dist/esm2022/components/chat/copilot-chat-audio-recorder.component.mjs +202 -0
  16. package/dist/esm2022/components/chat/copilot-chat-buttons.component.mjs +321 -0
  17. package/dist/esm2022/components/chat/copilot-chat-input-defaults.mjs +38 -0
  18. package/dist/esm2022/components/chat/copilot-chat-input.component.mjs +666 -0
  19. package/dist/esm2022/components/chat/copilot-chat-input.types.mjs +10 -0
  20. package/dist/esm2022/components/chat/copilot-chat-message-view-cursor.component.mjs +45 -0
  21. package/dist/esm2022/components/chat/copilot-chat-message-view.component.mjs +296 -0
  22. package/dist/esm2022/components/chat/copilot-chat-message-view.types.mjs +2 -0
  23. package/dist/esm2022/components/chat/copilot-chat-textarea.component.mjs +188 -0
  24. package/dist/esm2022/components/chat/copilot-chat-tool-calls-view.component.mjs +216 -0
  25. package/dist/esm2022/components/chat/copilot-chat-toolbar.component.mjs +25 -0
  26. package/dist/esm2022/components/chat/copilot-chat-tools-menu.component.mjs +199 -0
  27. package/dist/esm2022/components/chat/copilot-chat-user-message-branch-navigation.component.mjs +137 -0
  28. package/dist/esm2022/components/chat/copilot-chat-user-message-buttons.component.mjs +207 -0
  29. package/dist/esm2022/components/chat/copilot-chat-user-message-renderer.component.mjs +35 -0
  30. package/dist/esm2022/components/chat/copilot-chat-user-message-toolbar.component.mjs +34 -0
  31. package/dist/esm2022/components/chat/copilot-chat-user-message.component.mjs +341 -0
  32. package/dist/esm2022/components/chat/copilot-chat-user-message.types.mjs +2 -0
  33. package/dist/esm2022/components/chat/copilot-chat-view-disclaimer.component.mjs +52 -0
  34. package/dist/esm2022/components/chat/copilot-chat-view-feather.component.mjs +55 -0
  35. package/dist/esm2022/components/chat/copilot-chat-view-handlers.service.mjs +19 -0
  36. package/dist/esm2022/components/chat/copilot-chat-view-input-container.component.mjs +110 -0
  37. package/dist/esm2022/components/chat/copilot-chat-view-scroll-to-bottom-button.component.mjs +93 -0
  38. package/dist/esm2022/components/chat/copilot-chat-view-scroll-view.component.mjs +443 -0
  39. package/dist/esm2022/components/chat/copilot-chat-view.component.mjs +479 -0
  40. package/dist/esm2022/components/chat/copilot-chat-view.types.mjs +2 -0
  41. package/dist/esm2022/components/chat/copilot-chat.component.mjs +214 -0
  42. package/dist/esm2022/components/copilotkit-tool-render.component.mjs +153 -0
  43. package/dist/esm2022/copilotkitnext-angular.mjs +5 -0
  44. package/dist/esm2022/core/chat-configuration/chat-configuration.providers.mjs +65 -0
  45. package/dist/esm2022/core/chat-configuration/chat-configuration.service.mjs +145 -0
  46. package/dist/esm2022/core/chat-configuration/chat-configuration.types.mjs +26 -0
  47. package/dist/esm2022/core/copilotkit.providers.mjs +34 -0
  48. package/dist/esm2022/core/copilotkit.service.mjs +426 -0
  49. package/dist/esm2022/core/copilotkit.types.mjs +13 -0
  50. package/dist/esm2022/directives/copilotkit-agent-context.directive.mjs +130 -0
  51. package/dist/esm2022/directives/copilotkit-agent.directive.mjs +217 -0
  52. package/dist/esm2022/directives/copilotkit-chat-config.directive.mjs +218 -0
  53. package/dist/esm2022/directives/copilotkit-config.directive.mjs +94 -0
  54. package/dist/esm2022/directives/copilotkit-frontend-tool.directive.mjs +128 -0
  55. package/dist/esm2022/directives/copilotkit-human-in-the-loop.directive.mjs +265 -0
  56. package/dist/esm2022/directives/stick-to-bottom.directive.mjs +181 -0
  57. package/dist/esm2022/index.mjs +70 -0
  58. package/dist/esm2022/lib/directives/tooltip.directive.mjs +211 -0
  59. package/dist/esm2022/lib/slots/copilot-slot.component.mjs +144 -0
  60. package/dist/esm2022/lib/slots/slot.types.mjs +6 -0
  61. package/dist/esm2022/lib/slots/slot.utils.mjs +222 -0
  62. package/dist/esm2022/lib/utils.mjs +10 -0
  63. package/dist/esm2022/services/resize-observer.service.mjs +152 -0
  64. package/dist/esm2022/services/scroll-position.service.mjs +124 -0
  65. package/dist/esm2022/types/frontend-tool.mjs +2 -0
  66. package/dist/esm2022/types/human-in-the-loop.mjs +2 -0
  67. package/dist/esm2022/utils/agent-context.utils.mjs +114 -0
  68. package/dist/esm2022/utils/agent.utils.mjs +204 -0
  69. package/dist/esm2022/utils/chat-config.utils.mjs +186 -0
  70. package/dist/esm2022/utils/copilotkit.utils.mjs +20 -0
  71. package/dist/esm2022/utils/frontend-tool.utils.mjs +224 -0
  72. package/dist/esm2022/utils/human-in-the-loop.utils.mjs +293 -0
  73. package/dist/fesm2022/copilotkitnext-angular.mjs +174 -187
  74. package/dist/fesm2022/copilotkitnext-angular.mjs.map +1 -1
  75. package/dist/utils/frontend-tool.utils.d.ts +1 -1
  76. package/package.json +23 -20
  77. package/vitest.config.mts +32 -21
  78. package/.turbo/turbo-build.log +0 -38
  79. package/.turbo/turbo-check-types.log +0 -0
  80. package/.turbo/turbo-test.log +0 -71
  81. package/ng-package.json +0 -19
  82. package/src/components/chat/__tests__/copilot-chat-assistant-message.component.spec.ts +0 -282
  83. package/src/components/chat/__tests__/copilot-chat-input.component.spec.ts +0 -419
  84. package/src/components/chat/__tests__/copilot-chat-message-view.component.spec.ts +0 -372
  85. package/src/components/chat/__tests__/copilot-chat-user-message.component.spec.ts +0 -249
  86. package/src/components/chat/copilot-chat-assistant-message-buttons.component.ts +0 -292
  87. package/src/components/chat/copilot-chat-assistant-message-renderer.component.ts +0 -472
  88. package/src/components/chat/copilot-chat-assistant-message-toolbar.component.ts +0 -29
  89. package/src/components/chat/copilot-chat-assistant-message.component.ts +0 -463
  90. package/src/components/chat/copilot-chat-assistant-message.types.ts +0 -50
  91. package/src/components/chat/copilot-chat-audio-recorder.component.ts +0 -241
  92. package/src/components/chat/copilot-chat-buttons.component.ts +0 -308
  93. package/src/components/chat/copilot-chat-buttons.component.ts.bak +0 -471
  94. package/src/components/chat/copilot-chat-input-defaults.ts +0 -47
  95. package/src/components/chat/copilot-chat-input.component.ts +0 -512
  96. package/src/components/chat/copilot-chat-input.types.ts +0 -148
  97. package/src/components/chat/copilot-chat-message-view-cursor.component.ts +0 -51
  98. package/src/components/chat/copilot-chat-message-view.component.ts +0 -233
  99. package/src/components/chat/copilot-chat-message-view.types.ts +0 -39
  100. package/src/components/chat/copilot-chat-textarea.component.ts +0 -220
  101. package/src/components/chat/copilot-chat-tool-calls-view.component.ts +0 -261
  102. package/src/components/chat/copilot-chat-toolbar.component.ts +0 -35
  103. package/src/components/chat/copilot-chat-tools-menu.component.ts +0 -185
  104. package/src/components/chat/copilot-chat-user-message-branch-navigation.component.ts +0 -121
  105. package/src/components/chat/copilot-chat-user-message-buttons.component.ts +0 -170
  106. package/src/components/chat/copilot-chat-user-message-renderer.component.ts +0 -37
  107. package/src/components/chat/copilot-chat-user-message-toolbar.component.ts +0 -37
  108. package/src/components/chat/copilot-chat-user-message.component.ts +0 -247
  109. package/src/components/chat/copilot-chat-user-message.types.ts +0 -42
  110. package/src/components/chat/copilot-chat-view-disclaimer.component.ts +0 -51
  111. package/src/components/chat/copilot-chat-view-feather.component.ts +0 -47
  112. package/src/components/chat/copilot-chat-view-handlers.service.ts +0 -14
  113. package/src/components/chat/copilot-chat-view-input-container.component.ts +0 -87
  114. package/src/components/chat/copilot-chat-view-scroll-to-bottom-button.component.ts +0 -79
  115. package/src/components/chat/copilot-chat-view-scroll-view.component.ts +0 -322
  116. package/src/components/chat/copilot-chat-view.component.ts +0 -420
  117. package/src/components/chat/copilot-chat-view.types.ts +0 -52
  118. package/src/components/chat/copilot-chat.component.ts +0 -232
  119. package/src/components/copilotkit-tool-render.component.ts +0 -169
  120. package/src/core/__tests__/copilotkit.service.spec.ts +0 -1051
  121. package/src/core/__tests__/copilotkit.service.wildcard.spec.ts +0 -316
  122. package/src/core/chat-configuration/__tests__/chat-configuration.service.spec.ts +0 -287
  123. package/src/core/chat-configuration/chat-configuration.providers.ts +0 -71
  124. package/src/core/chat-configuration/chat-configuration.service.ts +0 -162
  125. package/src/core/chat-configuration/chat-configuration.types.ts +0 -57
  126. package/src/core/copilotkit.providers.ts +0 -59
  127. package/src/core/copilotkit.service.ts +0 -542
  128. package/src/core/copilotkit.types.ts +0 -132
  129. package/src/directives/__tests__/copilotkit-agent-context.directive.spec.ts +0 -384
  130. package/src/directives/__tests__/copilotkit-agent.directive.spec.ts +0 -253
  131. package/src/directives/__tests__/copilotkit-chat-config.directive.spec.ts +0 -385
  132. package/src/directives/__tests__/copilotkit-config.directive.spec.ts +0 -69
  133. package/src/directives/__tests__/copilotkit-frontend-tool-simple.directive.spec.ts +0 -60
  134. package/src/directives/__tests__/copilotkit-frontend-tool.directive.spec.ts +0 -108
  135. package/src/directives/__tests__/copilotkit-human-in-the-loop.directive.spec.ts +0 -452
  136. package/src/directives/copilotkit-agent-context.directive.ts +0 -138
  137. package/src/directives/copilotkit-agent.directive.ts +0 -225
  138. package/src/directives/copilotkit-chat-config.directive.ts +0 -241
  139. package/src/directives/copilotkit-config.directive.ts +0 -81
  140. package/src/directives/copilotkit-frontend-tool.directive.ts +0 -145
  141. package/src/directives/copilotkit-human-in-the-loop.directive.ts +0 -281
  142. package/src/directives/stick-to-bottom.directive.ts +0 -204
  143. package/src/index.ts +0 -105
  144. package/src/lib/directives/tooltip.directive.ts +0 -292
  145. package/src/lib/slots/__tests__/slot.utils.spec.ts +0 -377
  146. package/src/lib/slots/copilot-slot.component.ts +0 -135
  147. package/src/lib/slots/index.ts +0 -3
  148. package/src/lib/slots/slot.types.ts +0 -64
  149. package/src/lib/slots/slot.utils.ts +0 -289
  150. package/src/lib/utils.ts +0 -10
  151. package/src/public-api.ts +0 -1
  152. package/src/services/resize-observer.service.ts +0 -181
  153. package/src/services/scroll-position.service.ts +0 -169
  154. package/src/styles/globals.css +0 -266
  155. package/src/styles/index.css +0 -3
  156. package/src/test-setup.ts +0 -15
  157. package/src/testing/index.ts +0 -3
  158. package/src/testing/testing.utils.ts +0 -248
  159. package/src/types/frontend-tool.ts +0 -44
  160. package/src/types/human-in-the-loop.ts +0 -52
  161. package/src/utils/__tests__/agent.utils.spec.ts +0 -234
  162. package/src/utils/__tests__/chat-config.utils.spec.ts +0 -306
  163. package/src/utils/__tests__/frontend-tool-inject.spec.ts +0 -350
  164. package/src/utils/__tests__/frontend-tool-integration.spec.ts +0 -199
  165. package/src/utils/__tests__/frontend-tool.utils.spec.ts +0 -272
  166. package/src/utils/__tests__/human-in-the-loop.utils.spec.ts +0 -365
  167. package/src/utils/agent-context.utils.ts +0 -133
  168. package/src/utils/agent.utils.ts +0 -239
  169. package/src/utils/chat-config.utils.ts +0 -221
  170. package/src/utils/copilotkit.utils.ts +0 -20
  171. package/src/utils/frontend-tool.utils.ts +0 -266
  172. package/src/utils/human-in-the-loop.utils.ts +0 -359
  173. package/tsconfig.spec.json +0 -12
@@ -1,292 +0,0 @@
1
- import {
2
- Directive,
3
- Input,
4
- ElementRef,
5
- HostListener,
6
- OnDestroy,
7
- inject,
8
- ViewContainerRef
9
- } from '@angular/core';
10
- import {
11
- Overlay,
12
- OverlayRef,
13
- OverlayPositionBuilder,
14
- ConnectedPosition
15
- } from '@angular/cdk/overlay';
16
- import { ComponentPortal } from '@angular/cdk/portal';
17
- import { Component, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core';
18
-
19
- @Component({
20
- selector: 'copilot-tooltip-content',
21
- template: `
22
- <div class="copilot-tooltip-wrapper" [attr.data-position]="position">
23
- <div class="copilot-tooltip">
24
- {{ text }}
25
- </div>
26
- <div class="copilot-tooltip-arrow"></div>
27
- </div>
28
- `,
29
- styles: [`
30
- .copilot-tooltip-wrapper {
31
- position: relative;
32
- display: inline-block;
33
- animation: fadeIn 0.15s ease-in-out;
34
- }
35
-
36
- .copilot-tooltip {
37
- background-color: #1a1a1a;
38
- color: white;
39
- padding: 6px 10px;
40
- border-radius: 6px;
41
- font-size: 12px;
42
- font-weight: 500;
43
- white-space: nowrap;
44
- max-width: 200px;
45
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
46
- }
47
-
48
- .copilot-tooltip-arrow {
49
- position: absolute;
50
- width: 0;
51
- height: 0;
52
- border-style: solid;
53
- }
54
-
55
- /* Arrow for tooltip below element (arrow points up to tooltip) */
56
- .copilot-tooltip-wrapper[data-position="below"] .copilot-tooltip-arrow {
57
- top: -4px;
58
- left: 50%;
59
- transform: translateX(-50%);
60
- border-width: 0 4px 4px 4px;
61
- border-color: transparent transparent #1a1a1a transparent;
62
- }
63
-
64
- /* Arrow for tooltip above element (arrow points down to element) */
65
- .copilot-tooltip-wrapper[data-position="above"] .copilot-tooltip-arrow {
66
- bottom: -4px;
67
- left: 50%;
68
- transform: translateX(-50%);
69
- border-width: 4px 4px 0 4px;
70
- border-color: #1a1a1a transparent transparent transparent;
71
- }
72
-
73
- /* Arrow for tooltip to the left */
74
- .copilot-tooltip-wrapper[data-position="left"] .copilot-tooltip-arrow {
75
- right: -4px;
76
- top: 50%;
77
- transform: translateY(-50%);
78
- border-width: 4px 0 4px 4px;
79
- border-color: transparent transparent transparent #1a1a1a;
80
- }
81
-
82
- /* Arrow for tooltip to the right */
83
- .copilot-tooltip-wrapper[data-position="right"] .copilot-tooltip-arrow {
84
- left: -4px;
85
- top: 50%;
86
- transform: translateY(-50%);
87
- border-width: 4px 4px 4px 0;
88
- border-color: transparent #1a1a1a transparent transparent;
89
- }
90
-
91
- @keyframes fadeIn {
92
- from {
93
- opacity: 0;
94
- transform: translateY(2px);
95
- }
96
- to {
97
- opacity: 1;
98
- transform: translateY(0);
99
- }
100
- }
101
- `],
102
- changeDetection: ChangeDetectionStrategy.OnPush,
103
- standalone: true
104
- })
105
- export class TooltipContentComponent {
106
- text = '';
107
- private _position: 'above' | 'below' | 'left' | 'right' = 'below';
108
-
109
- get position(): 'above' | 'below' | 'left' | 'right' {
110
- return this._position;
111
- }
112
-
113
- set position(value: 'above' | 'below' | 'left' | 'right') {
114
- this._position = value;
115
- this.cdr.markForCheck();
116
- }
117
-
118
- constructor(private cdr: ChangeDetectorRef) {}
119
- }
120
-
121
- @Directive({
122
- selector: '[copilotTooltip]',
123
- standalone: true
124
- })
125
- export class CopilotTooltipDirective implements OnDestroy {
126
- @Input('copilotTooltip') tooltipText = '';
127
- @Input() tooltipPosition: 'above' | 'below' | 'left' | 'right' = 'below';
128
- @Input() tooltipDelay = 500; // milliseconds
129
-
130
- private overlay = inject(Overlay);
131
- private overlayPositionBuilder = inject(OverlayPositionBuilder);
132
- private elementRef = inject(ElementRef);
133
- private viewContainerRef = inject(ViewContainerRef);
134
-
135
- private overlayRef?: OverlayRef;
136
- private tooltipTimeout?: number;
137
- private originalTitle?: string;
138
-
139
- @HostListener('mouseenter')
140
- onMouseEnter(): void {
141
- if (!this.tooltipText) return;
142
-
143
- // Store and remove native title to prevent OS tooltip
144
- const element = this.elementRef.nativeElement;
145
- if (element.hasAttribute('title')) {
146
- this.originalTitle = element.getAttribute('title');
147
- element.removeAttribute('title');
148
- }
149
-
150
- // Clear any existing timeout
151
- if (this.tooltipTimeout) {
152
- clearTimeout(this.tooltipTimeout);
153
- }
154
-
155
- // Set timeout to show tooltip after delay
156
- this.tooltipTimeout = window.setTimeout(() => {
157
- this.show();
158
- }, this.tooltipDelay);
159
- }
160
-
161
- @HostListener('mouseleave')
162
- onMouseLeave(): void {
163
- // Clear timeout if mouse leaves before tooltip shows
164
- if (this.tooltipTimeout) {
165
- clearTimeout(this.tooltipTimeout);
166
- this.tooltipTimeout = undefined;
167
- }
168
-
169
- // Restore original title if it existed
170
- if (this.originalTitle !== undefined) {
171
- this.elementRef.nativeElement.setAttribute('title', this.originalTitle);
172
- this.originalTitle = undefined;
173
- }
174
-
175
- // Hide tooltip if it's showing
176
- this.hide();
177
- }
178
-
179
- private show(): void {
180
- if (this.overlayRef) {
181
- return;
182
- }
183
-
184
- // Create overlay
185
- const positionStrategy = this.overlayPositionBuilder
186
- .flexibleConnectedTo(this.elementRef)
187
- .withPositions(this.getPositions())
188
- .withPush(false);
189
-
190
- this.overlayRef = this.overlay.create({
191
- positionStrategy,
192
- scrollStrategy: this.overlay.scrollStrategies.close(),
193
- hasBackdrop: false
194
- });
195
-
196
- // Create component portal and attach
197
- const portal = new ComponentPortal(TooltipContentComponent, this.viewContainerRef);
198
- const componentRef = this.overlayRef.attach(portal);
199
- componentRef.instance.text = this.tooltipText;
200
-
201
- // Detect actual position after overlay is positioned
202
- setTimeout(() => {
203
- if (this.overlayRef && this.elementRef.nativeElement) {
204
- const tooltipRect = this.overlayRef.overlayElement.getBoundingClientRect();
205
- const elementRect = this.elementRef.nativeElement.getBoundingClientRect();
206
-
207
- let actualPosition: 'above' | 'below' | 'left' | 'right' = 'below';
208
-
209
- // Determine actual position based on relative positions
210
- if (tooltipRect.bottom <= elementRect.top) {
211
- actualPosition = 'above';
212
- } else if (tooltipRect.top >= elementRect.bottom) {
213
- actualPosition = 'below';
214
- } else if (tooltipRect.right <= elementRect.left) {
215
- actualPosition = 'left';
216
- } else if (tooltipRect.left >= elementRect.right) {
217
- actualPosition = 'right';
218
- }
219
-
220
- componentRef.instance.position = actualPosition;
221
- }
222
- }, 0);
223
- }
224
-
225
- private hide(): void {
226
- if (this.overlayRef) {
227
- this.overlayRef.dispose();
228
- this.overlayRef = undefined;
229
- }
230
- }
231
-
232
- private getPositions(): ConnectedPosition[] {
233
- const positions: Record<string, ConnectedPosition[]> = {
234
- above: [
235
- {
236
- originX: 'center',
237
- originY: 'top',
238
- overlayX: 'center',
239
- overlayY: 'bottom',
240
- offsetY: -12
241
- }
242
- ],
243
- below: [
244
- {
245
- originX: 'center',
246
- originY: 'bottom',
247
- overlayX: 'center',
248
- overlayY: 'top',
249
- offsetY: 12
250
- }
251
- ],
252
- left: [
253
- {
254
- originX: 'start',
255
- originY: 'center',
256
- overlayX: 'end',
257
- overlayY: 'center',
258
- offsetX: -12
259
- }
260
- ],
261
- right: [
262
- {
263
- originX: 'end',
264
- originY: 'center',
265
- overlayX: 'start',
266
- overlayY: 'center',
267
- offsetX: 12
268
- }
269
- ]
270
- };
271
-
272
- // Prefer below position, but use above as fallback
273
- const primary = positions[this.tooltipPosition] || positions.below;
274
- // For below position, add above as first fallback
275
- const fallbacks = this.tooltipPosition === 'below'
276
- ? [...(positions.above || []), ...(positions.left || []), ...(positions.right || [])]
277
- : Object.values(positions).filter(p => p !== primary).flat();
278
-
279
- return [...(primary || []), ...fallbacks];
280
- }
281
-
282
- ngOnDestroy(): void {
283
- if (this.tooltipTimeout) {
284
- clearTimeout(this.tooltipTimeout);
285
- }
286
- // Restore original title if it existed
287
- if (this.originalTitle !== undefined) {
288
- this.elementRef.nativeElement.setAttribute('title', this.originalTitle);
289
- }
290
- this.hide();
291
- }
292
- }
@@ -1,377 +0,0 @@
1
- import { Component, Input, TemplateRef, ViewChild, ViewContainerRef } from '@angular/core';
2
- import { TestBed } from '@angular/core/testing';
3
- import { describe, it, expect, beforeEach, vi } from 'vitest';
4
- import {
5
- renderSlot,
6
- isComponentType,
7
- isSlotValue,
8
- normalizeSlotValue,
9
- createSlotConfig,
10
- provideSlots,
11
- getSlotConfig,
12
- createSlotRenderer
13
- } from '../slot.utils';
14
- import { SLOT_CONFIG } from '../slot.types';
15
-
16
- // Test components
17
- @Component({
18
- selector: 'default-component',
19
- template: `<div class="default">{{ text }}</div>`,
20
- standalone: true
21
- })
22
- class DefaultComponent {
23
- @Input() text = 'Default';
24
- }
25
-
26
- @Component({
27
- selector: 'custom-component',
28
- template: `<div class="custom">{{ text }}</div>`,
29
- standalone: true
30
- })
31
- class CustomComponent {
32
- @Input() text = 'Custom';
33
- }
34
-
35
- describe('Slot Utilities', () => {
36
- describe('renderSlot', () => {
37
- let viewContainer: ViewContainerRef;
38
-
39
- beforeEach(() => {
40
- @Component({
41
- template: `<div #container></div>`,
42
- standalone: true
43
- })
44
- class TestComponent {
45
- @ViewChild('container', { read: ViewContainerRef }) container!: ViewContainerRef;
46
- }
47
-
48
- const fixture = TestBed.createComponent(TestComponent);
49
- fixture.detectChanges();
50
- viewContainer = fixture.componentInstance.container;
51
- });
52
-
53
- it('should render default component when no slot provided', () => {
54
- const ref = renderSlot(viewContainer, {
55
- defaultComponent: DefaultComponent
56
- });
57
-
58
- expect(ref).toBeTruthy();
59
- expect(ref?.location.nativeElement.querySelector('.default')).toBeTruthy();
60
- });
61
-
62
- it('should render custom component when provided', () => {
63
- const ref = renderSlot(viewContainer, {
64
- slot: CustomComponent,
65
- defaultComponent: DefaultComponent
66
- });
67
-
68
- expect(ref).toBeTruthy();
69
- expect(ref?.location.nativeElement.querySelector('.custom')).toBeTruthy();
70
- });
71
-
72
- it('should render default component when string slot is no longer supported', () => {
73
- // String slots are no longer supported - should render default component
74
- const ref = renderSlot(viewContainer, {
75
- slot: 'fancy-style' as any, // Type assertion needed since strings are no longer valid
76
- defaultComponent: DefaultComponent
77
- });
78
-
79
- expect(ref).toBeTruthy();
80
- // Should render default component, not apply class
81
- expect(ref?.location.nativeElement.querySelector('.default')).toBeTruthy();
82
- });
83
-
84
- it('should apply props to component using setInput', () => {
85
- const ref = renderSlot(viewContainer, {
86
- defaultComponent: DefaultComponent,
87
- props: { text: 'Hello World' }
88
- });
89
-
90
- expect(ref).toBeTruthy();
91
- if ('instance' in ref!) {
92
- // Props should be set via setInput, which updates the instance
93
- expect(ref.instance.text).toBe('Hello World');
94
- }
95
- });
96
-
97
- it('should render template when provided', () => {
98
- @Component({
99
- template: `
100
- <div #container></div>
101
- <ng-template #myTemplate let-props="props">
102
- <span class="template">{{ props?.message }}</span>
103
- </ng-template>
104
- `,
105
- standalone: true
106
- })
107
- class TestComponent {
108
- @ViewChild('container', { read: ViewContainerRef }) container!: ViewContainerRef;
109
- @ViewChild('myTemplate') template!: TemplateRef<any>;
110
- }
111
-
112
- const fixture = TestBed.createComponent(TestComponent);
113
- fixture.detectChanges();
114
-
115
- renderSlot(fixture.componentInstance.container, {
116
- slot: fixture.componentInstance.template,
117
- defaultComponent: DefaultComponent,
118
- props: { message: 'Template Content' }
119
- });
120
-
121
- fixture.detectChanges();
122
-
123
- const span = fixture.nativeElement.querySelector('.template');
124
- expect(span).toBeTruthy();
125
- expect(span.textContent).toBe('Template Content');
126
- });
127
-
128
- it('should render default component when object slot is no longer supported', () => {
129
- // Object slots are no longer supported - should render default component
130
- const ref = renderSlot(viewContainer, {
131
- slot: { text: 'Overridden' } as any, // Type assertion needed since objects are no longer valid
132
- defaultComponent: DefaultComponent
133
- });
134
-
135
- expect(ref).toBeTruthy();
136
- if ('instance' in ref!) {
137
- // Should use default text, not override
138
- expect(ref.instance.text).toBe('Default');
139
- }
140
- });
141
- });
142
-
143
- describe('isComponentType', () => {
144
- it('should identify Angular components', () => {
145
- // After removing ɵ checks, any function with prototype is considered a component
146
- expect(isComponentType(DefaultComponent)).toBe(true);
147
- expect(isComponentType(CustomComponent)).toBe(true);
148
- });
149
-
150
- it('should reject non-components', () => {
151
- expect(isComponentType('string')).toBe(false);
152
- expect(isComponentType(123)).toBe(false);
153
- expect(isComponentType({})).toBe(false);
154
- expect(isComponentType(null)).toBe(false);
155
- expect(isComponentType(undefined)).toBe(false);
156
- expect(isComponentType(() => {})).toBe(false);
157
- });
158
- });
159
-
160
- describe('isSlotValue', () => {
161
- it('should accept valid slot values', () => {
162
- // Only components and templates are valid now
163
- expect(isSlotValue(DefaultComponent)).toBe(true);
164
- expect(isSlotValue(CustomComponent)).toBe(true);
165
- // Strings and objects are no longer valid slot values
166
- expect(isSlotValue('css-class')).toBe(false);
167
- expect(isSlotValue({ prop: 'value' })).toBe(false);
168
- });
169
-
170
- it('should reject invalid slot values', () => {
171
- expect(isSlotValue(null)).toBe(false);
172
- expect(isSlotValue(undefined)).toBe(false);
173
- });
174
- });
175
-
176
- describe('normalizeSlotValue', () => {
177
- it('should return default component for string (no longer supported)', () => {
178
- const result = normalizeSlotValue('custom-class' as any, DefaultComponent);
179
- expect(result).toEqual({
180
- component: DefaultComponent
181
- });
182
- });
183
-
184
- it('should normalize component type', () => {
185
- const result = normalizeSlotValue(CustomComponent, DefaultComponent);
186
- expect(result).toEqual({
187
- component: CustomComponent
188
- });
189
- });
190
-
191
- it('should return default component for object (no longer supported)', () => {
192
- const props = { text: 'Test' };
193
- const result = normalizeSlotValue(props as any, DefaultComponent);
194
- expect(result).toEqual({
195
- component: DefaultComponent
196
- });
197
- });
198
-
199
- it('should handle undefined', () => {
200
- const result = normalizeSlotValue(undefined, DefaultComponent);
201
- expect(result).toEqual({
202
- component: DefaultComponent
203
- });
204
- });
205
- });
206
-
207
- describe('createSlotConfig', () => {
208
- it('should create configuration map', () => {
209
- const config = createSlotConfig(
210
- {
211
- button: CustomComponent,
212
- toolbar: 'toolbar-class' as any // String no longer supported but test the behavior
213
- },
214
- {
215
- button: DefaultComponent,
216
- toolbar: DefaultComponent
217
- }
218
- );
219
-
220
- expect(config.get('button')).toEqual({
221
- component: CustomComponent
222
- });
223
- // String slots no longer supported - should use default
224
- expect(config.get('toolbar')).toEqual({
225
- component: DefaultComponent
226
- });
227
- });
228
-
229
- it('should use defaults when no overrides', () => {
230
- const config = createSlotConfig(
231
- {},
232
- {
233
- button: DefaultComponent,
234
- toolbar: DefaultComponent
235
- }
236
- );
237
-
238
- expect(config.get('button')).toEqual({
239
- component: DefaultComponent
240
- });
241
- expect(config.get('toolbar')).toEqual({
242
- component: DefaultComponent
243
- });
244
- });
245
- });
246
-
247
- describe('provideSlots', () => {
248
- it('should create provider configuration with Map', () => {
249
- const provider = provideSlots({
250
- button: CustomComponent
251
- });
252
-
253
- expect(provider.provide).toBe(SLOT_CONFIG);
254
- expect(provider.useValue).toBeInstanceOf(Map);
255
- expect(provider.useValue.get('button')).toEqual({
256
- component: CustomComponent
257
- });
258
- });
259
- });
260
-
261
- describe('getSlotConfig', () => {
262
- it('should retrieve slot configuration from DI', () => {
263
- const slots = new Map([
264
- ['button', { component: CustomComponent }]
265
- ]);
266
-
267
- TestBed.configureTestingModule({
268
- providers: [
269
- { provide: SLOT_CONFIG, useValue: slots }
270
- ]
271
- });
272
-
273
- @Component({
274
- template: '',
275
- standalone: true
276
- })
277
- class TestComponent {
278
- slots = getSlotConfig();
279
- }
280
-
281
- const fixture = TestBed.createComponent(TestComponent);
282
- expect(fixture.componentInstance.slots).toBe(slots);
283
- });
284
-
285
- it('should return null when no config provided', () => {
286
- @Component({
287
- template: '',
288
- standalone: true
289
- })
290
- class TestComponent {
291
- slots = getSlotConfig();
292
- }
293
-
294
- const fixture = TestBed.createComponent(TestComponent);
295
- expect(fixture.componentInstance.slots).toBe(null);
296
- });
297
- });
298
-
299
- describe('createSlotRenderer', () => {
300
- let viewContainer: ViewContainerRef;
301
-
302
- beforeEach(() => {
303
- @Component({
304
- template: `<div #container></div>`,
305
- standalone: true
306
- })
307
- class TestComponent {
308
- @ViewChild('container', { read: ViewContainerRef }) container!: ViewContainerRef;
309
- }
310
-
311
- const fixture = TestBed.createComponent(TestComponent);
312
- fixture.detectChanges();
313
- viewContainer = fixture.componentInstance.container;
314
- });
315
-
316
- it('should create a renderer function', () => {
317
- const renderer = createSlotRenderer(DefaultComponent);
318
- expect(typeof renderer).toBe('function');
319
-
320
- const ref = renderer(viewContainer, CustomComponent);
321
- expect(ref).toBeTruthy();
322
- expect(ref?.location.nativeElement.querySelector('.custom')).toBeTruthy();
323
- });
324
-
325
- it('should use DI config when slot name provided', () => {
326
- const slots = new Map([
327
- ['button', { component: CustomComponent }]
328
- ]);
329
-
330
- TestBed.resetTestingModule();
331
- TestBed.configureTestingModule({
332
- providers: [
333
- { provide: SLOT_CONFIG, useValue: slots }
334
- ]
335
- });
336
-
337
- @Component({
338
- template: `<div #container></div>`,
339
- standalone: true
340
- })
341
- class TestComponent {
342
- @ViewChild('container', { read: ViewContainerRef }) container!: ViewContainerRef;
343
- renderButton = createSlotRenderer(DefaultComponent, 'button');
344
- }
345
-
346
- const fixture = TestBed.createComponent(TestComponent);
347
- fixture.detectChanges();
348
-
349
- const ref = fixture.componentInstance.renderButton(
350
- fixture.componentInstance.container
351
- );
352
-
353
- expect(ref).toBeTruthy();
354
- expect(ref?.location.nativeElement.querySelector('.custom')).toBeTruthy();
355
- });
356
-
357
- it('should apply props from renderer', () => {
358
- const renderer = createSlotRenderer(DefaultComponent);
359
- const ref = renderer(viewContainer, undefined, { text: 'Rendered' });
360
-
361
- expect(ref).toBeTruthy();
362
- if ('instance' in ref!) {
363
- // Props should be set via setInput
364
- expect(ref.instance.text).toBe('Rendered');
365
- }
366
- });
367
-
368
- it('should apply outputs when provided', () => {
369
- const renderer = createSlotRenderer(DefaultComponent);
370
- const clickHandler = vi.fn();
371
- const ref = renderer(viewContainer, undefined, undefined, { click: clickHandler });
372
-
373
- expect(ref).toBeTruthy();
374
- // Outputs would be wired if the component has the corresponding EventEmitter
375
- });
376
- });
377
- });