@envive-ai/react-widgets-v3 0.3.13 → 0.3.15-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. package/dist/CXIntegration/implementations/useHelpScoutUnifiedCXButton.cjs +65 -0
  2. package/dist/CXIntegration/implementations/useHelpScoutUnifiedCXButton.js +64 -0
  3. package/dist/CXIntegration/implementations/useTalkdeskUnifiedCXButton.cjs +64 -0
  4. package/dist/CXIntegration/implementations/useTalkdeskUnifiedCXButton.js +63 -0
  5. package/dist/CXIntegration/types.cjs +2 -0
  6. package/dist/CXIntegration/types.js +2 -0
  7. package/dist/CXIntegration/utils/functions.cjs +4 -0
  8. package/dist/CXIntegration/utils/functions.js +4 -0
  9. package/dist/hocs/withBaseWidget/types.d.cts +3 -3
  10. package/dist/hocs/withBaseWidget/types.d.ts +5 -3
  11. package/dist/hocs/withBaseWidget/withBaseWidget.d.cts +2 -2
  12. package/dist/hocs/withBaseWidget/withBaseWidget.d.ts +2 -2
  13. package/dist/hooks/dist/application/models/api/widgetText.d.cts +8 -0
  14. package/dist/hooks/dist/contexts/hardcopyContext/hardcopyContext.d.cts +12 -0
  15. package/dist/hooks/dist/contexts/types.d.cts +38 -0
  16. package/dist/hooks/dist/contexts/typesV3.d.cts +239 -0
  17. package/dist/hooks/dist/services/amplitudeService/eventNames.d.cts +43 -0
  18. package/dist/hooks/dist/types/customerService.d.cts +21 -0
  19. package/dist/packages/hooks/dist/application/models/api/orgConfigResults.d.ts +1 -0
  20. package/dist/packages/hooks/dist/application/models/api/widgetText.d.ts +8 -0
  21. package/dist/packages/hooks/dist/application/models/frontendConfig.d.ts +1 -0
  22. package/dist/packages/hooks/dist/contexts/amplitudeContext/amplitudeContext.d.ts +2 -0
  23. package/dist/packages/hooks/dist/contexts/amplitudeContext/index.d.ts +2 -0
  24. package/dist/packages/hooks/dist/contexts/featureFlagServiceContext/featureFlagServiceContext.d.ts +2 -0
  25. package/dist/packages/hooks/dist/contexts/hardcopyContext/hardcopyContext.d.ts +14 -0
  26. package/dist/packages/hooks/dist/contexts/hardcopyContext/index.d.ts +1 -0
  27. package/dist/packages/hooks/dist/contexts/types.d.ts +42 -0
  28. package/dist/packages/hooks/dist/contexts/typesV3.d.ts +239 -0
  29. package/dist/packages/hooks/dist/services/amplitudeService/amplitudeService.d.ts +1 -0
  30. package/dist/packages/hooks/dist/services/amplitudeService/eventNames.d.ts +43 -0
  31. package/dist/packages/hooks/dist/types/customerService.d.ts +21 -0
  32. package/dist/packages/widgets/dist/SearchResults/SearchResults.d.ts +3 -2
  33. package/dist/packages/widgets/dist/SearchZeroState/SearchZeroStateWidget.d.ts +2 -2
  34. package/dist/packages/widgets/dist/SearchZeroState/index.d.ts +2 -1
  35. package/dist/packages/widgets/dist/SearchZeroState/types.d.ts +2 -2
  36. package/dist/packages/widgets/dist/SuggestionBar/SuggestionBar.d.ts +3 -3
  37. package/dist/packages/widgets/dist/SuggestionButtonContainer/index.d.ts +2 -0
  38. package/dist/packages/widgets/dist/SuggestionButtonContainer/types.d.ts +3 -2
  39. package/dist/packages/widgets/dist/packages/hooks/dist/application/models/api/response.d.ts +14 -0
  40. package/dist/packages/widgets/dist/packages/hooks/dist/application/models/api/search.d.ts +15 -0
  41. package/dist/packages/widgets/dist/packages/hooks/dist/application/models/utilityTypes/camelCase.d.ts +73 -0
  42. package/dist/packages/widgets/dist/packages/hooks/dist/application/models/utilityTypes/camelCasedPropertiesDeep.d.ts +61 -0
  43. package/dist/packages/widgets/dist/packages/hooks/dist/application/models/utilityTypes/internal.d.ts +25 -0
  44. package/dist/packages/widgets/dist/packages/hooks/dist/application/models/utilityTypes/splitWords.d.ts +35 -0
  45. package/dist/packages/widgets/dist/packages/hooks/dist/application/models/utilityTypes/trim.d.ts +32 -0
  46. package/dist/packages/widgets/dist/packages/hooks/dist/application/models/utilityTypes/unknownArray.d.ts +32 -0
  47. package/dist/packages/widgets/dist/packages/hooks/dist/application/models/variantInfo/variantInfo.d.ts +1 -0
  48. package/dist/packages/widgets/dist/packages/hooks/dist/atoms/search/searchAPI.d.ts +15 -0
  49. package/dist/packages/widgets/dist/packages/hooks/dist/contexts/hardcopyContext/hardcopyContext.d.ts +1 -0
  50. package/dist/packages/widgets/dist/packages/hooks/dist/contexts/types.d.ts +63 -0
  51. package/dist/packages/widgets/dist/packages/hooks/dist/hooks/Search/useSearch.d.ts +60 -0
  52. package/dist/packages/widgets/dist/packages/hooks/dist/hooks/utils.d.ts +13 -0
  53. package/dist/packages/widgets/dist/packages/hooks/dist/types/OrgInfo.d.ts +1 -0
  54. package/dist/packages/widgets/dist/packages/hooks/dist/types/index.d.ts +1 -0
  55. package/dist/packages/widgets/dist/packages/hooks/dist/types/search-filter-types.d.ts +28 -0
  56. package/dist/packages/widgets/dist/packages/hooks/dist/types/test-types.d.ts +10 -0
  57. package/dist/widgets/ChatPreviewComparisonWidget/ChatPreviewComparisonWidget.d.cts +3 -3
  58. package/dist/widgets/ChatPreviewLoadingWidget/ChatPreviewLoadingWidget.d.cts +3 -3
  59. package/dist/widgets/ChatPreviewLoadingWidget/ChatPreviewLoadingWidget.d.ts +3 -3
  60. package/dist/widgets/ChatPreviewWidget/ChatPreviewWidget.d.cts +3 -3
  61. package/dist/widgets/ChatPreviewWidget/ChatPreviewWidget.d.ts +3 -3
  62. package/dist/widgets/FloatingChatWidget/FloatingChatWidget.cjs +29 -5
  63. package/dist/widgets/FloatingChatWidget/FloatingChatWidget.d.cts +2 -2
  64. package/dist/widgets/FloatingChatWidget/FloatingChatWidget.d.ts +2 -2
  65. package/dist/widgets/FloatingChatWidget/FloatingChatWidget.js +30 -6
  66. package/dist/widgets/FullPageSalesAgentWidget/FullPageSalesAgentWidget.d.cts +2 -2
  67. package/dist/widgets/FullPageSalesAgentWidget/FullPageSalesAgentWidget.d.ts +2 -2
  68. package/dist/widgets/ProductCardWidget/ProductCardWidget.cjs +19 -3
  69. package/dist/widgets/ProductCardWidget/ProductCardWidget.d.cts +2 -2
  70. package/dist/widgets/ProductCardWidget/ProductCardWidget.d.ts +2 -2
  71. package/dist/widgets/ProductCardWidget/ProductCardWidget.js +20 -4
  72. package/dist/widgets/PromptButtonCarouselWithImageWidget/PromptButtonCarouselWithImageWidget.cjs +10 -0
  73. package/dist/widgets/PromptButtonCarouselWithImageWidget/PromptButtonCarouselWithImageWidget.d.cts +3 -3
  74. package/dist/widgets/PromptButtonCarouselWithImageWidget/PromptButtonCarouselWithImageWidget.d.ts +3 -3
  75. package/dist/widgets/PromptButtonCarouselWithImageWidget/PromptButtonCarouselWithImageWidget.js +12 -2
  76. package/dist/widgets/PromptCarouselWidget/PromptCarouselWidget.cjs +15 -10
  77. package/dist/widgets/PromptCarouselWidget/PromptCarouselWidget.d.cts +2 -2
  78. package/dist/widgets/PromptCarouselWidget/PromptCarouselWidget.d.ts +2 -2
  79. package/dist/widgets/PromptCarouselWidget/PromptCarouselWidget.js +16 -11
  80. package/dist/widgets/SocialProofFlowWidget/SocialProofFlowWidget.d.cts +2 -2
  81. package/dist/widgets/SocialProofFlowWidget/SocialProofFlowWidget.d.ts +2 -2
  82. package/dist/widgets/SocialProofWidget/SocialProofWidget.cjs +10 -0
  83. package/dist/widgets/SocialProofWidget/SocialProofWidget.d.cts +3 -3
  84. package/dist/widgets/SocialProofWidget/SocialProofWidget.d.ts +3 -3
  85. package/dist/widgets/SocialProofWidget/SocialProofWidget.js +12 -2
  86. package/dist/widgets/TitledPromptCarouselWidget/TitledPromptCarouselWidget.cjs +11 -0
  87. package/dist/widgets/TitledPromptCarouselWidget/TitledPromptCarouselWidget.d.cts +2 -2
  88. package/dist/widgets/TitledPromptCarouselWidget/TitledPromptCarouselWidget.d.ts +2 -2
  89. package/dist/widgets/TitledPromptCarouselWidget/TitledPromptCarouselWidget.js +12 -1
  90. package/dist/widgets/TypingAnimationFlowWidget/TypingAnimationFlowWidget.d.cts +2 -2
  91. package/dist/widgets/TypingAnimationFlowWidget/TypingAnimationFlowWidget.d.ts +2 -2
  92. package/dist/widgets/TypingAnimationWidget/TypingAnimationWidget.cjs +23 -3
  93. package/dist/widgets/TypingAnimationWidget/TypingAnimationWidget.d.cts +3 -3
  94. package/dist/widgets/TypingAnimationWidget/TypingAnimationWidget.d.ts +3 -3
  95. package/dist/widgets/TypingAnimationWidget/TypingAnimationWidget.js +24 -4
  96. package/dist/widgets/dist/SearchResults/SearchResults.d.cts +3 -2
  97. package/dist/widgets/dist/SearchZeroState/SearchZeroStateWidget.d.cts +2 -2
  98. package/dist/widgets/dist/SearchZeroState/types.d.cts +2 -2
  99. package/dist/widgets/dist/SuggestionBar/SuggestionBar.d.cts +3 -3
  100. package/dist/widgets/dist/SuggestionButtonContainer/types.d.cts +3 -2
  101. package/dist/widgets/dist/packages/hooks/dist/application/models/api/response.d.cts +14 -0
  102. package/dist/widgets/dist/packages/hooks/dist/application/models/api/search.d.cts +15 -0
  103. package/dist/widgets/dist/packages/hooks/dist/application/models/utilityTypes/camelCase.d.cts +73 -0
  104. package/dist/widgets/dist/packages/hooks/dist/application/models/utilityTypes/camelCasedPropertiesDeep.d.cts +61 -0
  105. package/dist/widgets/dist/packages/hooks/dist/application/models/utilityTypes/internal.d.cts +25 -0
  106. package/dist/widgets/dist/packages/hooks/dist/application/models/utilityTypes/splitWords.d.cts +35 -0
  107. package/dist/widgets/dist/packages/hooks/dist/application/models/utilityTypes/trim.d.cts +32 -0
  108. package/dist/widgets/dist/packages/hooks/dist/application/models/utilityTypes/unknownArray.d.cts +32 -0
  109. package/dist/widgets/dist/packages/hooks/dist/atoms/search/searchAPI.d.cts +14 -0
  110. package/dist/widgets/dist/packages/hooks/dist/contexts/types.d.cts +61 -0
  111. package/dist/widgets/dist/packages/hooks/dist/hooks/Search/useSearch.d.cts +60 -0
  112. package/dist/widgets/dist/packages/hooks/dist/hooks/utils.d.cts +12 -0
  113. package/dist/widgets/dist/packages/hooks/dist/types/search-filter-types.d.cts +28 -0
  114. package/dist/widgets/dist/packages/hooks/dist/types/test-types.d.cts +10 -0
  115. package/dist/widgets/utils/functions.cjs +9 -0
  116. package/dist/widgets/utils/functions.js +9 -1
  117. package/dist/widgets-v2/SearchZeroState/index.d.cts +2 -1
  118. package/dist/widgets-v2/SearchZeroState/index.d.ts +2 -1
  119. package/dist/widgets-v2/SuggestionButtonContainer/index.d.ts +1 -0
  120. package/package.json +1 -1
  121. package/src/CXIntegration/implementations/useHelpScoutUnifiedCXButton.ts +108 -0
  122. package/src/CXIntegration/implementations/useTalkdeskUnifiedCXButton.ts +94 -0
  123. package/src/CXIntegration/types.ts +2 -0
  124. package/src/CXIntegration/utils/functions.ts +8 -0
  125. package/src/hocs/withBaseWidget/__tests__/withBaseWidget.test.tsx +15 -3
  126. package/src/widgets/ChatPreviewWidget/__tests__/ChatPreviewWidget.test.tsx +114 -0
  127. package/src/widgets/FloatingChatWidget/FloatingChatWidget.tsx +56 -15
  128. package/src/widgets/FloatingChatWidget/__tests__/FloatingChatWidget.test.tsx +119 -0
  129. package/src/widgets/ProductCardWidget/ProductCardWidget.tsx +15 -3
  130. package/src/widgets/ProductCardWidget/__tests__/ProductCardWidget.test.tsx +144 -0
  131. package/src/widgets/PromptButtonCarouselWithImageWidget/PromptButtonCarouselWithImageWidget.tsx +12 -1
  132. package/src/widgets/PromptButtonCarouselWithImageWidget/__tests__/PromptButtonCarouselWithImageWidget.test.tsx +179 -0
  133. package/src/widgets/PromptCarouselWidget/PromptCarouselWidget.tsx +33 -23
  134. package/src/widgets/PromptCarouselWidget/__tests__/PromptCarouselWidget.test.tsx +150 -0
  135. package/src/widgets/SocialProofWidget/SocialProofWidget.tsx +12 -1
  136. package/src/widgets/SocialProofWidget/__tests__/SocialProofWidget.test.tsx +184 -0
  137. package/src/widgets/TitledPromptCarouselWidget/TitledPromptCarouselWidget.tsx +12 -0
  138. package/src/widgets/TitledPromptCarouselWidget/__tests__/TitledPromptCarouselWidget.test.tsx +150 -0
  139. package/src/widgets/TypingAnimationWidget/TypingAnimationWidget.tsx +19 -2
  140. package/src/widgets/TypingAnimationWidget/__tests__/TypingAnimationWidget.test.tsx +163 -0
  141. package/src/widgets/__tests__/testUtils.tsx +63 -0
  142. package/src/widgets/__tests__/trackEventCanary.test.ts +45 -0
  143. package/src/widgets/utils/functions.ts +16 -0
@@ -0,0 +1,61 @@
1
+ //#region ../widgets/dist/packages/hooks/dist/contexts/types.d.ts
2
+
3
+ type ProductCardVariant = 'filled' | 'bordered' | 'minimal' | 'transparentBordered';
4
+ type ProductCardHoverVariant = 'backgroundDark' | 'none';
5
+ type ProductCardLayoutVariant = 'normal' | 'tall' | 'square';
6
+ interface ProductCardConfig {
7
+ variant: ProductCardVariant;
8
+ hoverVariant: ProductCardHoverVariant;
9
+ layoutVariant: ProductCardLayoutVariant;
10
+ pricePrefix?: string;
11
+ }
12
+ type SuggestionButtonVariant = 'outlined' | 'outlinedLight' | 'primary' | 'secondary' | 'tertiary' | 'dark' | 'darkAccent' | 'darkPrimary' | 'secondaryDark' | 'secondaryAccent' | 'transparent' | 'transparentDark' | 'lightDark';
13
+ interface SuggestionButtonConfig {
14
+ variant?: SuggestionButtonVariant;
15
+ hoverVariant?: SuggestionButtonVariant;
16
+ answerVariant?: SuggestionButtonVariant;
17
+ borderRadius?: 'sm' | 'md' | 'lg';
18
+ }
19
+ type SearchZeroStateVariant = 'backgroundTertiary' | 'backgroundDark' | 'backgroundPrimary';
20
+ type SearchZeroStateLayout = 'input' | 'icon';
21
+ type SearchInputVariant = 'standard';
22
+ type SearchIconVariant = 'thin' | 'bold';
23
+ declare enum WidgetType {
24
+ ChatPreview = "ChatPreview",
25
+ SocialProofV2 = "SocialProofV2",
26
+ ChatPreviewV2 = "ChatPreviewV2",
27
+ ChatPreviewIsLoading = "ChatPreviewIsLoading",
28
+ ChatPreviewPostInteraction = "ChatPreviewPostInteraction",
29
+ ChatPreviewProductComparison = "ChatPreviewProductComparison",
30
+ SuggestionBar = "SuggestionBar",
31
+ SuggestionBarV2 = "SuggestionBarV2",
32
+ ImagePromptCard = "ImagePromptCard",
33
+ ImageBanner = "ImageBanner",
34
+ SingleImagePrompt = "SingleImagePrompt",
35
+ SearchPrompt = "SearchPrompt",
36
+ SearchZeroStateEntryPoint = "SearchZeroStateEntryPoint",
37
+ SearchResultsEntryPoint = "SearchResultsEntryPoint",
38
+ SearchOverlayHost = "SearchOverlayHost",
39
+ NoOp = "NoOp",
40
+ }
41
+ interface BaseWidgetConfig<T extends WidgetType> {
42
+ widgetConfigId: string;
43
+ type: T;
44
+ contentId?: string;
45
+ }
46
+ interface SearchEntryPointWidgetConfig extends BaseWidgetConfig<WidgetType.SearchZeroStateEntryPoint> {
47
+ searchZeroStateVariant: SearchZeroStateVariant;
48
+ searchInputVariant: SearchInputVariant;
49
+ searchIconVariant?: SearchIconVariant;
50
+ searchIconSize?: number;
51
+ suggestionButtonConfig: SuggestionButtonConfig;
52
+ layout: SearchZeroStateLayout;
53
+ searchBoxPlaceholder: string;
54
+ animationSpeed?: 'standard' | 'slow' | 'none';
55
+ compactLabel?: string;
56
+ initialSuggestions?: string[];
57
+ usingPortal?: boolean;
58
+ includeSubtitle?: boolean;
59
+ }
60
+ //#endregion
61
+ export { type ProductCardConfig, type SearchEntryPointWidgetConfig, SearchInputVariant, type SuggestionButtonVariant };
@@ -0,0 +1,60 @@
1
+ import { ProductCardConfig } from "../../contexts/types.cjs";
2
+ import { SearchResponseProductAttributes } from "../../application/models/api/response.cjs";
3
+ import { SearchResult } from "../../application/models/api/search.cjs";
4
+ import { SelectedFilterOption } from "../../atoms/search/searchAPI.cjs";
5
+ import { SearchFilterDatum, SelectFilterItem } from "../../types/search-filter-types.cjs";
6
+ import { SearchResultsState } from "../utils.cjs";
7
+ import { SearchResponseProduct } from "@spiffy-ai/commerce-api-client";
8
+
9
+ //#region ../widgets/dist/packages/hooks/dist/hooks/Search/useSearch.d.ts
10
+
11
+ //#region ../hooks/dist/hooks/Search/useSearch.d.ts
12
+ //#region src/hooks/Search/useSearch.d.ts
13
+ interface SearchResultsHocProps {
14
+ searchData: SearchResult | null;
15
+ searchResponseId: string;
16
+ merchantShortName: string;
17
+ productCardConfig: ProductCardConfig;
18
+ productList: SearchResponseProduct[];
19
+ autocompleteResults: string[];
20
+ searchFilters: SearchFilterDatum[];
21
+ availableDynamicFilters: {
22
+ name: string;
23
+ displayName: string;
24
+ }[];
25
+ selectedFilterOptions: SelectedFilterOption[];
26
+ recommendedProducts: SearchResponseProductAttributes['attributes'][];
27
+ recommendedProductsHeading: string;
28
+ searchOverlayHeading: string;
29
+ searchText: string;
30
+ query: string;
31
+ searchResultsState: SearchResultsState;
32
+ isLoadingSearch: boolean;
33
+ isFilterOpen: boolean;
34
+ shouldShowAutocomplete: boolean;
35
+ focusedIndex: number;
36
+ focusedOptionId: string | undefined;
37
+ filterButtonText: string;
38
+ onSearchInputChange: (value: string) => void;
39
+ onSubmitSearch: () => void;
40
+ onAutocompleteSelect: (suggestion: string) => void;
41
+ onKeyDown: (event: React.KeyboardEvent<HTMLInputElement>) => void;
42
+ onSearchInputFocus: () => void;
43
+ onSearchInputBlur: () => void;
44
+ onToggleDynamicFilter: ({
45
+ filter,
46
+ dynamicFilterDisplayName
47
+ }: {
48
+ filter: string;
49
+ dynamicFilterDisplayName: string;
50
+ }) => void;
51
+ onSelectFilterItem: SelectFilterItem;
52
+ onRemoveFilter: (filter: SelectedFilterOption) => void;
53
+ onClearAllFilters: () => void;
54
+ setIsFilterOpen: (isFilterOpen: boolean) => void;
55
+ resetSearch: () => void;
56
+ searchResultsRef: React.RefObject<HTMLDivElement>;
57
+ }
58
+ //#endregion
59
+ //#endregion
60
+ export { SearchResultsHocProps };
@@ -0,0 +1,12 @@
1
+ //#region ../widgets/dist/packages/hooks/dist/hooks/utils.d.ts
2
+
3
+ //#region ../hooks/dist/hooks/utils.d.ts
4
+
5
+ declare enum SearchResultsState {
6
+ Loading = 0,
7
+ Results = 1,
8
+ NoResults = 2,
9
+ }
10
+ //#endregion
11
+ //#endregion
12
+ export { SearchResultsState };
@@ -0,0 +1,28 @@
1
+ //#region ../widgets/dist/packages/hooks/dist/types/search-filter-types.d.ts
2
+ //#region ../hooks/dist/types/search-filter-types.d.ts
3
+
4
+ type SelectFilterItem = ({
5
+ filterId,
6
+ filterItemId,
7
+ isSelected,
8
+ displayName
9
+ }: {
10
+ filterId: string;
11
+ filterItemId: string;
12
+ isSelected: boolean;
13
+ displayName: string;
14
+ }) => void;
15
+ type SearchFilterDatum = {
16
+ filterId: string;
17
+ displayName: string;
18
+ items: SearchFilterItemDatum[];
19
+ };
20
+ type SearchFilterItemDatum = {
21
+ filterItemId: string;
22
+ displayName: string;
23
+ productCount: number;
24
+ isSelected: boolean;
25
+ };
26
+ //#endregion
27
+ //#endregion
28
+ export { SearchFilterDatum, SelectFilterItem };
@@ -0,0 +1,10 @@
1
+ //#region ../widgets/dist/packages/hooks/dist/types/test-types.d.ts
2
+ //#region ../hooks/dist/types/test-types.d.ts
3
+ //#region src/types/test-types.d.ts
4
+ interface TestProps {
5
+ dataTestId?: string;
6
+ }
7
+ //#endregion
8
+ //#endregion
9
+ //#endregion
10
+ export { TestProps };
@@ -44,11 +44,20 @@ const isProductComparison = (lastMsg) => {
44
44
  if (lastMsg) return lastMsg?.filter((msg) => msg.type === __envive_ai_react_hooks_application_models.MessageType.Product).length === 2;
45
45
  return (0, __envive_ai_react_hooks_atoms_atomStore.getAtomStore)().get(__envive_ai_react_hooks_atoms_chat.lastAssistantMessageAtom)?.filter((msg) => msg.type === __envive_ai_react_hooks_application_models.MessageType.Product).length === 2;
46
46
  };
47
+ /** Finds the string_id (id) from raw widget text values for the given display text */
48
+ const getStringIdForText = (rawValues, text) => {
49
+ if (!rawValues) return void 0;
50
+ for (const raw of Object.values(rawValues)) {
51
+ const found = (Array.isArray(raw) ? raw : [raw]).find((s) => s.value === text);
52
+ if (found) return found.id;
53
+ }
54
+ };
47
55
 
48
56
  //#endregion
49
57
  exports.getMessageText = getMessageText;
50
58
  exports.getProductImageUrl = getProductImageUrl;
51
59
  exports.getRecentProductImageUrls = getRecentProductImageUrls;
60
+ exports.getStringIdForText = getStringIdForText;
52
61
  exports.isLoading = isLoading;
53
62
  exports.isProductComparison = isProductComparison;
54
63
  exports.userHasInteracted = userHasInteracted;
@@ -43,6 +43,14 @@ const isProductComparison = (lastMsg) => {
43
43
  if (lastMsg) return lastMsg?.filter((msg) => msg.type === MessageType.Product).length === 2;
44
44
  return getAtomStore().get(lastAssistantMessageAtom)?.filter((msg) => msg.type === MessageType.Product).length === 2;
45
45
  };
46
+ /** Finds the string_id (id) from raw widget text values for the given display text */
47
+ const getStringIdForText = (rawValues, text) => {
48
+ if (!rawValues) return void 0;
49
+ for (const raw of Object.values(rawValues)) {
50
+ const found = (Array.isArray(raw) ? raw : [raw]).find((s) => s.value === text);
51
+ if (found) return found.id;
52
+ }
53
+ };
46
54
 
47
55
  //#endregion
48
- export { getMessageText, getProductImageUrl, getRecentProductImageUrls, isLoading, isProductComparison, userHasInteracted, userHasNotInteracted };
56
+ export { getMessageText, getProductImageUrl, getRecentProductImageUrls, getStringIdForText, isLoading, isProductComparison, userHasInteracted, userHasNotInteracted };
@@ -1,4 +1,5 @@
1
- import { SearchEntryPointWidgetConfig, SearchInputVariant, SearchZeroStateProps } from "../../widgets/dist/SearchZeroState/types.cjs";
1
+ import { SearchEntryPointWidgetConfig, SearchInputVariant } from "../../widgets/dist/packages/hooks/dist/contexts/types.cjs";
2
+ import { SearchZeroStateProps } from "../../widgets/dist/SearchZeroState/types.cjs";
2
3
  import { SearchZeroState } from "../../widgets/dist/SearchZeroState/SearchZeroState.cjs";
3
4
  import { SearchZeroStateWidget } from "../../widgets/dist/SearchZeroState/SearchZeroStateWidget.cjs";
4
5
  export { SearchEntryPointWidgetConfig, SearchInputVariant, SearchZeroState, SearchZeroStateProps, SearchZeroStateWidget };
@@ -1,4 +1,5 @@
1
- import { SearchEntryPointWidgetConfig, SearchInputVariant, SearchZeroStateProps } from "../../packages/widgets/dist/SearchZeroState/types.js";
1
+ import { SearchEntryPointWidgetConfig, SearchInputVariant } from "../../packages/widgets/dist/packages/hooks/dist/contexts/types.js";
2
+ import { SearchZeroStateProps } from "../../packages/widgets/dist/SearchZeroState/types.js";
2
3
  import { SearchZeroState } from "../../packages/widgets/dist/SearchZeroState/SearchZeroState.js";
3
4
  import { SearchZeroStateWidget } from "../../packages/widgets/dist/SearchZeroState/SearchZeroStateWidget.js";
4
5
  import "../../packages/widgets/dist/SearchZeroState/index.js";
@@ -1,3 +1,4 @@
1
1
  import { SuggestionButtonContainerProps } from "../../packages/widgets/dist/SuggestionButtonContainer/types.js";
2
2
  import { SuggestionButtonContainer } from "../../packages/widgets/dist/SuggestionButtonContainer/SuggestionButtonContainer.js";
3
+ import "../../packages/widgets/dist/SuggestionButtonContainer/index.js";
3
4
  export { SuggestionButtonContainer, SuggestionButtonContainerProps };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@envive-ai/react-widgets-v3",
3
- "version": "0.3.13",
3
+ "version": "0.3.15-beta.0",
4
4
  "description": "React widget library v3 for Envive services.",
5
5
  "keywords": [
6
6
  "react",
@@ -0,0 +1,108 @@
1
+ import { SelectorFactory } from '@envive-ai/react-hooks/application/utils';
2
+ import { useElementObserver } from '@envive-ai/react-hooks/hooks/ElementObserver';
3
+ import { FLOATING_BUTTON_ID } from '../../widgets/FloatingChatWidget/constants';
4
+ import { CustomerServiceImplProps, UnifiedCXButton } from '../types';
5
+
6
+ const BEACON_CONTAINER_ID = 'beacon-container';
7
+
8
+ interface UseHelpScoutUnifiedCXButtonProps extends CustomerServiceImplProps {}
9
+
10
+ export const useHelpScoutUnifiedCXButton = ({
11
+ onSwitchToAgent,
12
+ suppressMerchantButton,
13
+ }: UseHelpScoutUnifiedCXButtonProps): UnifiedCXButton => {
14
+ const beaconContainer = useElementObserver(SelectorFactory.id(BEACON_CONTAINER_ID));
15
+ const enviveFloatingButton = useElementObserver(SelectorFactory.id(FLOATING_BUTTON_ID));
16
+
17
+ const toggle = () => {
18
+ onSwitchToAgent();
19
+
20
+ // Try using the Help Scout Beacon API first
21
+ if (typeof (window as any).Beacon !== 'undefined') {
22
+ (window as any).Beacon('open');
23
+ } else {
24
+ // Fallback: Find and click the FAB button
25
+ const container = document.getElementById(BEACON_CONTAINER_ID);
26
+ const fabButton = container?.querySelector('.BeaconFabButtonFrame') as HTMLElement;
27
+ if (fabButton) {
28
+ fabButton.style.display = '';
29
+ fabButton.click();
30
+ }
31
+ }
32
+
33
+ if (suppressMerchantButton) {
34
+ enviveFloatingButton.hide();
35
+ }
36
+ };
37
+
38
+ const isHelpScoutButtonEnabled = () => {
39
+ // Check for the main Help Scout Beacon container
40
+ const beaconContainerEl = document.getElementById('beacon-container');
41
+ if (!beaconContainerEl) return false;
42
+
43
+ // Check for the FAB button (Floating Action Button) or the global Beacon API
44
+ const fabButton = beaconContainerEl.querySelector('.BeaconFabButtonFrame');
45
+ const hasBeaconAPI = typeof (window as any).Beacon !== 'undefined';
46
+
47
+ return !!(fabButton || hasBeaconAPI);
48
+ };
49
+
50
+ const isSwitchEnabled = () => isHelpScoutButtonEnabled();
51
+
52
+ // Hide Help Scout FAB button when beacon container is added
53
+ beaconContainer.onAdd(() => {
54
+ if (suppressMerchantButton) {
55
+ const fabButton = document
56
+ .getElementById(BEACON_CONTAINER_ID)
57
+ ?.querySelector('.BeaconFabButtonFrame') as HTMLElement;
58
+ if (fabButton) {
59
+ fabButton.style.display = 'none';
60
+ }
61
+ enviveFloatingButton.show();
62
+ }
63
+ });
64
+
65
+ // Observe the beacon container for visibility changes
66
+ // Help Scout Beacon adds/removes classes to show/hide the widget
67
+ beaconContainer.onChange(el => {
68
+ if (el && suppressMerchantButton) {
69
+ // Check if the BeaconContainer widget is open
70
+ const beaconWidget = el.querySelector('.BeaconContainer');
71
+ const fabButton = el.querySelector('.BeaconFabButtonFrame') as HTMLElement;
72
+
73
+ if (beaconWidget && fabButton) {
74
+ const { classList } = beaconWidget;
75
+
76
+ // When beacon is open (has enter-done class)
77
+ if (
78
+ classList.contains('BeaconContainer-enter-done') ||
79
+ classList.contains('BeaconContainer-enter-active')
80
+ ) {
81
+ fabButton.style.display = '';
82
+ enviveFloatingButton.hide();
83
+ }
84
+
85
+ // When beacon is closed (has exit class)
86
+ if (
87
+ classList.contains('BeaconContainer-exit') ||
88
+ classList.contains('BeaconContainer-exit-active')
89
+ ) {
90
+ fabButton.style.display = 'none';
91
+ enviveFloatingButton.show();
92
+ }
93
+ }
94
+ }
95
+ });
96
+
97
+ // Cleanup when container is removed
98
+ beaconContainer.onRemove(() => {
99
+ if (suppressMerchantButton) {
100
+ enviveFloatingButton.show();
101
+ }
102
+ });
103
+
104
+ return {
105
+ toggle,
106
+ isSwitchEnabled,
107
+ };
108
+ };
@@ -0,0 +1,94 @@
1
+ import { SelectorFactory } from '@envive-ai/react-hooks/application/utils';
2
+ import { useElementObserver } from '@envive-ai/react-hooks/hooks/ElementObserver';
3
+ import { FLOATING_BUTTON_ID } from '../../widgets/FloatingChatWidget/constants';
4
+ import { CustomerServiceImplProps, UnifiedCXButton } from '../types';
5
+
6
+ interface UseTalkdeskUnifiedCXButtonProps extends CustomerServiceImplProps {}
7
+
8
+ export const useTalkdeskUnifiedCXButton = ({
9
+ onSwitchToAgent,
10
+ suppressMerchantButton,
11
+ }: UseTalkdeskUnifiedCXButtonProps): UnifiedCXButton => {
12
+ const talkdeskButton = useElementObserver(SelectorFactory.id('talkdesk-chat-widget-trigger'));
13
+ const talkdeskWidget = useElementObserver(SelectorFactory.id('talkdesk-chat-widget'));
14
+ const talkdeskContainer = useElementObserver(SelectorFactory.id('tdWebchat'));
15
+ const enviveFloatingButton = useElementObserver(SelectorFactory.id(FLOATING_BUTTON_ID));
16
+
17
+ const toggle = () => {
18
+ onSwitchToAgent();
19
+ if (suppressMerchantButton) {
20
+ enviveFloatingButton.hide();
21
+ }
22
+ talkdeskButton.show();
23
+ talkdeskButton.fire('click');
24
+ };
25
+
26
+ const isTalkdeskButtonEnabled = () => {
27
+ // Check for the main Talkdesk container
28
+ const talkdeskMainContainer = document.getElementById('tdWebchat');
29
+ if (!talkdeskMainContainer) return false;
30
+
31
+ // Check for the trigger button (it's directly in the DOM, not inside an iframe)
32
+ const talkdeskTriggerButton = document.getElementById('talkdesk-chat-widget-trigger');
33
+ return !!talkdeskTriggerButton;
34
+ };
35
+
36
+ const isSwitchEnabled = () => isTalkdeskButtonEnabled();
37
+
38
+ // Hide Talkdesk button when it's added to DOM (if suppressMerchantButton is enabled)
39
+ talkdeskButton.onAdd(() => {
40
+ if (suppressMerchantButton) {
41
+ talkdeskButton.hide();
42
+ enviveFloatingButton.show();
43
+ }
44
+ });
45
+
46
+ // Observe the widget container (NOT the button) to avoid infinite loops
47
+ talkdeskWidget.onAdd(el => {
48
+ if (el && suppressMerchantButton) {
49
+ const ariaHidden = el.getAttribute('aria-hidden');
50
+
51
+ // Widget is open initially (aria-hidden="false")
52
+ if (ariaHidden === 'false') {
53
+ talkdeskButton.show();
54
+ enviveFloatingButton.hide();
55
+ } else {
56
+ // Widget is closed initially
57
+ talkdeskButton.hide();
58
+ enviveFloatingButton.show();
59
+ }
60
+ }
61
+ });
62
+
63
+ // Track widget visibility changes
64
+ talkdeskWidget.onChange(el => {
65
+ if (el && suppressMerchantButton) {
66
+ const ariaHidden = el.getAttribute('aria-hidden');
67
+
68
+ // Widget is open (aria-hidden="false")
69
+ if (ariaHidden === 'false') {
70
+ talkdeskButton.show();
71
+ enviveFloatingButton.hide();
72
+ }
73
+
74
+ // Widget is closed (aria-hidden="true")
75
+ if (ariaHidden === 'true') {
76
+ talkdeskButton.hide();
77
+ enviveFloatingButton.show();
78
+ }
79
+ }
80
+ });
81
+
82
+ // Cleanup when container is removed
83
+ talkdeskContainer.onRemove(() => {
84
+ if (suppressMerchantButton) {
85
+ talkdeskButton.hide();
86
+ enviveFloatingButton.show();
87
+ }
88
+ });
89
+
90
+ return {
91
+ toggle,
92
+ isSwitchEnabled,
93
+ };
94
+ };
@@ -9,6 +9,8 @@ export enum CustomerServiceType {
9
9
  gladly = 'gladly',
10
10
  richpanel = 'richpanel',
11
11
  zendesk = 'zendesk',
12
+ helpscout = 'helpscout',
13
+ talkdesk = 'talkdesk',
12
14
  eightByEight = '8x8',
13
15
  unsupported = 'unsupported',
14
16
  }
@@ -10,6 +10,8 @@ import { useReDoUnifiedCXButton } from '../implementations/useReDoUnifiedCXButto
10
10
  import { useRichpanelUnifiedCXButton } from '../implementations/useRichpanelUnifiedCXButton';
11
11
  import { useZendeskUnifiedCXButton } from '../implementations/useZendeskUnifiedCXButton';
12
12
  import { useKustomerUnifiedCXButton } from '../implementations/useKustomerUnifiedCXButton';
13
+ import { useHelpScoutUnifiedCXButton } from '../implementations/useHelpScoutUnifiedCXButton';
14
+ import { useTalkdeskUnifiedCXButton } from '../implementations/useTalkdeskUnifiedCXButton';
13
15
  import { useEightByEightUnifiedCXButton } from '../implementations/useEightByEightUnifiedCXButton';
14
16
  import { useDefaultUnifiedCXButton } from '../implementations/useDefaultUnifiedCXButton';
15
17
 
@@ -46,6 +48,12 @@ export const findCustomerServiceImpl = (
46
48
  if (provider === CustomerServiceType.zendesk) {
47
49
  return useZendeskUnifiedCXButton;
48
50
  }
51
+ if (provider === CustomerServiceType.helpscout) {
52
+ return useHelpScoutUnifiedCXButton;
53
+ }
54
+ if (provider === CustomerServiceType.talkdesk) {
55
+ return useTalkdeskUnifiedCXButton;
56
+ }
49
57
  if (provider === CustomerServiceType.eightByEight) {
50
58
  return useEightByEightUnifiedCXButton;
51
59
  }
@@ -66,7 +66,7 @@ class MockIntersectionObserver implements IntersectionObserver {
66
66
  const mockTrackEvent = vi.fn();
67
67
  const mockGetHardcopy = vi.fn();
68
68
 
69
- vi.mock('src/contexts/amplitudeContext/amplitudeContext', () => ({
69
+ vi.mock('@envive-ai/react-hooks/contexts/amplitudeContext', () => ({
70
70
  useAmplitude: () => ({
71
71
  trackEvent: mockTrackEvent,
72
72
  isReady: true,
@@ -77,13 +77,13 @@ vi.mock('src/contexts/amplitudeContext/amplitudeContext', () => ({
77
77
  },
78
78
  }));
79
79
 
80
- vi.mock('src/contexts/hardcopyContext', () => ({
80
+ vi.mock('@envive-ai/react-hooks/contexts/hardcopyContext', () => ({
81
81
  useHardcopy: () => ({
82
82
  getHardcopy: mockGetHardcopy,
83
83
  }),
84
84
  }));
85
85
 
86
- vi.mock('src/contexts/pageContext', () => ({
86
+ vi.mock('@envive-ai/react-hooks/contexts/pageContext', () => ({
87
87
  usePage: () => ({
88
88
  userEvent: {
89
89
  id: 'test-user-event-id',
@@ -94,6 +94,18 @@ vi.mock('src/contexts/pageContext', () => ({
94
94
  }),
95
95
  }));
96
96
 
97
+ vi.mock('@envive-ai/react-hooks/contexts/widgetConfigContext', () => ({
98
+ useWidgetConfig: () => ({
99
+ getWidgetConfig: vi.fn().mockResolvedValue({}),
100
+ }),
101
+ }));
102
+
103
+ vi.mock('@envive-ai/react-hooks/contexts/uiConfigContext', () => ({
104
+ useUiConfig: () => ({
105
+ getUiConfig: vi.fn().mockResolvedValue({}),
106
+ }),
107
+ }));
108
+
97
109
  // Test widget component
98
110
  interface TestWidgetProps extends BaseWidgetProps {
99
111
  testId?: string;
@@ -0,0 +1,114 @@
1
+ import { render, waitFor } from '@testing-library/react';
2
+ import { SpiffyMetricsEventName } from '@envive-ai/react-hooks/contexts/amplitudeContext';
3
+ import { WidgetTypeV3 } from '@envive-ai/react-hooks/contexts/typesV3';
4
+ import { UserEvent, UserEventCategory } from '@spiffy-ai/commerce-api-client';
5
+ import { ChatPreviewWidget } from '../ChatPreviewWidget';
6
+
7
+ const mockTrackEvent = vi.fn();
8
+ const mockGetHardcopy = vi.fn();
9
+
10
+ vi.mock('@envive-ai/react-toolkit-v3/ChatPreview', () => ({
11
+ ChatPreview: () => <div data-testid="chat-preview-mock">Chat Preview</div>,
12
+ }));
13
+
14
+ vi.mock('@envive-ai/react-hooks/contexts/amplitudeContext', () => ({
15
+ useAmplitude: () => ({
16
+ trackEvent: mockTrackEvent,
17
+ isReady: true,
18
+ }),
19
+ SpiffyMetricsEventName: {
20
+ ChatComponentVisible: 'Chat Component Visible',
21
+ SearchComponentVisible: 'Search Component Visible',
22
+ },
23
+ }));
24
+
25
+ vi.mock('@envive-ai/react-hooks/contexts/hardcopyContext', () => ({
26
+ useHardcopy: () => ({
27
+ getHardcopy: mockGetHardcopy,
28
+ }),
29
+ }));
30
+
31
+ vi.mock('@envive-ai/react-hooks/contexts/pageContext', () => ({
32
+ usePage: () => ({
33
+ userEvent: {
34
+ id: 'test-user-event-id',
35
+ category: UserEventCategory.PageVisit,
36
+ created_at: '2025-01-01T00:00:00Z',
37
+ event_id: 'test-event-id',
38
+ } as UserEvent,
39
+ }),
40
+ }));
41
+
42
+ vi.mock('@envive-ai/react-hooks/contexts/widgetConfigContext', () => ({
43
+ useWidgetConfig: () => ({
44
+ getWidgetConfig: vi.fn().mockResolvedValue({}),
45
+ }),
46
+ }));
47
+
48
+ vi.mock('@envive-ai/react-hooks/contexts/uiConfigContext', () => ({
49
+ useUiConfig: () => ({
50
+ getUiConfig: vi.fn().mockResolvedValue({}),
51
+ }),
52
+ }));
53
+
54
+ vi.mock('@envive-ai/react-hooks/contexts/salesAgentContext', () => ({
55
+ useSalesAgent: () => ({
56
+ onSuggestionClicked: vi.fn(),
57
+ }),
58
+ }));
59
+
60
+ vi.mock('@envive-ai/react-hooks/hooks/ChatToggle', () => ({
61
+ useChatToggle: () => ({
62
+ openChat: vi.fn(),
63
+ }),
64
+ }));
65
+
66
+ vi.mock('@envive-ai/react-hooks/atoms/chat', () => ({
67
+ chatAtom: {},
68
+ lastAssistantMessageAtom: {},
69
+ }));
70
+
71
+ vi.mock('jotai', async importOriginal => {
72
+ const actual = await importOriginal();
73
+ return {
74
+ ...actual,
75
+ useAtomValue: vi
76
+ .fn()
77
+ .mockReturnValueOnce([])
78
+ .mockReturnValueOnce({ messages: [], suggestions: [] })
79
+ .mockReturnValue([]),
80
+ };
81
+ });
82
+
83
+ describe('ChatPreviewWidget analytics', () => {
84
+ const defaultHardcopy = {
85
+ responseId: 'test-response-id',
86
+ language: 'en',
87
+ values: {
88
+ titleLabel: 'Chat Preview',
89
+ textFieldPlaceholderText: 'Type here',
90
+ },
91
+ };
92
+
93
+ beforeEach(() => {
94
+ vi.clearAllMocks();
95
+ mockGetHardcopy.mockResolvedValue(defaultHardcopy);
96
+ });
97
+
98
+ it('should track ChatComponentVisible when widget mounts', async () => {
99
+ render(<ChatPreviewWidget widgetConfigId="test-config-1" />);
100
+
101
+ await waitFor(
102
+ () => {
103
+ expect(mockTrackEvent).toHaveBeenCalledWith({
104
+ eventName: SpiffyMetricsEventName.ChatComponentVisible,
105
+ eventProps: {
106
+ widget_config_id: 'test-config-1',
107
+ widget_type: WidgetTypeV3.ChatPreviewV3,
108
+ },
109
+ });
110
+ },
111
+ { timeout: 3000 },
112
+ );
113
+ }, 5000);
114
+ });