@constructor-io/constructorio-ui-quizzes 1.3.2 → 1.3.4
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/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/CoverTypeQuestion/CoverTypeQuestion.js +1 -1
- package/lib/cjs/components/OpenTextTypeQuestion/OpenTextTypeQuestion.js +1 -1
- package/lib/cjs/components/SelectTypeQuestion/SelectTypeQuestion.js +2 -2
- 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/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/CoverTypeQuestion/CoverTypeQuestion.js +1 -1
- package/lib/mjs/components/OpenTextTypeQuestion/OpenTextTypeQuestion.js +1 -1
- package/lib/mjs/components/SelectTypeQuestion/SelectTypeQuestion.js +2 -2
- 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 +43 -0
- 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
|
@@ -10,6 +10,7 @@ var QuestionTypes;
|
|
|
10
10
|
QuestionTypes["MultipleSelect"] = "multiple";
|
|
11
11
|
QuestionTypes["Back"] = "back";
|
|
12
12
|
QuestionTypes["Reset"] = "reset";
|
|
13
|
+
QuestionTypes["Hydrate"] = "hydrate";
|
|
13
14
|
})(QuestionTypes = exports.QuestionTypes || (exports.QuestionTypes = {}));
|
|
14
15
|
// API actions
|
|
15
16
|
var QuizAPIActionTypes;
|
|
@@ -1,16 +1,28 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const tslib_1 = require("tslib");
|
|
4
|
-
const react_1 = tslib_1.
|
|
4
|
+
const react_1 = tslib_1.__importStar(require("react"));
|
|
5
5
|
const context_1 = tslib_1.__importDefault(require("./context"));
|
|
6
6
|
const QuizQuestions_1 = tslib_1.__importDefault(require("../QuizQuestions"));
|
|
7
7
|
const ResultContainer_1 = tslib_1.__importDefault(require("../ResultContainer/ResultContainer"));
|
|
8
8
|
const constants_1 = require("../../constants");
|
|
9
9
|
const Spinner_1 = tslib_1.__importDefault(require("../Spinner/Spinner"));
|
|
10
10
|
const useQuiz_1 = tslib_1.__importDefault(require("../../hooks/useQuiz"));
|
|
11
|
+
const SessionPromptModal_1 = tslib_1.__importDefault(require("../SessionPromptModal/SessionPromptModal"));
|
|
11
12
|
function CioQuiz(props) {
|
|
12
|
-
const { cioClient, state, events: { nextQuestion, previousQuestion, resetQuiz, addToCart, resultClick }, } = (0, useQuiz_1.default)(props);
|
|
13
|
-
const
|
|
13
|
+
const { cioClient, state, events: { nextQuestion, previousQuestion, resetQuiz, addToCart, resultClick, hydrateQuiz, hasStoredState, resetStoredState, }, } = (0, useQuiz_1.default)(props);
|
|
14
|
+
const [showSessionPrompt, setShowSessionPrompt] = (0, react_1.useState)(false);
|
|
15
|
+
const { resultsPageOptions, sessionStateOptions } = props;
|
|
16
|
+
(0, react_1.useEffect)(() => {
|
|
17
|
+
// Respect showSessionModal if defined, else default to true.
|
|
18
|
+
if ((sessionStateOptions === null || sessionStateOptions === void 0 ? void 0 : sessionStateOptions.showSessionModal) !== undefined) {
|
|
19
|
+
setShowSessionPrompt((sessionStateOptions === null || sessionStateOptions === void 0 ? void 0 : sessionStateOptions.showSessionModal) && hasStoredState());
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
setShowSessionPrompt(hasStoredState());
|
|
23
|
+
}
|
|
24
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
25
|
+
}, []);
|
|
14
26
|
const contextValue = {
|
|
15
27
|
cioClient,
|
|
16
28
|
state,
|
|
@@ -27,6 +39,7 @@ function CioQuiz(props) {
|
|
|
27
39
|
}
|
|
28
40
|
if (state.quiz.requestState === constants_1.RequestStates.Success) {
|
|
29
41
|
return (react_1.default.createElement("div", { className: 'cio-quiz' },
|
|
42
|
+
react_1.default.createElement(SessionPromptModal_1.default, { resetStoredState: resetStoredState, continueSession: hydrateQuiz, showSessionPrompt: showSessionPrompt, setShowSessionPrompt: setShowSessionPrompt }),
|
|
30
43
|
react_1.default.createElement(context_1.default.Provider, { value: contextValue },
|
|
31
44
|
state.quiz.results && react_1.default.createElement(ResultContainer_1.default, { options: resultsPageOptions }),
|
|
32
45
|
state.quiz.currentQuestion && react_1.default.createElement(QuizQuestions_1.default, null))));
|
|
@@ -8,20 +8,20 @@ exports.initialState = {
|
|
|
8
8
|
quizRequestState: constants_1.RequestStates.Stale,
|
|
9
9
|
};
|
|
10
10
|
function apiReducer(state, action) {
|
|
11
|
-
var _a, _b, _c, _d, _e, _f
|
|
11
|
+
var _a, _b, _c, _d, _e, _f;
|
|
12
12
|
switch (action.type) {
|
|
13
13
|
case actions_1.QuizAPIActionTypes.SET_IS_LOADING:
|
|
14
14
|
return Object.assign(Object.assign({}, state), { quizRequestState: constants_1.RequestStates.Loading });
|
|
15
15
|
case actions_1.QuizAPIActionTypes.SET_IS_ERROR:
|
|
16
16
|
return Object.assign(Object.assign({}, state), { quizRequestState: constants_1.RequestStates.Error, quizResults: undefined });
|
|
17
17
|
case actions_1.QuizAPIActionTypes.SET_CURRENT_QUESTION:
|
|
18
|
-
return Object.assign(Object.assign(Object.assign({}, state), { quizRequestState: constants_1.RequestStates.Success,
|
|
19
|
-
quizFirstQuestion: (
|
|
18
|
+
return Object.assign(Object.assign(Object.assign({}, state), { quizRequestState: constants_1.RequestStates.Success, quizCurrentQuestion: (_a = action.payload) === null || _a === void 0 ? void 0 : _a.quizCurrentQuestion, quizResults: undefined }), (!state.quizCurrentQuestion && {
|
|
19
|
+
quizFirstQuestion: (_b = action.payload) === null || _b === void 0 ? void 0 : _b.quizCurrentQuestion,
|
|
20
20
|
}));
|
|
21
21
|
case actions_1.QuizAPIActionTypes.SET_QUIZ_RESULTS: {
|
|
22
|
-
const filterExpression = ((
|
|
22
|
+
const filterExpression = ((_e = (_d = (_c = action.payload) === null || _c === void 0 ? void 0 : _c.quizResults) === null || _d === void 0 ? void 0 : _d.request) === null || _e === void 0 ? void 0 : _e.collection_filter_expression) || null;
|
|
23
23
|
const quizResultsFilters = [...new Set((0, utils_1.getFilterValuesFromExpression)(filterExpression))];
|
|
24
|
-
return Object.assign(Object.assign({}, state), { quizRequestState: constants_1.RequestStates.Success, quizResults: (
|
|
24
|
+
return Object.assign(Object.assign({}, state), { quizRequestState: constants_1.RequestStates.Success, quizResults: (_f = action.payload) === null || _f === void 0 ? void 0 : _f.quizResults, quizResultsFilters, quizCurrentQuestion: undefined });
|
|
25
25
|
}
|
|
26
26
|
case actions_1.QuizAPIActionTypes.RESET_QUIZ:
|
|
27
27
|
return exports.initialState;
|
|
@@ -25,6 +25,8 @@ function quizLocalReducer(state, action) {
|
|
|
25
25
|
return Object.assign(Object.assign({}, state), { answers: [...state.answers.slice(0, -1)], isLastAnswer: false });
|
|
26
26
|
case actions_1.QuestionTypes.Reset:
|
|
27
27
|
return Object.assign({}, exports.initialState);
|
|
28
|
+
case actions_1.QuestionTypes.Hydrate:
|
|
29
|
+
return Object.assign(Object.assign({}, state), action.payload);
|
|
28
30
|
default:
|
|
29
31
|
return state;
|
|
30
32
|
}
|
|
@@ -24,7 +24,7 @@ function CoverTypeQuestion() {
|
|
|
24
24
|
return (react_1.default.createElement("div", { className: `
|
|
25
25
|
cio-container${hasImage ? '--with-image' : ''}
|
|
26
26
|
cio-cover-question-container${hasImage ? '--with-image' : ''}
|
|
27
|
-
|
|
27
|
+
`, "data-question-key": question.key },
|
|
28
28
|
react_1.default.createElement("div", { className: 'cio-question-content' },
|
|
29
29
|
react_1.default.createElement(QuestionTitle_1.default, { title: question === null || question === void 0 ? void 0 : question.title }),
|
|
30
30
|
react_1.default.createElement(QuestionDescription_1.default, { description: question.description }),
|
|
@@ -47,7 +47,7 @@ function OpenTextQuestion(props) {
|
|
|
47
47
|
return (react_1.default.createElement("div", { className: `
|
|
48
48
|
cio-container${hasImage ? '--with-image' : ''}
|
|
49
49
|
cio-open-text-question-container${hasImage ? '--with-image' : ''}
|
|
50
|
-
|
|
50
|
+
`, "data-question-key": question.key },
|
|
51
51
|
hasImage ? (0, utils_1.renderImages)(question.images, 'cio-question-image-container') : '',
|
|
52
52
|
react_1.default.createElement("div", { className: 'cio-question-content' },
|
|
53
53
|
react_1.default.createElement(QuestionTitle_1.default, { title: question.title }),
|
|
@@ -61,7 +61,7 @@ function SelectTypeQuestion() {
|
|
|
61
61
|
}
|
|
62
62
|
};
|
|
63
63
|
if (question) {
|
|
64
|
-
return (react_1.default.createElement("div", { className: 'cio-select-question-container' },
|
|
64
|
+
return (react_1.default.createElement("div", { className: 'cio-select-question-container', "data-question-key": question.key },
|
|
65
65
|
react_1.default.createElement("div", { className: 'cio-select-question-text' },
|
|
66
66
|
react_1.default.createElement(QuestionTitle_1.default, { title: question.title }),
|
|
67
67
|
(question === null || question === void 0 ? void 0 : question.description) ? react_1.default.createElement(QuestionDescription_1.default, { description: question.description }) : ''),
|
|
@@ -69,7 +69,7 @@ function SelectTypeQuestion() {
|
|
|
69
69
|
? 'cio-question-options-container-text-only'
|
|
70
70
|
: 'cio-question-options-container'}` }, (_b = question === null || question === void 0 ? void 0 : question.options) === null || _b === void 0 ? void 0 : _b.map((option) => (react_1.default.createElement("div", { className: `${!hasImages
|
|
71
71
|
? 'cio-question-option-container-text-only'
|
|
72
|
-
: 'cio-question-option-container'} ${selected[option.id] ? 'selected' : ''}`, onClick: () => {
|
|
72
|
+
: 'cio-question-option-container'} ${selected[option.id] ? 'selected' : ''}`, "data-question-option-key": option.key, onClick: () => {
|
|
73
73
|
toggleIdSelected(option.id);
|
|
74
74
|
}, onKeyDown: (event) => {
|
|
75
75
|
onOptionKeyDown(event, option.id);
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
const react_1 = tslib_1.__importDefault(require("react"));
|
|
5
|
+
const CTAButton_1 = tslib_1.__importDefault(require("../CTAButton/CTAButton"));
|
|
6
|
+
function SessionPromptModal({ continueSession, resetStoredState, setShowSessionPrompt, showSessionPrompt, }) {
|
|
7
|
+
const onNoClickHandler = () => {
|
|
8
|
+
resetStoredState();
|
|
9
|
+
setShowSessionPrompt(false);
|
|
10
|
+
};
|
|
11
|
+
if (showSessionPrompt)
|
|
12
|
+
return (react_1.default.createElement("div", { className: 'cio-session-prompt-modal', role: 'presentation', onClick: onNoClickHandler },
|
|
13
|
+
react_1.default.createElement("div", { className: 'cio-session-prompt-container' },
|
|
14
|
+
react_1.default.createElement("div", { className: 'cio-session-prompt-content', role: 'presentation', onClick: (e) => e.stopPropagation() },
|
|
15
|
+
react_1.default.createElement("div", null, "Do you want to continue where you left off?"),
|
|
16
|
+
react_1.default.createElement("div", { className: 'cio-session-prompt-controls-container' },
|
|
17
|
+
react_1.default.createElement(CTAButton_1.default, { ctaText: 'No', type: 'button', onClick: onNoClickHandler }),
|
|
18
|
+
react_1.default.createElement(CTAButton_1.default, { type: 'button', ctaText: 'Yes', onClick: () => {
|
|
19
|
+
continueSession();
|
|
20
|
+
setShowSessionPrompt(false);
|
|
21
|
+
} }))))));
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
exports.default = SessionPromptModal;
|
package/lib/cjs/constants.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.RequestStates = exports.smallContainerDescription = exports.cioJsClientDescription = exports.basicDescription = exports.componentDescription = exports.quizId = exports.apiKey = void 0;
|
|
3
|
+
exports.RequestStates = exports.smallContainerDescription = exports.cioJsClientDescription = exports.basicDescription = exports.componentDescription = exports.quizSessionStateKey = exports.quizId = exports.apiKey = void 0;
|
|
4
4
|
// Autocomplete key index
|
|
5
5
|
exports.apiKey = 'key_wJSdZSiesX5hiVLt';
|
|
6
6
|
exports.quizId = 'coffee-quiz';
|
|
7
|
+
// Session Storage default key
|
|
8
|
+
exports.quizSessionStateKey = 'constructorIOQuizState';
|
|
7
9
|
/// //////////////////////////////
|
|
8
10
|
// Storybook Folder Descriptions
|
|
9
11
|
/// //////////////////////////////
|
package/lib/cjs/hooks/useQuiz.js
CHANGED
|
@@ -6,15 +6,15 @@ const useConsoleErrors_1 = tslib_1.__importDefault(require("./useConsoleErrors")
|
|
|
6
6
|
const useQuizApiState_1 = tslib_1.__importDefault(require("./useQuizApiState"));
|
|
7
7
|
const useQuizEvents_1 = tslib_1.__importDefault(require("./useQuizEvents"));
|
|
8
8
|
const useQuizLocalState_1 = tslib_1.__importDefault(require("./useQuizLocalState"));
|
|
9
|
-
const useQuiz = ({ quizId, apiKey, cioJsClient, quizVersionId, resultsPageOptions }) => {
|
|
9
|
+
const useQuiz = ({ quizId, apiKey, cioJsClient, quizVersionId, resultsPageOptions, sessionStateOptions, }) => {
|
|
10
10
|
// Log console errors for required parameters quizId and resultsPageOptions
|
|
11
11
|
(0, useConsoleErrors_1.default)(quizId, resultsPageOptions);
|
|
12
12
|
// Quiz Local state
|
|
13
|
-
const { quizLocalState, resetQuizLocalState, dispatchLocalState } = (0, useQuizLocalState_1.default)();
|
|
13
|
+
const { quizLocalState, resetQuizLocalState, dispatchLocalState, hydrateQuizLocalState, hasQuizStoredState, resetQuizStoredState, } = (0, useQuizLocalState_1.default)(sessionStateOptions === null || sessionStateOptions === void 0 ? void 0 : sessionStateOptions.sessionStateKey);
|
|
14
14
|
// Quiz Cio Client
|
|
15
15
|
const cioClient = (0, useCioClient_1.default)({ apiKey, cioJsClient });
|
|
16
16
|
// Quiz API state
|
|
17
|
-
const { isFirstQuestion, quizApiState, resetQuizApiState } = (0, useQuizApiState_1.default)(quizId, quizLocalState, resultsPageOptions, quizVersionId, cioClient);
|
|
17
|
+
const { isFirstQuestion, quizApiState, resetQuizApiState } = (0, useQuizApiState_1.default)(quizId, quizLocalState, dispatchLocalState, resultsPageOptions, quizVersionId, cioClient);
|
|
18
18
|
// Quiz callback events
|
|
19
19
|
const quizEvents = (0, useQuizEvents_1.default)({
|
|
20
20
|
cioClient,
|
|
@@ -23,6 +23,9 @@ const useQuiz = ({ quizId, apiKey, cioJsClient, quizVersionId, resultsPageOption
|
|
|
23
23
|
dispatchLocalState,
|
|
24
24
|
resetQuizApiState,
|
|
25
25
|
resetQuizLocalState,
|
|
26
|
+
hydrateQuizLocalState,
|
|
27
|
+
resetQuizStoredState,
|
|
28
|
+
hasQuizStoredState,
|
|
26
29
|
});
|
|
27
30
|
return {
|
|
28
31
|
cioClient,
|
|
@@ -33,8 +36,8 @@ const useQuiz = ({ quizId, apiKey, cioJsClient, quizVersionId, resultsPageOption
|
|
|
33
36
|
},
|
|
34
37
|
quiz: {
|
|
35
38
|
requestState: quizApiState.quizRequestState,
|
|
36
|
-
versionId:
|
|
37
|
-
sessionId:
|
|
39
|
+
versionId: quizLocalState.quizVersionId,
|
|
40
|
+
sessionId: quizLocalState.quizSessionId,
|
|
38
41
|
firstQuestion: quizApiState.quizFirstQuestion,
|
|
39
42
|
currentQuestion: quizApiState.quizCurrentQuestion,
|
|
40
43
|
results: quizApiState.quizResults,
|
|
@@ -5,7 +5,7 @@ const react_1 = require("react");
|
|
|
5
5
|
const actions_1 = require("../components/CioQuiz/actions");
|
|
6
6
|
const quizApiReducer_1 = tslib_1.__importStar(require("../components/CioQuiz/quizApiReducer"));
|
|
7
7
|
const services_1 = require("../services");
|
|
8
|
-
const useFetchQuiz = (quizId, quizLocalState, resultsPageOptions, quizVersionIdProp, cioClient) => {
|
|
8
|
+
const useFetchQuiz = (quizId, quizLocalState, dispatchLocalState, resultsPageOptions, quizVersionIdProp, cioClient) => {
|
|
9
9
|
var _a, _b;
|
|
10
10
|
const [quizApiState, dispatch] = (0, react_1.useReducer)(quizApiReducer_1.default, quizApiReducer_1.initialState);
|
|
11
11
|
const firstQuestionId = (_a = quizApiState.quizFirstQuestion) === null || _a === void 0 ? void 0 : _a.next_question.id;
|
|
@@ -21,8 +21,8 @@ const useFetchQuiz = (quizId, quizLocalState, resultsPageOptions, quizVersionIdP
|
|
|
21
21
|
const quizResults = yield (0, services_1.getQuizResults)(cioClient, quizId, {
|
|
22
22
|
answers: quizLocalState.answers,
|
|
23
23
|
resultsPerPage: resultsPageOptions === null || resultsPageOptions === void 0 ? void 0 : resultsPageOptions.numResultsToDisplay,
|
|
24
|
-
quizVersionId:
|
|
25
|
-
quizSessionId:
|
|
24
|
+
quizVersionId: quizLocalState.quizVersionId,
|
|
25
|
+
quizSessionId: quizLocalState.quizSessionId,
|
|
26
26
|
});
|
|
27
27
|
// Set quiz results state
|
|
28
28
|
dispatch({
|
|
@@ -40,25 +40,28 @@ const useFetchQuiz = (quizId, quizLocalState, resultsPageOptions, quizVersionIdP
|
|
|
40
40
|
}
|
|
41
41
|
else {
|
|
42
42
|
try {
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
const quizVersionId = quizLocalState.quizVersionId || quizVersionIdProp;
|
|
44
|
+
const { quizSessionId } = quizLocalState;
|
|
45
45
|
const questionResult = yield (0, services_1.nextQuestion)(cioClient, quizId, {
|
|
46
46
|
answers: quizLocalState.answers,
|
|
47
47
|
quizVersionId,
|
|
48
48
|
quizSessionId,
|
|
49
49
|
});
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
50
|
+
// Update quizSessionId, quizVersionId
|
|
51
|
+
if ((!quizSessionId && (questionResult === null || questionResult === void 0 ? void 0 : questionResult.quiz_session_id)) ||
|
|
52
|
+
(!quizVersionId && questionResult.quiz_version_id)) {
|
|
53
|
+
dispatchLocalState({
|
|
54
|
+
type: actions_1.QuestionTypes.Hydrate,
|
|
55
|
+
payload: {
|
|
56
|
+
quizVersionId: questionResult === null || questionResult === void 0 ? void 0 : questionResult.quiz_version_id,
|
|
57
|
+
quizSessionId: questionResult === null || questionResult === void 0 ? void 0 : questionResult.quiz_session_id,
|
|
58
|
+
},
|
|
59
|
+
});
|
|
55
60
|
}
|
|
56
61
|
// Set current question state
|
|
57
62
|
dispatch({
|
|
58
63
|
type: actions_1.QuizAPIActionTypes.SET_CURRENT_QUESTION,
|
|
59
64
|
payload: {
|
|
60
|
-
quizSessionId,
|
|
61
|
-
quizVersionId,
|
|
62
65
|
quizCurrentQuestion: questionResult,
|
|
63
66
|
},
|
|
64
67
|
});
|
|
@@ -74,7 +77,7 @@ const useFetchQuiz = (quizId, quizLocalState, resultsPageOptions, quizVersionIdP
|
|
|
74
77
|
}, [
|
|
75
78
|
cioClient,
|
|
76
79
|
quizId,
|
|
77
|
-
quizLocalState,
|
|
80
|
+
quizLocalState.answers,
|
|
78
81
|
quizLocalState.isLastAnswer,
|
|
79
82
|
resultsPageOptions === null || resultsPageOptions === void 0 ? void 0 : resultsPageOptions.numResultsToDisplay,
|
|
80
83
|
]);
|
|
@@ -7,7 +7,7 @@ const useQuizAddToCart_1 = tslib_1.__importDefault(require("./useQuizAddToCart")
|
|
|
7
7
|
const useQuizNextClick_1 = tslib_1.__importDefault(require("./useQuizNextClick"));
|
|
8
8
|
const useQuizBackClick_1 = tslib_1.__importDefault(require("./useQuizBackClick"));
|
|
9
9
|
const useQuizEvents = (options) => {
|
|
10
|
-
const { cioClient, quizApiState, resultsPageOptions, dispatchLocalState, resetQuizApiState, resetQuizLocalState, } = options;
|
|
10
|
+
const { cioClient, quizApiState, resultsPageOptions, dispatchLocalState, resetQuizApiState, resetQuizLocalState, hydrateQuizLocalState, resetQuizStoredState, hasQuizStoredState, } = options;
|
|
11
11
|
const { onAddToCartClick, onQuizResultClick, onQuizResultsLoaded } = resultsPageOptions;
|
|
12
12
|
// Quiz Next button click
|
|
13
13
|
const nextQuestion = (0, useQuizNextClick_1.default)(quizApiState, dispatchLocalState);
|
|
@@ -23,6 +23,7 @@ const useQuizEvents = (options) => {
|
|
|
23
23
|
if (quizApiState.quizResults) {
|
|
24
24
|
resetQuizApiState();
|
|
25
25
|
resetQuizLocalState();
|
|
26
|
+
resetQuizStoredState();
|
|
26
27
|
}
|
|
27
28
|
};
|
|
28
29
|
return {
|
|
@@ -31,6 +32,9 @@ const useQuizEvents = (options) => {
|
|
|
31
32
|
nextQuestion,
|
|
32
33
|
previousQuestion,
|
|
33
34
|
resetQuiz,
|
|
35
|
+
hydrateQuiz: hydrateQuizLocalState,
|
|
36
|
+
hasStoredState: hasQuizStoredState,
|
|
37
|
+
resetStoredState: resetQuizStoredState,
|
|
34
38
|
};
|
|
35
39
|
};
|
|
36
40
|
exports.default = useQuizEvents;
|
|
@@ -4,16 +4,50 @@ const tslib_1 = require("tslib");
|
|
|
4
4
|
const react_1 = require("react");
|
|
5
5
|
const actions_1 = require("../components/CioQuiz/actions");
|
|
6
6
|
const quizLocalReducer_1 = tslib_1.__importStar(require("../components/CioQuiz/quizLocalReducer"));
|
|
7
|
-
const
|
|
7
|
+
const constants_1 = require("../constants");
|
|
8
|
+
const useQuizLocalState = (sessionStateKey) => {
|
|
8
9
|
const [quizLocalState, dispatch] = (0, react_1.useReducer)(quizLocalReducer_1.default, quizLocalReducer_1.initialState);
|
|
10
|
+
const quizStateKey = sessionStateKey || constants_1.quizSessionStateKey;
|
|
11
|
+
const getStateFromSessionStorage = () => {
|
|
12
|
+
var _a;
|
|
13
|
+
const state = (_a = window === null || window === void 0 ? void 0 : window.sessionStorage) === null || _a === void 0 ? void 0 : _a.getItem(quizStateKey);
|
|
14
|
+
if (state) {
|
|
15
|
+
return JSON.parse(state);
|
|
16
|
+
}
|
|
17
|
+
return null;
|
|
18
|
+
};
|
|
19
|
+
(0, react_1.useEffect)(() => {
|
|
20
|
+
var _a, _b;
|
|
21
|
+
// don't save state if initial state
|
|
22
|
+
if ((_a = quizLocalState === null || quizLocalState === void 0 ? void 0 : quizLocalState.answers) === null || _a === void 0 ? void 0 : _a.length) {
|
|
23
|
+
(_b = window === null || window === void 0 ? void 0 : window.sessionStorage) === null || _b === void 0 ? void 0 : _b.setItem(quizStateKey, JSON.stringify(quizLocalState));
|
|
24
|
+
}
|
|
25
|
+
}, [quizLocalState, quizStateKey]);
|
|
9
26
|
const resetQuizLocalState = () => {
|
|
10
27
|
dispatch({
|
|
11
28
|
type: actions_1.QuestionTypes.Reset,
|
|
12
29
|
});
|
|
13
30
|
};
|
|
31
|
+
const hydrateQuizLocalState = () => {
|
|
32
|
+
const quizState = getStateFromSessionStorage();
|
|
33
|
+
if (quizState) {
|
|
34
|
+
dispatch({
|
|
35
|
+
type: actions_1.QuestionTypes.Hydrate,
|
|
36
|
+
payload: quizState,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
const resetQuizStoredState = () => {
|
|
41
|
+
var _a;
|
|
42
|
+
(_a = window === null || window === void 0 ? void 0 : window.sessionStorage) === null || _a === void 0 ? void 0 : _a.removeItem(quizStateKey);
|
|
43
|
+
};
|
|
44
|
+
const hasQuizStoredState = () => getStateFromSessionStorage() !== null;
|
|
14
45
|
return {
|
|
15
46
|
quizLocalState,
|
|
16
47
|
resetQuizLocalState,
|
|
48
|
+
hydrateQuizLocalState,
|
|
49
|
+
hasQuizStoredState,
|
|
50
|
+
resetQuizStoredState,
|
|
17
51
|
dispatchLocalState: dispatch,
|
|
18
52
|
};
|
|
19
53
|
};
|
|
@@ -7,6 +7,7 @@ export var QuestionTypes;
|
|
|
7
7
|
QuestionTypes["MultipleSelect"] = "multiple";
|
|
8
8
|
QuestionTypes["Back"] = "back";
|
|
9
9
|
QuestionTypes["Reset"] = "reset";
|
|
10
|
+
QuestionTypes["Hydrate"] = "hydrate";
|
|
10
11
|
})(QuestionTypes || (QuestionTypes = {}));
|
|
11
12
|
// API actions
|
|
12
13
|
export var QuizAPIActionTypes;
|
|
@@ -1,13 +1,25 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
|
2
2
|
import QuizContext from './context';
|
|
3
3
|
import QuizQuestions from '../QuizQuestions';
|
|
4
4
|
import ResultContainer from '../ResultContainer/ResultContainer';
|
|
5
5
|
import { RequestStates } from '../../constants';
|
|
6
6
|
import Spinner from '../Spinner/Spinner';
|
|
7
7
|
import useQuiz from '../../hooks/useQuiz';
|
|
8
|
+
import SessionPromptModal from '../SessionPromptModal/SessionPromptModal';
|
|
8
9
|
export default function CioQuiz(props) {
|
|
9
|
-
const { cioClient, state, events: { nextQuestion, previousQuestion, resetQuiz, addToCart, resultClick }, } = useQuiz(props);
|
|
10
|
-
const
|
|
10
|
+
const { cioClient, state, events: { nextQuestion, previousQuestion, resetQuiz, addToCart, resultClick, hydrateQuiz, hasStoredState, resetStoredState, }, } = useQuiz(props);
|
|
11
|
+
const [showSessionPrompt, setShowSessionPrompt] = useState(false);
|
|
12
|
+
const { resultsPageOptions, sessionStateOptions } = props;
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
// Respect showSessionModal if defined, else default to true.
|
|
15
|
+
if (sessionStateOptions?.showSessionModal !== undefined) {
|
|
16
|
+
setShowSessionPrompt(sessionStateOptions?.showSessionModal && hasStoredState());
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
setShowSessionPrompt(hasStoredState());
|
|
20
|
+
}
|
|
21
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
22
|
+
}, []);
|
|
11
23
|
const contextValue = {
|
|
12
24
|
cioClient,
|
|
13
25
|
state,
|
|
@@ -24,6 +36,7 @@ export default function CioQuiz(props) {
|
|
|
24
36
|
}
|
|
25
37
|
if (state.quiz.requestState === RequestStates.Success) {
|
|
26
38
|
return (React.createElement("div", { className: 'cio-quiz' },
|
|
39
|
+
React.createElement(SessionPromptModal, { resetStoredState: resetStoredState, continueSession: hydrateQuiz, showSessionPrompt: showSessionPrompt, setShowSessionPrompt: setShowSessionPrompt }),
|
|
27
40
|
React.createElement(QuizContext.Provider, { value: contextValue },
|
|
28
41
|
state.quiz.results && React.createElement(ResultContainer, { options: resultsPageOptions }),
|
|
29
42
|
state.quiz.currentQuestion && React.createElement(QuizQuestions, null))));
|
|
@@ -21,8 +21,6 @@ export default function apiReducer(state, action) {
|
|
|
21
21
|
return {
|
|
22
22
|
...state,
|
|
23
23
|
quizRequestState: RequestStates.Success,
|
|
24
|
-
quizVersionId: action.payload?.quizVersionId,
|
|
25
|
-
quizSessionId: action.payload?.quizSessionId,
|
|
26
24
|
quizCurrentQuestion: action.payload?.quizCurrentQuestion,
|
|
27
25
|
quizResults: undefined,
|
|
28
26
|
// If no current question set first question value
|
|
@@ -20,7 +20,7 @@ export default function CoverTypeQuestion() {
|
|
|
20
20
|
return (React.createElement("div", { className: `
|
|
21
21
|
cio-container${hasImage ? '--with-image' : ''}
|
|
22
22
|
cio-cover-question-container${hasImage ? '--with-image' : ''}
|
|
23
|
-
|
|
23
|
+
`, "data-question-key": question.key },
|
|
24
24
|
React.createElement("div", { className: 'cio-question-content' },
|
|
25
25
|
React.createElement(QuestionTitle, { title: question?.title }),
|
|
26
26
|
React.createElement(QuestionDescription, { description: question.description }),
|
|
@@ -42,7 +42,7 @@ function OpenTextQuestion(props) {
|
|
|
42
42
|
return (React.createElement("div", { className: `
|
|
43
43
|
cio-container${hasImage ? '--with-image' : ''}
|
|
44
44
|
cio-open-text-question-container${hasImage ? '--with-image' : ''}
|
|
45
|
-
|
|
45
|
+
`, "data-question-key": question.key },
|
|
46
46
|
hasImage ? renderImages(question.images, 'cio-question-image-container') : '',
|
|
47
47
|
React.createElement("div", { className: 'cio-question-content' },
|
|
48
48
|
React.createElement(QuestionTitle, { title: question.title }),
|
|
@@ -56,7 +56,7 @@ function SelectTypeQuestion() {
|
|
|
56
56
|
}
|
|
57
57
|
};
|
|
58
58
|
if (question) {
|
|
59
|
-
return (React.createElement("div", { className: 'cio-select-question-container' },
|
|
59
|
+
return (React.createElement("div", { className: 'cio-select-question-container', "data-question-key": question.key },
|
|
60
60
|
React.createElement("div", { className: 'cio-select-question-text' },
|
|
61
61
|
React.createElement(QuestionTitle, { title: question.title }),
|
|
62
62
|
question?.description ? React.createElement(QuestionDescription, { description: question.description }) : ''),
|
|
@@ -64,7 +64,7 @@ function SelectTypeQuestion() {
|
|
|
64
64
|
? 'cio-question-options-container-text-only'
|
|
65
65
|
: 'cio-question-options-container'}` }, question?.options?.map((option) => (React.createElement("div", { className: `${!hasImages
|
|
66
66
|
? 'cio-question-option-container-text-only'
|
|
67
|
-
: 'cio-question-option-container'} ${selected[option.id] ? 'selected' : ''}`, onClick: () => {
|
|
67
|
+
: 'cio-question-option-container'} ${selected[option.id] ? 'selected' : ''}`, "data-question-option-key": option.key, onClick: () => {
|
|
68
68
|
toggleIdSelected(option.id);
|
|
69
69
|
}, onKeyDown: (event) => {
|
|
70
70
|
onOptionKeyDown(event, option.id);
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import CTAButton from '../CTAButton/CTAButton';
|
|
3
|
+
export default function SessionPromptModal({ continueSession, resetStoredState, setShowSessionPrompt, showSessionPrompt, }) {
|
|
4
|
+
const onNoClickHandler = () => {
|
|
5
|
+
resetStoredState();
|
|
6
|
+
setShowSessionPrompt(false);
|
|
7
|
+
};
|
|
8
|
+
if (showSessionPrompt)
|
|
9
|
+
return (React.createElement("div", { className: 'cio-session-prompt-modal', role: 'presentation', onClick: onNoClickHandler },
|
|
10
|
+
React.createElement("div", { className: 'cio-session-prompt-container' },
|
|
11
|
+
React.createElement("div", { className: 'cio-session-prompt-content', role: 'presentation', onClick: (e) => e.stopPropagation() },
|
|
12
|
+
React.createElement("div", null, "Do you want to continue where you left off?"),
|
|
13
|
+
React.createElement("div", { className: 'cio-session-prompt-controls-container' },
|
|
14
|
+
React.createElement(CTAButton, { ctaText: 'No', type: 'button', onClick: onNoClickHandler }),
|
|
15
|
+
React.createElement(CTAButton, { type: 'button', ctaText: 'Yes', onClick: () => {
|
|
16
|
+
continueSession();
|
|
17
|
+
setShowSessionPrompt(false);
|
|
18
|
+
} }))))));
|
|
19
|
+
return null;
|
|
20
|
+
}
|
package/lib/mjs/constants.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
// Autocomplete key index
|
|
2
2
|
export const apiKey = 'key_wJSdZSiesX5hiVLt';
|
|
3
3
|
export const quizId = 'coffee-quiz';
|
|
4
|
+
// Session Storage default key
|
|
5
|
+
export const quizSessionStateKey = 'constructorIOQuizState';
|
|
4
6
|
/// //////////////////////////////
|
|
5
7
|
// Storybook Folder Descriptions
|
|
6
8
|
/// //////////////////////////////
|
package/lib/mjs/hooks/useQuiz.js
CHANGED
|
@@ -3,15 +3,15 @@ import useConsoleErrors from './useConsoleErrors';
|
|
|
3
3
|
import useQuizApiState from './useQuizApiState';
|
|
4
4
|
import useQuizEvents from './useQuizEvents';
|
|
5
5
|
import useQuizLocalState from './useQuizLocalState';
|
|
6
|
-
const useQuiz = ({ quizId, apiKey, cioJsClient, quizVersionId, resultsPageOptions }) => {
|
|
6
|
+
const useQuiz = ({ quizId, apiKey, cioJsClient, quizVersionId, resultsPageOptions, sessionStateOptions, }) => {
|
|
7
7
|
// Log console errors for required parameters quizId and resultsPageOptions
|
|
8
8
|
useConsoleErrors(quizId, resultsPageOptions);
|
|
9
9
|
// Quiz Local state
|
|
10
|
-
const { quizLocalState, resetQuizLocalState, dispatchLocalState } = useQuizLocalState();
|
|
10
|
+
const { quizLocalState, resetQuizLocalState, dispatchLocalState, hydrateQuizLocalState, hasQuizStoredState, resetQuizStoredState, } = useQuizLocalState(sessionStateOptions?.sessionStateKey);
|
|
11
11
|
// Quiz Cio Client
|
|
12
12
|
const cioClient = useCioClient({ apiKey, cioJsClient });
|
|
13
13
|
// Quiz API state
|
|
14
|
-
const { isFirstQuestion, quizApiState, resetQuizApiState } = useQuizApiState(quizId, quizLocalState, resultsPageOptions, quizVersionId, cioClient);
|
|
14
|
+
const { isFirstQuestion, quizApiState, resetQuizApiState } = useQuizApiState(quizId, quizLocalState, dispatchLocalState, resultsPageOptions, quizVersionId, cioClient);
|
|
15
15
|
// Quiz callback events
|
|
16
16
|
const quizEvents = useQuizEvents({
|
|
17
17
|
cioClient,
|
|
@@ -20,6 +20,9 @@ const useQuiz = ({ quizId, apiKey, cioJsClient, quizVersionId, resultsPageOption
|
|
|
20
20
|
dispatchLocalState,
|
|
21
21
|
resetQuizApiState,
|
|
22
22
|
resetQuizLocalState,
|
|
23
|
+
hydrateQuizLocalState,
|
|
24
|
+
resetQuizStoredState,
|
|
25
|
+
hasQuizStoredState,
|
|
23
26
|
});
|
|
24
27
|
return {
|
|
25
28
|
cioClient,
|
|
@@ -30,8 +33,8 @@ const useQuiz = ({ quizId, apiKey, cioJsClient, quizVersionId, resultsPageOption
|
|
|
30
33
|
},
|
|
31
34
|
quiz: {
|
|
32
35
|
requestState: quizApiState.quizRequestState,
|
|
33
|
-
versionId:
|
|
34
|
-
sessionId:
|
|
36
|
+
versionId: quizLocalState.quizVersionId,
|
|
37
|
+
sessionId: quizLocalState.quizSessionId,
|
|
35
38
|
firstQuestion: quizApiState.quizFirstQuestion,
|
|
36
39
|
currentQuestion: quizApiState.quizCurrentQuestion,
|
|
37
40
|
results: quizApiState.quizResults,
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { useEffect, useReducer } from 'react';
|
|
2
|
-
import { QuizAPIActionTypes } from '../components/CioQuiz/actions';
|
|
2
|
+
import { QuestionTypes, QuizAPIActionTypes, } from '../components/CioQuiz/actions';
|
|
3
3
|
import apiReducer, { initialState } from '../components/CioQuiz/quizApiReducer';
|
|
4
4
|
import { nextQuestion, getQuizResults } from '../services';
|
|
5
|
-
const useFetchQuiz = (quizId, quizLocalState, resultsPageOptions, quizVersionIdProp, cioClient) => {
|
|
5
|
+
const useFetchQuiz = (quizId, quizLocalState, dispatchLocalState, resultsPageOptions, quizVersionIdProp, cioClient) => {
|
|
6
6
|
const [quizApiState, dispatch] = useReducer(apiReducer, initialState);
|
|
7
7
|
const firstQuestionId = quizApiState.quizFirstQuestion?.next_question.id;
|
|
8
8
|
const currentQuestionId = quizApiState.quizCurrentQuestion?.next_question.id;
|
|
@@ -17,8 +17,8 @@ const useFetchQuiz = (quizId, quizLocalState, resultsPageOptions, quizVersionIdP
|
|
|
17
17
|
const quizResults = await getQuizResults(cioClient, quizId, {
|
|
18
18
|
answers: quizLocalState.answers,
|
|
19
19
|
resultsPerPage: resultsPageOptions?.numResultsToDisplay,
|
|
20
|
-
quizVersionId:
|
|
21
|
-
quizSessionId:
|
|
20
|
+
quizVersionId: quizLocalState.quizVersionId,
|
|
21
|
+
quizSessionId: quizLocalState.quizSessionId,
|
|
22
22
|
});
|
|
23
23
|
// Set quiz results state
|
|
24
24
|
dispatch({
|
|
@@ -36,25 +36,28 @@ const useFetchQuiz = (quizId, quizLocalState, resultsPageOptions, quizVersionIdP
|
|
|
36
36
|
}
|
|
37
37
|
else {
|
|
38
38
|
try {
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
const quizVersionId = quizLocalState.quizVersionId || quizVersionIdProp;
|
|
40
|
+
const { quizSessionId } = quizLocalState;
|
|
41
41
|
const questionResult = await nextQuestion(cioClient, quizId, {
|
|
42
42
|
answers: quizLocalState.answers,
|
|
43
43
|
quizVersionId,
|
|
44
44
|
quizSessionId,
|
|
45
45
|
});
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
46
|
+
// Update quizSessionId, quizVersionId
|
|
47
|
+
if ((!quizSessionId && questionResult?.quiz_session_id) ||
|
|
48
|
+
(!quizVersionId && questionResult.quiz_version_id)) {
|
|
49
|
+
dispatchLocalState({
|
|
50
|
+
type: QuestionTypes.Hydrate,
|
|
51
|
+
payload: {
|
|
52
|
+
quizVersionId: questionResult?.quiz_version_id,
|
|
53
|
+
quizSessionId: questionResult?.quiz_session_id,
|
|
54
|
+
},
|
|
55
|
+
});
|
|
51
56
|
}
|
|
52
57
|
// Set current question state
|
|
53
58
|
dispatch({
|
|
54
59
|
type: QuizAPIActionTypes.SET_CURRENT_QUESTION,
|
|
55
60
|
payload: {
|
|
56
|
-
quizSessionId,
|
|
57
|
-
quizVersionId,
|
|
58
61
|
quizCurrentQuestion: questionResult,
|
|
59
62
|
},
|
|
60
63
|
});
|
|
@@ -70,7 +73,7 @@ const useFetchQuiz = (quizId, quizLocalState, resultsPageOptions, quizVersionIdP
|
|
|
70
73
|
}, [
|
|
71
74
|
cioClient,
|
|
72
75
|
quizId,
|
|
73
|
-
quizLocalState,
|
|
76
|
+
quizLocalState.answers,
|
|
74
77
|
quizLocalState.isLastAnswer,
|
|
75
78
|
resultsPageOptions?.numResultsToDisplay,
|
|
76
79
|
]);
|