@constructor-io/constructorio-ui-pia 2.13.1 → 2.15.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.
- package/README.md +76 -30
- package/dist/constructorio-ui-pia-bundled.js +39 -39
- package/lib/cjs/components/CioPia/CheckoutTriggerBar.js +25 -0
- package/lib/cjs/components/CioPia/CioPia.js +6 -1
- package/lib/cjs/components/CioPia/CioPiaQna.js +7 -2
- package/lib/cjs/components/CioPia/CioPiaStyles.css +33 -0
- package/lib/cjs/components/CioPiaControl/CioPiaControl.js +38 -0
- package/lib/cjs/components/CioPiaRecs/CioPiaRecs.js +3 -1
- package/lib/cjs/components/PiaConversation/PiaConversation.js +2 -1
- package/lib/cjs/components/PiaConversation/PiaConversationStyles.css +5 -0
- package/lib/cjs/hooks/useCioPia.js +2 -1
- package/lib/cjs/hooks/usePiaClient.js +14 -7
- package/lib/cjs/hooks/useViewportTracking.js +9 -4
- package/lib/cjs/stories/assets/storybook-styles.css +12 -10
- package/lib/cjs/version.js +1 -1
- package/lib/mjs/components/CioPia/CheckoutTriggerBar.js +20 -0
- package/lib/mjs/components/CioPia/CioPia.js +5 -0
- package/lib/mjs/components/CioPia/CioPiaQna.js +7 -2
- package/lib/mjs/components/CioPia/CioPiaStyles.css +33 -0
- package/lib/mjs/components/CioPiaControl/CioPiaControl.js +34 -0
- package/lib/mjs/components/CioPiaRecs/CioPiaRecs.js +3 -1
- package/lib/mjs/components/PiaConversation/PiaConversation.js +2 -1
- package/lib/mjs/components/PiaConversation/PiaConversationStyles.css +5 -0
- package/lib/mjs/hooks/useCioPia.js +2 -1
- package/lib/mjs/hooks/usePiaClient.js +15 -3
- package/lib/mjs/hooks/useViewportTracking.js +9 -4
- package/lib/mjs/stories/assets/storybook-styles.css +12 -10
- package/lib/mjs/version.js +1 -1
- package/lib/types/components/CioPia/CheckoutTriggerBar.d.ts +11 -0
- package/lib/types/components/CioPia/index.d.ts +1 -1
- package/lib/types/components/CioPia/types.d.ts +45 -1
- package/lib/types/components/CioPiaControl/CioPiaControl.d.ts +9 -0
- package/lib/types/components/PiaConversation/PiaConversation.d.ts +3 -2
- package/lib/types/hooks/useCioPia.d.ts +2 -0
- package/lib/types/hooks/usePiaClient.d.ts +2 -1
- package/lib/types/hooks/useViewportTracking.d.ts +6 -1
- package/lib/types/index.d.ts +1 -1
- package/lib/types/types.d.ts +33 -0
- package/lib/types/version.d.ts +1 -1
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useCallback, useEffect, useRef } from 'react';
|
|
2
2
|
const FLUSH_INTERVAL_MS = 5 * 60 * 1000;
|
|
3
3
|
const DEFAULT_VIEW_THRESHOLD = 0.5;
|
|
4
|
-
export default function useViewportTracking({ tracking, questions, viewThreshold = DEFAULT_VIEW_THRESHOLD, }) {
|
|
4
|
+
export default function useViewportTracking({ tracking, questions, viewThreshold = DEFAULT_VIEW_THRESHOLD, awaitQuestions = true, }) {
|
|
5
5
|
const nodeRef = useRef(null);
|
|
6
6
|
const observerRef = useRef(null);
|
|
7
7
|
const intervalRef = useRef(null);
|
|
@@ -9,10 +9,14 @@ export default function useViewportTracking({ tracking, questions, viewThreshold
|
|
|
9
9
|
const entryTimeRef = useRef(null);
|
|
10
10
|
const questionsRef = useRef(questions);
|
|
11
11
|
const trackingRef = useRef(tracking);
|
|
12
|
+
const awaitQuestionsRef = useRef(awaitQuestions);
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
awaitQuestionsRef.current = awaitQuestions;
|
|
15
|
+
}, [awaitQuestions]);
|
|
12
16
|
useEffect(() => {
|
|
13
17
|
const questionsJustArrived = questionsRef.current.length === 0 && questions.length > 0;
|
|
14
18
|
questionsRef.current = questions;
|
|
15
|
-
if (questionsJustArrived && entryTimeRef.current) {
|
|
19
|
+
if (awaitQuestionsRef.current && questionsJustArrived && entryTimeRef.current) {
|
|
16
20
|
trackingRef.current.trackView(questions);
|
|
17
21
|
}
|
|
18
22
|
}, [questions]);
|
|
@@ -28,7 +32,8 @@ export default function useViewportTracking({ tracking, questions, viewThreshold
|
|
|
28
32
|
});
|
|
29
33
|
entryTimeRef.current = null;
|
|
30
34
|
}
|
|
31
|
-
|
|
35
|
+
const hasPayload = !awaitQuestionsRef.current || questionsRef.current.length > 0;
|
|
36
|
+
if (timespansRef.current.length > 0 && hasPayload) {
|
|
32
37
|
trackingRef.current.trackViews(questionsRef.current, [...timespansRef.current]);
|
|
33
38
|
timespansRef.current = [];
|
|
34
39
|
}
|
|
@@ -37,7 +42,7 @@ export default function useViewportTracking({ tracking, questions, viewThreshold
|
|
|
37
42
|
const observer = new IntersectionObserver(([entry]) => {
|
|
38
43
|
if (entry.isIntersecting) {
|
|
39
44
|
entryTimeRef.current = new Date().toISOString();
|
|
40
|
-
if (questionsRef.current.length > 0) {
|
|
45
|
+
if (!awaitQuestionsRef.current || questionsRef.current.length > 0) {
|
|
41
46
|
trackingRef.current.trackView(questionsRef.current);
|
|
42
47
|
}
|
|
43
48
|
}
|
|
@@ -25,8 +25,8 @@ div.link-list {
|
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
@media all and (-ms-high-contrast:none) {
|
|
29
|
-
div.link-list {
|
|
28
|
+
@media all and (-ms-high-contrast: none) {
|
|
29
|
+
div.link-list {
|
|
30
30
|
display: -ms-grid;
|
|
31
31
|
-ms-grid-columns: 1fr 1fr;
|
|
32
32
|
-ms-grid-rows: 1fr 1fr;
|
|
@@ -38,20 +38,23 @@ a.link-item {
|
|
|
38
38
|
padding: 20px;
|
|
39
39
|
border: 1px solid #00000010;
|
|
40
40
|
border-radius: 5px;
|
|
41
|
-
transition:
|
|
41
|
+
transition:
|
|
42
|
+
background 150ms ease-out,
|
|
43
|
+
border 150ms ease-out,
|
|
44
|
+
transform 150ms ease-out;
|
|
42
45
|
color: #333333;
|
|
43
46
|
display: flex;
|
|
44
47
|
align-items: flex-start;
|
|
45
48
|
}
|
|
46
49
|
|
|
47
50
|
a.link-item:hover {
|
|
48
|
-
border-color: #
|
|
51
|
+
border-color: #1ea7fd50;
|
|
49
52
|
transform: translate3d(0, -3px, 0);
|
|
50
53
|
box-shadow: rgba(0, 0, 0, 0.08) 0 3px 10px 0;
|
|
51
54
|
}
|
|
52
55
|
|
|
53
56
|
a.link-item:active {
|
|
54
|
-
border-color: #
|
|
57
|
+
border-color: #1ea7fd;
|
|
55
58
|
transform: translate3d(0, 0, 0);
|
|
56
59
|
}
|
|
57
60
|
|
|
@@ -81,8 +84,8 @@ span.badge {
|
|
|
81
84
|
font-size: 11px;
|
|
82
85
|
line-height: 12px;
|
|
83
86
|
font-weight: 700;
|
|
84
|
-
background: #
|
|
85
|
-
color: #
|
|
87
|
+
background: #e7fdd8;
|
|
88
|
+
color: #66bf3c;
|
|
86
89
|
padding: 4px 12px;
|
|
87
90
|
margin-right: 10px;
|
|
88
91
|
vertical-align: top;
|
|
@@ -100,10 +103,9 @@ div.badge-wrapper code {
|
|
|
100
103
|
display: inline-block;
|
|
101
104
|
}
|
|
102
105
|
|
|
103
|
-
|
|
104
106
|
.badge.tip {
|
|
105
|
-
background: #
|
|
106
|
-
color: #
|
|
107
|
+
background: #e7fdd8;
|
|
108
|
+
color: #66bf3c;
|
|
107
109
|
}
|
|
108
110
|
|
|
109
111
|
.badge.note {
|
package/lib/mjs/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export default '2.
|
|
1
|
+
export default '2.15.0';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { CioPiaRenderProps } from '../../types';
|
|
3
|
+
import { translate } from '../../utils/translate';
|
|
4
|
+
import type { CheckoutTrigger } from './types';
|
|
5
|
+
interface CheckoutTriggerBarProps {
|
|
6
|
+
triggers: CheckoutTrigger[];
|
|
7
|
+
state: CioPiaRenderProps;
|
|
8
|
+
translations?: Parameters<typeof translate>[1];
|
|
9
|
+
}
|
|
10
|
+
export default function CheckoutTriggerBar({ triggers, state, translations, }: CheckoutTriggerBarProps): React.JSX.Element | null;
|
|
11
|
+
export {};
|
|
@@ -1,6 +1,43 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
1
2
|
import { IncludeComponentOverrides, IncludeRenderProps } from '@constructor-io/constructorio-ui-components';
|
|
2
|
-
import { CioPiaRenderProps, CioPiaComponentOverrides, Callbacks, CioPiaDisplayConfigs, CioPiaTrackingConfigs, Translations, SuggestedQuestionsParameters, AnswerRequestParameters, RecsPodParameters, Formatters, ProductCardDisplayProps } from '../../types';
|
|
3
|
+
import { CioPiaRenderProps, CioPiaComponentOverrides, Callbacks, CioPiaAbTest, CioPiaDisplayConfigs, CioPiaTrackingConfigs, Translations, SuggestedQuestionsParameters, AnswerRequestParameters, RecsPodParameters, Formatters, ProductCardDisplayProps } from '../../types';
|
|
3
4
|
import type { CioClient } from '../../hooks/usePiaClient';
|
|
5
|
+
/**
|
|
6
|
+
* A single checkout entry point rendered inside the PIA `default` mode.
|
|
7
|
+
* The merchant owns the checkout flow itself (typically the `@constructor-io/constructorio-ui-checkout`
|
|
8
|
+
* `CheckoutFlowProvider`); PIA only surfaces the button(s) that start it.
|
|
9
|
+
*/
|
|
10
|
+
export interface CheckoutTrigger {
|
|
11
|
+
/** Stable identifier for the trigger — used as React key and forwarded to `renderButton`. */
|
|
12
|
+
id: string;
|
|
13
|
+
/** Button label. Defaults to "Checkout". */
|
|
14
|
+
label?: string;
|
|
15
|
+
/** Disables the button. Forwarded to `renderButton`, which is responsible for applying it. */
|
|
16
|
+
disabled?: boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Predicate deciding whether this trigger should render. Called with the same
|
|
19
|
+
* render-props snapshot the `children`/`componentOverrides.reactNode` render callback
|
|
20
|
+
* receives, so consumers can gate on conversation state (e.g. "at least one answer
|
|
21
|
+
* shown", "at least two turns"). Defaults to always visible.
|
|
22
|
+
*
|
|
23
|
+
* `state.conversationHistory` is only filled in `conversation` mode and the modal; in
|
|
24
|
+
* `default` mode it stays empty, so gate on `currentAnswer` there.
|
|
25
|
+
*/
|
|
26
|
+
triggerWhen?: (state: CioPiaRenderProps) => boolean;
|
|
27
|
+
/** Custom button renderer. When provided, `label` / `disabled` are passed through but styling is your call. */
|
|
28
|
+
renderButton?: (props: {
|
|
29
|
+
onClick: () => void;
|
|
30
|
+
label: string;
|
|
31
|
+
disabled?: boolean;
|
|
32
|
+
id: string;
|
|
33
|
+
}) => ReactNode;
|
|
34
|
+
/**
|
|
35
|
+
* Fires when the button is clicked. The merchant wires this to their checkout entry
|
|
36
|
+
* (e.g. `flow.start()` from `useCheckoutFlow`). Receives the render-props snapshot so
|
|
37
|
+
* the handler can seed the checkout with the current answer/items/conversation.
|
|
38
|
+
*/
|
|
39
|
+
onTrigger: (state: CioPiaRenderProps) => void;
|
|
40
|
+
}
|
|
4
41
|
export interface CioPiaProps extends IncludeRenderProps<CioPiaRenderProps>, IncludeComponentOverrides<CioPiaComponentOverrides> {
|
|
5
42
|
/** Your Constructor.io API key. */
|
|
6
43
|
apiKey: string;
|
|
@@ -18,6 +55,8 @@ export interface CioPiaProps extends IncludeRenderProps<CioPiaRenderProps>, Incl
|
|
|
18
55
|
displayConfigs?: CioPiaDisplayConfigs;
|
|
19
56
|
/** Tracking configuration options (viewThreshold, etc.). */
|
|
20
57
|
trackingConfigs?: CioPiaTrackingConfigs;
|
|
58
|
+
/** A/B test configuration: test cells to attach to events, and the control-group flag. */
|
|
59
|
+
abTest?: CioPiaAbTest;
|
|
21
60
|
/** Callback handlers for user interactions (onQuestionSubmit, onProductCardClick, onFeedback). */
|
|
22
61
|
callbacks?: Callbacks;
|
|
23
62
|
/** Custom component overrides via reactNode or render props functions. */
|
|
@@ -26,6 +65,11 @@ export interface CioPiaProps extends IncludeRenderProps<CioPiaRenderProps>, Incl
|
|
|
26
65
|
formatters?: Formatters;
|
|
27
66
|
/** Props forwarded to the ProductCard rendered inside the carousel. */
|
|
28
67
|
productCardProps?: ProductCardDisplayProps;
|
|
68
|
+
/**
|
|
69
|
+
* One or more checkout entry points. Rendered in `default` and `conversation` modes and
|
|
70
|
+
* in the modal, after the answer; ignored in `recommendations` mode.
|
|
71
|
+
*/
|
|
72
|
+
checkoutTriggers?: CheckoutTrigger[];
|
|
29
73
|
/** UI string translations for internationalization. */
|
|
30
74
|
translations?: Translations;
|
|
31
75
|
/** Parameters for the suggested questions request. */
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { CioPiaProps } from '../CioPia/types';
|
|
3
|
+
/**
|
|
4
|
+
* What a control-group shopper gets instead of the widget: nothing to look at, but a node the
|
|
5
|
+
* view event can be tied to, so both arms of an A/B test are measured the same way.
|
|
6
|
+
*
|
|
7
|
+
* None of the question or answer hooks mount here, so the control arm cannot issue a request.
|
|
8
|
+
*/
|
|
9
|
+
export default function CioPiaControl(props: CioPiaProps): React.JSX.Element;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { ReactNode } from 'react';
|
|
2
2
|
import { FeedbackType, Item, Question } from '../../types';
|
|
3
3
|
import { ConversationHistoryProps } from '../ConversationHistory/ConversationHistory';
|
|
4
4
|
export interface PiaConversationProps extends ConversationHistoryProps {
|
|
@@ -10,5 +10,6 @@ export interface PiaConversationProps extends ConversationHistoryProps {
|
|
|
10
10
|
onResultClick?: (item: Item, position: number, question: string, qnaResultId?: string) => void;
|
|
11
11
|
qnaResultId?: string;
|
|
12
12
|
onInputFocus?: () => void;
|
|
13
|
+
checkoutElement?: ReactNode;
|
|
13
14
|
}
|
|
14
|
-
export default function PiaConversation({ conversationHistory, isLoading, error, currentItems, showFeedback, showPreviousItems, learnMoreUrl, disclaimerPosition, translations, callbacks, componentOverrides, displayedQuestions, handleSubmitQuestion, handleQuestionClick, containerRef, handleFeedback, onResultClick, qnaResultId, onInputFocus, priceCurrency, }: PiaConversationProps): React.JSX.Element;
|
|
15
|
+
export default function PiaConversation({ conversationHistory, isLoading, error, currentItems, showFeedback, showPreviousItems, learnMoreUrl, disclaimerPosition, translations, callbacks, componentOverrides, displayedQuestions, handleSubmitQuestion, handleQuestionClick, containerRef, handleFeedback, onResultClick, qnaResultId, onInputFocus, priceCurrency, checkoutElement, }: PiaConversationProps): React.JSX.Element;
|
|
@@ -32,6 +32,8 @@ export interface UseCioPiaProps {
|
|
|
32
32
|
parameters?: Record<string, string | number | boolean>;
|
|
33
33
|
/** Define outside the component or wrap with useCallback to avoid unnecessary re-renders. */
|
|
34
34
|
formatImageUrl?: Formatters['formatImageUrl'];
|
|
35
|
+
/** Test cells to attach to tracking events. Ignored when `cioClient` is supplied. */
|
|
36
|
+
testCells?: Record<string, string>;
|
|
35
37
|
}
|
|
36
38
|
export interface UseCioPiaReturn {
|
|
37
39
|
cioClient: {
|
|
@@ -4,9 +4,10 @@ export interface UsePiaClientProps {
|
|
|
4
4
|
apiKey: string;
|
|
5
5
|
threadId?: string;
|
|
6
6
|
cioClient?: CioClient;
|
|
7
|
+
testCells?: Record<string, string>;
|
|
7
8
|
}
|
|
8
9
|
export interface UsePiaClientReturn {
|
|
9
10
|
cioClient: CioClient;
|
|
10
11
|
threadId: string;
|
|
11
12
|
}
|
|
12
|
-
export default function usePiaClient({ apiKey, threadId: providedThreadId, cioClient: providedClient, }: UsePiaClientProps): UsePiaClientReturn;
|
|
13
|
+
export default function usePiaClient({ apiKey, threadId: providedThreadId, cioClient: providedClient, testCells, }: UsePiaClientProps): UsePiaClientReturn;
|
|
@@ -4,8 +4,13 @@ export interface UseViewportTrackingProps {
|
|
|
4
4
|
tracking: UseTrackingReturn;
|
|
5
5
|
questions: Question[];
|
|
6
6
|
viewThreshold?: number;
|
|
7
|
+
/**
|
|
8
|
+
* Whether an empty `questions` array means "not loaded yet". False for a control-group
|
|
9
|
+
* placeholder, where no questions are ever fetched and the view must still be recorded.
|
|
10
|
+
*/
|
|
11
|
+
awaitQuestions?: boolean;
|
|
7
12
|
}
|
|
8
13
|
export interface UseViewportTrackingReturn {
|
|
9
14
|
containerRef: (node: HTMLDivElement | null) => void;
|
|
10
15
|
}
|
|
11
|
-
export default function useViewportTracking({ tracking, questions, viewThreshold, }: UseViewportTrackingProps): UseViewportTrackingReturn;
|
|
16
|
+
export default function useViewportTracking({ tracking, questions, viewThreshold, awaitQuestions, }: UseViewportTrackingProps): UseViewportTrackingReturn;
|
package/lib/types/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import CioPia from './components/CioPia';
|
|
2
2
|
export { default as CioPia } from './components/CioPia';
|
|
3
|
-
export type { CioPiaProps } from './components/CioPia';
|
|
3
|
+
export type { CioPiaProps, CheckoutTrigger } from './components/CioPia';
|
|
4
4
|
export { sanitizeHtml, renderMarkdown } from './utils/contentTransformers';
|
|
5
5
|
export type { RenderMarkdownOptions, SanitizeOptions } from './utils/contentTransformers';
|
|
6
6
|
export { AgentRequestError } from './errors';
|
package/lib/types/types.d.ts
CHANGED
|
@@ -24,6 +24,11 @@ export interface CioPiaProviderProps {
|
|
|
24
24
|
threadId?: string;
|
|
25
25
|
cioClient?: Nullable<CioClient>;
|
|
26
26
|
}
|
|
27
|
+
/**
|
|
28
|
+
* Display mode. `'recommendations'` is accepted but **not yet available**: the
|
|
29
|
+
* recommendations endpoint is absent from the client SDK, so that mode renders
|
|
30
|
+
* nothing. Use `'default'` or `'conversation'`.
|
|
31
|
+
*/
|
|
27
32
|
export type CioPiaMode = 'default' | 'conversation' | 'recommendations';
|
|
28
33
|
export type CioPiaType = 'inline' | 'modal';
|
|
29
34
|
export type DisclaimerPosition = 'top' | 'bottom';
|
|
@@ -34,6 +39,34 @@ export type CioPiaTrackingConfigs = {
|
|
|
34
39
|
*/
|
|
35
40
|
viewThreshold?: number;
|
|
36
41
|
};
|
|
42
|
+
export type CioPiaAbTest = {
|
|
43
|
+
/**
|
|
44
|
+
* Test cells to attach to PIA tracking events, as `{ [testName]: cellName }`. Each entry is
|
|
45
|
+
* sent as an `ef-<testName>` parameter. Source them however you like — Constructor's docs have
|
|
46
|
+
* the page set `window.cnstrc.testCell` to a bare cell name, so label it with your test name:
|
|
47
|
+
* `{ constructorio: window.cnstrc.testCell }`. Some integrations expose a `window.cnstrc.testCells`
|
|
48
|
+
* map instead, which you can pass straight through.
|
|
49
|
+
*
|
|
50
|
+
* Ignored when you supply your own `cioClient`: that client owns its own options, so set
|
|
51
|
+
* `testCells` there instead, as a `ConstructorIOClient` constructor option. Passing both
|
|
52
|
+
* logs a warning.
|
|
53
|
+
*
|
|
54
|
+
* Empty, non-string, or whitespace-only values are silently dropped, so a cell sourced from
|
|
55
|
+
* a global that resolves to `undefined` will simply not be sent.
|
|
56
|
+
*/
|
|
57
|
+
testCells?: Record<string, string>;
|
|
58
|
+
/**
|
|
59
|
+
* Mark this shopper as the A/B control group. Renders an invisible, tracking-only placeholder
|
|
60
|
+
* instead of the widget, so both arms of the test record a view event. Takes precedence over
|
|
61
|
+
* `displayConfigs.mode`. The placeholder renders neither `children` nor `componentOverrides`,
|
|
62
|
+
* and does not fire the `onView` / `onOutOfView` callbacks — the control arm reports through
|
|
63
|
+
* Constructor's own view event only.
|
|
64
|
+
*
|
|
65
|
+
* Required: stating the arm per shopper is the one thing only you can know, and leaving it
|
|
66
|
+
* out would silently put every shopper in the test arm with no control group at all.
|
|
67
|
+
*/
|
|
68
|
+
isControl: boolean;
|
|
69
|
+
};
|
|
37
70
|
export type CioPiaDisplayConfigs = {
|
|
38
71
|
learnMoreUrl?: string;
|
|
39
72
|
showFeedback?: boolean;
|
package/lib/types/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare const _default: "2.
|
|
1
|
+
declare const _default: "2.15.0";
|
|
2
2
|
export default _default;
|
package/package.json
CHANGED