@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.
- package/dist/CXIntegration/hooks/useUnifiedCXButton.cjs +23 -0
- package/dist/CXIntegration/hooks/useUnifiedCXButton.js +22 -0
- package/dist/CXIntegration/implementations/useDefaultUnifiedCXButton.cjs +11 -0
- package/dist/CXIntegration/implementations/useDefaultUnifiedCXButton.js +10 -0
- package/dist/CXIntegration/implementations/useGladlyUnifiedCXButton.cjs +20 -0
- package/dist/CXIntegration/implementations/useGladlyUnifiedCXButton.js +19 -0
- package/dist/CXIntegration/implementations/useGorgiasUnifiedCXButton.cjs +53 -0
- package/dist/CXIntegration/implementations/useGorgiasUnifiedCXButton.js +52 -0
- package/dist/CXIntegration/implementations/useGrooveUnifiedCXButton.cjs +45 -0
- package/dist/CXIntegration/implementations/useGrooveUnifiedCXButton.js +44 -0
- package/dist/CXIntegration/implementations/useKustomerUnifiedCXButton.cjs +70 -0
- package/dist/CXIntegration/implementations/useKustomerUnifiedCXButton.js +69 -0
- package/dist/CXIntegration/implementations/useReDoUnifiedCXButton.cjs +59 -0
- package/dist/CXIntegration/implementations/useReDoUnifiedCXButton.js +58 -0
- package/dist/CXIntegration/implementations/useRichpanelUnifiedCXButton.cjs +65 -0
- package/dist/CXIntegration/implementations/useRichpanelUnifiedCXButton.js +64 -0
- package/dist/CXIntegration/implementations/useShopifyChatUnifiedCXButton.cjs +53 -0
- package/dist/CXIntegration/implementations/useShopifyChatUnifiedCXButton.js +52 -0
- package/dist/CXIntegration/implementations/useTidioUnifiedCXButton.cjs +18 -0
- package/dist/CXIntegration/implementations/useTidioUnifiedCXButton.js +17 -0
- package/dist/CXIntegration/implementations/useZendeskUnifiedCXButton.cjs +54 -0
- package/dist/CXIntegration/implementations/useZendeskUnifiedCXButton.js +53 -0
- package/dist/CXIntegration/implementations/useZowieUnifiedCXButton.cjs +18 -0
- package/dist/CXIntegration/implementations/useZowieUnifiedCXButton.js +17 -0
- package/dist/CXIntegration/types.cjs +19 -0
- package/dist/CXIntegration/types.js +18 -0
- package/dist/CXIntegration/utils/functions.cjs +30 -0
- package/dist/CXIntegration/utils/functions.js +30 -0
- package/dist/_virtual/rolldown_runtime.cjs +29 -0
- package/dist/hocs/withBaseWidget/index.cjs +3 -0
- package/dist/hocs/withBaseWidget/index.d.cts +3 -0
- package/dist/hocs/withBaseWidget/index.d.ts +3 -0
- package/dist/hocs/withBaseWidget/index.js +3 -0
- package/dist/hocs/withBaseWidget/types.d.cts +37 -0
- package/dist/hocs/withBaseWidget/types.d.ts +39 -0
- package/dist/hocs/withBaseWidget/withBaseWidget.cjs +101 -0
- package/dist/hocs/withBaseWidget/withBaseWidget.d.cts +10 -0
- package/dist/hocs/withBaseWidget/withBaseWidget.d.ts +10 -0
- package/dist/hocs/withBaseWidget/withBaseWidget.js +100 -0
- package/dist/hooks/dist/application/models/api/orgConfigResults.d.ts +1 -0
- package/dist/hooks/dist/application/models/frontendConfig.d.ts +1 -0
- package/dist/hooks/dist/contexts/amplitudeContext/amplitudeContext.d.ts +2 -0
- package/dist/hooks/dist/contexts/amplitudeContext/index.d.ts +2 -0
- package/dist/hooks/dist/contexts/featureFlagServiceContext/featureFlagServiceContext.d.ts +2 -0
- package/dist/hooks/dist/contexts/hardcopyContext/hardcopyContext.d.cts +8 -0
- package/dist/hooks/dist/contexts/hardcopyContext/hardcopyContext.d.ts +11 -0
- package/dist/hooks/dist/contexts/hardcopyContext/index.d.ts +1 -0
- package/dist/hooks/dist/contexts/types.d.cts +44 -0
- package/dist/hooks/dist/contexts/types.d.ts +47 -0
- package/dist/hooks/dist/contexts/typesV3.d.cts +201 -0
- package/dist/hooks/dist/contexts/typesV3.d.ts +201 -0
- package/dist/hooks/dist/services/amplitudeService/amplitudeService.d.cts +36 -0
- package/dist/hooks/dist/services/amplitudeService/amplitudeService.d.ts +37 -0
- package/dist/hooks/dist/types/customerService.d.cts +18 -0
- package/dist/hooks/dist/types/customerService.d.ts +18 -0
- package/dist/stories/SalesAgentTest/index.cjs +0 -0
- package/dist/stories/SalesAgentTest/index.d.cts +1 -0
- package/dist/stories/SalesAgentTest/index.d.ts +1 -0
- package/dist/stories/SalesAgentTest/index.js +0 -0
- package/dist/widgets/ChatPreviewComparisonWidget/ChatPreviewComparisonWidget.cjs +98 -0
- package/dist/widgets/ChatPreviewComparisonWidget/ChatPreviewComparisonWidget.d.cts +19 -0
- package/dist/widgets/ChatPreviewComparisonWidget/ChatPreviewComparisonWidget.d.ts +19 -0
- package/dist/widgets/ChatPreviewComparisonWidget/ChatPreviewComparisonWidget.js +96 -0
- package/dist/widgets/ChatPreviewComparisonWidget/index.cjs +4 -0
- package/dist/widgets/ChatPreviewComparisonWidget/index.d.cts +2 -0
- package/dist/widgets/ChatPreviewComparisonWidget/index.d.ts +2 -0
- package/dist/widgets/ChatPreviewComparisonWidget/index.js +3 -0
- package/dist/widgets/ChatPreviewLoadingWidget/ChatPreviewLoadingWidget.cjs +44 -0
- package/dist/widgets/ChatPreviewLoadingWidget/ChatPreviewLoadingWidget.d.cts +16 -0
- package/dist/widgets/ChatPreviewLoadingWidget/ChatPreviewLoadingWidget.d.ts +16 -0
- package/dist/widgets/ChatPreviewLoadingWidget/ChatPreviewLoadingWidget.js +42 -0
- package/dist/widgets/ChatPreviewLoadingWidget/index.cjs +4 -0
- package/dist/widgets/ChatPreviewLoadingWidget/index.d.cts +2 -0
- package/dist/widgets/ChatPreviewLoadingWidget/index.d.ts +2 -0
- package/dist/widgets/ChatPreviewLoadingWidget/index.js +3 -0
- package/dist/widgets/ChatPreviewWidget/ChatPreviewWidget.cjs +86 -0
- package/dist/widgets/ChatPreviewWidget/ChatPreviewWidget.d.cts +19 -0
- package/dist/widgets/ChatPreviewWidget/ChatPreviewWidget.d.ts +19 -0
- package/dist/widgets/ChatPreviewWidget/ChatPreviewWidget.js +84 -0
- package/dist/widgets/ChatPreviewWidget/index.cjs +4 -0
- package/dist/widgets/ChatPreviewWidget/index.d.cts +2 -0
- package/dist/widgets/ChatPreviewWidget/index.d.ts +2 -0
- package/dist/widgets/ChatPreviewWidget/index.js +3 -0
- package/dist/widgets/FloatingChatWidget/FloatingChatOverlay.cjs +66 -0
- package/dist/widgets/FloatingChatWidget/FloatingChatOverlay.js +64 -0
- package/dist/widgets/FloatingChatWidget/FloatingChatWidget.cjs +70 -0
- package/dist/widgets/FloatingChatWidget/FloatingChatWidget.d.cts +10 -0
- package/dist/widgets/FloatingChatWidget/FloatingChatWidget.d.ts +10 -0
- package/dist/widgets/FloatingChatWidget/FloatingChatWidget.js +69 -0
- package/dist/widgets/FloatingChatWidget/constants.cjs +6 -0
- package/dist/widgets/FloatingChatWidget/constants.d.cts +4 -0
- package/dist/widgets/FloatingChatWidget/constants.d.ts +4 -0
- package/dist/widgets/FloatingChatWidget/constants.js +5 -0
- package/dist/widgets/FloatingChatWidget/index.cjs +5 -0
- package/dist/widgets/FloatingChatWidget/index.d.cts +3 -0
- package/dist/widgets/FloatingChatWidget/index.d.ts +3 -0
- package/dist/widgets/FloatingChatWidget/index.js +4 -0
- package/dist/widgets/PromptButtonCarouselWithImageWidget/PromptButtonCarouselWithImageWidget.cjs +60 -0
- package/dist/widgets/PromptButtonCarouselWithImageWidget/PromptButtonCarouselWithImageWidget.d.cts +19 -0
- package/dist/widgets/PromptButtonCarouselWithImageWidget/PromptButtonCarouselWithImageWidget.d.ts +19 -0
- package/dist/widgets/PromptButtonCarouselWithImageWidget/PromptButtonCarouselWithImageWidget.js +58 -0
- package/dist/widgets/PromptButtonCarouselWithImageWidget/index.cjs +4 -0
- package/dist/widgets/PromptButtonCarouselWithImageWidget/index.d.cts +2 -0
- package/dist/widgets/PromptButtonCarouselWithImageWidget/index.d.ts +2 -0
- package/dist/widgets/PromptButtonCarouselWithImageWidget/index.js +3 -0
- package/dist/widgets/PromptCarouselWidget/PromptCarouselWidget.cjs +64 -0
- package/dist/widgets/PromptCarouselWidget/PromptCarouselWidget.d.cts +14 -0
- package/dist/widgets/PromptCarouselWidget/PromptCarouselWidget.d.ts +14 -0
- package/dist/widgets/PromptCarouselWidget/PromptCarouselWidget.js +63 -0
- package/dist/widgets/PromptCarouselWidget/index.cjs +3 -0
- package/dist/widgets/PromptCarouselWidget/index.d.cts +2 -0
- package/dist/widgets/PromptCarouselWidget/index.d.ts +2 -0
- package/dist/widgets/PromptCarouselWidget/index.js +3 -0
- package/dist/widgets/SocialProofFlowWidget/SocialProofFlowWidget.cjs +35 -0
- package/dist/widgets/SocialProofFlowWidget/SocialProofFlowWidget.d.cts +14 -0
- package/dist/widgets/SocialProofFlowWidget/SocialProofFlowWidget.d.ts +14 -0
- package/dist/widgets/SocialProofFlowWidget/SocialProofFlowWidget.js +34 -0
- package/dist/widgets/SocialProofFlowWidget/index.cjs +3 -0
- package/dist/widgets/SocialProofFlowWidget/index.d.cts +2 -0
- package/dist/widgets/SocialProofFlowWidget/index.d.ts +2 -0
- package/dist/widgets/SocialProofFlowWidget/index.js +3 -0
- package/dist/widgets/SocialProofWidget/SocialProofWidget.cjs +118 -0
- package/dist/widgets/SocialProofWidget/SocialProofWidget.d.cts +19 -0
- package/dist/widgets/SocialProofWidget/SocialProofWidget.d.ts +19 -0
- package/dist/widgets/SocialProofWidget/SocialProofWidget.js +116 -0
- package/dist/widgets/SocialProofWidget/index.cjs +4 -0
- package/dist/widgets/SocialProofWidget/index.d.cts +2 -0
- package/dist/widgets/SocialProofWidget/index.d.ts +2 -0
- package/dist/widgets/SocialProofWidget/index.js +3 -0
- package/dist/widgets/TitledPromptCarouselWidget/TitledPromptCarouselWidget.cjs +67 -0
- package/dist/widgets/TitledPromptCarouselWidget/TitledPromptCarouselWidget.d.cts +14 -0
- package/dist/widgets/TitledPromptCarouselWidget/TitledPromptCarouselWidget.d.ts +14 -0
- package/dist/widgets/TitledPromptCarouselWidget/TitledPromptCarouselWidget.js +66 -0
- package/dist/widgets/TitledPromptCarouselWidget/index.cjs +3 -0
- package/dist/widgets/TitledPromptCarouselWidget/index.d.cts +2 -0
- package/dist/widgets/TitledPromptCarouselWidget/index.d.ts +2 -0
- package/dist/widgets/TitledPromptCarouselWidget/index.js +3 -0
- package/dist/widgets/TypingAnimationFlowWidget/TypingAnimationFlowWidget.cjs +35 -0
- package/dist/widgets/TypingAnimationFlowWidget/TypingAnimationFlowWidget.d.cts +14 -0
- package/dist/widgets/TypingAnimationFlowWidget/TypingAnimationFlowWidget.d.ts +14 -0
- package/dist/widgets/TypingAnimationFlowWidget/TypingAnimationFlowWidget.js +34 -0
- package/dist/widgets/TypingAnimationFlowWidget/index.cjs +3 -0
- package/dist/widgets/TypingAnimationFlowWidget/index.d.cts +2 -0
- package/dist/widgets/TypingAnimationFlowWidget/index.d.ts +2 -0
- package/dist/widgets/TypingAnimationFlowWidget/index.js +3 -0
- package/dist/widgets/TypingAnimationWidget/TypingAnimationWidget.cjs +90 -0
- package/dist/widgets/TypingAnimationWidget/TypingAnimationWidget.d.cts +19 -0
- package/dist/widgets/TypingAnimationWidget/TypingAnimationWidget.d.ts +19 -0
- package/dist/widgets/TypingAnimationWidget/TypingAnimationWidget.js +88 -0
- package/dist/widgets/TypingAnimationWidget/index.cjs +4 -0
- package/dist/widgets/TypingAnimationWidget/index.d.cts +2 -0
- package/dist/widgets/TypingAnimationWidget/index.d.ts +2 -0
- package/dist/widgets/TypingAnimationWidget/index.js +3 -0
- package/dist/widgets/dist/SearchResults/SearchResults.d.cts +15 -0
- package/dist/widgets/dist/SearchResults/SearchResults.d.ts +15 -0
- package/dist/widgets/dist/SearchResults/SearchResultsWidget.d.cts +9 -0
- package/dist/widgets/dist/SearchResults/SearchResultsWidget.d.ts +9 -0
- package/dist/widgets/dist/SearchResults/index.d.ts +2 -0
- package/dist/widgets/dist/SearchResults/types.d.cts +20 -0
- package/dist/widgets/dist/SearchResults/types.d.ts +20 -0
- package/dist/widgets/dist/SearchZeroState/SearchZeroState.d.cts +10 -0
- package/dist/widgets/dist/SearchZeroState/SearchZeroState.d.ts +10 -0
- package/dist/widgets/dist/SearchZeroState/SearchZeroStateWidget.d.cts +18 -0
- package/dist/widgets/dist/SearchZeroState/SearchZeroStateWidget.d.ts +18 -0
- package/dist/widgets/dist/SearchZeroState/index.d.ts +4 -0
- package/dist/widgets/dist/SearchZeroState/types.d.cts +13 -0
- package/dist/widgets/dist/SearchZeroState/types.d.ts +13 -0
- package/dist/widgets/dist/SuggestionBar/SuggestionBar.d.cts +36 -0
- package/dist/widgets/dist/SuggestionBar/SuggestionBar.d.ts +36 -0
- package/dist/widgets/dist/SuggestionBar/index.d.ts +2 -0
- package/dist/widgets/dist/SuggestionBar/types.d.cts +9 -0
- package/dist/widgets/dist/SuggestionBar/types.d.ts +9 -0
- package/dist/widgets/dist/SuggestionButtonContainer/SuggestionButtonContainer.d.cts +9 -0
- package/dist/widgets/dist/SuggestionButtonContainer/SuggestionButtonContainer.d.ts +9 -0
- package/dist/widgets/dist/SuggestionButtonContainer/types.d.cts +20 -0
- package/dist/widgets/dist/SuggestionButtonContainer/types.d.ts +20 -0
- package/dist/widgets/dist/config/BaseWidgetConfig.d.cts +13 -0
- package/dist/widgets/dist/config/BaseWidgetConfig.d.ts +13 -0
- package/dist/widgets/dist/config/WidgetType.d.cts +23 -0
- package/dist/widgets/dist/config/WidgetType.d.ts +23 -0
- package/dist/widgets/dist/packages/hooks/dist/application/models/api/response.d.cts +14 -0
- package/dist/widgets/dist/packages/hooks/dist/application/models/api/response.d.ts +14 -0
- package/dist/widgets/dist/packages/hooks/dist/application/models/api/search.d.cts +15 -0
- package/dist/widgets/dist/packages/hooks/dist/application/models/api/search.d.ts +15 -0
- package/dist/widgets/dist/packages/hooks/dist/application/models/utilityTypes/camelCase.d.cts +73 -0
- package/dist/widgets/dist/packages/hooks/dist/application/models/utilityTypes/camelCase.d.ts +73 -0
- package/dist/widgets/dist/packages/hooks/dist/application/models/utilityTypes/camelCasedPropertiesDeep.d.cts +61 -0
- package/dist/widgets/dist/packages/hooks/dist/application/models/utilityTypes/camelCasedPropertiesDeep.d.ts +61 -0
- package/dist/widgets/dist/packages/hooks/dist/application/models/utilityTypes/internal.d.cts +25 -0
- package/dist/widgets/dist/packages/hooks/dist/application/models/utilityTypes/internal.d.ts +25 -0
- package/dist/widgets/dist/packages/hooks/dist/application/models/utilityTypes/splitWords.d.cts +35 -0
- package/dist/widgets/dist/packages/hooks/dist/application/models/utilityTypes/splitWords.d.ts +35 -0
- package/dist/widgets/dist/packages/hooks/dist/application/models/utilityTypes/trim.d.cts +32 -0
- package/dist/widgets/dist/packages/hooks/dist/application/models/utilityTypes/trim.d.ts +32 -0
- package/dist/widgets/dist/packages/hooks/dist/application/models/utilityTypes/unknownArray.d.cts +32 -0
- package/dist/widgets/dist/packages/hooks/dist/application/models/utilityTypes/unknownArray.d.ts +32 -0
- package/dist/widgets/dist/packages/hooks/dist/atoms/search/searchAPI.d.cts +14 -0
- package/dist/widgets/dist/packages/hooks/dist/atoms/search/searchAPI.d.ts +15 -0
- package/dist/widgets/dist/packages/hooks/dist/contexts/types.d.cts +61 -0
- package/dist/widgets/dist/packages/hooks/dist/contexts/types.d.ts +61 -0
- package/dist/widgets/dist/packages/hooks/dist/hooks/Search/useSearch.d.cts +60 -0
- package/dist/widgets/dist/packages/hooks/dist/hooks/Search/useSearch.d.ts +60 -0
- package/dist/widgets/dist/packages/hooks/dist/hooks/utils.d.cts +11 -0
- package/dist/widgets/dist/packages/hooks/dist/hooks/utils.d.ts +11 -0
- package/dist/widgets/dist/packages/hooks/dist/types/search-filter-types.d.cts +28 -0
- package/dist/widgets/dist/packages/hooks/dist/types/search-filter-types.d.ts +28 -0
- package/dist/widgets/dist/packages/hooks/dist/types/test-types.d.cts +10 -0
- package/dist/widgets/dist/packages/hooks/dist/types/test-types.d.ts +10 -0
- package/dist/widgets/hooks/useGetWidgetStatus.cjs +27 -0
- package/dist/widgets/hooks/useGetWidgetStatus.js +26 -0
- package/dist/widgets/utils/functions.cjs +55 -0
- package/dist/widgets/utils/functions.js +48 -0
- package/dist/widgets-v2/SearchResults/index.cjs +5 -0
- package/dist/widgets-v2/SearchResults/index.d.cts +3 -0
- package/dist/widgets-v2/SearchResults/index.d.ts +4 -0
- package/dist/widgets-v2/SearchResults/index.js +3 -0
- package/dist/widgets-v2/SearchZeroState/index.cjs +8 -0
- package/dist/widgets-v2/SearchZeroState/index.d.cts +5 -0
- package/dist/widgets-v2/SearchZeroState/index.d.ts +6 -0
- package/dist/widgets-v2/SearchZeroState/index.js +3 -0
- package/dist/widgets-v2/SuggestionBar/index.cjs +5 -0
- package/dist/widgets-v2/SuggestionBar/index.d.cts +3 -0
- package/dist/widgets-v2/SuggestionBar/index.d.ts +4 -0
- package/dist/widgets-v2/SuggestionBar/index.js +3 -0
- package/dist/widgets-v2/SuggestionButtonContainer/index.cjs +5 -0
- package/dist/widgets-v2/SuggestionButtonContainer/index.d.cts +3 -0
- package/dist/widgets-v2/SuggestionButtonContainer/index.d.ts +3 -0
- package/dist/widgets-v2/SuggestionButtonContainer/index.js +3 -0
- package/package.json +158 -0
- package/src/CXIntegration/hooks/useUnifiedCXButton.ts +38 -0
- package/src/CXIntegration/implementations/useDefaultUnifiedCXButton.ts +8 -0
- package/src/CXIntegration/implementations/useGladlyUnifiedCXButton.ts +28 -0
- package/src/CXIntegration/implementations/useGorgiasUnifiedCXButton.ts +67 -0
- package/src/CXIntegration/implementations/useGrooveUnifiedCXButton.ts +83 -0
- package/src/CXIntegration/implementations/useKustomerUnifiedCXButton.ts +120 -0
- package/src/CXIntegration/implementations/useReDoUnifiedCXButton.ts +80 -0
- package/src/CXIntegration/implementations/useRichpanelUnifiedCXButton.ts +83 -0
- package/src/CXIntegration/implementations/useShopifyChatUnifiedCXButton.ts +78 -0
- package/src/CXIntegration/implementations/useTidioUnifiedCXButton.ts +34 -0
- package/src/CXIntegration/implementations/useZendeskUnifiedCXButton.ts +69 -0
- package/src/CXIntegration/implementations/useZowieUnifiedCXButton.ts +34 -0
- package/src/CXIntegration/types.ts +24 -0
- package/src/CXIntegration/utils/functions.ts +50 -0
- package/src/hocs/withBaseWidget/__tests__/withBaseWidget.test.tsx +689 -0
- package/src/hocs/withBaseWidget/index.ts +2 -0
- package/src/hocs/withBaseWidget/types.ts +39 -0
- package/src/hocs/withBaseWidget/withBaseWidget.tsx +126 -0
- package/src/stories/FloatingChatWidget.stories.tsx +56 -0
- package/src/stories/PromptButtonCarouselWithImageWidget.stories.tsx +54 -0
- package/src/stories/PromptCarouselWidget.stories.tsx +54 -0
- package/src/stories/SalesAgentTest/SalesAgentTest.stories.tsx +18 -0
- package/src/stories/SalesAgentTest/SalesAgentTest.tsx +111 -0
- package/src/stories/SalesAgentTest/index.ts +0 -0
- package/src/stories/SearchResults.stories.tsx +29 -0
- package/src/stories/SearchZeroState.stories.tsx +52 -0
- package/src/stories/SocialProofFlowWidget.stories.tsx +77 -0
- package/src/stories/SuggestionBar.stories.tsx +45 -0
- package/src/stories/TitledPromptCarouselWidget.stories.tsx +71 -0
- package/src/stories/TypingAnimationFlowWidget.stories.tsx +67 -0
- package/src/widgets/ChatPreviewComparisonWidget/ChatPreviewComparisonWidget.tsx +152 -0
- package/src/widgets/ChatPreviewComparisonWidget/index.ts +7 -0
- package/src/widgets/ChatPreviewLoadingWidget/ChatPreviewLoadingWidget.tsx +82 -0
- package/src/widgets/ChatPreviewLoadingWidget/index.ts +7 -0
- package/src/widgets/ChatPreviewWidget/ChatPreviewWidget.tsx +119 -0
- package/src/widgets/ChatPreviewWidget/index.ts +4 -0
- package/src/widgets/FloatingChatWidget/FloatingChatOverlay.tsx +115 -0
- package/src/widgets/FloatingChatWidget/FloatingChatWidget.tsx +110 -0
- package/src/widgets/FloatingChatWidget/constants.ts +1 -0
- package/src/widgets/FloatingChatWidget/index.ts +5 -0
- package/src/widgets/PromptButtonCarouselWithImageWidget/PromptButtonCarouselWithImageWidget.tsx +87 -0
- package/src/widgets/PromptButtonCarouselWithImageWidget/index.ts +6 -0
- package/src/widgets/PromptCarouselWidget/PromptCarouselWidget.tsx +83 -0
- package/src/widgets/PromptCarouselWidget/index.ts +2 -0
- package/src/widgets/SocialProofFlowWidget/SocialProofFlowWidget.tsx +61 -0
- package/src/widgets/SocialProofFlowWidget/index.ts +4 -0
- package/src/widgets/SocialProofWidget/SocialProofWidget.tsx +160 -0
- package/src/widgets/SocialProofWidget/index.ts +2 -0
- package/src/widgets/TitledPromptCarouselWidget/TitledPromptCarouselWidget.tsx +93 -0
- package/src/widgets/TitledPromptCarouselWidget/index.ts +2 -0
- package/src/widgets/TypingAnimationFlowWidget/TypingAnimationFlowWidget.tsx +61 -0
- package/src/widgets/TypingAnimationFlowWidget/index.ts +4 -0
- package/src/widgets/TypingAnimationWidget/TypingAnimationWidget.tsx +115 -0
- package/src/widgets/TypingAnimationWidget/index.ts +2 -0
- package/src/widgets/hooks/useGetWidgetStatus.tsx +29 -0
- package/src/widgets/utils/functions.ts +104 -0
- package/src/widgets-v2/SearchResults/index.ts +3 -0
- package/src/widgets-v2/SearchZeroState/index.ts +15 -0
- package/src/widgets-v2/SuggestionBar/index.ts +6 -0
- 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
|
+
});
|