@envive-ai/react-widgets-v3 0.3.2

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 (289) hide show
  1. package/dist/CXIntegration/hooks/useUnifiedCXButton.cjs +23 -0
  2. package/dist/CXIntegration/hooks/useUnifiedCXButton.js +22 -0
  3. package/dist/CXIntegration/implementations/useDefaultUnifiedCXButton.cjs +11 -0
  4. package/dist/CXIntegration/implementations/useDefaultUnifiedCXButton.js +10 -0
  5. package/dist/CXIntegration/implementations/useGladlyUnifiedCXButton.cjs +20 -0
  6. package/dist/CXIntegration/implementations/useGladlyUnifiedCXButton.js +19 -0
  7. package/dist/CXIntegration/implementations/useGorgiasUnifiedCXButton.cjs +53 -0
  8. package/dist/CXIntegration/implementations/useGorgiasUnifiedCXButton.js +52 -0
  9. package/dist/CXIntegration/implementations/useGrooveUnifiedCXButton.cjs +45 -0
  10. package/dist/CXIntegration/implementations/useGrooveUnifiedCXButton.js +44 -0
  11. package/dist/CXIntegration/implementations/useKustomerUnifiedCXButton.cjs +70 -0
  12. package/dist/CXIntegration/implementations/useKustomerUnifiedCXButton.js +69 -0
  13. package/dist/CXIntegration/implementations/useReDoUnifiedCXButton.cjs +59 -0
  14. package/dist/CXIntegration/implementations/useReDoUnifiedCXButton.js +58 -0
  15. package/dist/CXIntegration/implementations/useRichpanelUnifiedCXButton.cjs +65 -0
  16. package/dist/CXIntegration/implementations/useRichpanelUnifiedCXButton.js +64 -0
  17. package/dist/CXIntegration/implementations/useShopifyChatUnifiedCXButton.cjs +53 -0
  18. package/dist/CXIntegration/implementations/useShopifyChatUnifiedCXButton.js +52 -0
  19. package/dist/CXIntegration/implementations/useTidioUnifiedCXButton.cjs +18 -0
  20. package/dist/CXIntegration/implementations/useTidioUnifiedCXButton.js +17 -0
  21. package/dist/CXIntegration/implementations/useZendeskUnifiedCXButton.cjs +54 -0
  22. package/dist/CXIntegration/implementations/useZendeskUnifiedCXButton.js +53 -0
  23. package/dist/CXIntegration/implementations/useZowieUnifiedCXButton.cjs +18 -0
  24. package/dist/CXIntegration/implementations/useZowieUnifiedCXButton.js +17 -0
  25. package/dist/CXIntegration/types.cjs +19 -0
  26. package/dist/CXIntegration/types.js +18 -0
  27. package/dist/CXIntegration/utils/functions.cjs +30 -0
  28. package/dist/CXIntegration/utils/functions.js +30 -0
  29. package/dist/_virtual/rolldown_runtime.cjs +29 -0
  30. package/dist/hocs/withBaseWidget/index.cjs +3 -0
  31. package/dist/hocs/withBaseWidget/index.d.cts +3 -0
  32. package/dist/hocs/withBaseWidget/index.d.ts +3 -0
  33. package/dist/hocs/withBaseWidget/index.js +3 -0
  34. package/dist/hocs/withBaseWidget/types.d.cts +37 -0
  35. package/dist/hocs/withBaseWidget/types.d.ts +39 -0
  36. package/dist/hocs/withBaseWidget/withBaseWidget.cjs +101 -0
  37. package/dist/hocs/withBaseWidget/withBaseWidget.d.cts +10 -0
  38. package/dist/hocs/withBaseWidget/withBaseWidget.d.ts +10 -0
  39. package/dist/hocs/withBaseWidget/withBaseWidget.js +100 -0
  40. package/dist/hooks/dist/application/models/api/orgConfigResults.d.ts +1 -0
  41. package/dist/hooks/dist/application/models/frontendConfig.d.ts +1 -0
  42. package/dist/hooks/dist/contexts/amplitudeContext/amplitudeContext.d.ts +2 -0
  43. package/dist/hooks/dist/contexts/amplitudeContext/index.d.ts +2 -0
  44. package/dist/hooks/dist/contexts/featureFlagServiceContext/featureFlagServiceContext.d.ts +2 -0
  45. package/dist/hooks/dist/contexts/hardcopyContext/hardcopyContext.d.cts +8 -0
  46. package/dist/hooks/dist/contexts/hardcopyContext/hardcopyContext.d.ts +11 -0
  47. package/dist/hooks/dist/contexts/hardcopyContext/index.d.ts +1 -0
  48. package/dist/hooks/dist/contexts/types.d.cts +44 -0
  49. package/dist/hooks/dist/contexts/types.d.ts +47 -0
  50. package/dist/hooks/dist/contexts/typesV3.d.cts +201 -0
  51. package/dist/hooks/dist/contexts/typesV3.d.ts +201 -0
  52. package/dist/hooks/dist/services/amplitudeService/amplitudeService.d.cts +36 -0
  53. package/dist/hooks/dist/services/amplitudeService/amplitudeService.d.ts +37 -0
  54. package/dist/hooks/dist/types/customerService.d.cts +18 -0
  55. package/dist/hooks/dist/types/customerService.d.ts +18 -0
  56. package/dist/stories/SalesAgentTest/index.cjs +0 -0
  57. package/dist/stories/SalesAgentTest/index.d.cts +1 -0
  58. package/dist/stories/SalesAgentTest/index.d.ts +1 -0
  59. package/dist/stories/SalesAgentTest/index.js +0 -0
  60. package/dist/widgets/ChatPreviewComparisonWidget/ChatPreviewComparisonWidget.cjs +98 -0
  61. package/dist/widgets/ChatPreviewComparisonWidget/ChatPreviewComparisonWidget.d.cts +19 -0
  62. package/dist/widgets/ChatPreviewComparisonWidget/ChatPreviewComparisonWidget.d.ts +19 -0
  63. package/dist/widgets/ChatPreviewComparisonWidget/ChatPreviewComparisonWidget.js +96 -0
  64. package/dist/widgets/ChatPreviewComparisonWidget/index.cjs +4 -0
  65. package/dist/widgets/ChatPreviewComparisonWidget/index.d.cts +2 -0
  66. package/dist/widgets/ChatPreviewComparisonWidget/index.d.ts +2 -0
  67. package/dist/widgets/ChatPreviewComparisonWidget/index.js +3 -0
  68. package/dist/widgets/ChatPreviewLoadingWidget/ChatPreviewLoadingWidget.cjs +44 -0
  69. package/dist/widgets/ChatPreviewLoadingWidget/ChatPreviewLoadingWidget.d.cts +16 -0
  70. package/dist/widgets/ChatPreviewLoadingWidget/ChatPreviewLoadingWidget.d.ts +16 -0
  71. package/dist/widgets/ChatPreviewLoadingWidget/ChatPreviewLoadingWidget.js +42 -0
  72. package/dist/widgets/ChatPreviewLoadingWidget/index.cjs +4 -0
  73. package/dist/widgets/ChatPreviewLoadingWidget/index.d.cts +2 -0
  74. package/dist/widgets/ChatPreviewLoadingWidget/index.d.ts +2 -0
  75. package/dist/widgets/ChatPreviewLoadingWidget/index.js +3 -0
  76. package/dist/widgets/ChatPreviewWidget/ChatPreviewWidget.cjs +86 -0
  77. package/dist/widgets/ChatPreviewWidget/ChatPreviewWidget.d.cts +19 -0
  78. package/dist/widgets/ChatPreviewWidget/ChatPreviewWidget.d.ts +19 -0
  79. package/dist/widgets/ChatPreviewWidget/ChatPreviewWidget.js +84 -0
  80. package/dist/widgets/ChatPreviewWidget/index.cjs +4 -0
  81. package/dist/widgets/ChatPreviewWidget/index.d.cts +2 -0
  82. package/dist/widgets/ChatPreviewWidget/index.d.ts +2 -0
  83. package/dist/widgets/ChatPreviewWidget/index.js +3 -0
  84. package/dist/widgets/FloatingChatWidget/FloatingChatOverlay.cjs +66 -0
  85. package/dist/widgets/FloatingChatWidget/FloatingChatOverlay.js +64 -0
  86. package/dist/widgets/FloatingChatWidget/FloatingChatWidget.cjs +70 -0
  87. package/dist/widgets/FloatingChatWidget/FloatingChatWidget.d.cts +10 -0
  88. package/dist/widgets/FloatingChatWidget/FloatingChatWidget.d.ts +10 -0
  89. package/dist/widgets/FloatingChatWidget/FloatingChatWidget.js +69 -0
  90. package/dist/widgets/FloatingChatWidget/constants.cjs +6 -0
  91. package/dist/widgets/FloatingChatWidget/constants.d.cts +4 -0
  92. package/dist/widgets/FloatingChatWidget/constants.d.ts +4 -0
  93. package/dist/widgets/FloatingChatWidget/constants.js +5 -0
  94. package/dist/widgets/FloatingChatWidget/index.cjs +5 -0
  95. package/dist/widgets/FloatingChatWidget/index.d.cts +3 -0
  96. package/dist/widgets/FloatingChatWidget/index.d.ts +3 -0
  97. package/dist/widgets/FloatingChatWidget/index.js +4 -0
  98. package/dist/widgets/PromptButtonCarouselWithImageWidget/PromptButtonCarouselWithImageWidget.cjs +60 -0
  99. package/dist/widgets/PromptButtonCarouselWithImageWidget/PromptButtonCarouselWithImageWidget.d.cts +19 -0
  100. package/dist/widgets/PromptButtonCarouselWithImageWidget/PromptButtonCarouselWithImageWidget.d.ts +19 -0
  101. package/dist/widgets/PromptButtonCarouselWithImageWidget/PromptButtonCarouselWithImageWidget.js +58 -0
  102. package/dist/widgets/PromptButtonCarouselWithImageWidget/index.cjs +4 -0
  103. package/dist/widgets/PromptButtonCarouselWithImageWidget/index.d.cts +2 -0
  104. package/dist/widgets/PromptButtonCarouselWithImageWidget/index.d.ts +2 -0
  105. package/dist/widgets/PromptButtonCarouselWithImageWidget/index.js +3 -0
  106. package/dist/widgets/PromptCarouselWidget/PromptCarouselWidget.cjs +64 -0
  107. package/dist/widgets/PromptCarouselWidget/PromptCarouselWidget.d.cts +14 -0
  108. package/dist/widgets/PromptCarouselWidget/PromptCarouselWidget.d.ts +14 -0
  109. package/dist/widgets/PromptCarouselWidget/PromptCarouselWidget.js +63 -0
  110. package/dist/widgets/PromptCarouselWidget/index.cjs +3 -0
  111. package/dist/widgets/PromptCarouselWidget/index.d.cts +2 -0
  112. package/dist/widgets/PromptCarouselWidget/index.d.ts +2 -0
  113. package/dist/widgets/PromptCarouselWidget/index.js +3 -0
  114. package/dist/widgets/SocialProofFlowWidget/SocialProofFlowWidget.cjs +35 -0
  115. package/dist/widgets/SocialProofFlowWidget/SocialProofFlowWidget.d.cts +14 -0
  116. package/dist/widgets/SocialProofFlowWidget/SocialProofFlowWidget.d.ts +14 -0
  117. package/dist/widgets/SocialProofFlowWidget/SocialProofFlowWidget.js +34 -0
  118. package/dist/widgets/SocialProofFlowWidget/index.cjs +3 -0
  119. package/dist/widgets/SocialProofFlowWidget/index.d.cts +2 -0
  120. package/dist/widgets/SocialProofFlowWidget/index.d.ts +2 -0
  121. package/dist/widgets/SocialProofFlowWidget/index.js +3 -0
  122. package/dist/widgets/SocialProofWidget/SocialProofWidget.cjs +118 -0
  123. package/dist/widgets/SocialProofWidget/SocialProofWidget.d.cts +19 -0
  124. package/dist/widgets/SocialProofWidget/SocialProofWidget.d.ts +19 -0
  125. package/dist/widgets/SocialProofWidget/SocialProofWidget.js +116 -0
  126. package/dist/widgets/SocialProofWidget/index.cjs +4 -0
  127. package/dist/widgets/SocialProofWidget/index.d.cts +2 -0
  128. package/dist/widgets/SocialProofWidget/index.d.ts +2 -0
  129. package/dist/widgets/SocialProofWidget/index.js +3 -0
  130. package/dist/widgets/TitledPromptCarouselWidget/TitledPromptCarouselWidget.cjs +67 -0
  131. package/dist/widgets/TitledPromptCarouselWidget/TitledPromptCarouselWidget.d.cts +14 -0
  132. package/dist/widgets/TitledPromptCarouselWidget/TitledPromptCarouselWidget.d.ts +14 -0
  133. package/dist/widgets/TitledPromptCarouselWidget/TitledPromptCarouselWidget.js +66 -0
  134. package/dist/widgets/TitledPromptCarouselWidget/index.cjs +3 -0
  135. package/dist/widgets/TitledPromptCarouselWidget/index.d.cts +2 -0
  136. package/dist/widgets/TitledPromptCarouselWidget/index.d.ts +2 -0
  137. package/dist/widgets/TitledPromptCarouselWidget/index.js +3 -0
  138. package/dist/widgets/TypingAnimationFlowWidget/TypingAnimationFlowWidget.cjs +35 -0
  139. package/dist/widgets/TypingAnimationFlowWidget/TypingAnimationFlowWidget.d.cts +14 -0
  140. package/dist/widgets/TypingAnimationFlowWidget/TypingAnimationFlowWidget.d.ts +14 -0
  141. package/dist/widgets/TypingAnimationFlowWidget/TypingAnimationFlowWidget.js +34 -0
  142. package/dist/widgets/TypingAnimationFlowWidget/index.cjs +3 -0
  143. package/dist/widgets/TypingAnimationFlowWidget/index.d.cts +2 -0
  144. package/dist/widgets/TypingAnimationFlowWidget/index.d.ts +2 -0
  145. package/dist/widgets/TypingAnimationFlowWidget/index.js +3 -0
  146. package/dist/widgets/TypingAnimationWidget/TypingAnimationWidget.cjs +90 -0
  147. package/dist/widgets/TypingAnimationWidget/TypingAnimationWidget.d.cts +19 -0
  148. package/dist/widgets/TypingAnimationWidget/TypingAnimationWidget.d.ts +19 -0
  149. package/dist/widgets/TypingAnimationWidget/TypingAnimationWidget.js +88 -0
  150. package/dist/widgets/TypingAnimationWidget/index.cjs +4 -0
  151. package/dist/widgets/TypingAnimationWidget/index.d.cts +2 -0
  152. package/dist/widgets/TypingAnimationWidget/index.d.ts +2 -0
  153. package/dist/widgets/TypingAnimationWidget/index.js +3 -0
  154. package/dist/widgets/dist/SearchResults/SearchResults.d.cts +15 -0
  155. package/dist/widgets/dist/SearchResults/SearchResults.d.ts +15 -0
  156. package/dist/widgets/dist/SearchResults/SearchResultsWidget.d.cts +9 -0
  157. package/dist/widgets/dist/SearchResults/SearchResultsWidget.d.ts +9 -0
  158. package/dist/widgets/dist/SearchResults/index.d.ts +2 -0
  159. package/dist/widgets/dist/SearchResults/types.d.cts +20 -0
  160. package/dist/widgets/dist/SearchResults/types.d.ts +20 -0
  161. package/dist/widgets/dist/SearchZeroState/SearchZeroState.d.cts +10 -0
  162. package/dist/widgets/dist/SearchZeroState/SearchZeroState.d.ts +10 -0
  163. package/dist/widgets/dist/SearchZeroState/SearchZeroStateWidget.d.cts +18 -0
  164. package/dist/widgets/dist/SearchZeroState/SearchZeroStateWidget.d.ts +18 -0
  165. package/dist/widgets/dist/SearchZeroState/index.d.ts +4 -0
  166. package/dist/widgets/dist/SearchZeroState/types.d.cts +13 -0
  167. package/dist/widgets/dist/SearchZeroState/types.d.ts +13 -0
  168. package/dist/widgets/dist/SuggestionBar/SuggestionBar.d.cts +36 -0
  169. package/dist/widgets/dist/SuggestionBar/SuggestionBar.d.ts +36 -0
  170. package/dist/widgets/dist/SuggestionBar/index.d.ts +2 -0
  171. package/dist/widgets/dist/SuggestionBar/types.d.cts +9 -0
  172. package/dist/widgets/dist/SuggestionBar/types.d.ts +9 -0
  173. package/dist/widgets/dist/SuggestionButtonContainer/SuggestionButtonContainer.d.cts +9 -0
  174. package/dist/widgets/dist/SuggestionButtonContainer/SuggestionButtonContainer.d.ts +9 -0
  175. package/dist/widgets/dist/SuggestionButtonContainer/types.d.cts +20 -0
  176. package/dist/widgets/dist/SuggestionButtonContainer/types.d.ts +20 -0
  177. package/dist/widgets/dist/config/BaseWidgetConfig.d.cts +13 -0
  178. package/dist/widgets/dist/config/BaseWidgetConfig.d.ts +13 -0
  179. package/dist/widgets/dist/config/WidgetType.d.cts +23 -0
  180. package/dist/widgets/dist/config/WidgetType.d.ts +23 -0
  181. package/dist/widgets/dist/packages/hooks/dist/application/models/api/response.d.cts +14 -0
  182. package/dist/widgets/dist/packages/hooks/dist/application/models/api/response.d.ts +14 -0
  183. package/dist/widgets/dist/packages/hooks/dist/application/models/api/search.d.cts +15 -0
  184. package/dist/widgets/dist/packages/hooks/dist/application/models/api/search.d.ts +15 -0
  185. package/dist/widgets/dist/packages/hooks/dist/application/models/utilityTypes/camelCase.d.cts +73 -0
  186. package/dist/widgets/dist/packages/hooks/dist/application/models/utilityTypes/camelCase.d.ts +73 -0
  187. package/dist/widgets/dist/packages/hooks/dist/application/models/utilityTypes/camelCasedPropertiesDeep.d.cts +61 -0
  188. package/dist/widgets/dist/packages/hooks/dist/application/models/utilityTypes/camelCasedPropertiesDeep.d.ts +61 -0
  189. package/dist/widgets/dist/packages/hooks/dist/application/models/utilityTypes/internal.d.cts +25 -0
  190. package/dist/widgets/dist/packages/hooks/dist/application/models/utilityTypes/internal.d.ts +25 -0
  191. package/dist/widgets/dist/packages/hooks/dist/application/models/utilityTypes/splitWords.d.cts +35 -0
  192. package/dist/widgets/dist/packages/hooks/dist/application/models/utilityTypes/splitWords.d.ts +35 -0
  193. package/dist/widgets/dist/packages/hooks/dist/application/models/utilityTypes/trim.d.cts +32 -0
  194. package/dist/widgets/dist/packages/hooks/dist/application/models/utilityTypes/trim.d.ts +32 -0
  195. package/dist/widgets/dist/packages/hooks/dist/application/models/utilityTypes/unknownArray.d.cts +32 -0
  196. package/dist/widgets/dist/packages/hooks/dist/application/models/utilityTypes/unknownArray.d.ts +32 -0
  197. package/dist/widgets/dist/packages/hooks/dist/atoms/search/searchAPI.d.cts +14 -0
  198. package/dist/widgets/dist/packages/hooks/dist/atoms/search/searchAPI.d.ts +15 -0
  199. package/dist/widgets/dist/packages/hooks/dist/contexts/types.d.cts +61 -0
  200. package/dist/widgets/dist/packages/hooks/dist/contexts/types.d.ts +61 -0
  201. package/dist/widgets/dist/packages/hooks/dist/hooks/Search/useSearch.d.cts +60 -0
  202. package/dist/widgets/dist/packages/hooks/dist/hooks/Search/useSearch.d.ts +60 -0
  203. package/dist/widgets/dist/packages/hooks/dist/hooks/utils.d.cts +11 -0
  204. package/dist/widgets/dist/packages/hooks/dist/hooks/utils.d.ts +11 -0
  205. package/dist/widgets/dist/packages/hooks/dist/types/search-filter-types.d.cts +28 -0
  206. package/dist/widgets/dist/packages/hooks/dist/types/search-filter-types.d.ts +28 -0
  207. package/dist/widgets/dist/packages/hooks/dist/types/test-types.d.cts +10 -0
  208. package/dist/widgets/dist/packages/hooks/dist/types/test-types.d.ts +10 -0
  209. package/dist/widgets/hooks/useGetWidgetStatus.cjs +27 -0
  210. package/dist/widgets/hooks/useGetWidgetStatus.js +26 -0
  211. package/dist/widgets/utils/functions.cjs +55 -0
  212. package/dist/widgets/utils/functions.js +48 -0
  213. package/dist/widgets-v2/SearchResults/index.cjs +5 -0
  214. package/dist/widgets-v2/SearchResults/index.d.cts +3 -0
  215. package/dist/widgets-v2/SearchResults/index.d.ts +4 -0
  216. package/dist/widgets-v2/SearchResults/index.js +3 -0
  217. package/dist/widgets-v2/SearchZeroState/index.cjs +8 -0
  218. package/dist/widgets-v2/SearchZeroState/index.d.cts +5 -0
  219. package/dist/widgets-v2/SearchZeroState/index.d.ts +6 -0
  220. package/dist/widgets-v2/SearchZeroState/index.js +3 -0
  221. package/dist/widgets-v2/SuggestionBar/index.cjs +5 -0
  222. package/dist/widgets-v2/SuggestionBar/index.d.cts +3 -0
  223. package/dist/widgets-v2/SuggestionBar/index.d.ts +4 -0
  224. package/dist/widgets-v2/SuggestionBar/index.js +3 -0
  225. package/dist/widgets-v2/SuggestionButtonContainer/index.cjs +5 -0
  226. package/dist/widgets-v2/SuggestionButtonContainer/index.d.cts +3 -0
  227. package/dist/widgets-v2/SuggestionButtonContainer/index.d.ts +3 -0
  228. package/dist/widgets-v2/SuggestionButtonContainer/index.js +3 -0
  229. package/package.json +158 -0
  230. package/src/CXIntegration/hooks/useUnifiedCXButton.ts +38 -0
  231. package/src/CXIntegration/implementations/useDefaultUnifiedCXButton.ts +8 -0
  232. package/src/CXIntegration/implementations/useGladlyUnifiedCXButton.ts +28 -0
  233. package/src/CXIntegration/implementations/useGorgiasUnifiedCXButton.ts +67 -0
  234. package/src/CXIntegration/implementations/useGrooveUnifiedCXButton.ts +83 -0
  235. package/src/CXIntegration/implementations/useKustomerUnifiedCXButton.ts +120 -0
  236. package/src/CXIntegration/implementations/useReDoUnifiedCXButton.ts +80 -0
  237. package/src/CXIntegration/implementations/useRichpanelUnifiedCXButton.ts +83 -0
  238. package/src/CXIntegration/implementations/useShopifyChatUnifiedCXButton.ts +78 -0
  239. package/src/CXIntegration/implementations/useTidioUnifiedCXButton.ts +34 -0
  240. package/src/CXIntegration/implementations/useZendeskUnifiedCXButton.ts +69 -0
  241. package/src/CXIntegration/implementations/useZowieUnifiedCXButton.ts +34 -0
  242. package/src/CXIntegration/types.ts +24 -0
  243. package/src/CXIntegration/utils/functions.ts +50 -0
  244. package/src/hocs/withBaseWidget/__tests__/withBaseWidget.test.tsx +689 -0
  245. package/src/hocs/withBaseWidget/index.ts +2 -0
  246. package/src/hocs/withBaseWidget/types.ts +39 -0
  247. package/src/hocs/withBaseWidget/withBaseWidget.tsx +126 -0
  248. package/src/stories/FloatingChatWidget.stories.tsx +56 -0
  249. package/src/stories/PromptButtonCarouselWithImageWidget.stories.tsx +54 -0
  250. package/src/stories/PromptCarouselWidget.stories.tsx +54 -0
  251. package/src/stories/SalesAgentTest/SalesAgentTest.stories.tsx +18 -0
  252. package/src/stories/SalesAgentTest/SalesAgentTest.tsx +111 -0
  253. package/src/stories/SalesAgentTest/index.ts +0 -0
  254. package/src/stories/SearchResults.stories.tsx +29 -0
  255. package/src/stories/SearchZeroState.stories.tsx +52 -0
  256. package/src/stories/SocialProofFlowWidget.stories.tsx +77 -0
  257. package/src/stories/SuggestionBar.stories.tsx +45 -0
  258. package/src/stories/TitledPromptCarouselWidget.stories.tsx +71 -0
  259. package/src/stories/TypingAnimationFlowWidget.stories.tsx +67 -0
  260. package/src/widgets/ChatPreviewComparisonWidget/ChatPreviewComparisonWidget.tsx +152 -0
  261. package/src/widgets/ChatPreviewComparisonWidget/index.ts +7 -0
  262. package/src/widgets/ChatPreviewLoadingWidget/ChatPreviewLoadingWidget.tsx +82 -0
  263. package/src/widgets/ChatPreviewLoadingWidget/index.ts +7 -0
  264. package/src/widgets/ChatPreviewWidget/ChatPreviewWidget.tsx +119 -0
  265. package/src/widgets/ChatPreviewWidget/index.ts +4 -0
  266. package/src/widgets/FloatingChatWidget/FloatingChatOverlay.tsx +115 -0
  267. package/src/widgets/FloatingChatWidget/FloatingChatWidget.tsx +110 -0
  268. package/src/widgets/FloatingChatWidget/constants.ts +1 -0
  269. package/src/widgets/FloatingChatWidget/index.ts +5 -0
  270. package/src/widgets/PromptButtonCarouselWithImageWidget/PromptButtonCarouselWithImageWidget.tsx +87 -0
  271. package/src/widgets/PromptButtonCarouselWithImageWidget/index.ts +6 -0
  272. package/src/widgets/PromptCarouselWidget/PromptCarouselWidget.tsx +83 -0
  273. package/src/widgets/PromptCarouselWidget/index.ts +2 -0
  274. package/src/widgets/SocialProofFlowWidget/SocialProofFlowWidget.tsx +61 -0
  275. package/src/widgets/SocialProofFlowWidget/index.ts +4 -0
  276. package/src/widgets/SocialProofWidget/SocialProofWidget.tsx +160 -0
  277. package/src/widgets/SocialProofWidget/index.ts +2 -0
  278. package/src/widgets/TitledPromptCarouselWidget/TitledPromptCarouselWidget.tsx +93 -0
  279. package/src/widgets/TitledPromptCarouselWidget/index.ts +2 -0
  280. package/src/widgets/TypingAnimationFlowWidget/TypingAnimationFlowWidget.tsx +61 -0
  281. package/src/widgets/TypingAnimationFlowWidget/index.ts +4 -0
  282. package/src/widgets/TypingAnimationWidget/TypingAnimationWidget.tsx +115 -0
  283. package/src/widgets/TypingAnimationWidget/index.ts +2 -0
  284. package/src/widgets/hooks/useGetWidgetStatus.tsx +29 -0
  285. package/src/widgets/utils/functions.ts +104 -0
  286. package/src/widgets-v2/SearchResults/index.ts +3 -0
  287. package/src/widgets-v2/SearchZeroState/index.ts +15 -0
  288. package/src/widgets-v2/SuggestionBar/index.ts +6 -0
  289. package/src/widgets-v2/SuggestionButtonContainer/index.ts +6 -0
@@ -0,0 +1,689 @@
1
+ import { render, screen, waitFor } from '@testing-library/react';
2
+ import { RefObject } from 'react';
3
+ import { SpiffyMetricsEventName } from '@envive-ai/react-hooks/contexts/amplitudeContext';
4
+ import { HardcopyResponse } from '@envive-ai/react-hooks/contexts/hardcopyContext';
5
+ import { WidgetTypeV3 } from '@envive-ai/react-hooks/contexts/typesV3';
6
+ import { UserEvent, UserEventCategory } from '@spiffy-ai/commerce-api-client';
7
+ import { BaseWidgetProps } from '../types';
8
+ import { withBaseWidget } from '../withBaseWidget';
9
+
10
+ // Mock IntersectionObserver
11
+ class MockIntersectionObserver implements IntersectionObserver {
12
+ observe = vi.fn();
13
+
14
+ disconnect = vi.fn();
15
+
16
+ unobserve = vi.fn();
17
+
18
+ root: Element | null = null;
19
+
20
+ rootMargin: string = '';
21
+
22
+ thresholds: ReadonlyArray<number> = [];
23
+
24
+ private callback: (entries: IntersectionObserverEntry[]) => void;
25
+
26
+ private options?: IntersectionObserverInit;
27
+
28
+ constructor(
29
+ callback: (entries: IntersectionObserverEntry[]) => void,
30
+ options?: IntersectionObserverInit,
31
+ ) {
32
+ this.callback = callback;
33
+ this.options = options;
34
+ if (options) {
35
+ this.rootMargin = options.rootMargin || '';
36
+ }
37
+ }
38
+
39
+ // Helper method to trigger intersection
40
+ triggerIntersection(isIntersecting: boolean, target?: Element) {
41
+ const entry = {
42
+ isIntersecting,
43
+ intersectionRatio: isIntersecting ? 1 : 0,
44
+ boundingClientRect: {} as DOMRectReadOnly,
45
+ rootBounds: {} as DOMRectReadOnly,
46
+ target: target || ({} as Element),
47
+ time: Date.now(),
48
+ } as IntersectionObserverEntry;
49
+
50
+ this.callback([entry]);
51
+ }
52
+
53
+ // Helper method to get options
54
+ getOptions() {
55
+ return this.options;
56
+ }
57
+
58
+ // Required by IntersectionObserver interface but not used in tests
59
+ // eslint-disable-next-line class-methods-use-this
60
+ takeRecords(): IntersectionObserverEntry[] {
61
+ return [];
62
+ }
63
+ }
64
+
65
+ // Mock the contexts
66
+ const mockTrackEvent = vi.fn();
67
+ const mockGetHardcopy = vi.fn();
68
+
69
+ vi.mock('src/contexts/amplitudeContext/amplitudeContext', () => ({
70
+ useAmplitude: () => ({
71
+ trackEvent: mockTrackEvent,
72
+ isReady: true,
73
+ }),
74
+ SpiffyMetricsEventName: {
75
+ ChatComponentVisible: 'Chat Component Visible',
76
+ SearchComponentVisible: 'Search Component Visible',
77
+ },
78
+ }));
79
+
80
+ vi.mock('src/contexts/hardcopyContext', () => ({
81
+ useHardcopy: () => ({
82
+ getHardcopy: mockGetHardcopy,
83
+ }),
84
+ }));
85
+
86
+ vi.mock('src/contexts/pageContext', () => ({
87
+ usePage: () => ({
88
+ userEvent: {
89
+ id: 'test-user-event-id',
90
+ category: UserEventCategory.PageVisit,
91
+ created_at: '2025-01-01T00:00:00Z',
92
+ event_id: 'test-event-id',
93
+ } as UserEvent,
94
+ }),
95
+ }));
96
+
97
+ // Test widget component
98
+ interface TestWidgetProps extends BaseWidgetProps {
99
+ testId?: string;
100
+ children?: React.ReactNode;
101
+ }
102
+
103
+ const TestWidget: React.FC<TestWidgetProps> = ({
104
+ widgetConfigId,
105
+ widgetType,
106
+ hardcopyContent,
107
+ observedWidget,
108
+ testId = 'test-widget',
109
+ children,
110
+ }) => {
111
+ // Attach the ref to the root div so IntersectionObserver can observe it
112
+ // Note: The ref type is RefObject<HTMLDivElement> but BaseWidgetProps expects RefObject<BaseWidgetProps>
113
+ // This is a type mismatch in the implementation, but we handle it for testing
114
+ const ref = observedWidget as unknown as RefObject<HTMLDivElement> | undefined;
115
+
116
+ return (
117
+ <div
118
+ ref={ref}
119
+ data-testid={testId}
120
+ >
121
+ <div data-testid="widget-config-id">{widgetConfigId}</div>
122
+ <div data-testid="widget-type">{widgetType}</div>
123
+ {hardcopyContent && (
124
+ <div data-testid="hardcopy-content">
125
+ <div data-testid="hardcopy-language">{hardcopyContent.language}</div>
126
+ <div data-testid="hardcopy-values">{JSON.stringify(hardcopyContent.values)}</div>
127
+ </div>
128
+ )}
129
+ {observedWidget && <div data-testid="observed-widget-ref">Ref attached</div>}
130
+ {children}
131
+ </div>
132
+ );
133
+ };
134
+
135
+ describe('withBaseWidget', () => {
136
+ let mockObserver: MockIntersectionObserver;
137
+ let mockObservers: MockIntersectionObserver[];
138
+ let originalIntersectionObserver: typeof IntersectionObserver;
139
+
140
+ beforeEach(() => {
141
+ vi.clearAllMocks();
142
+ mockObservers = [];
143
+
144
+ // Set default mock for getHardcopy to return a resolved promise
145
+ mockGetHardcopy.mockResolvedValue({
146
+ language: 'en',
147
+ values: {},
148
+ });
149
+
150
+ // Store original IntersectionObserver
151
+ originalIntersectionObserver = global.IntersectionObserver;
152
+
153
+ // Replace IntersectionObserver with mock class
154
+ global.IntersectionObserver = class extends MockIntersectionObserver {
155
+ constructor(
156
+ callback: (entries: IntersectionObserverEntry[]) => void,
157
+ options?: IntersectionObserverInit,
158
+ ) {
159
+ super(callback, options);
160
+ // eslint-disable-next-line @typescript-eslint/no-this-alias
161
+ mockObserver = this;
162
+ mockObservers.push(this);
163
+ }
164
+ } as unknown as typeof IntersectionObserver;
165
+ });
166
+
167
+ afterEach(() => {
168
+ // Restore original IntersectionObserver
169
+ global.IntersectionObserver = originalIntersectionObserver;
170
+ });
171
+
172
+ describe('IntersectionObserver setup', () => {
173
+ it('should create an IntersectionObserver with default rootMargin', () => {
174
+ const WrappedWidget = withBaseWidget(TestWidget);
175
+ render(
176
+ <WrappedWidget
177
+ widgetConfigId="test-config-1"
178
+ widgetType={WidgetTypeV3.PromptCarouselV3}
179
+ />,
180
+ );
181
+
182
+ expect(mockObserver).toBeDefined();
183
+ expect(mockObserver.rootMargin).toBe('0px');
184
+ });
185
+
186
+ it('should create an IntersectionObserver with custom rootMargin', () => {
187
+ const WrappedWidget = withBaseWidget(TestWidget, {
188
+ rootMargin: '100px',
189
+ });
190
+ render(
191
+ <WrappedWidget
192
+ widgetConfigId="test-config-2"
193
+ widgetType={WidgetTypeV3.ChatPreviewV3}
194
+ />,
195
+ );
196
+
197
+ expect(mockObserver).toBeDefined();
198
+ expect(mockObserver.rootMargin).toBe('100px');
199
+ });
200
+
201
+ it('should observe the widget element', () => {
202
+ const WrappedWidget = withBaseWidget(TestWidget);
203
+ render(
204
+ <WrappedWidget
205
+ widgetConfigId="test-config-3"
206
+ widgetType={WidgetTypeV3.PromptCarouselV3}
207
+ />,
208
+ );
209
+
210
+ expect(mockObserver.observe).toHaveBeenCalled();
211
+ });
212
+ });
213
+
214
+ describe('Amplitude event tracking', () => {
215
+ it('should track default event when widget becomes visible', async () => {
216
+ const WrappedWidget = withBaseWidget(TestWidget);
217
+ const { container } = render(
218
+ <WrappedWidget
219
+ widgetConfigId="test-config-4"
220
+ widgetType={WidgetTypeV3.PromptCarouselV3}
221
+ />,
222
+ );
223
+
224
+ const widgetElement = container.querySelector('[data-testid="test-widget"]');
225
+
226
+ // Trigger intersection with the actual element
227
+ mockObserver.triggerIntersection(true, widgetElement as Element);
228
+
229
+ await waitFor(() => {
230
+ expect(mockTrackEvent).toHaveBeenCalledWith({
231
+ eventName: SpiffyMetricsEventName.ChatComponentVisible,
232
+ eventProps: {
233
+ widget_config_id: 'test-config-4',
234
+ widget_type: WidgetTypeV3.PromptCarouselV3,
235
+ },
236
+ });
237
+ });
238
+ });
239
+
240
+ it('should track custom event when widget becomes visible', async () => {
241
+ const WrappedWidget = withBaseWidget(TestWidget, {
242
+ visibilityEventName: SpiffyMetricsEventName.SearchComponentVisible,
243
+ });
244
+ const { container } = render(
245
+ <WrappedWidget
246
+ widgetConfigId="test-config-5"
247
+ widgetType={WidgetTypeV3.ChatPreviewV3}
248
+ />,
249
+ );
250
+
251
+ const widgetElement = container.querySelector('[data-testid="test-widget"]');
252
+
253
+ // Trigger intersection
254
+ mockObserver.triggerIntersection(true, widgetElement as Element);
255
+
256
+ await waitFor(() => {
257
+ expect(mockTrackEvent).toHaveBeenCalledWith({
258
+ eventName: SpiffyMetricsEventName.SearchComponentVisible,
259
+ eventProps: {
260
+ widget_config_id: 'test-config-5',
261
+ widget_type: WidgetTypeV3.ChatPreviewV3,
262
+ },
263
+ });
264
+ });
265
+ });
266
+
267
+ it('should include custom event props when tracking', async () => {
268
+ const WrappedWidget = withBaseWidget(TestWidget, {
269
+ visibilityEventProps: {
270
+ custom_prop: 'custom_value',
271
+ another_prop: 123,
272
+ },
273
+ });
274
+ const { container } = render(
275
+ <WrappedWidget
276
+ widgetConfigId="test-config-6"
277
+ widgetType={WidgetTypeV3.SocialProofV3}
278
+ />,
279
+ );
280
+
281
+ const widgetElement = container.querySelector('[data-testid="test-widget"]');
282
+
283
+ // Trigger intersection
284
+ mockObserver.triggerIntersection(true, widgetElement as Element);
285
+
286
+ await waitFor(() => {
287
+ expect(mockTrackEvent).toHaveBeenCalledWith({
288
+ eventName: SpiffyMetricsEventName.ChatComponentVisible,
289
+ eventProps: {
290
+ widget_config_id: 'test-config-6',
291
+ widget_type: WidgetTypeV3.SocialProofV3,
292
+ custom_prop: 'custom_value',
293
+ another_prop: 123,
294
+ },
295
+ });
296
+ });
297
+ });
298
+
299
+ it('should only track event once even if intersection happens multiple times', async () => {
300
+ const WrappedWidget = withBaseWidget(TestWidget);
301
+ const { container } = render(
302
+ <WrappedWidget
303
+ widgetConfigId="test-config-7"
304
+ widgetType={WidgetTypeV3.PromptCarouselV3}
305
+ />,
306
+ );
307
+
308
+ const widgetElement = container.querySelector('[data-testid="test-widget"]');
309
+
310
+ // Trigger intersection multiple times
311
+ mockObserver.triggerIntersection(true, widgetElement as Element);
312
+ mockObserver.triggerIntersection(false, widgetElement as Element);
313
+ mockObserver.triggerIntersection(true, widgetElement as Element);
314
+
315
+ await waitFor(() => {
316
+ expect(mockTrackEvent).toHaveBeenCalledTimes(1);
317
+ });
318
+ });
319
+
320
+ it('should disconnect observer after tracking event', async () => {
321
+ const WrappedWidget = withBaseWidget(TestWidget);
322
+ const { container } = render(
323
+ <WrappedWidget
324
+ widgetConfigId="test-config-8"
325
+ widgetType={WidgetTypeV3.PromptCarouselV3}
326
+ />,
327
+ );
328
+
329
+ const widgetElement = container.querySelector('[data-testid="test-widget"]');
330
+
331
+ // Trigger intersection
332
+ mockObserver.triggerIntersection(true, widgetElement as Element);
333
+
334
+ await waitFor(() => {
335
+ expect(mockObserver.disconnect).toHaveBeenCalled();
336
+ });
337
+ });
338
+
339
+ it('should not track event if widget is not intersecting', async () => {
340
+ const WrappedWidget = withBaseWidget(TestWidget);
341
+ const { container } = render(
342
+ <WrappedWidget
343
+ widgetConfigId="test-config-9"
344
+ widgetType={WidgetTypeV3.PromptCarouselV3}
345
+ />,
346
+ );
347
+
348
+ const widgetElement = container.querySelector('[data-testid="test-widget"]');
349
+
350
+ // Trigger non-intersection
351
+ mockObserver.triggerIntersection(false, widgetElement as Element);
352
+
353
+ // Wait a bit to ensure no event is tracked
354
+ await new Promise(resolve => {
355
+ setTimeout(resolve, 100);
356
+ });
357
+
358
+ expect(mockTrackEvent).not.toHaveBeenCalled();
359
+ });
360
+ });
361
+
362
+ describe('Hardcopy content fetching', () => {
363
+ it('should fetch hardcopy content when widget mounts', async () => {
364
+ const mockHardcopy: HardcopyResponse = {
365
+ language: 'en',
366
+ values: {
367
+ suggestionBarButtons: ['Button 1', 'Button 2'],
368
+ },
369
+ };
370
+
371
+ mockGetHardcopy.mockResolvedValue(mockHardcopy);
372
+
373
+ const WrappedWidget = withBaseWidget(TestWidget);
374
+ render(
375
+ <WrappedWidget
376
+ widgetConfigId="test-config-10"
377
+ widgetType={WidgetTypeV3.PromptCarouselV3}
378
+ />,
379
+ );
380
+
381
+ await waitFor(() => {
382
+ expect(mockGetHardcopy).toHaveBeenCalledWith({
383
+ widgetType: WidgetTypeV3.PromptCarouselV3,
384
+ userEvent: {
385
+ id: 'test-user-event-id',
386
+ category: UserEventCategory.PageVisit,
387
+ created_at: '2025-01-01T00:00:00Z',
388
+ event_id: 'test-event-id',
389
+ },
390
+ });
391
+ });
392
+ });
393
+
394
+ it('should pass hardcopy content to widget', async () => {
395
+ const mockHardcopy: HardcopyResponse = {
396
+ language: 'en',
397
+ values: {
398
+ chatPreviewTitle: 'Chat Preview Title',
399
+ typewriterTextPrefix: 'Want to know',
400
+ },
401
+ };
402
+
403
+ mockGetHardcopy.mockResolvedValue(mockHardcopy);
404
+
405
+ const WrappedWidget = withBaseWidget(TestWidget);
406
+ render(
407
+ <WrappedWidget
408
+ widgetConfigId="test-config-11"
409
+ widgetType={WidgetTypeV3.ChatPreviewV3}
410
+ />,
411
+ );
412
+
413
+ await waitFor(() => {
414
+ expect(screen.getByTestId('hardcopy-content')).toBeInTheDocument();
415
+ expect(screen.getByTestId('hardcopy-language')).toHaveTextContent('en');
416
+ expect(screen.getByTestId('hardcopy-values')).toHaveTextContent(
417
+ JSON.stringify(mockHardcopy.values),
418
+ );
419
+ });
420
+ });
421
+
422
+ it('should not fetch hardcopy if widgetType is not provided', () => {
423
+ const WrappedWidget = withBaseWidget(TestWidget);
424
+ // @ts-expect-error - Testing invalid props
425
+ render(<WrappedWidget widgetConfigId="test-config-12" />);
426
+
427
+ expect(mockGetHardcopy).not.toHaveBeenCalled();
428
+ });
429
+
430
+ it('should handle hardcopy fetch errors gracefully', async () => {
431
+ const error = new Error('Failed to fetch hardcopy');
432
+ mockGetHardcopy.mockRejectedValue(error);
433
+
434
+ const WrappedWidget = withBaseWidget(TestWidget);
435
+ render(
436
+ <WrappedWidget
437
+ widgetConfigId="test-config-13"
438
+ widgetType={WidgetTypeV3.PromptCarouselV3}
439
+ />,
440
+ );
441
+
442
+ await waitFor(() => {
443
+ expect(mockGetHardcopy).toHaveBeenCalled();
444
+ });
445
+
446
+ // Wait a bit for the promise to reject and be handled
447
+ await new Promise(resolve => {
448
+ setTimeout(resolve, 100);
449
+ });
450
+
451
+ // Widget should still render without hardcopy
452
+ expect(screen.getByTestId('test-widget')).toBeInTheDocument();
453
+ expect(screen.queryByTestId('hardcopy-content')).not.toBeInTheDocument();
454
+ });
455
+ });
456
+
457
+ describe('Widget rendering', () => {
458
+ it('should render the wrapped widget with correct props', () => {
459
+ const WrappedWidget = withBaseWidget(TestWidget);
460
+ render(
461
+ <WrappedWidget
462
+ widgetConfigId="test-config-14"
463
+ widgetType={WidgetTypeV3.PromptCarouselV3}
464
+ />,
465
+ );
466
+
467
+ expect(screen.getByTestId('test-widget')).toBeInTheDocument();
468
+ expect(screen.getByTestId('widget-config-id')).toHaveTextContent('test-config-14');
469
+ expect(screen.getByTestId('widget-type')).toHaveTextContent(WidgetTypeV3.PromptCarouselV3);
470
+ });
471
+
472
+ it('should pass observedWidget ref to the widget', () => {
473
+ const WrappedWidget = withBaseWidget(TestWidget);
474
+ render(
475
+ <WrappedWidget
476
+ widgetConfigId="test-config-15"
477
+ widgetType={WidgetTypeV3.PromptCarouselV3}
478
+ />,
479
+ );
480
+
481
+ expect(screen.getByTestId('observed-widget-ref')).toBeInTheDocument();
482
+ });
483
+
484
+ it('should render widget children correctly', () => {
485
+ const WrappedWidget = withBaseWidget(TestWidget);
486
+ render(
487
+ <WrappedWidget
488
+ widgetConfigId="test-config-16"
489
+ widgetType={WidgetTypeV3.PromptCarouselV3}
490
+ >
491
+ <div data-testid="child-content">Child Content</div>
492
+ </WrappedWidget>,
493
+ );
494
+
495
+ expect(screen.getByTestId('child-content')).toBeInTheDocument();
496
+ expect(screen.getByTestId('child-content')).toHaveTextContent('Child Content');
497
+ });
498
+
499
+ it('should render widget with hardcopy content when available', async () => {
500
+ const mockHardcopy: HardcopyResponse = {
501
+ language: 'en',
502
+ values: {
503
+ imagePromptTitle: 'Image Prompt Title',
504
+ },
505
+ };
506
+
507
+ mockGetHardcopy.mockResolvedValue(mockHardcopy);
508
+
509
+ const WrappedWidget = withBaseWidget(TestWidget);
510
+ render(
511
+ <WrappedWidget
512
+ widgetConfigId="test-config-17"
513
+ widgetType={WidgetTypeV3.ImagePromptCardV3}
514
+ />,
515
+ );
516
+
517
+ await waitFor(() => {
518
+ expect(screen.getByTestId('hardcopy-content')).toBeInTheDocument();
519
+ });
520
+
521
+ expect(screen.getByTestId('hardcopy-language')).toHaveTextContent('en');
522
+ expect(screen.getByTestId('hardcopy-values')).toHaveTextContent(
523
+ JSON.stringify(mockHardcopy.values),
524
+ );
525
+ });
526
+ });
527
+
528
+ describe('Integration tests', () => {
529
+ it('should handle full flow: visibility tracking + hardcopy rendering', async () => {
530
+ const mockHardcopy: HardcopyResponse = {
531
+ language: 'en',
532
+ values: {
533
+ suggestionBarButtons: ['Button 1', 'Button 2', 'Button 3'],
534
+ },
535
+ };
536
+
537
+ mockGetHardcopy.mockResolvedValue(mockHardcopy);
538
+
539
+ const WrappedWidget = withBaseWidget(TestWidget, {
540
+ visibilityEventName: SpiffyMetricsEventName.SearchComponentVisible,
541
+ visibilityEventProps: {
542
+ test_integration: true,
543
+ },
544
+ });
545
+
546
+ render(
547
+ <WrappedWidget
548
+ widgetConfigId="test-config-integration"
549
+ widgetType={WidgetTypeV3.PromptCarouselV3}
550
+ />,
551
+ );
552
+
553
+ // Wait for hardcopy to be fetched and rendered
554
+ await waitFor(() => {
555
+ expect(screen.getByTestId('hardcopy-content')).toBeInTheDocument();
556
+ });
557
+
558
+ const widgetElement = screen.getByTestId('test-widget');
559
+
560
+ // Trigger intersection to track event
561
+ mockObserver.triggerIntersection(true, widgetElement);
562
+
563
+ // Verify both hardcopy and event tracking worked
564
+ await waitFor(() => {
565
+ expect(mockTrackEvent).toHaveBeenCalledWith({
566
+ eventName: SpiffyMetricsEventName.SearchComponentVisible,
567
+ eventProps: {
568
+ widget_config_id: 'test-config-integration',
569
+ widget_type: WidgetTypeV3.PromptCarouselV3,
570
+ test_integration: true,
571
+ },
572
+ });
573
+ });
574
+
575
+ expect(screen.getByTestId('hardcopy-language')).toHaveTextContent('en');
576
+ expect(screen.getByTestId('hardcopy-values')).toHaveTextContent(
577
+ JSON.stringify(mockHardcopy.values),
578
+ );
579
+ });
580
+
581
+ it('should handle multiple widget instances independently', async () => {
582
+ const mockHardcopy1: HardcopyResponse = {
583
+ language: 'en',
584
+ values: { buttons: ['Button 1'] },
585
+ };
586
+ const mockHardcopy2: HardcopyResponse = {
587
+ language: 'en',
588
+ values: { buttons: ['Button 2'] },
589
+ };
590
+
591
+ mockGetHardcopy.mockResolvedValueOnce(mockHardcopy1).mockResolvedValueOnce(mockHardcopy2);
592
+
593
+ const WrappedWidget = withBaseWidget(TestWidget);
594
+
595
+ // Render both widgets simultaneously to test independence
596
+ render(
597
+ <>
598
+ <WrappedWidget
599
+ widgetConfigId="widget-1"
600
+ widgetType={WidgetTypeV3.PromptCarouselV3}
601
+ testId="widget-1"
602
+ />
603
+ <WrappedWidget
604
+ widgetConfigId="widget-2"
605
+ widgetType={WidgetTypeV3.ChatPreviewV3}
606
+ testId="widget-2"
607
+ />
608
+ </>,
609
+ );
610
+
611
+ // Wait for both widgets to render and fetch hardcopy
612
+ await waitFor(() => {
613
+ expect(screen.getByTestId('widget-1')).toBeInTheDocument();
614
+ expect(screen.getByTestId('widget-2')).toBeInTheDocument();
615
+ });
616
+
617
+ // Verify both widgets have their own config IDs
618
+ const widget1ConfigId = screen
619
+ .getByTestId('widget-1')
620
+ .querySelector('[data-testid="widget-config-id"]');
621
+ const widget2ConfigId = screen
622
+ .getByTestId('widget-2')
623
+ .querySelector('[data-testid="widget-config-id"]');
624
+ expect(widget1ConfigId).toHaveTextContent('widget-1');
625
+ expect(widget2ConfigId).toHaveTextContent('widget-2');
626
+
627
+ // Verify both widgets fetched their own hardcopy independently
628
+ expect(mockGetHardcopy).toHaveBeenCalledWith({
629
+ widgetType: WidgetTypeV3.PromptCarouselV3,
630
+ userEvent: expect.any(Object),
631
+ });
632
+ expect(mockGetHardcopy).toHaveBeenCalledWith({
633
+ widgetType: WidgetTypeV3.ChatPreviewV3,
634
+ userEvent: expect.any(Object),
635
+ });
636
+
637
+ // Verify first widget can trigger intersection and track event
638
+ // When multiple widgets are rendered, each creates its own IntersectionObserver
639
+ // We need to find the observer that's observing widget-1's element
640
+ const widget1Element = screen.getByTestId('widget-1');
641
+ const widget2Element = screen.getByTestId('widget-2');
642
+
643
+ // Clear any previous calls to isolate this test
644
+ mockTrackEvent.mockClear();
645
+
646
+ // Find which observer is observing widget-1 by checking which one was called with widget1Element
647
+ // In our mock setup, the first observer should be for widget-1, second for widget-2
648
+ const widget1Observer = mockObservers[0];
649
+ const widget2Observer = mockObservers[1];
650
+
651
+ // Verify we have both observers
652
+ expect(widget1Observer).toBeDefined();
653
+ expect(widget2Observer).toBeDefined();
654
+
655
+ // Trigger intersection for widget-1 using its observer
656
+ widget1Observer.triggerIntersection(true, widget1Element);
657
+
658
+ await waitFor(() => {
659
+ // Verify that an event was tracked for widget-1
660
+ expect(mockTrackEvent).toHaveBeenCalled();
661
+ const { calls } = mockTrackEvent.mock;
662
+ // Find any call that matches widget-1's properties
663
+ const widget1Call = calls.find(
664
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
665
+ (call: any) =>
666
+ call[0]?.eventProps?.widget_config_id === 'widget-1' &&
667
+ call[0]?.eventProps?.widget_type === WidgetTypeV3.PromptCarouselV3,
668
+ );
669
+ expect(widget1Call).toBeDefined();
670
+ });
671
+
672
+ // Verify widget-2 can also track independently
673
+ mockTrackEvent.mockClear();
674
+ widget2Observer.triggerIntersection(true, widget2Element);
675
+
676
+ await waitFor(() => {
677
+ expect(mockTrackEvent).toHaveBeenCalled();
678
+ const { calls } = mockTrackEvent.mock;
679
+ const widget2Call = calls.find(
680
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
681
+ (call: any) =>
682
+ call[0]?.eventProps?.widget_config_id === 'widget-2' &&
683
+ call[0]?.eventProps?.widget_type === WidgetTypeV3.ChatPreviewV3,
684
+ );
685
+ expect(widget2Call).toBeDefined();
686
+ });
687
+ });
688
+ });
689
+ });
@@ -0,0 +1,2 @@
1
+ export { withBaseWidget } from './withBaseWidget';
2
+ export type { BaseWidgetProps, WithBaseWidgetOptions } from './types';