@constructor-io/constructorio-ui-quizzes 1.3.1 → 1.3.3
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/constructorio-ui-quizzes-bundled.js +15 -15
- package/lib/cjs/components/BackButton/BackButton.js +1 -1
- package/lib/cjs/components/CioQuiz/actions.js +1 -0
- package/lib/cjs/components/CioQuiz/index.js +16 -3
- package/lib/cjs/components/CioQuiz/quizApiReducer.js +5 -5
- package/lib/cjs/components/CioQuiz/quizLocalReducer.js +2 -0
- package/lib/cjs/components/ResultCard/ResultCard.js +6 -4
- package/lib/cjs/components/SessionPromptModal/SessionPromptModal.js +24 -0
- package/lib/cjs/constants.js +3 -1
- package/lib/cjs/hooks/useQuiz.js +8 -5
- package/lib/cjs/hooks/useQuizApiState.js +16 -13
- package/lib/cjs/hooks/useQuizEvents/index.js +5 -1
- package/lib/cjs/hooks/useQuizLocalState.js +35 -1
- package/lib/mjs/components/BackButton/BackButton.js +1 -1
- package/lib/mjs/components/CioQuiz/actions.js +1 -0
- package/lib/mjs/components/CioQuiz/index.js +16 -3
- package/lib/mjs/components/CioQuiz/quizApiReducer.js +0 -2
- package/lib/mjs/components/CioQuiz/quizLocalReducer.js +5 -0
- package/lib/mjs/components/ResultCard/ResultCard.js +5 -3
- package/lib/mjs/components/SessionPromptModal/SessionPromptModal.js +20 -0
- package/lib/mjs/constants.js +2 -0
- package/lib/mjs/hooks/useQuiz.js +8 -5
- package/lib/mjs/hooks/useQuizApiState.js +17 -14
- package/lib/mjs/hooks/useQuizEvents/index.js +5 -1
- package/lib/mjs/hooks/useQuizLocalState.js +34 -3
- package/lib/styles.css +63 -6
- package/lib/types/components/CioQuiz/actions.d.ts +4 -2
- package/lib/types/components/CioQuiz/quizApiReducer.d.ts +0 -2
- package/lib/types/components/CioQuiz/quizLocalReducer.d.ts +2 -0
- package/lib/types/components/SessionPromptModal/SessionPromptModal.d.ts +9 -0
- package/lib/types/constants.d.ts +1 -0
- package/lib/types/hooks/useQuizApiState.d.ts +2 -1
- package/lib/types/hooks/useQuizEvents/index.d.ts +3 -0
- package/lib/types/hooks/useQuizLocalState.d.ts +6 -2
- package/lib/types/types.d.ts +11 -0
- package/package.json +1 -1
|
@@ -1,16 +1,47 @@
|
|
|
1
|
-
import { useReducer } from 'react';
|
|
1
|
+
import { useEffect, useReducer } from 'react';
|
|
2
2
|
import { QuestionTypes } from '../components/CioQuiz/actions';
|
|
3
|
-
import quizLocalReducer, { initialState } from '../components/CioQuiz/quizLocalReducer';
|
|
4
|
-
|
|
3
|
+
import quizLocalReducer, { initialState, } from '../components/CioQuiz/quizLocalReducer';
|
|
4
|
+
import { quizSessionStateKey } from '../constants';
|
|
5
|
+
const useQuizLocalState = (sessionStateKey) => {
|
|
5
6
|
const [quizLocalState, dispatch] = useReducer(quizLocalReducer, initialState);
|
|
7
|
+
const quizStateKey = sessionStateKey || quizSessionStateKey;
|
|
8
|
+
const getStateFromSessionStorage = () => {
|
|
9
|
+
const state = window?.sessionStorage?.getItem(quizStateKey);
|
|
10
|
+
if (state) {
|
|
11
|
+
return JSON.parse(state);
|
|
12
|
+
}
|
|
13
|
+
return null;
|
|
14
|
+
};
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
// don't save state if initial state
|
|
17
|
+
if (quizLocalState?.answers?.length) {
|
|
18
|
+
window?.sessionStorage?.setItem(quizStateKey, JSON.stringify(quizLocalState));
|
|
19
|
+
}
|
|
20
|
+
}, [quizLocalState, quizStateKey]);
|
|
6
21
|
const resetQuizLocalState = () => {
|
|
7
22
|
dispatch({
|
|
8
23
|
type: QuestionTypes.Reset,
|
|
9
24
|
});
|
|
10
25
|
};
|
|
26
|
+
const hydrateQuizLocalState = () => {
|
|
27
|
+
const quizState = getStateFromSessionStorage();
|
|
28
|
+
if (quizState) {
|
|
29
|
+
dispatch({
|
|
30
|
+
type: QuestionTypes.Hydrate,
|
|
31
|
+
payload: quizState,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
const resetQuizStoredState = () => {
|
|
36
|
+
window?.sessionStorage?.removeItem(quizStateKey);
|
|
37
|
+
};
|
|
38
|
+
const hasQuizStoredState = () => getStateFromSessionStorage() !== null;
|
|
11
39
|
return {
|
|
12
40
|
quizLocalState,
|
|
13
41
|
resetQuizLocalState,
|
|
42
|
+
hydrateQuizLocalState,
|
|
43
|
+
hasQuizStoredState,
|
|
44
|
+
resetQuizStoredState,
|
|
14
45
|
dispatchLocalState: dispatch,
|
|
15
46
|
};
|
|
16
47
|
};
|
package/lib/styles.css
CHANGED
|
@@ -73,6 +73,49 @@
|
|
|
73
73
|
padding-bottom: var(--bottom-bar-height);
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
+
/* Session Prompt Modal */
|
|
77
|
+
.cio-session-prompt-modal {
|
|
78
|
+
position: absolute;
|
|
79
|
+
display: flex;
|
|
80
|
+
z-index: 100;
|
|
81
|
+
width: 100%;
|
|
82
|
+
height: 100%;
|
|
83
|
+
background-color: rgba(0,0,0,0.5);
|
|
84
|
+
justify-content: space-between;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.cio-session-prompt-container {
|
|
88
|
+
margin: auto;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.cio-session-prompt-content {
|
|
92
|
+
display: flex;
|
|
93
|
+
flex-direction: column;
|
|
94
|
+
gap: 1rem;
|
|
95
|
+
height: fit-content;
|
|
96
|
+
margin: 2rem;
|
|
97
|
+
padding: 2rem;
|
|
98
|
+
border-radius: 8px;
|
|
99
|
+
background: #ffffff;
|
|
100
|
+
line-height: 1.5;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.cio-session-prompt-controls-container {
|
|
104
|
+
display: flex;
|
|
105
|
+
justify-content: space-between;
|
|
106
|
+
gap: 1rem;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.cio-session-prompt-content .cio-button-container {
|
|
110
|
+
margin: 0px;
|
|
111
|
+
padding: 0px;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.cio-session-prompt-controls-container .cio-button-container:nth-child(1) .cio-question-cta-button {
|
|
115
|
+
color: var(--primary-color);
|
|
116
|
+
background-color: #ffffff;
|
|
117
|
+
}
|
|
118
|
+
|
|
76
119
|
/* Content */
|
|
77
120
|
.cio-question-content {
|
|
78
121
|
display: flex;
|
|
@@ -113,17 +156,18 @@
|
|
|
113
156
|
width: 100%;
|
|
114
157
|
padding: 0.5rem;
|
|
115
158
|
border: 0;
|
|
116
|
-
border-bottom: 1px solid var(--
|
|
159
|
+
border-bottom: 1px solid var(--gray-dust-100);
|
|
117
160
|
color: var(--blue-denim-500);
|
|
118
161
|
padding: 0.8rem;
|
|
119
162
|
outline: none;
|
|
120
163
|
font-size: 1rem;
|
|
121
164
|
}
|
|
122
|
-
.cio-question-input-text:focus
|
|
165
|
+
.cio-question-input-text:focus,
|
|
166
|
+
.cio-question-input-text:active {
|
|
123
167
|
border-bottom: 1px solid var(--blue-denim-500);
|
|
124
168
|
}
|
|
125
169
|
.cio-question-input-text::placeholder {
|
|
126
|
-
color: var(--
|
|
170
|
+
color: var(--gray-dust-100);
|
|
127
171
|
}
|
|
128
172
|
|
|
129
173
|
/* CTA Button Component */
|
|
@@ -164,10 +208,15 @@
|
|
|
164
208
|
background: var(--blue-denim-700);
|
|
165
209
|
border: 2px solid var(--blue-denim-800);
|
|
166
210
|
}
|
|
211
|
+
.cio-question-cta-button:focus {
|
|
212
|
+
outline: 3px solid var(--blue-denim-100);
|
|
213
|
+
}
|
|
167
214
|
.cio-question-cta-button:disabled,
|
|
168
215
|
.cio-question-cta-button.disabled {
|
|
169
|
-
background: var(--
|
|
170
|
-
border: 2px solid var(--
|
|
216
|
+
background: var(--gray-dust-200);
|
|
217
|
+
border: 2px solid var(--gray-dust-200);
|
|
218
|
+
cursor: auto;
|
|
219
|
+
outline: none;
|
|
171
220
|
}
|
|
172
221
|
|
|
173
222
|
/* Back Button Component */
|
|
@@ -187,6 +236,9 @@
|
|
|
187
236
|
.cio-question-back-button:active {
|
|
188
237
|
border: 2px solid var(--blue-denim-700);
|
|
189
238
|
}
|
|
239
|
+
.cio-question-back-button:focus {
|
|
240
|
+
outline: 3px solid var(--blue-denim-100);
|
|
241
|
+
}
|
|
190
242
|
.cio-question-back-button svg {
|
|
191
243
|
display: inline;
|
|
192
244
|
}
|
|
@@ -379,7 +431,8 @@
|
|
|
379
431
|
display: flex;
|
|
380
432
|
flex-direction: column;
|
|
381
433
|
}
|
|
382
|
-
.cio-result-card-anchor
|
|
434
|
+
.cio-result-card-anchor,
|
|
435
|
+
.cio-result-card-container {
|
|
383
436
|
text-decoration: none;
|
|
384
437
|
background-color: transparent;
|
|
385
438
|
color: inherit;
|
|
@@ -404,6 +457,7 @@
|
|
|
404
457
|
display: flex;
|
|
405
458
|
flex-direction: column;
|
|
406
459
|
justify-content: space-between;
|
|
460
|
+
text-decoration: none;
|
|
407
461
|
}
|
|
408
462
|
.cio-result-card-title {
|
|
409
463
|
color: var(--gray-dust-500);
|
|
@@ -445,6 +499,9 @@
|
|
|
445
499
|
background: var(--blue-denim-700);
|
|
446
500
|
border: 2px solid var(--blue-denim-800);
|
|
447
501
|
}
|
|
502
|
+
.cio-result-card-cta-button:focus {
|
|
503
|
+
outline: 3px solid var(--blue-denim-100);
|
|
504
|
+
}
|
|
448
505
|
.cio-result-card-cta-button:disabled,
|
|
449
506
|
.cio-result-card-cta-button.disabled {
|
|
450
507
|
background: var(--blue-denim-200);
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { NextQuestionResponse, QuizResultsResponse } from '../../types';
|
|
2
|
+
import type { QuizLocalReducerState } from './quizLocalReducer';
|
|
2
3
|
export declare enum QuestionTypes {
|
|
3
4
|
OpenText = "open",
|
|
4
5
|
Cover = "cover",
|
|
5
6
|
SingleSelect = "single",
|
|
6
7
|
MultipleSelect = "multiple",
|
|
7
8
|
Back = "back",
|
|
8
|
-
Reset = "reset"
|
|
9
|
+
Reset = "reset",
|
|
10
|
+
Hydrate = "hydrate"
|
|
9
11
|
}
|
|
10
12
|
export interface QuestionAnswer<Value> {
|
|
11
13
|
questionId: number;
|
|
@@ -22,7 +24,7 @@ interface Action<Type, Payload = {}> {
|
|
|
22
24
|
payload?: Payload;
|
|
23
25
|
}
|
|
24
26
|
export type ActionAnswerInputQuestion = Action<QuestionTypes.OpenText, OpenTextQuestionPayload> | Action<QuestionTypes.SingleSelect, SelectQuestionPayload> | Action<QuestionTypes.MultipleSelect, SelectQuestionPayload>;
|
|
25
|
-
export type ActionAnswerQuestion = ActionAnswerInputQuestion | Action<QuestionTypes.Cover, CoverQuestionPayload> | Action<QuestionTypes.Back> | Action<QuestionTypes.Reset
|
|
27
|
+
export type ActionAnswerQuestion = ActionAnswerInputQuestion | Action<QuestionTypes.Cover, CoverQuestionPayload> | Action<QuestionTypes.Back> | Action<QuestionTypes.Reset> | Action<QuestionTypes.Hydrate, Partial<QuizLocalReducerState>>;
|
|
26
28
|
export declare enum QuizAPIActionTypes {
|
|
27
29
|
SET_IS_LOADING = 0,
|
|
28
30
|
SET_IS_ERROR = 1,
|
|
@@ -3,8 +3,6 @@ import { NextQuestionResponse, QuizResultsResponse } from '../../types';
|
|
|
3
3
|
import { ActionQuizAPI } from './actions';
|
|
4
4
|
export type QuizAPIReducerState = {
|
|
5
5
|
quizRequestState: RequestStates;
|
|
6
|
-
quizVersionId?: string;
|
|
7
|
-
quizSessionId?: string;
|
|
8
6
|
quizFirstQuestion?: NextQuestionResponse;
|
|
9
7
|
quizCurrentQuestion?: NextQuestionResponse;
|
|
10
8
|
quizResults?: QuizResultsResponse;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
interface SessionPromptModalProps {
|
|
3
|
+
continueSession: () => void;
|
|
4
|
+
resetStoredState: () => void;
|
|
5
|
+
setShowSessionPrompt: (showSessionPrompt: boolean) => void;
|
|
6
|
+
showSessionPrompt: boolean;
|
|
7
|
+
}
|
|
8
|
+
export default function SessionPromptModal({ continueSession, resetStoredState, setShowSessionPrompt, showSessionPrompt, }: SessionPromptModalProps): JSX.Element | null;
|
|
9
|
+
export {};
|
package/lib/types/constants.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export declare const apiKey = "key_wJSdZSiesX5hiVLt";
|
|
2
2
|
export declare const quizId = "coffee-quiz";
|
|
3
|
+
export declare const quizSessionStateKey = "constructorIOQuizState";
|
|
3
4
|
export declare const componentDescription = "- import `CioQuiz` to render in your JSX.\n- This component handles state management, data fetching, and rendering logic.\n- To use this component, `quizId`, `resultsPageOptions`, and one of `apiKey` or `cioJsClient` are required.\n- `resultsPageOptions` lets you configure the results page\n - `onAddToCartClick` is a callback function that will be called when the \"Add to cart\" button is clicked\n - `onQuizResultClick` is an optional callback function that will be called when the result card is clicked. The default behavior is redirecting the user to the item's URL\n - `onQuizResultsLoaded` is an optional callback function that will be called when the quiz results are loaded\n - `resultCardRegularPriceKey` is a parameter that specifies the metadata field name for the regular price\n - `resultCardSalePriceKey` is an optional parameter that specifies the metadata field name for the sale price\n- Use different props to configure the behavior of this component.\n- The following stories show how different props affect the component's behavior\n\n> Note: `cioJsClient` refers to an instance of the [constructorio-client-javascript](https://www.npmjs.com/package/@constructor-io/constructorio-client-javascript)\n";
|
|
4
5
|
export declare const basicDescription = "Pass an `apiKey` and a `quizId` to request questions and quiz results from Constructor's servers";
|
|
5
6
|
export declare const cioJsClientDescription = "If you are already using an instance of the `ConstructorIOClient`, you can pass a `cioJsClient` instead of an `apiKey` to request results from Constructor's servers\n\n> Note: `cioJsClient` refers to an instance of the [constructorio-client-javascript](https://www.npmjs.com/package/@constructor-io/constructorio-client-javascript)";
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import ConstructorIOClient from '@constructor-io/constructorio-client-javascript';
|
|
2
|
+
import { ActionAnswerQuestion } from '../components/CioQuiz/actions';
|
|
2
3
|
import { QuizLocalReducerState } from '../components/CioQuiz/quizLocalReducer';
|
|
3
4
|
import { ResultsPageOptions } from '../types';
|
|
4
|
-
declare const useFetchQuiz: (quizId: string, quizLocalState: QuizLocalReducerState, resultsPageOptions: ResultsPageOptions, quizVersionIdProp: string | undefined, cioClient: ConstructorIOClient) => {
|
|
5
|
+
declare const useFetchQuiz: (quizId: string, quizLocalState: QuizLocalReducerState, dispatchLocalState: React.Dispatch<ActionAnswerQuestion>, resultsPageOptions: ResultsPageOptions, quizVersionIdProp: string | undefined, cioClient: ConstructorIOClient) => {
|
|
5
6
|
cioClient: ConstructorIOClient;
|
|
6
7
|
quizApiState: import("../components/CioQuiz/quizApiReducer").QuizAPIReducerState;
|
|
7
8
|
isFirstQuestion: boolean;
|
|
@@ -10,6 +10,9 @@ export type UseQuizEventOptions = {
|
|
|
10
10
|
dispatchLocalState: React.Dispatch<ActionAnswerQuestion>;
|
|
11
11
|
resetQuizApiState: () => void;
|
|
12
12
|
resetQuizLocalState: () => void;
|
|
13
|
+
hydrateQuizLocalState: () => void;
|
|
14
|
+
resetQuizStoredState: () => void;
|
|
15
|
+
hasQuizStoredState: () => boolean;
|
|
13
16
|
};
|
|
14
17
|
declare const useQuizEvents: (options: UseQuizEventOptions) => QuizEventsReturn;
|
|
15
18
|
export default useQuizEvents;
|
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { QuizLocalReducerState } from '../components/CioQuiz/quizLocalReducer';
|
|
2
|
+
declare const useQuizLocalState: (sessionStateKey?: string) => {
|
|
3
|
+
quizLocalState: QuizLocalReducerState;
|
|
3
4
|
resetQuizLocalState: () => void;
|
|
5
|
+
hydrateQuizLocalState: () => void;
|
|
6
|
+
hasQuizStoredState: () => boolean;
|
|
7
|
+
resetQuizStoredState: () => void;
|
|
4
8
|
dispatchLocalState: import("react").Dispatch<import("../components/CioQuiz/actions").ActionAnswerQuestion>;
|
|
5
9
|
};
|
|
6
10
|
export default useQuizLocalState;
|
package/lib/types/types.d.ts
CHANGED
|
@@ -19,12 +19,17 @@ export interface ResultsPageOptions extends ResultsProps {
|
|
|
19
19
|
onQuizResultClick?: QuizResultsEventsProps.OnQuizResultClick;
|
|
20
20
|
onAddToCartClick: QuizResultsEventsProps.OnAddToCartClick;
|
|
21
21
|
}
|
|
22
|
+
export interface SessionStateOptions {
|
|
23
|
+
showSessionModal?: boolean;
|
|
24
|
+
sessionStateKey?: string;
|
|
25
|
+
}
|
|
22
26
|
export interface IQuizProps {
|
|
23
27
|
apiKey?: string;
|
|
24
28
|
cioJsClient?: ConstructorIOClient;
|
|
25
29
|
quizId: string;
|
|
26
30
|
quizVersionId?: string;
|
|
27
31
|
resultsPageOptions: ResultsPageOptions;
|
|
32
|
+
sessionStateOptions?: SessionStateOptions;
|
|
28
33
|
}
|
|
29
34
|
export interface QuizReturnState {
|
|
30
35
|
answers: {
|
|
@@ -48,6 +53,9 @@ export declare namespace QuizEventsReturn {
|
|
|
48
53
|
type ResetQuiz = () => void;
|
|
49
54
|
type ResultClick = (result: QuizResultDataPartial, position: number) => void;
|
|
50
55
|
type AddToCart = (e: React.MouseEvent<HTMLElement>, result: QuizResultDataPartial, price?: number) => void;
|
|
56
|
+
type HydrateQuiz = () => void;
|
|
57
|
+
type HasStoredState = () => boolean;
|
|
58
|
+
type ResetStoredState = () => void;
|
|
51
59
|
}
|
|
52
60
|
export interface QuizEventsReturn {
|
|
53
61
|
nextQuestion: QuizEventsReturn.NextQuestion;
|
|
@@ -55,6 +63,9 @@ export interface QuizEventsReturn {
|
|
|
55
63
|
resetQuiz: QuizEventsReturn.ResetQuiz;
|
|
56
64
|
resultClick: QuizEventsReturn.ResultClick;
|
|
57
65
|
addToCart: QuizEventsReturn.AddToCart;
|
|
66
|
+
hydrateQuiz: QuizEventsReturn.HydrateQuiz;
|
|
67
|
+
hasStoredState: QuizEventsReturn.HasStoredState;
|
|
68
|
+
resetStoredState: QuizEventsReturn.ResetStoredState;
|
|
58
69
|
}
|
|
59
70
|
export interface UseQuizReturn {
|
|
60
71
|
cioClient?: ConstructorIOClient;
|