@envive-ai/react-widgets-v3 0.3.61 → 0.3.63
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/hocs/withBaseWidget/types.d.cts +7 -0
- package/dist/hocs/withBaseWidget/types.d.ts +7 -0
- package/dist/hocs/withBaseWidget/withBaseWidget.cjs +2 -1
- package/dist/hocs/withBaseWidget/withBaseWidget.d.cts +2 -2
- package/dist/hocs/withBaseWidget/withBaseWidget.d.ts +2 -2
- package/dist/hocs/withBaseWidget/withBaseWidget.js +2 -1
- package/dist/packages/widgets/dist/SearchResults/SearchResultsWidget.d.ts +2 -2
- package/dist/packages/widgets/dist/SuggestionBar/SuggestionBar.d.ts +2 -2
- package/dist/widgets/ChatPreviewComparisonWidget/ChatPreviewComparisonWidget.d.cts +3 -3
- package/dist/widgets/ChatPreviewComparisonWidget/ChatPreviewComparisonWidget.d.ts +3 -3
- package/dist/widgets/ChatPreviewLoadingWidget/ChatPreviewLoadingWidget.d.cts +3 -3
- package/dist/widgets/ChatPreviewLoadingWidget/ChatPreviewLoadingWidget.d.ts +3 -3
- package/dist/widgets/ChatPreviewWidget/ChatPreviewWidget.d.cts +3 -3
- package/dist/widgets/ChatPreviewWidget/ChatPreviewWidget.d.ts +3 -3
- package/dist/widgets/FloatingChatWidget/FloatingChatWidget.d.cts +2 -2
- package/dist/widgets/FloatingChatWidget/FloatingChatWidget.d.ts +2 -2
- package/dist/widgets/FullPageSalesAgentWidget/FullPageSalesAgentWidget.d.cts +2 -2
- package/dist/widgets/FullPageSalesAgentWidget/FullPageSalesAgentWidget.d.ts +2 -2
- package/dist/widgets/ProductCardWidget/ProductCardWidget.cjs +6 -16
- package/dist/widgets/ProductCardWidget/ProductCardWidget.d.cts +2 -2
- package/dist/widgets/ProductCardWidget/ProductCardWidget.js +6 -16
- package/dist/widgets/PromptButtonCarouselWithImageWidget/PromptButtonCarouselWithImageWidget.d.cts +3 -3
- package/dist/widgets/PromptButtonCarouselWithImageWidget/PromptButtonCarouselWithImageWidget.d.ts +3 -3
- package/dist/widgets/PromptCarouselWidget/PromptCarouselWidget.d.cts +2 -2
- package/dist/widgets/PromptCarouselWidget/PromptCarouselWidget.d.ts +2 -2
- package/dist/widgets/SocialProofFlowWidget/SocialProofFlowWidget.d.cts +2 -2
- package/dist/widgets/SocialProofWidget/SocialProofWidget.d.cts +3 -3
- package/dist/widgets/SocialProofWidget/SocialProofWidget.d.ts +3 -3
- package/dist/widgets/TitledPromptCarouselWidget/TitledPromptCarouselWidget.d.cts +2 -2
- package/dist/widgets/TitledPromptCarouselWidget/TitledPromptCarouselWidget.d.ts +2 -2
- package/dist/widgets/TypingAnimationFlowWidget/TypingAnimationFlowWidget.d.cts +2 -2
- package/dist/widgets/TypingAnimationFlowWidget/TypingAnimationFlowWidget.d.ts +2 -2
- package/dist/widgets/TypingAnimationWidget/TypingAnimationWidget.d.cts +3 -3
- package/dist/widgets/TypingAnimationWidget/TypingAnimationWidget.d.ts +3 -3
- package/dist/widgets/dist/SearchResults/SearchResultsWidget.d.cts +2 -2
- package/dist/widgets/dist/SuggestionBar/SuggestionBar.d.cts +2 -2
- package/package.json +1 -1
- package/src/hocs/withBaseWidget/__tests__/withBaseWidget.test.tsx +36 -0
- package/src/hocs/withBaseWidget/types.ts +7 -0
- package/src/hocs/withBaseWidget/withBaseWidget.tsx +10 -4
- package/src/stories/FullPageSalesAgentWidget.stories.tsx +64 -0
- package/src/widgets/ProductCardWidget/ProductCardWidget.tsx +13 -18
- package/src/widgets/ProductCardWidget/__tests__/ProductCardWidget.test.tsx +69 -2
|
@@ -12,6 +12,13 @@ interface BaseWidgetProps {
|
|
|
12
12
|
uiConfig?: OrgUIConfigV3;
|
|
13
13
|
isLoading?: boolean;
|
|
14
14
|
isHardcopyLoading?: boolean;
|
|
15
|
+
/**
|
|
16
|
+
* True once THIS widget's own hardcopy request has settled (resolved or errored),
|
|
17
|
+
* regardless of whether it produced content. Lets a widget tell "hardcopy not received
|
|
18
|
+
* yet" apart from "hardcopy finished with no content" so it can leave a loading placeholder
|
|
19
|
+
* after a failed fetch instead of showing it forever.
|
|
20
|
+
*/
|
|
21
|
+
isHardcopySettled?: boolean;
|
|
15
22
|
isPageLoading?: boolean;
|
|
16
23
|
isWidgetConfigLoading?: boolean;
|
|
17
24
|
isUiConfigLoading?: boolean;
|
|
@@ -12,6 +12,13 @@ interface BaseWidgetProps {
|
|
|
12
12
|
uiConfig?: OrgUIConfigV3;
|
|
13
13
|
isLoading?: boolean;
|
|
14
14
|
isHardcopyLoading?: boolean;
|
|
15
|
+
/**
|
|
16
|
+
* True once THIS widget's own hardcopy request has settled (resolved or errored),
|
|
17
|
+
* regardless of whether it produced content. Lets a widget tell "hardcopy not received
|
|
18
|
+
* yet" apart from "hardcopy finished with no content" so it can leave a loading placeholder
|
|
19
|
+
* after a failed fetch instead of showing it forever.
|
|
20
|
+
*/
|
|
21
|
+
isHardcopySettled?: boolean;
|
|
15
22
|
isPageLoading?: boolean;
|
|
16
23
|
isWidgetConfigLoading?: boolean;
|
|
17
24
|
isUiConfigLoading?: boolean;
|
|
@@ -30,7 +30,6 @@ function withBaseWidget(Widget, options) {
|
|
|
30
30
|
const [widgetConfigSettled, setWidgetConfigSettled] = (0, react.useState)(false);
|
|
31
31
|
const [uiConfigSettled, setUiConfigSettled] = (0, react.useState)(false);
|
|
32
32
|
const ownRequestsSettled = hardcopySettled && widgetConfigSettled && uiConfigSettled;
|
|
33
|
-
const isLoading = !ownRequestsSettled && (isHardcopyLoading || isPageLoading || isWidgetConfigLoading || isUiConfigLoading);
|
|
34
33
|
const { isVisible } = (0, __envive_ai_react_hooks_hooks_TrackComponentVisibleEvent.useTrackComponentVisibleEvent)(widgetRef, {
|
|
35
34
|
widget_config_id: widgetConfigId,
|
|
36
35
|
widget_type: widgetType,
|
|
@@ -44,6 +43,7 @@ function withBaseWidget(Widget, options) {
|
|
|
44
43
|
widgetConfigId,
|
|
45
44
|
widgetType
|
|
46
45
|
]);
|
|
46
|
+
const isLoading = startLoading && !ownRequestsSettled;
|
|
47
47
|
const settledStateRef = (0, react.useRef)({
|
|
48
48
|
hardcopy: false,
|
|
49
49
|
widgetConfig: false,
|
|
@@ -119,6 +119,7 @@ function withBaseWidget(Widget, options) {
|
|
|
119
119
|
...props,
|
|
120
120
|
isLoading,
|
|
121
121
|
isHardcopyLoading,
|
|
122
|
+
isHardcopySettled: hardcopySettled,
|
|
122
123
|
isPageLoading,
|
|
123
124
|
isWidgetConfigLoading,
|
|
124
125
|
isUiConfigLoading,
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { BaseWidgetProps, WithBaseWidgetOptions } from "./types.cjs";
|
|
2
|
-
import * as
|
|
2
|
+
import * as react_jsx_runtime11 from "react/jsx-runtime";
|
|
3
3
|
|
|
4
4
|
//#region src/hocs/withBaseWidget/withBaseWidget.d.ts
|
|
5
5
|
declare function withBaseWidget<P extends BaseWidgetProps>(Widget: React.ComponentType<P>, options?: WithBaseWidgetOptions): {
|
|
6
|
-
(props: P):
|
|
6
|
+
(props: P): react_jsx_runtime11.JSX.Element;
|
|
7
7
|
displayName: string;
|
|
8
8
|
};
|
|
9
9
|
//#endregion
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { BaseWidgetProps, WithBaseWidgetOptions } from "./types.js";
|
|
2
|
-
import * as
|
|
2
|
+
import * as react_jsx_runtime19 from "react/jsx-runtime";
|
|
3
3
|
|
|
4
4
|
//#region src/hocs/withBaseWidget/withBaseWidget.d.ts
|
|
5
5
|
declare function withBaseWidget<P extends BaseWidgetProps>(Widget: React.ComponentType<P>, options?: WithBaseWidgetOptions): {
|
|
6
|
-
(props: P):
|
|
6
|
+
(props: P): react_jsx_runtime19.JSX.Element;
|
|
7
7
|
displayName: string;
|
|
8
8
|
};
|
|
9
9
|
//#endregion
|
|
@@ -28,7 +28,6 @@ function withBaseWidget(Widget, options) {
|
|
|
28
28
|
const [widgetConfigSettled, setWidgetConfigSettled] = useState(false);
|
|
29
29
|
const [uiConfigSettled, setUiConfigSettled] = useState(false);
|
|
30
30
|
const ownRequestsSettled = hardcopySettled && widgetConfigSettled && uiConfigSettled;
|
|
31
|
-
const isLoading = !ownRequestsSettled && (isHardcopyLoading || isPageLoading || isWidgetConfigLoading || isUiConfigLoading);
|
|
32
31
|
const { isVisible } = useTrackComponentVisibleEvent(widgetRef, {
|
|
33
32
|
widget_config_id: widgetConfigId,
|
|
34
33
|
widget_type: widgetType,
|
|
@@ -42,6 +41,7 @@ function withBaseWidget(Widget, options) {
|
|
|
42
41
|
widgetConfigId,
|
|
43
42
|
widgetType
|
|
44
43
|
]);
|
|
44
|
+
const isLoading = startLoading && !ownRequestsSettled;
|
|
45
45
|
const settledStateRef = useRef({
|
|
46
46
|
hardcopy: false,
|
|
47
47
|
widgetConfig: false,
|
|
@@ -117,6 +117,7 @@ function withBaseWidget(Widget, options) {
|
|
|
117
117
|
...props,
|
|
118
118
|
isLoading,
|
|
119
119
|
isHardcopyLoading,
|
|
120
|
+
isHardcopySettled: hardcopySettled,
|
|
120
121
|
isPageLoading,
|
|
121
122
|
isWidgetConfigLoading,
|
|
122
123
|
isUiConfigLoading,
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as react_jsx_runtime2 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: () =>
|
|
5
|
+
declare const SearchResultsWidget: () => react_jsx_runtime2.JSX.Element;
|
|
6
6
|
//#endregion
|
|
7
7
|
|
|
8
8
|
//#endregion
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { SuggestionBarLocationForMetrics } from "./types.js";
|
|
2
|
-
import * as
|
|
2
|
+
import * as react_jsx_runtime0 from "react/jsx-runtime";
|
|
3
3
|
import { SuggestionButtonVariant } from "@envive-ai/react-hooks/contexts/types";
|
|
4
4
|
import { Message } from "postcss";
|
|
5
5
|
|
|
@@ -30,7 +30,7 @@ declare function SuggestionBar({
|
|
|
30
30
|
buttonBorderRadius,
|
|
31
31
|
handleReply,
|
|
32
32
|
dataTestId
|
|
33
|
-
}: Readonly<SuggestionBarProps>):
|
|
33
|
+
}: Readonly<SuggestionBarProps>): react_jsx_runtime0.JSX.Element;
|
|
34
34
|
//#endregion
|
|
35
35
|
//#endregion
|
|
36
36
|
export { SuggestionBar };
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { BaseWidgetProps } from "../../hocs/withBaseWidget/types.cjs";
|
|
2
|
-
import * as
|
|
2
|
+
import * as react_jsx_runtime9 from "react/jsx-runtime";
|
|
3
3
|
|
|
4
4
|
//#region src/widgets/ChatPreviewComparisonWidget/ChatPreviewComparisonWidget.d.ts
|
|
5
5
|
declare const ChatPreviewComparisonWidgetWithBaseWidget: {
|
|
6
|
-
(props: BaseWidgetProps):
|
|
6
|
+
(props: BaseWidgetProps): react_jsx_runtime9.JSX.Element;
|
|
7
7
|
displayName: string;
|
|
8
8
|
};
|
|
9
9
|
interface ChatPreviewComparisonWidgetProps {
|
|
@@ -12,7 +12,7 @@ interface ChatPreviewComparisonWidgetProps {
|
|
|
12
12
|
declare const ChatPreviewComparisonWidget: {
|
|
13
13
|
({
|
|
14
14
|
widgetConfigId
|
|
15
|
-
}: ChatPreviewComparisonWidgetProps):
|
|
15
|
+
}: ChatPreviewComparisonWidgetProps): react_jsx_runtime9.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
|
|
2
|
+
import * as react_jsx_runtime17 from "react/jsx-runtime";
|
|
3
3
|
|
|
4
4
|
//#region src/widgets/ChatPreviewComparisonWidget/ChatPreviewComparisonWidget.d.ts
|
|
5
5
|
declare const ChatPreviewComparisonWidgetWithBaseWidget: {
|
|
6
|
-
(props: BaseWidgetProps):
|
|
6
|
+
(props: BaseWidgetProps): react_jsx_runtime17.JSX.Element;
|
|
7
7
|
displayName: string;
|
|
8
8
|
};
|
|
9
9
|
interface ChatPreviewComparisonWidgetProps {
|
|
@@ -12,7 +12,7 @@ interface ChatPreviewComparisonWidgetProps {
|
|
|
12
12
|
declare const ChatPreviewComparisonWidget: {
|
|
13
13
|
({
|
|
14
14
|
widgetConfigId
|
|
15
|
-
}: ChatPreviewComparisonWidgetProps):
|
|
15
|
+
}: ChatPreviewComparisonWidgetProps): 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.cjs";
|
|
2
|
-
import * as
|
|
2
|
+
import * as react_jsx_runtime7 from "react/jsx-runtime";
|
|
3
3
|
|
|
4
4
|
//#region src/widgets/ChatPreviewLoadingWidget/ChatPreviewLoadingWidget.d.ts
|
|
5
5
|
declare const ChatPreviewLoadingWidgetWithBaseWidget: {
|
|
6
|
-
(props: BaseWidgetProps):
|
|
6
|
+
(props: BaseWidgetProps): react_jsx_runtime7.JSX.Element;
|
|
7
7
|
displayName: string;
|
|
8
8
|
};
|
|
9
9
|
interface ChatPreviewLoadingWidgetProps {
|
|
@@ -11,6 +11,6 @@ interface ChatPreviewLoadingWidgetProps {
|
|
|
11
11
|
}
|
|
12
12
|
declare const ChatPreviewLoadingWidget: ({
|
|
13
13
|
widgetConfigId
|
|
14
|
-
}: ChatPreviewLoadingWidgetProps) =>
|
|
14
|
+
}: ChatPreviewLoadingWidgetProps) => react_jsx_runtime7.JSX.Element;
|
|
15
15
|
//#endregion
|
|
16
16
|
export { ChatPreviewLoadingWidget, ChatPreviewLoadingWidgetProps, ChatPreviewLoadingWidgetWithBaseWidget };
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { BaseWidgetProps } from "../../hocs/withBaseWidget/types.js";
|
|
2
|
-
import * as
|
|
2
|
+
import * as react_jsx_runtime10 from "react/jsx-runtime";
|
|
3
3
|
|
|
4
4
|
//#region src/widgets/ChatPreviewLoadingWidget/ChatPreviewLoadingWidget.d.ts
|
|
5
5
|
declare const ChatPreviewLoadingWidgetWithBaseWidget: {
|
|
6
|
-
(props: BaseWidgetProps):
|
|
6
|
+
(props: BaseWidgetProps): react_jsx_runtime10.JSX.Element;
|
|
7
7
|
displayName: string;
|
|
8
8
|
};
|
|
9
9
|
interface ChatPreviewLoadingWidgetProps {
|
|
@@ -11,6 +11,6 @@ interface ChatPreviewLoadingWidgetProps {
|
|
|
11
11
|
}
|
|
12
12
|
declare const ChatPreviewLoadingWidget: ({
|
|
13
13
|
widgetConfigId
|
|
14
|
-
}: ChatPreviewLoadingWidgetProps) =>
|
|
14
|
+
}: ChatPreviewLoadingWidgetProps) => react_jsx_runtime10.JSX.Element;
|
|
15
15
|
//#endregion
|
|
16
16
|
export { ChatPreviewLoadingWidget, ChatPreviewLoadingWidgetProps, ChatPreviewLoadingWidgetWithBaseWidget };
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { BaseWidgetProps } from "../../hocs/withBaseWidget/types.cjs";
|
|
2
|
-
import * as
|
|
2
|
+
import * as react_jsx_runtime4 from "react/jsx-runtime";
|
|
3
3
|
|
|
4
4
|
//#region src/widgets/ChatPreviewWidget/ChatPreviewWidget.d.ts
|
|
5
5
|
declare const ChatPreviewWidgetWithBaseWidget: {
|
|
6
|
-
(props: BaseWidgetProps):
|
|
6
|
+
(props: BaseWidgetProps): react_jsx_runtime4.JSX.Element;
|
|
7
7
|
displayName: string;
|
|
8
8
|
};
|
|
9
9
|
interface ChatPreviewWidgetProps {
|
|
@@ -12,7 +12,7 @@ interface ChatPreviewWidgetProps {
|
|
|
12
12
|
declare const ChatPreviewWidget: {
|
|
13
13
|
({
|
|
14
14
|
widgetConfigId
|
|
15
|
-
}: ChatPreviewWidgetProps):
|
|
15
|
+
}: ChatPreviewWidgetProps): react_jsx_runtime4.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
|
|
2
|
+
import * as react_jsx_runtime6 from "react/jsx-runtime";
|
|
3
3
|
|
|
4
4
|
//#region src/widgets/ChatPreviewWidget/ChatPreviewWidget.d.ts
|
|
5
5
|
declare const ChatPreviewWidgetWithBaseWidget: {
|
|
6
|
-
(props: BaseWidgetProps):
|
|
6
|
+
(props: BaseWidgetProps): react_jsx_runtime6.JSX.Element;
|
|
7
7
|
displayName: string;
|
|
8
8
|
};
|
|
9
9
|
interface ChatPreviewWidgetProps {
|
|
@@ -12,7 +12,7 @@ interface ChatPreviewWidgetProps {
|
|
|
12
12
|
declare const ChatPreviewWidget: {
|
|
13
13
|
({
|
|
14
14
|
widgetConfigId
|
|
15
|
-
}: ChatPreviewWidgetProps):
|
|
15
|
+
}: ChatPreviewWidgetProps): react_jsx_runtime6.JSX.Element;
|
|
16
16
|
displayName: string;
|
|
17
17
|
};
|
|
18
18
|
//#endregion
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as react_jsx_runtime3 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):
|
|
16
|
+
}?: FloatingChatWidgetProps): react_jsx_runtime3.JSX.Element;
|
|
17
17
|
displayName: string;
|
|
18
18
|
};
|
|
19
19
|
//#endregion
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as react_jsx_runtime3 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):
|
|
16
|
+
}?: FloatingChatWidgetProps): react_jsx_runtime3.JSX.Element;
|
|
17
17
|
displayName: string;
|
|
18
18
|
};
|
|
19
19
|
//#endregion
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as react_jsx_runtime16 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):
|
|
11
|
+
}: FullPageSalesAgentBaseWidgetProps): react_jsx_runtime16.JSX.Element;
|
|
12
12
|
displayName: string;
|
|
13
13
|
};
|
|
14
14
|
//#endregion
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as react_jsx_runtime15 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):
|
|
11
|
+
}: FullPageSalesAgentBaseWidgetProps): react_jsx_runtime15.JSX.Element;
|
|
12
12
|
displayName: string;
|
|
13
13
|
};
|
|
14
14
|
//#endregion
|
|
@@ -18,25 +18,14 @@ let __envive_ai_react_toolkit_v3_ProductCard = require("@envive-ai/react-toolkit
|
|
|
18
18
|
let __envive_ai_react_toolkit_v3_PromptButton_types = require("@envive-ai/react-toolkit-v3/PromptButton/types");
|
|
19
19
|
|
|
20
20
|
//#region src/widgets/ProductCardWidget/ProductCardWidget.tsx
|
|
21
|
-
const mockPrompts = [
|
|
22
|
-
"Loading prompt 1",
|
|
23
|
-
"Loading prompt 2",
|
|
24
|
-
"Loading prompt 3",
|
|
25
|
-
"Loading prompt 4",
|
|
26
|
-
"Loading prompt 5"
|
|
27
|
-
];
|
|
28
|
-
const mockHeadline = "Loading...";
|
|
29
|
-
const mockAnimatedText = ["Loading..."];
|
|
30
21
|
const ProductCardWidgetHandler = (props) => {
|
|
31
22
|
const { onTypedMessageSubmitted } = (0, __envive_ai_react_hooks_contexts_salesAgentContext.useSalesAgent)();
|
|
32
23
|
const { openChat } = (0, __envive_ai_react_hooks_hooks_ChatToggle.useChatToggle)();
|
|
33
|
-
const { hardcopyContent, widgetConfig, uiConfig, isLoading } = props;
|
|
34
|
-
const
|
|
35
|
-
const headline =
|
|
36
|
-
const
|
|
37
|
-
const
|
|
38
|
-
const hardCopyPrompts = hardcopyContent?.values?.prompts || [];
|
|
39
|
-
const prompts = isLoading ? mockPrompts : hardCopyPrompts;
|
|
24
|
+
const { hardcopyContent, widgetConfig, uiConfig, isLoading, isHardcopySettled } = props;
|
|
25
|
+
const showSkeleton = isLoading || !hardcopyContent && !isHardcopySettled;
|
|
26
|
+
const headline = hardcopyContent?.values?.headline || "";
|
|
27
|
+
const animatedText = hardcopyContent?.values?.animatedText || [];
|
|
28
|
+
const prompts = hardcopyContent?.values?.prompts || [];
|
|
40
29
|
const placeholder = hardcopyContent?.values?.placeholder;
|
|
41
30
|
const productCardWidgetConfig = widgetConfig;
|
|
42
31
|
const carouselId = productCardWidgetConfig?.contentId || "";
|
|
@@ -110,6 +99,7 @@ const ProductCardWidgetHandler = (props) => {
|
|
|
110
99
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__envive_ai_react_toolkit_v3_ProductCard.ProductCard, {
|
|
111
100
|
theme: __envive_ai_react_toolkit_v3_Tokens.Theme.GLOBAL_CUSTOM,
|
|
112
101
|
variant,
|
|
102
|
+
isLoading: showSkeleton,
|
|
113
103
|
imageSrc,
|
|
114
104
|
fallbackColor,
|
|
115
105
|
headline,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as react_jsx_runtime15 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):
|
|
10
|
+
}: ProductCardWidgetProps): react_jsx_runtime15.JSX.Element;
|
|
11
11
|
displayName: string;
|
|
12
12
|
};
|
|
13
13
|
//#endregion
|
|
@@ -17,25 +17,14 @@ import { ProductCard, ProductCardVariant } from "@envive-ai/react-toolkit-v3/Pro
|
|
|
17
17
|
import { PromptButtonVariant } from "@envive-ai/react-toolkit-v3/PromptButton/types";
|
|
18
18
|
|
|
19
19
|
//#region src/widgets/ProductCardWidget/ProductCardWidget.tsx
|
|
20
|
-
const mockPrompts = [
|
|
21
|
-
"Loading prompt 1",
|
|
22
|
-
"Loading prompt 2",
|
|
23
|
-
"Loading prompt 3",
|
|
24
|
-
"Loading prompt 4",
|
|
25
|
-
"Loading prompt 5"
|
|
26
|
-
];
|
|
27
|
-
const mockHeadline = "Loading...";
|
|
28
|
-
const mockAnimatedText = ["Loading..."];
|
|
29
20
|
const ProductCardWidgetHandler = (props) => {
|
|
30
21
|
const { onTypedMessageSubmitted } = useSalesAgent();
|
|
31
22
|
const { openChat } = useChatToggle();
|
|
32
|
-
const { hardcopyContent, widgetConfig, uiConfig, isLoading } = props;
|
|
33
|
-
const
|
|
34
|
-
const headline =
|
|
35
|
-
const
|
|
36
|
-
const
|
|
37
|
-
const hardCopyPrompts = hardcopyContent?.values?.prompts || [];
|
|
38
|
-
const prompts = isLoading ? mockPrompts : hardCopyPrompts;
|
|
23
|
+
const { hardcopyContent, widgetConfig, uiConfig, isLoading, isHardcopySettled } = props;
|
|
24
|
+
const showSkeleton = isLoading || !hardcopyContent && !isHardcopySettled;
|
|
25
|
+
const headline = hardcopyContent?.values?.headline || "";
|
|
26
|
+
const animatedText = hardcopyContent?.values?.animatedText || [];
|
|
27
|
+
const prompts = hardcopyContent?.values?.prompts || [];
|
|
39
28
|
const placeholder = hardcopyContent?.values?.placeholder;
|
|
40
29
|
const productCardWidgetConfig = widgetConfig;
|
|
41
30
|
const carouselId = productCardWidgetConfig?.contentId || "";
|
|
@@ -109,6 +98,7 @@ const ProductCardWidgetHandler = (props) => {
|
|
|
109
98
|
return /* @__PURE__ */ jsx(ProductCard, {
|
|
110
99
|
theme: Theme.GLOBAL_CUSTOM,
|
|
111
100
|
variant,
|
|
101
|
+
isLoading: showSkeleton,
|
|
112
102
|
imageSrc,
|
|
113
103
|
fallbackColor,
|
|
114
104
|
headline,
|
package/dist/widgets/PromptButtonCarouselWithImageWidget/PromptButtonCarouselWithImageWidget.d.cts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { BaseWidgetProps } from "../../hocs/withBaseWidget/types.cjs";
|
|
2
|
-
import * as
|
|
2
|
+
import * as react_jsx_runtime18 from "react/jsx-runtime";
|
|
3
3
|
|
|
4
4
|
//#region src/widgets/PromptButtonCarouselWithImageWidget/PromptButtonCarouselWithImageWidget.d.ts
|
|
5
5
|
declare const PromptButtonCarouselWithImageWidgetWithBaseWidget: {
|
|
6
|
-
(props: BaseWidgetProps):
|
|
6
|
+
(props: BaseWidgetProps): react_jsx_runtime18.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):
|
|
15
|
+
}: PromptButtonCarouselWithImageWidgetProps): react_jsx_runtime18.JSX.Element;
|
|
16
16
|
displayName: string;
|
|
17
17
|
};
|
|
18
18
|
//#endregion
|
package/dist/widgets/PromptButtonCarouselWithImageWidget/PromptButtonCarouselWithImageWidget.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { BaseWidgetProps } from "../../hocs/withBaseWidget/types.js";
|
|
2
|
-
import * as
|
|
2
|
+
import * as react_jsx_runtime13 from "react/jsx-runtime";
|
|
3
3
|
|
|
4
4
|
//#region src/widgets/PromptButtonCarouselWithImageWidget/PromptButtonCarouselWithImageWidget.d.ts
|
|
5
5
|
declare const PromptButtonCarouselWithImageWidgetWithBaseWidget: {
|
|
6
|
-
(props: BaseWidgetProps):
|
|
6
|
+
(props: BaseWidgetProps): react_jsx_runtime13.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):
|
|
15
|
+
}: PromptButtonCarouselWithImageWidgetProps): react_jsx_runtime13.JSX.Element;
|
|
16
16
|
displayName: string;
|
|
17
17
|
};
|
|
18
18
|
//#endregion
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as react_jsx_runtime2 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):
|
|
10
|
+
}: PromptCarouselWidgetProps): react_jsx_runtime2.JSX.Element;
|
|
11
11
|
displayName: string;
|
|
12
12
|
};
|
|
13
13
|
//#endregion
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as react_jsx_runtime8 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):
|
|
10
|
+
}: PromptCarouselWidgetProps): react_jsx_runtime8.JSX.Element;
|
|
11
11
|
displayName: string;
|
|
12
12
|
};
|
|
13
13
|
//#endregion
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as react_jsx_runtime14 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):
|
|
10
|
+
}: SocialProofFlowWidgetProps): react_jsx_runtime14.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
|
|
2
|
+
import * as react_jsx_runtime12 from "react/jsx-runtime";
|
|
3
3
|
|
|
4
4
|
//#region src/widgets/SocialProofWidget/SocialProofWidget.d.ts
|
|
5
5
|
declare const SocialProofWidgetWithBaseWidget: {
|
|
6
|
-
(props: BaseWidgetProps):
|
|
6
|
+
(props: BaseWidgetProps): react_jsx_runtime12.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):
|
|
15
|
+
}: SocialProofWidgetProps): react_jsx_runtime12.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
|
|
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):
|
|
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):
|
|
15
|
+
}: SocialProofWidgetProps): react_jsx_runtime4.JSX.Element;
|
|
16
16
|
displayName: string;
|
|
17
17
|
};
|
|
18
18
|
//#endregion
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import * as
|
|
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):
|
|
10
|
+
}: TitledPromptCarouselWidgetProps): react_jsx_runtime1.JSX.Element;
|
|
11
11
|
displayName: string;
|
|
12
12
|
};
|
|
13
13
|
//#endregion
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as react_jsx_runtime12 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):
|
|
10
|
+
}: TitledPromptCarouselWidgetProps): react_jsx_runtime12.JSX.Element;
|
|
11
11
|
displayName: string;
|
|
12
12
|
};
|
|
13
13
|
//#endregion
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as react_jsx_runtime6 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):
|
|
10
|
+
}: TypingAnimationFlowWidgetProps): react_jsx_runtime6.JSX.Element;
|
|
11
11
|
displayName: string;
|
|
12
12
|
};
|
|
13
13
|
//#endregion
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as react_jsx_runtime0 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):
|
|
10
|
+
}: TypingAnimationFlowWidgetProps): react_jsx_runtime0.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
|
|
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):
|
|
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):
|
|
15
|
+
}: TypingAnimationWidgetProps): react_jsx_runtime0.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
|
|
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):
|
|
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):
|
|
15
|
+
}: TypingAnimationWidgetProps): react_jsx_runtime0.JSX.Element;
|
|
16
16
|
displayName: string;
|
|
17
17
|
};
|
|
18
18
|
//#endregion
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as react_jsx_runtime2 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: () =>
|
|
5
|
+
declare const SearchResultsWidget: () => react_jsx_runtime2.JSX.Element;
|
|
6
6
|
//#endregion
|
|
7
7
|
|
|
8
8
|
//#endregion
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { SuggestionBarLocationForMetrics } from "./types.cjs";
|
|
2
|
-
import * as
|
|
2
|
+
import * as react_jsx_runtime0 from "react/jsx-runtime";
|
|
3
3
|
import { SuggestionButtonVariant } from "@envive-ai/react-hooks/contexts/types";
|
|
4
4
|
import { Message } from "postcss";
|
|
5
5
|
|
|
@@ -30,7 +30,7 @@ declare function SuggestionBar({
|
|
|
30
30
|
buttonBorderRadius,
|
|
31
31
|
handleReply,
|
|
32
32
|
dataTestId
|
|
33
|
-
}: Readonly<SuggestionBarProps>):
|
|
33
|
+
}: Readonly<SuggestionBarProps>): react_jsx_runtime0.JSX.Element;
|
|
34
34
|
//#endregion
|
|
35
35
|
//#endregion
|
|
36
36
|
export { SuggestionBar };
|
package/package.json
CHANGED
|
@@ -408,6 +408,42 @@ describe('withBaseWidget', () => {
|
|
|
408
408
|
});
|
|
409
409
|
});
|
|
410
410
|
|
|
411
|
+
it('stays loading until its OWN hardcopy settles even after the shared flag clears', async () => {
|
|
412
|
+
// Complements INTERRUPT-424: that fix stops a settled widget being reverted when a
|
|
413
|
+
// shared flag flips TRUE. This is the mirror case — another widget's hardcopy finishing
|
|
414
|
+
// flips the single shared HardcopyProvider boolean to FALSE while THIS widget's own
|
|
415
|
+
// hardcopy is still in flight. isLoading must track this widget's own requests, not
|
|
416
|
+
// follow the shared flag down and paint empty content early.
|
|
417
|
+
mockHardcopyLoading = false;
|
|
418
|
+
mockPageLoading = false;
|
|
419
|
+
mockWidgetConfigLoading = false;
|
|
420
|
+
mockUiConfigLoading = false;
|
|
421
|
+
let resolveHardcopy!: (value: HardcopyResponse) => void;
|
|
422
|
+
mockGetHardcopy.mockReturnValue(
|
|
423
|
+
new Promise<HardcopyResponse>(resolve => {
|
|
424
|
+
resolveHardcopy = resolve;
|
|
425
|
+
}),
|
|
426
|
+
);
|
|
427
|
+
|
|
428
|
+
const WrappedWidget = withBaseWidget(TestWidget);
|
|
429
|
+
render(
|
|
430
|
+
<WrappedWidget
|
|
431
|
+
widgetConfigId="test-config-own-hardcopy"
|
|
432
|
+
widgetType={WidgetTypeV3.PromptCarouselV3}
|
|
433
|
+
/>,
|
|
434
|
+
);
|
|
435
|
+
|
|
436
|
+
await waitFor(() => {
|
|
437
|
+
expect(screen.getByTestId('is-loading')).toHaveTextContent('true');
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
resolveHardcopy({ responseId: 'r', language: 'en', values: {} });
|
|
441
|
+
|
|
442
|
+
await waitFor(() => {
|
|
443
|
+
expect(screen.getByTestId('is-loading')).toHaveTextContent('false');
|
|
444
|
+
});
|
|
445
|
+
});
|
|
446
|
+
|
|
411
447
|
it.each([
|
|
412
448
|
[
|
|
413
449
|
'widgetConfig',
|
|
@@ -15,6 +15,13 @@ export interface BaseWidgetProps {
|
|
|
15
15
|
uiConfig?: OrgUIConfigV3;
|
|
16
16
|
isLoading?: boolean;
|
|
17
17
|
isHardcopyLoading?: boolean;
|
|
18
|
+
/**
|
|
19
|
+
* True once THIS widget's own hardcopy request has settled (resolved or errored),
|
|
20
|
+
* regardless of whether it produced content. Lets a widget tell "hardcopy not received
|
|
21
|
+
* yet" apart from "hardcopy finished with no content" so it can leave a loading placeholder
|
|
22
|
+
* after a failed fetch instead of showing it forever.
|
|
23
|
+
*/
|
|
24
|
+
isHardcopySettled?: boolean;
|
|
18
25
|
isPageLoading?: boolean;
|
|
19
26
|
isWidgetConfigLoading?: boolean;
|
|
20
27
|
isUiConfigLoading?: boolean;
|
|
@@ -51,10 +51,6 @@ export function withBaseWidget<P extends BaseWidgetProps>(
|
|
|
51
51
|
const [uiConfigSettled, setUiConfigSettled] = useState(false);
|
|
52
52
|
const ownRequestsSettled = hardcopySettled && widgetConfigSettled && uiConfigSettled;
|
|
53
53
|
|
|
54
|
-
const isLoading =
|
|
55
|
-
!ownRequestsSettled &&
|
|
56
|
-
(isHardcopyLoading || isPageLoading || isWidgetConfigLoading || isUiConfigLoading);
|
|
57
|
-
|
|
58
54
|
const visibilityEventProps = {
|
|
59
55
|
widget_config_id: widgetConfigId,
|
|
60
56
|
widget_type: widgetType,
|
|
@@ -74,6 +70,15 @@ export function withBaseWidget<P extends BaseWidgetProps>(
|
|
|
74
70
|
return isVisible && !!widgetConfigId && !!widgetType;
|
|
75
71
|
}, [isVisible, widgetConfigId, widgetType]);
|
|
76
72
|
|
|
73
|
+
// A widget is loading only while its OWN requests are in flight. Deliberately NOT
|
|
74
|
+
// derived from the shared provider isLoading flags (isHardcopyLoading, isPageLoading,
|
|
75
|
+
// …): each of those is a single boolean shared by every widget, so any widget issuing a
|
|
76
|
+
// fetch flips them for all of them — which let another widget's activity pin this one in
|
|
77
|
+
// (or stuck in) its loading state even after its own requests had settled. `startLoading`
|
|
78
|
+
// gates the leading edge so a not-yet-started widget doesn't report loading off the
|
|
79
|
+
// still-false latches.
|
|
80
|
+
const isLoading = startLoading && !ownRequestsSettled;
|
|
81
|
+
|
|
77
82
|
// Watchdog: a widget that is still in its loading state this long after it started
|
|
78
83
|
// fetching is stuck (or on a very slow connection). Log a console error so session
|
|
79
84
|
// tools that capture the console (Quantum Metric, Datadog RUM) can count and segment
|
|
@@ -163,6 +168,7 @@ export function withBaseWidget<P extends BaseWidgetProps>(
|
|
|
163
168
|
{...props}
|
|
164
169
|
isLoading={isLoading}
|
|
165
170
|
isHardcopyLoading={isHardcopyLoading}
|
|
171
|
+
isHardcopySettled={hardcopySettled}
|
|
166
172
|
isPageLoading={isPageLoading}
|
|
167
173
|
isWidgetConfigLoading={isWidgetConfigLoading}
|
|
168
174
|
isUiConfigLoading={isUiConfigLoading}
|
|
@@ -1,13 +1,43 @@
|
|
|
1
|
+
import {
|
|
2
|
+
HardcopyProvider,
|
|
3
|
+
HardcopyResponse,
|
|
4
|
+
} from '@envive-ai/react-hooks/contexts/hardcopyContext';
|
|
1
5
|
import {
|
|
2
6
|
PageVisitCategory,
|
|
3
7
|
useSalesAgent,
|
|
4
8
|
} from '@envive-ai/react-hooks/contexts/salesAgentContext';
|
|
9
|
+
import { WidgetTypeV3 } from '@envive-ai/react-hooks/contexts/typesV3';
|
|
10
|
+
import { MockHardcopyService } from '@envive-ai/react-hooks/services/hardcopyService';
|
|
5
11
|
import { UserEventCategory } from '@spiffy-ai/commerce-api-client';
|
|
6
12
|
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
7
13
|
import { useEffect } from 'react';
|
|
8
14
|
import { v4 as uuid } from 'uuid';
|
|
9
15
|
import { FullPageSalesAgentWidget } from '../widgets/FullPageSalesAgentWidget/FullPageSalesAgentWidget';
|
|
10
16
|
|
|
17
|
+
// Overlay content for the PartialView story below — needs welcomeOverlayTitle
|
|
18
|
+
// set for FullPageSalesAgent's own overlayEnabled check to pass, since
|
|
19
|
+
// partialViewActive is `partialViewEnabled && overlayEnabled`.
|
|
20
|
+
const partialViewHardcopy: HardcopyResponse = {
|
|
21
|
+
responseId: 'mock-fpsa-partial-view',
|
|
22
|
+
language: 'en',
|
|
23
|
+
values: {
|
|
24
|
+
welcomeMessageTitle: "Hi! I'm your personal shopping assistant",
|
|
25
|
+
welcomeMessageText: 'How can I help you today?',
|
|
26
|
+
chatFooterTextFieldPlaceholderText: 'Ask me anything...',
|
|
27
|
+
welcomeOverlayTitle: 'How can I help you find the right product?',
|
|
28
|
+
welcomeOverlayPromptSuggestions: [
|
|
29
|
+
'Help me find my size',
|
|
30
|
+
'What are your best sellers?',
|
|
31
|
+
'Gift ideas for a friend',
|
|
32
|
+
],
|
|
33
|
+
welcomeOverlayProductCarouselTitle: 'Fan favorites',
|
|
34
|
+
welcomeOverlayProductIds: [
|
|
35
|
+
'mix-match-nursing-pumping-bra-bundle',
|
|
36
|
+
'mothers-day-morning-bundle',
|
|
37
|
+
],
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
|
|
11
41
|
const meta = {
|
|
12
42
|
title: 'Widgets V3/FullPageSalesAgentWidget',
|
|
13
43
|
component: FullPageSalesAgentWidget,
|
|
@@ -66,3 +96,37 @@ export const Playground: Story = {
|
|
|
66
96
|
},
|
|
67
97
|
},
|
|
68
98
|
};
|
|
99
|
+
|
|
100
|
+
export const PartialView: Story = {
|
|
101
|
+
name: 'Partial View (default)',
|
|
102
|
+
// 'fake-widget-config-id' short-circuits withBaseWidget's real widget-config
|
|
103
|
+
// fetch (see WidgetConfigProvider.getWidgetConfig) and resolves to `{}` —
|
|
104
|
+
// partialViewEnabled then comes out `undefined`, which now correctly
|
|
105
|
+
// defaults to enabled regardless of which merchant is selected in the
|
|
106
|
+
// toolbar or what that merchant's own backend config has configured.
|
|
107
|
+
args: {
|
|
108
|
+
widgetConfigId: 'fake-widget-config-id',
|
|
109
|
+
},
|
|
110
|
+
decorators: [
|
|
111
|
+
Story => (
|
|
112
|
+
<HardcopyProvider
|
|
113
|
+
hardcopyService={
|
|
114
|
+
new MockHardcopyService({ [WidgetTypeV3.FullPageSalesAgentV3]: partialViewHardcopy })
|
|
115
|
+
}
|
|
116
|
+
>
|
|
117
|
+
<Story />
|
|
118
|
+
</HardcopyProvider>
|
|
119
|
+
),
|
|
120
|
+
],
|
|
121
|
+
parameters: {
|
|
122
|
+
docs: {
|
|
123
|
+
description: {
|
|
124
|
+
story:
|
|
125
|
+
'Partial view is enabled by default — submitting a question opens the conversation in a ' +
|
|
126
|
+
"closed/partial/full bottom sheet instead of taking over the whole screen, and the widget's " +
|
|
127
|
+
'own product grid stays visible above it. Self-contained (a fake widgetConfigId plus mocked ' +
|
|
128
|
+
"hardcopy) so it renders the same regardless of the toolbar's selected merchant.",
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
};
|
|
@@ -23,30 +23,24 @@ import { useCallback } from 'react';
|
|
|
23
23
|
import { BaseWidgetProps, withBaseWidget } from '../../hocs/withBaseWidget';
|
|
24
24
|
import { RawValues, getStringIdForText } from '../utils/functions';
|
|
25
25
|
|
|
26
|
-
const mockPrompts = [
|
|
27
|
-
'Loading prompt 1',
|
|
28
|
-
'Loading prompt 2',
|
|
29
|
-
'Loading prompt 3',
|
|
30
|
-
'Loading prompt 4',
|
|
31
|
-
'Loading prompt 5',
|
|
32
|
-
];
|
|
33
|
-
|
|
34
|
-
const mockHeadline = 'Loading...';
|
|
35
|
-
const mockAnimatedText = ['Loading...'];
|
|
36
|
-
|
|
37
26
|
const ProductCardWidgetHandler = (props: BaseWidgetProps) => {
|
|
38
27
|
const { onTypedMessageSubmitted } = useSalesAgent();
|
|
39
28
|
const { openChat } = useChatToggle();
|
|
40
|
-
const { hardcopyContent, widgetConfig, uiConfig, isLoading } = props;
|
|
29
|
+
const { hardcopyContent, widgetConfig, uiConfig, isLoading, isHardcopySettled } = props;
|
|
30
|
+
|
|
31
|
+
// Show the skeleton while the card is loading, or while its hardcopy hasn't arrived yet (a
|
|
32
|
+
// widget deferred until visible has none). Gate the "no content yet" case on the hardcopy
|
|
33
|
+
// NOT having settled — otherwise a request that finishes without content (a failed/timed-out
|
|
34
|
+
// fetch) would leave `hardcopyContent` undefined and pin the skeleton on forever. Once the
|
|
35
|
+
// hardcopy settles empty, fall through to the normal render (static-copy fallback / empty
|
|
36
|
+
// state) instead of an endless skeleton.
|
|
37
|
+
const showSkeleton = isLoading || (!hardcopyContent && !isHardcopySettled);
|
|
41
38
|
|
|
42
|
-
const
|
|
43
|
-
const headline = isLoading ? mockHeadline : hardCopyHeadline || '';
|
|
39
|
+
const headline = (hardcopyContent?.values?.headline as string | undefined) || '';
|
|
44
40
|
|
|
45
|
-
const
|
|
46
|
-
const animatedText = isLoading ? mockAnimatedText : hardCopyAnimatedText;
|
|
41
|
+
const animatedText = (hardcopyContent?.values?.animatedText as string[]) || [];
|
|
47
42
|
|
|
48
|
-
const
|
|
49
|
-
const prompts = isLoading ? mockPrompts : hardCopyPrompts;
|
|
43
|
+
const prompts = (hardcopyContent?.values?.prompts as string[]) || [];
|
|
50
44
|
|
|
51
45
|
const hardCopyPlaceholder = hardcopyContent?.values?.placeholder as string | undefined;
|
|
52
46
|
const placeholder = hardCopyPlaceholder;
|
|
@@ -141,6 +135,7 @@ const ProductCardWidgetHandler = (props: BaseWidgetProps) => {
|
|
|
141
135
|
<ProductCard
|
|
142
136
|
theme={Theme.GLOBAL_CUSTOM}
|
|
143
137
|
variant={variant}
|
|
138
|
+
isLoading={showSkeleton}
|
|
144
139
|
imageSrc={imageSrc}
|
|
145
140
|
fallbackColor={fallbackColor}
|
|
146
141
|
headline={headline}
|
|
@@ -10,8 +10,21 @@ const mockUseTrackComponentVisibleEvent = vi.fn(() => ({ isVisible: true }));
|
|
|
10
10
|
|
|
11
11
|
vi.mock('@envive-ai/react-toolkit-v3/ProductCard', () => ({
|
|
12
12
|
ProductCardVariant: { DARK: 'dark', LIGHT: 'light' },
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
// Expose isLoading (the widget's showSkeleton decision) so tests can assert the loading
|
|
14
|
+
// placeholder vs. rendered content across the deferred / resolved / failed fetch paths.
|
|
15
|
+
ProductCard: ({
|
|
16
|
+
prompts,
|
|
17
|
+
onSelect,
|
|
18
|
+
isLoading,
|
|
19
|
+
}: {
|
|
20
|
+
prompts: string[];
|
|
21
|
+
onSelect: (text: string) => void;
|
|
22
|
+
isLoading?: boolean;
|
|
23
|
+
}) => (
|
|
24
|
+
<div
|
|
25
|
+
data-testid="product-card-mock"
|
|
26
|
+
data-loading={String(Boolean(isLoading))}
|
|
27
|
+
>
|
|
15
28
|
{prompts.map(text => (
|
|
16
29
|
<button
|
|
17
30
|
key={text}
|
|
@@ -151,3 +164,57 @@ describe('ProductCardWidget analytics', () => {
|
|
|
151
164
|
});
|
|
152
165
|
}, 5000);
|
|
153
166
|
});
|
|
167
|
+
|
|
168
|
+
describe('ProductCardWidget loading state', () => {
|
|
169
|
+
beforeEach(() => {
|
|
170
|
+
vi.clearAllMocks();
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
const hardcopy = {
|
|
174
|
+
responseId: 'test-response-id',
|
|
175
|
+
language: 'en',
|
|
176
|
+
values: { prompts: ['Prompt 1', 'Prompt 2'] },
|
|
177
|
+
rawValues: { prompts: [{ id: 'id-1', value: 'Prompt 1' }] },
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
const loadingState = () => screen.getByTestId('product-card-mock').dataset.loading;
|
|
181
|
+
|
|
182
|
+
it('shows the skeleton while the hardcopy is pending, then renders content once it resolves', async () => {
|
|
183
|
+
let resolveHardcopy: (value: typeof hardcopy) => void = () => {};
|
|
184
|
+
mockGetHardcopy.mockReturnValue(
|
|
185
|
+
new Promise<typeof hardcopy>(resolve => {
|
|
186
|
+
resolveHardcopy = resolve;
|
|
187
|
+
}),
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
render(<ProductCardWidget widgetConfigId="loading-1" />);
|
|
191
|
+
|
|
192
|
+
// Hardcopy not received yet → skeleton.
|
|
193
|
+
await waitFor(() => expect(loadingState()).toBe('true'), { timeout: 3000 });
|
|
194
|
+
|
|
195
|
+
resolveHardcopy(hardcopy);
|
|
196
|
+
|
|
197
|
+
// Content arrived → skeleton gone.
|
|
198
|
+
await waitFor(() => expect(loadingState()).toBe('false'), { timeout: 3000 });
|
|
199
|
+
expect(screen.getByText('Prompt 1')).toBeInTheDocument();
|
|
200
|
+
}, 5000);
|
|
201
|
+
|
|
202
|
+
it('renders content and drops the skeleton after a successful fetch', async () => {
|
|
203
|
+
mockGetHardcopy.mockResolvedValue(hardcopy);
|
|
204
|
+
|
|
205
|
+
render(<ProductCardWidget widgetConfigId="loading-2" />);
|
|
206
|
+
|
|
207
|
+
await waitFor(() => expect(loadingState()).toBe('false'), { timeout: 3000 });
|
|
208
|
+
expect(screen.getByText('Prompt 1')).toBeInTheDocument();
|
|
209
|
+
}, 5000);
|
|
210
|
+
|
|
211
|
+
it('drops the skeleton after the hardcopy fetch fails instead of hanging on it forever', async () => {
|
|
212
|
+
// A rejected (or timed-out) hardcopy settles without content. The widget must fall through
|
|
213
|
+
// to the normal render rather than pinning the skeleton on indefinitely.
|
|
214
|
+
mockGetHardcopy.mockRejectedValue(new Error('hardcopy failed'));
|
|
215
|
+
|
|
216
|
+
render(<ProductCardWidget widgetConfigId="loading-3" />);
|
|
217
|
+
|
|
218
|
+
await waitFor(() => expect(loadingState()).toBe('false'), { timeout: 3000 });
|
|
219
|
+
}, 5000);
|
|
220
|
+
});
|