@envive-ai/react-widgets-v3 0.3.67 → 0.3.69

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 (43) hide show
  1. package/dist/CXIntegration/implementations/useGladlyUnifiedCXButton.cjs +112 -36
  2. package/dist/CXIntegration/implementations/useGladlyUnifiedCXButton.js +112 -36
  3. package/dist/debug/reportIssue.cjs +15 -3
  4. package/dist/debug/reportIssue.js +16 -4
  5. package/dist/hocs/withBaseWidget/withBaseWidget.d.cts +2 -2
  6. package/dist/hocs/withBaseWidget/withBaseWidget.d.ts +2 -2
  7. package/dist/packages/widgets/dist/SearchResults/SearchResultsWidget.d.ts +2 -2
  8. package/dist/widgets/ChatPreviewComparisonWidget/ChatPreviewComparisonWidget.d.cts +3 -3
  9. package/dist/widgets/ChatPreviewLoadingWidget/ChatPreviewLoadingWidget.d.cts +3 -3
  10. package/dist/widgets/ChatPreviewLoadingWidget/ChatPreviewLoadingWidget.d.ts +3 -3
  11. package/dist/widgets/ChatPreviewWidget/ChatPreviewWidget.d.cts +3 -3
  12. package/dist/widgets/ChatPreviewWidget/ChatPreviewWidget.d.ts +3 -3
  13. package/dist/widgets/FloatingChatWidget/FloatingChatWidget.cjs +4 -1
  14. package/dist/widgets/FloatingChatWidget/FloatingChatWidget.d.cts +2 -2
  15. package/dist/widgets/FloatingChatWidget/FloatingChatWidget.d.ts +2 -2
  16. package/dist/widgets/FloatingChatWidget/FloatingChatWidget.js +4 -1
  17. package/dist/widgets/FullPageSalesAgentWidget/FullPageSalesAgentWidget.d.ts +2 -2
  18. package/dist/widgets/ProductCardWidget/ProductCardWidget.cjs +2 -0
  19. package/dist/widgets/ProductCardWidget/ProductCardWidget.d.cts +2 -2
  20. package/dist/widgets/ProductCardWidget/ProductCardWidget.d.ts +2 -2
  21. package/dist/widgets/ProductCardWidget/ProductCardWidget.js +2 -0
  22. package/dist/widgets/PromptButtonCarouselWithImageWidget/PromptButtonCarouselWithImageWidget.d.cts +3 -3
  23. package/dist/widgets/PromptButtonCarouselWithImageWidget/PromptButtonCarouselWithImageWidget.d.ts +3 -3
  24. package/dist/widgets/PromptCarouselWidget/PromptCarouselWidget.d.cts +2 -2
  25. package/dist/widgets/PromptCarouselWidget/PromptCarouselWidget.d.ts +2 -2
  26. package/dist/widgets/SocialProofFlowWidget/SocialProofFlowWidget.d.cts +2 -2
  27. package/dist/widgets/SocialProofFlowWidget/SocialProofFlowWidget.d.ts +2 -2
  28. package/dist/widgets/SocialProofWidget/SocialProofWidget.d.cts +3 -3
  29. package/dist/widgets/SocialProofWidget/SocialProofWidget.d.ts +3 -3
  30. package/dist/widgets/TitledPromptCarouselWidget/TitledPromptCarouselWidget.d.cts +2 -2
  31. package/dist/widgets/TitledPromptCarouselWidget/TitledPromptCarouselWidget.d.ts +2 -2
  32. package/dist/widgets/TypingAnimationFlowWidget/TypingAnimationFlowWidget.d.cts +2 -2
  33. package/dist/widgets/TypingAnimationFlowWidget/TypingAnimationFlowWidget.d.ts +2 -2
  34. package/dist/widgets/TypingAnimationWidget/TypingAnimationWidget.d.cts +3 -3
  35. package/dist/widgets/dist/SearchResults/SearchResultsWidget.d.cts +2 -2
  36. package/package.json +1 -1
  37. package/src/CXIntegration/implementations/__tests__/useGladlyUnifiedCXButton.test.ts +233 -0
  38. package/src/CXIntegration/implementations/useGladlyUnifiedCXButton.ts +173 -40
  39. package/src/debug/reportIssue.tsx +44 -4
  40. package/src/widgets/FloatingChatWidget/FloatingChatWidget.tsx +14 -1
  41. package/src/widgets/FloatingChatWidget/__tests__/FloatingChatWidget.cxClose.test.tsx +138 -0
  42. package/src/widgets/ProductCardWidget/ProductCardWidget.tsx +2 -0
  43. package/src/widgets/ProductCardWidget/__tests__/ProductCardWidget.test.tsx +24 -1
@@ -1,4 +1,4 @@
1
- import * as react_jsx_runtime16 from "react/jsx-runtime";
1
+ import * as react_jsx_runtime8 from "react/jsx-runtime";
2
2
 
3
3
  //#region src/widgets/FloatingChatWidget/FloatingChatWidget.d.ts
4
4
  interface FloatingChatWidgetProps {
@@ -13,7 +13,7 @@ declare const FloatingChatWidget: {
13
13
  previewChatAlwaysOpen,
14
14
  previewCXButtonsEnabled,
15
15
  previewDisclaimer
16
- }?: FloatingChatWidgetProps): react_jsx_runtime16.JSX.Element;
16
+ }?: FloatingChatWidgetProps): react_jsx_runtime8.JSX.Element;
17
17
  displayName: string;
18
18
  };
19
19
  //#endregion
@@ -84,7 +84,10 @@ const FloatingChatWidgetHandler = (props) => {
84
84
  enabled: customerServiceIntegration?.enabled ?? false,
85
85
  suppressMerchantButton: customerServiceIntegration?.suppressMerchantButton ?? false,
86
86
  onSwitchToAgent: () => setIsCXOpen(true),
87
- onCXClose: () => setIsCXOpen(false),
87
+ onCXClose: () => {
88
+ setIsCXOpen(false);
89
+ if (!previewChatAlwaysOpen) closeChat(ChatElementDisplayLocationV3.FLOATING_CHAT_CLOSE_BUTTON, void 0, "close_button");
90
+ },
88
91
  children: ({ isSwitchEnabled, toggle }) => /* @__PURE__ */ jsxs(Fragment, { children: [!previewButtonOnly && /* @__PURE__ */ jsx(FloatingChatOverlay, {
89
92
  isOpened: effectiveIsOpen,
90
93
  isResultsModalOpen,
@@ -1,4 +1,4 @@
1
- import * as react_jsx_runtime12 from "react/jsx-runtime";
1
+ import * as react_jsx_runtime10 from "react/jsx-runtime";
2
2
 
3
3
  //#region src/widgets/FullPageSalesAgentWidget/FullPageSalesAgentWidget.d.ts
4
4
 
@@ -8,7 +8,7 @@ interface FullPageSalesAgentBaseWidgetProps {
8
8
  declare const FullPageSalesAgentWidget: {
9
9
  ({
10
10
  widgetConfigId
11
- }: FullPageSalesAgentBaseWidgetProps): react_jsx_runtime12.JSX.Element;
11
+ }: FullPageSalesAgentBaseWidgetProps): react_jsx_runtime10.JSX.Element;
12
12
  displayName: string;
13
13
  };
14
14
  //#endregion
@@ -30,6 +30,7 @@ const ProductCardWidgetHandler = (props) => {
30
30
  const productCardWidgetConfig = widgetConfig;
31
31
  const carouselId = productCardWidgetConfig?.contentId || "";
32
32
  const promptButtonType = productCardWidgetConfig?.promptButtonType || __envive_ai_react_toolkit_v3_PromptButton_types.PromptButtonVariant.LIGHT;
33
+ const promptButtonLayout = productCardWidgetConfig?.promptButtonLayout;
33
34
  const italicizeHeadline = productCardWidgetConfig?.italicizeHeadline;
34
35
  const fallbackColor = productCardWidgetConfig?.fallbackColor;
35
36
  const imageSrc = productCardWidgetConfig?.imageSrc || "";
@@ -107,6 +108,7 @@ const ProductCardWidgetHandler = (props) => {
107
108
  animatedText,
108
109
  prompts,
109
110
  promptButtonType,
111
+ promptButtonLayout,
110
112
  carouselId,
111
113
  placeholder,
112
114
  voiceInputEnabled: isVoiceInputEnabled,
@@ -1,4 +1,4 @@
1
- import * as react_jsx_runtime9 from "react/jsx-runtime";
1
+ import * as react_jsx_runtime3 from "react/jsx-runtime";
2
2
 
3
3
  //#region src/widgets/ProductCardWidget/ProductCardWidget.d.ts
4
4
  interface ProductCardWidgetProps {
@@ -7,7 +7,7 @@ interface ProductCardWidgetProps {
7
7
  declare const ProductCardWidget: {
8
8
  ({
9
9
  widgetConfigId
10
- }: ProductCardWidgetProps): react_jsx_runtime9.JSX.Element;
10
+ }: ProductCardWidgetProps): react_jsx_runtime3.JSX.Element;
11
11
  displayName: string;
12
12
  };
13
13
  //#endregion
@@ -1,4 +1,4 @@
1
- import * as react_jsx_runtime11 from "react/jsx-runtime";
1
+ import * as react_jsx_runtime9 from "react/jsx-runtime";
2
2
 
3
3
  //#region src/widgets/ProductCardWidget/ProductCardWidget.d.ts
4
4
  interface ProductCardWidgetProps {
@@ -7,7 +7,7 @@ interface ProductCardWidgetProps {
7
7
  declare const ProductCardWidget: {
8
8
  ({
9
9
  widgetConfigId
10
- }: ProductCardWidgetProps): react_jsx_runtime11.JSX.Element;
10
+ }: ProductCardWidgetProps): react_jsx_runtime9.JSX.Element;
11
11
  displayName: string;
12
12
  };
13
13
  //#endregion
@@ -29,6 +29,7 @@ const ProductCardWidgetHandler = (props) => {
29
29
  const productCardWidgetConfig = widgetConfig;
30
30
  const carouselId = productCardWidgetConfig?.contentId || "";
31
31
  const promptButtonType = productCardWidgetConfig?.promptButtonType || PromptButtonVariant.LIGHT;
32
+ const promptButtonLayout = productCardWidgetConfig?.promptButtonLayout;
32
33
  const italicizeHeadline = productCardWidgetConfig?.italicizeHeadline;
33
34
  const fallbackColor = productCardWidgetConfig?.fallbackColor;
34
35
  const imageSrc = productCardWidgetConfig?.imageSrc || "";
@@ -106,6 +107,7 @@ const ProductCardWidgetHandler = (props) => {
106
107
  animatedText,
107
108
  prompts,
108
109
  promptButtonType,
110
+ promptButtonLayout,
109
111
  carouselId,
110
112
  placeholder,
111
113
  voiceInputEnabled: isVoiceInputEnabled,
@@ -1,9 +1,9 @@
1
1
  import { BaseWidgetProps } from "../../hocs/withBaseWidget/types.cjs";
2
- import * as react_jsx_runtime12 from "react/jsx-runtime";
2
+ import * as react_jsx_runtime17 from "react/jsx-runtime";
3
3
 
4
4
  //#region src/widgets/PromptButtonCarouselWithImageWidget/PromptButtonCarouselWithImageWidget.d.ts
5
5
  declare const PromptButtonCarouselWithImageWidgetWithBaseWidget: {
6
- (props: BaseWidgetProps): react_jsx_runtime12.JSX.Element;
6
+ (props: BaseWidgetProps): react_jsx_runtime17.JSX.Element;
7
7
  displayName: string;
8
8
  };
9
9
  interface PromptButtonCarouselWithImageWidgetProps {
@@ -12,7 +12,7 @@ interface PromptButtonCarouselWithImageWidgetProps {
12
12
  declare const PromptButtonCarouselWithImageWidget: {
13
13
  ({
14
14
  widgetConfigId
15
- }: PromptButtonCarouselWithImageWidgetProps): react_jsx_runtime12.JSX.Element;
15
+ }: PromptButtonCarouselWithImageWidgetProps): react_jsx_runtime17.JSX.Element;
16
16
  displayName: string;
17
17
  };
18
18
  //#endregion
@@ -1,9 +1,9 @@
1
1
  import { BaseWidgetProps } from "../../hocs/withBaseWidget/types.js";
2
- import * as react_jsx_runtime9 from "react/jsx-runtime";
2
+ import * as react_jsx_runtime2 from "react/jsx-runtime";
3
3
 
4
4
  //#region src/widgets/PromptButtonCarouselWithImageWidget/PromptButtonCarouselWithImageWidget.d.ts
5
5
  declare const PromptButtonCarouselWithImageWidgetWithBaseWidget: {
6
- (props: BaseWidgetProps): react_jsx_runtime9.JSX.Element;
6
+ (props: BaseWidgetProps): react_jsx_runtime2.JSX.Element;
7
7
  displayName: string;
8
8
  };
9
9
  interface PromptButtonCarouselWithImageWidgetProps {
@@ -12,7 +12,7 @@ interface PromptButtonCarouselWithImageWidgetProps {
12
12
  declare const PromptButtonCarouselWithImageWidget: {
13
13
  ({
14
14
  widgetConfigId
15
- }: PromptButtonCarouselWithImageWidgetProps): react_jsx_runtime9.JSX.Element;
15
+ }: PromptButtonCarouselWithImageWidgetProps): react_jsx_runtime2.JSX.Element;
16
16
  displayName: string;
17
17
  };
18
18
  //#endregion
@@ -1,4 +1,4 @@
1
- import * as react_jsx_runtime0 from "react/jsx-runtime";
1
+ import * as react_jsx_runtime4 from "react/jsx-runtime";
2
2
 
3
3
  //#region src/widgets/PromptCarouselWidget/PromptCarouselWidget.d.ts
4
4
  interface PromptCarouselWidgetProps {
@@ -7,7 +7,7 @@ interface PromptCarouselWidgetProps {
7
7
  declare const PromptCarouselWidget: {
8
8
  ({
9
9
  widgetConfigId
10
- }: PromptCarouselWidgetProps): react_jsx_runtime0.JSX.Element;
10
+ }: PromptCarouselWidgetProps): react_jsx_runtime4.JSX.Element;
11
11
  displayName: string;
12
12
  };
13
13
  //#endregion
@@ -1,4 +1,4 @@
1
- import * as react_jsx_runtime8 from "react/jsx-runtime";
1
+ import * as react_jsx_runtime6 from "react/jsx-runtime";
2
2
 
3
3
  //#region src/widgets/PromptCarouselWidget/PromptCarouselWidget.d.ts
4
4
  interface PromptCarouselWidgetProps {
@@ -7,7 +7,7 @@ interface PromptCarouselWidgetProps {
7
7
  declare const PromptCarouselWidget: {
8
8
  ({
9
9
  widgetConfigId
10
- }: PromptCarouselWidgetProps): react_jsx_runtime8.JSX.Element;
10
+ }: PromptCarouselWidgetProps): react_jsx_runtime6.JSX.Element;
11
11
  displayName: string;
12
12
  };
13
13
  //#endregion
@@ -1,4 +1,4 @@
1
- import * as react_jsx_runtime1 from "react/jsx-runtime";
1
+ import * as react_jsx_runtime2 from "react/jsx-runtime";
2
2
 
3
3
  //#region src/widgets/SocialProofFlowWidget/SocialProofFlowWidget.d.ts
4
4
  interface SocialProofFlowWidgetProps {
@@ -7,7 +7,7 @@ interface SocialProofFlowWidgetProps {
7
7
  declare const SocialProofFlowWidget: {
8
8
  ({
9
9
  widgetConfigId
10
- }: SocialProofFlowWidgetProps): react_jsx_runtime1.JSX.Element;
10
+ }: SocialProofFlowWidgetProps): react_jsx_runtime2.JSX.Element;
11
11
  displayName: string;
12
12
  };
13
13
  //#endregion
@@ -1,4 +1,4 @@
1
- import * as react_jsx_runtime2 from "react/jsx-runtime";
1
+ import * as react_jsx_runtime7 from "react/jsx-runtime";
2
2
 
3
3
  //#region src/widgets/SocialProofFlowWidget/SocialProofFlowWidget.d.ts
4
4
  interface SocialProofFlowWidgetProps {
@@ -7,7 +7,7 @@ interface SocialProofFlowWidgetProps {
7
7
  declare const SocialProofFlowWidget: {
8
8
  ({
9
9
  widgetConfigId
10
- }: SocialProofFlowWidgetProps): react_jsx_runtime2.JSX.Element;
10
+ }: SocialProofFlowWidgetProps): react_jsx_runtime7.JSX.Element;
11
11
  displayName: string;
12
12
  };
13
13
  //#endregion
@@ -1,9 +1,9 @@
1
1
  import { BaseWidgetProps } from "../../hocs/withBaseWidget/types.cjs";
2
- import * as react_jsx_runtime4 from "react/jsx-runtime";
2
+ import * as react_jsx_runtime15 from "react/jsx-runtime";
3
3
 
4
4
  //#region src/widgets/SocialProofWidget/SocialProofWidget.d.ts
5
5
  declare const SocialProofWidgetWithBaseWidget: {
6
- (props: BaseWidgetProps): react_jsx_runtime4.JSX.Element;
6
+ (props: BaseWidgetProps): react_jsx_runtime15.JSX.Element;
7
7
  displayName: string;
8
8
  };
9
9
  interface SocialProofWidgetProps {
@@ -12,7 +12,7 @@ interface SocialProofWidgetProps {
12
12
  declare const SocialProofWidget: {
13
13
  ({
14
14
  widgetConfigId
15
- }: SocialProofWidgetProps): react_jsx_runtime4.JSX.Element;
15
+ }: SocialProofWidgetProps): react_jsx_runtime15.JSX.Element;
16
16
  displayName: string;
17
17
  };
18
18
  //#endregion
@@ -1,9 +1,9 @@
1
1
  import { BaseWidgetProps } from "../../hocs/withBaseWidget/types.js";
2
- import * as react_jsx_runtime14 from "react/jsx-runtime";
2
+ import * as react_jsx_runtime4 from "react/jsx-runtime";
3
3
 
4
4
  //#region src/widgets/SocialProofWidget/SocialProofWidget.d.ts
5
5
  declare const SocialProofWidgetWithBaseWidget: {
6
- (props: BaseWidgetProps): react_jsx_runtime14.JSX.Element;
6
+ (props: BaseWidgetProps): react_jsx_runtime4.JSX.Element;
7
7
  displayName: string;
8
8
  };
9
9
  interface SocialProofWidgetProps {
@@ -12,7 +12,7 @@ interface SocialProofWidgetProps {
12
12
  declare const SocialProofWidget: {
13
13
  ({
14
14
  widgetConfigId
15
- }: SocialProofWidgetProps): react_jsx_runtime14.JSX.Element;
15
+ }: SocialProofWidgetProps): react_jsx_runtime4.JSX.Element;
16
16
  displayName: string;
17
17
  };
18
18
  //#endregion
@@ -1,4 +1,4 @@
1
- import * as react_jsx_runtime6 from "react/jsx-runtime";
1
+ import * as react_jsx_runtime1 from "react/jsx-runtime";
2
2
 
3
3
  //#region src/widgets/TitledPromptCarouselWidget/TitledPromptCarouselWidget.d.ts
4
4
  interface TitledPromptCarouselWidgetProps {
@@ -7,7 +7,7 @@ interface TitledPromptCarouselWidgetProps {
7
7
  declare const TitledPromptCarouselWidget: {
8
8
  ({
9
9
  widgetConfigId
10
- }: TitledPromptCarouselWidgetProps): react_jsx_runtime6.JSX.Element;
10
+ }: TitledPromptCarouselWidgetProps): react_jsx_runtime1.JSX.Element;
11
11
  displayName: string;
12
12
  };
13
13
  //#endregion
@@ -1,4 +1,4 @@
1
- import * as react_jsx_runtime3 from "react/jsx-runtime";
1
+ import * as react_jsx_runtime0 from "react/jsx-runtime";
2
2
 
3
3
  //#region src/widgets/TitledPromptCarouselWidget/TitledPromptCarouselWidget.d.ts
4
4
  interface TitledPromptCarouselWidgetProps {
@@ -7,7 +7,7 @@ interface TitledPromptCarouselWidgetProps {
7
7
  declare const TitledPromptCarouselWidget: {
8
8
  ({
9
9
  widgetConfigId
10
- }: TitledPromptCarouselWidgetProps): react_jsx_runtime3.JSX.Element;
10
+ }: TitledPromptCarouselWidgetProps): react_jsx_runtime0.JSX.Element;
11
11
  displayName: string;
12
12
  };
13
13
  //#endregion
@@ -1,4 +1,4 @@
1
- import * as react_jsx_runtime0 from "react/jsx-runtime";
1
+ import * as react_jsx_runtime14 from "react/jsx-runtime";
2
2
 
3
3
  //#region src/widgets/TypingAnimationFlowWidget/TypingAnimationFlowWidget.d.ts
4
4
  interface TypingAnimationFlowWidgetProps {
@@ -7,7 +7,7 @@ interface TypingAnimationFlowWidgetProps {
7
7
  declare const TypingAnimationFlowWidget: {
8
8
  ({
9
9
  widgetConfigId
10
- }: TypingAnimationFlowWidgetProps): react_jsx_runtime0.JSX.Element;
10
+ }: TypingAnimationFlowWidgetProps): react_jsx_runtime14.JSX.Element;
11
11
  displayName: string;
12
12
  };
13
13
  //#endregion
@@ -1,4 +1,4 @@
1
- import * as react_jsx_runtime0 from "react/jsx-runtime";
1
+ import * as react_jsx_runtime19 from "react/jsx-runtime";
2
2
 
3
3
  //#region src/widgets/TypingAnimationFlowWidget/TypingAnimationFlowWidget.d.ts
4
4
  interface TypingAnimationFlowWidgetProps {
@@ -7,7 +7,7 @@ interface TypingAnimationFlowWidgetProps {
7
7
  declare const TypingAnimationFlowWidget: {
8
8
  ({
9
9
  widgetConfigId
10
- }: TypingAnimationFlowWidgetProps): react_jsx_runtime0.JSX.Element;
10
+ }: TypingAnimationFlowWidgetProps): react_jsx_runtime19.JSX.Element;
11
11
  displayName: string;
12
12
  };
13
13
  //#endregion
@@ -1,9 +1,9 @@
1
1
  import { BaseWidgetProps } from "../../hocs/withBaseWidget/types.cjs";
2
- import * as react_jsx_runtime16 from "react/jsx-runtime";
2
+ import * as react_jsx_runtime0 from "react/jsx-runtime";
3
3
 
4
4
  //#region src/widgets/TypingAnimationWidget/TypingAnimationWidget.d.ts
5
5
  declare const TypingAnimationWithBaseWidget: {
6
- (props: BaseWidgetProps): react_jsx_runtime16.JSX.Element;
6
+ (props: BaseWidgetProps): react_jsx_runtime0.JSX.Element;
7
7
  displayName: string;
8
8
  };
9
9
  interface TypingAnimationWidgetProps {
@@ -12,7 +12,7 @@ interface TypingAnimationWidgetProps {
12
12
  declare const TypingAnimationWidget: {
13
13
  ({
14
14
  widgetConfigId
15
- }: TypingAnimationWidgetProps): react_jsx_runtime16.JSX.Element;
15
+ }: TypingAnimationWidgetProps): react_jsx_runtime0.JSX.Element;
16
16
  displayName: string;
17
17
  };
18
18
  //#endregion
@@ -1,8 +1,8 @@
1
- import * as react_jsx_runtime0 from "react/jsx-runtime";
1
+ import * as react_jsx_runtime1 from "react/jsx-runtime";
2
2
 
3
3
  //#region ../widgets/dist/SearchResults/SearchResultsWidget.d.ts
4
4
  //#region src/SearchResults/SearchResultsWidget.d.ts
5
- declare const SearchResultsWidget: () => react_jsx_runtime0.JSX.Element;
5
+ declare const SearchResultsWidget: () => react_jsx_runtime1.JSX.Element;
6
6
  //#endregion
7
7
 
8
8
  //#endregion
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@envive-ai/react-widgets-v3",
3
- "version": "0.3.67",
3
+ "version": "0.3.69",
4
4
  "description": "React widget library v3 for Envive services.",
5
5
  "keywords": [
6
6
  "react",
@@ -0,0 +1,233 @@
1
+ import { renderHook, waitFor } from '@testing-library/react';
2
+ import { DOMObserver } from '@envive-ai/react-hooks/application/utils';
3
+ import { useGladlyUnifiedCXButton } from '../useGladlyUnifiedCXButton';
4
+
5
+ const GLADLY_CONTAINER_ID = 'gladlyChat_container';
6
+ const HIDE_STYLE_ID = 'envive-gladly-pill-hide';
7
+ const ENVIVE_BUTTON_HIDE_STYLE_ID = 'envive-floating-button-hide';
8
+
9
+ /**
10
+ * Gladly renders into an OPEN shadow root attached to `#gladlyChat_container`
11
+ * (confirmed via live DOM inspection — see PR history). The host's own
12
+ * attributes/size never change between open and closed, and it has zero
13
+ * light-DOM children, so open/close can only be read from inside the shadow
14
+ * tree. Closed state: a small launcher icon (~50x50). Open state: a chat
15
+ * panel comfortably larger than the 200px threshold in both dimensions.
16
+ */
17
+ describe('useGladlyUnifiedCXButton', () => {
18
+ beforeEach(() => {
19
+ document.body.innerHTML = '';
20
+ document.head.innerHTML = '';
21
+ });
22
+
23
+ afterEach(() => {
24
+ DOMObserver.destroy();
25
+ });
26
+
27
+ const flush = (ms = 0) =>
28
+ new Promise<void>(resolve => {
29
+ setTimeout(resolve, ms);
30
+ });
31
+
32
+ // jsdom has no layout engine — getBoundingClientRect() always returns all
33
+ // zeroes — so the sizes our production code reads from the real DOM have
34
+ // to be stubbed per element to exercise that branch here.
35
+ const stubRect = (el: HTMLElement, width: number, height: number) => {
36
+ Object.defineProperty(el, 'getBoundingClientRect', {
37
+ configurable: true,
38
+ value: () => ({ width, height, top: 0, left: 0, right: width, bottom: height }) as DOMRect,
39
+ });
40
+ };
41
+
42
+ const createHostWithShadowRoot = () => {
43
+ const host = document.createElement('div');
44
+ host.id = GLADLY_CONTAINER_ID;
45
+ document.body.appendChild(host);
46
+ const shadowRoot = host.attachShadow({ mode: 'open' });
47
+ return shadowRoot;
48
+ };
49
+
50
+ const renderClosedIcon = (shadowRoot: ShadowRoot) => {
51
+ const icon = document.createElement('button');
52
+ icon.className = '_chatIconButton_z80r8_17';
53
+ stubRect(icon, 50, 50);
54
+ shadowRoot.appendChild(icon);
55
+ return icon;
56
+ };
57
+
58
+ const renderOpenPanel = (shadowRoot: ShadowRoot) => {
59
+ const panel = document.createElement('div');
60
+ panel.className = '_baseWidgetContainer_1mosp_1 _panel_1mosp_21';
61
+ stubRect(panel, 364, 577);
62
+ shadowRoot.appendChild(panel);
63
+ return panel;
64
+ };
65
+
66
+ it('opens, then closes when the panel is replaced by the closed-state launcher icon', async () => {
67
+ const onSwitchToAgent = vi.fn();
68
+ const onCXClose = vi.fn();
69
+
70
+ // Host + shadow root already exist before the hook mounts, so its
71
+ // mount-time attach() finds them synchronously (no need to wait out the
72
+ // retry interval here — that path is covered by the next test).
73
+ const shadowRoot = createHostWithShadowRoot();
74
+ const icon = renderClosedIcon(shadowRoot);
75
+
76
+ const { result } = renderHook(() =>
77
+ useGladlyUnifiedCXButton({ onSwitchToAgent, onCXClose, suppressMerchantButton: true }),
78
+ );
79
+ await flush();
80
+
81
+ result.current.toggle();
82
+ expect(onSwitchToAgent).toHaveBeenCalledTimes(1);
83
+ expect(document.getElementById(ENVIVE_BUTTON_HIDE_STYLE_ID)).not.toBeNull();
84
+
85
+ // Gladly swaps the closed-state icon for the open chat panel.
86
+ icon.remove();
87
+ renderOpenPanel(shadowRoot);
88
+ await flush();
89
+
90
+ expect(onCXClose).not.toHaveBeenCalled();
91
+ expect(document.getElementById(ENVIVE_BUTTON_HIDE_STYLE_ID)).not.toBeNull();
92
+
93
+ // Real close: panel replaced by the small launcher icon again.
94
+ shadowRoot.querySelector('div')?.remove();
95
+ renderClosedIcon(shadowRoot);
96
+ await flush();
97
+
98
+ await waitFor(() => expect(onCXClose).toHaveBeenCalledTimes(1));
99
+ expect(document.getElementById(ENVIVE_BUTTON_HIDE_STYLE_ID)).toBeNull();
100
+ // The closed-state launcher icon lives inside the shadow root, out of
101
+ // reach of the `#custom-gladly-pill` document-level suppression rule —
102
+ // the whole host must be force-hidden again so it doesn't reappear.
103
+ expect(document.getElementById(GLADLY_CONTAINER_ID)?.style.display).toBe('none');
104
+ });
105
+
106
+ it('attaches to the shadow root even when it is not available until after the host node is created', async () => {
107
+ const onSwitchToAgent = vi.fn();
108
+ const onCXClose = vi.fn();
109
+
110
+ renderHook(() =>
111
+ useGladlyUnifiedCXButton({ onSwitchToAgent, onCXClose, suppressMerchantButton: true }),
112
+ ).result.current.toggle();
113
+
114
+ // Host appears first (e.g. Gladly SDK init), shadow root + content a tick
115
+ // later — after the hook's first attach() attempt already ran and found
116
+ // nothing, so this exercises the retry interval actually catching up.
117
+ const host = document.createElement('div');
118
+ host.id = GLADLY_CONTAINER_ID;
119
+ document.body.appendChild(host);
120
+ await flush();
121
+
122
+ const shadowRoot = host.attachShadow({ mode: 'open' });
123
+ renderOpenPanel(shadowRoot);
124
+ await flush(350);
125
+
126
+ expect(onCXClose).not.toHaveBeenCalled();
127
+
128
+ shadowRoot.querySelector('div')?.remove();
129
+ renderClosedIcon(shadowRoot);
130
+ await flush();
131
+
132
+ await waitFor(() => expect(onCXClose).toHaveBeenCalledTimes(1));
133
+ });
134
+
135
+ it('does not treat a transient small render during the opening animation as a close', async () => {
136
+ // toggle() sets wasOpenRef optimistically before the panel has actually
137
+ // grown to its open size. Without the hasConfirmedOpenRef gate, reading
138
+ // the panel here (still mid-animation) would be misread as "closed" and
139
+ // immediately flip the Envive button back — the panel flashing open then
140
+ // shut that an earlier version of this fix regressed on.
141
+ const onSwitchToAgent = vi.fn();
142
+ const onCXClose = vi.fn();
143
+
144
+ const shadowRoot = createHostWithShadowRoot();
145
+ const panel = document.createElement('div');
146
+ panel.className = '_baseWidgetContainer_1mosp_1 _panel_1mosp_21';
147
+ stubRect(panel, 40, 40); // still animating in, hasn't reached open size yet
148
+ shadowRoot.appendChild(panel);
149
+
150
+ renderHook(() =>
151
+ useGladlyUnifiedCXButton({ onSwitchToAgent, onCXClose, suppressMerchantButton: true }),
152
+ ).result.current.toggle();
153
+ await flush();
154
+
155
+ expect(onCXClose).not.toHaveBeenCalled();
156
+ expect(document.getElementById(ENVIVE_BUTTON_HIDE_STYLE_ID)).not.toBeNull();
157
+ });
158
+
159
+ it('confirms the open state via polling when the panel grows without an accompanying DOM mutation', async () => {
160
+ // MutationObserver never fires for a size change that isn't driven by a
161
+ // DOM mutation (e.g. a CSS transition finishing). Only the polling
162
+ // fallback can pick this up and set hasConfirmedOpenRef, which the later
163
+ // real (mutation-based) close then depends on to fire at all.
164
+ const onSwitchToAgent = vi.fn();
165
+ const onCXClose = vi.fn();
166
+
167
+ const shadowRoot = createHostWithShadowRoot();
168
+ const panel = document.createElement('div');
169
+ panel.className = '_baseWidgetContainer_1mosp_1 _panel_1mosp_21';
170
+ stubRect(panel, 40, 40);
171
+ shadowRoot.appendChild(panel);
172
+
173
+ renderHook(() =>
174
+ useGladlyUnifiedCXButton({ onSwitchToAgent, onCXClose, suppressMerchantButton: true }),
175
+ ).result.current.toggle();
176
+ await flush();
177
+
178
+ // Panel reaches its open size purely via layout/animation — no DOM
179
+ // mutation accompanies it.
180
+ stubRect(panel, 364, 577);
181
+ await flush(300); // > SHADOW_POLL_MS, no further mutation triggered
182
+
183
+ panel.remove();
184
+ renderClosedIcon(shadowRoot);
185
+ await flush();
186
+
187
+ await waitFor(() => expect(onCXClose).toHaveBeenCalledTimes(1));
188
+ });
189
+
190
+ it('removes the suppression rule and restores the host when suppressMerchantButton turns off', async () => {
191
+ const onSwitchToAgent = vi.fn();
192
+ const onCXClose = vi.fn();
193
+
194
+ const { rerender } = renderHook(
195
+ ({ suppressMerchantButton }) =>
196
+ useGladlyUnifiedCXButton({ onSwitchToAgent, onCXClose, suppressMerchantButton }),
197
+ { initialProps: { suppressMerchantButton: true } },
198
+ );
199
+
200
+ expect(document.getElementById(HIDE_STYLE_ID)).not.toBeNull();
201
+
202
+ const host = document.createElement('div');
203
+ host.id = GLADLY_CONTAINER_ID;
204
+ host.style.display = 'none';
205
+ document.body.appendChild(host);
206
+
207
+ rerender({ suppressMerchantButton: false });
208
+
209
+ expect(document.getElementById(HIDE_STYLE_ID)).toBeNull();
210
+ expect(document.getElementById(GLADLY_CONTAINER_ID)?.style.display).toBe('');
211
+ });
212
+
213
+ it('cleans up the suppression rule and host on unmount, so switching providers does not leave Gladly hidden', () => {
214
+ const onSwitchToAgent = vi.fn();
215
+ const onCXClose = vi.fn();
216
+
217
+ const host = document.createElement('div');
218
+ host.id = GLADLY_CONTAINER_ID;
219
+ document.body.appendChild(host);
220
+
221
+ const { unmount } = renderHook(() =>
222
+ useGladlyUnifiedCXButton({ onSwitchToAgent, onCXClose, suppressMerchantButton: true }),
223
+ );
224
+ expect(document.getElementById(HIDE_STYLE_ID)).not.toBeNull();
225
+
226
+ // FloatingChatWidget remounts this provider (key={cxProvider}) when the
227
+ // configured provider changes away from Gladly.
228
+ unmount();
229
+
230
+ expect(document.getElementById(HIDE_STYLE_ID)).toBeNull();
231
+ expect(document.getElementById(GLADLY_CONTAINER_ID)?.style.display).toBe('');
232
+ });
233
+ });