@constructor-io/constructorio-ui-quizzes 1.7.3 → 1.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/dist/constructorio-ui-quizzes-bundled.js +13 -13
  2. package/lib/cjs/components/CioQuiz/index.js +5 -8
  3. package/lib/cjs/components/CioQuiz/quizApiReducer.js +5 -5
  4. package/lib/cjs/components/CioQuiz/quizLocalReducer.js +7 -4
  5. package/lib/cjs/components/ProgressBar/ProgressBar.js +1 -1
  6. package/lib/cjs/components/SelectTypeQuestion/SelectTypeQuestion.js +2 -2
  7. package/lib/cjs/hooks/usePropsGetters/useNextQuestionButtonProps.js +5 -5
  8. package/lib/cjs/hooks/useQuiz.js +9 -2
  9. package/lib/cjs/hooks/useQuizEvents/index.js +4 -5
  10. package/lib/cjs/hooks/useQuizEvents/useHydrateQuizLocalState.js +5 -5
  11. package/lib/cjs/hooks/useQuizEvents/useQuizAnswerChangeHandler.js +38 -36
  12. package/lib/cjs/hooks/useQuizState/index.js +10 -5
  13. package/lib/cjs/hooks/useQuizState/useQuizApiState.js +31 -24
  14. package/lib/cjs/hooks/useQuizState/useQuizLocalState.js +1 -17
  15. package/lib/cjs/hooks/useQuizState/useSessionStorageState.js +26 -0
  16. package/lib/cjs/stories/Quiz/tests/mocks.js +5 -1
  17. package/lib/mjs/components/CioQuiz/index.js +5 -8
  18. package/lib/mjs/components/CioQuiz/quizApiReducer.js +1 -1
  19. package/lib/mjs/components/CioQuiz/quizLocalReducer.js +6 -3
  20. package/lib/mjs/components/ProgressBar/ProgressBar.js +1 -1
  21. package/lib/mjs/components/SelectTypeQuestion/SelectTypeQuestion.js +2 -2
  22. package/lib/mjs/hooks/usePropsGetters/useNextQuestionButtonProps.js +1 -1
  23. package/lib/mjs/hooks/useQuiz.js +9 -2
  24. package/lib/mjs/hooks/useQuizEvents/index.js +4 -5
  25. package/lib/mjs/hooks/useQuizEvents/useHydrateQuizLocalState.js +5 -5
  26. package/lib/mjs/hooks/useQuizEvents/useQuizAnswerChangeHandler.js +37 -35
  27. package/lib/mjs/hooks/useQuizState/index.js +10 -5
  28. package/lib/mjs/hooks/useQuizState/useQuizApiState.js +31 -24
  29. package/lib/mjs/hooks/useQuizState/useQuizLocalState.js +3 -17
  30. package/lib/mjs/hooks/useQuizState/useSessionStorageState.js +22 -0
  31. package/lib/mjs/stories/Quiz/tests/mocks.js +6 -2
  32. package/lib/types/components/CioQuiz/quizLocalReducer.d.ts +1 -0
  33. package/lib/types/hooks/usePropsGetters/useCoverQuestionProps.d.ts +2 -1
  34. package/lib/types/hooks/usePropsGetters/useOpenTextInputProps.d.ts +2 -1
  35. package/lib/types/hooks/usePropsGetters/useSelectInputProps.d.ts +2 -1
  36. package/lib/types/hooks/useQuizEvents/useHydrateQuizLocalState.d.ts +1 -1
  37. package/lib/types/hooks/useQuizState/index.d.ts +2 -4
  38. package/lib/types/hooks/useQuizState/useQuizLocalState.d.ts +1 -5
  39. package/lib/types/hooks/useQuizState/useSessionStorageState.d.ts +8 -0
  40. package/lib/types/types.d.ts +7 -3
  41. package/package.json +1 -1
@@ -14,21 +14,18 @@ const utils_1 = require("../../utils");
14
14
  const ProgressBar_1 = tslib_1.__importDefault(require("../ProgressBar/ProgressBar"));
15
15
  function CioQuiz(props) {
16
16
  var _a;
17
- const { cioClient, state, events: { hydrateQuiz, hasSessionStorageState, resetSessionStorageState }, getAddToCartButtonProps, getAddToFavoritesButtonProps, getCoverQuestionProps, getHydrateQuizButtonProps, getNextQuestionButtonProps, getOpenTextInputProps, getPreviousQuestionButtonProps, getQuizImageProps, getQuizResultButtonProps, getQuizResultLinkProps, getResetQuizButtonProps, getSelectInputProps, primaryColorStyles, } = (0, useQuiz_1.default)(props);
17
+ const { cioClient, state, events: { hydrateQuiz, resetSessionStorageState }, getAddToCartButtonProps, getAddToFavoritesButtonProps, getCoverQuestionProps, getHydrateQuizButtonProps, getNextQuestionButtonProps, getOpenTextInputProps, getPreviousQuestionButtonProps, getQuizImageProps, getQuizResultButtonProps, getQuizResultLinkProps, getResetQuizButtonProps, getSelectInputProps, primaryColorStyles, } = (0, useQuiz_1.default)(props);
18
18
  const [showSessionPrompt, setShowSessionPrompt] = (0, react_1.useState)(false);
19
19
  const { resultsPageOptions, sessionStateOptions } = props;
20
+ const { quizSessionStorageState: { hasSessionStorageState, skipToResults }, } = state;
20
21
  (0, react_1.useEffect)(() => {
21
22
  // Respect showSessionModal if defined, else default to true.
22
23
  if ((sessionStateOptions === null || sessionStateOptions === void 0 ? void 0 : sessionStateOptions.showSessionModal) !== undefined) {
23
- setShowSessionPrompt((sessionStateOptions === null || sessionStateOptions === void 0 ? void 0 : sessionStateOptions.showSessionModal) &&
24
- hasSessionStorageState() &&
25
- !state.quiz.skipToResults);
24
+ setShowSessionPrompt((sessionStateOptions === null || sessionStateOptions === void 0 ? void 0 : sessionStateOptions.showSessionModal) && hasSessionStorageState() && !skipToResults);
26
25
  }
27
26
  else {
28
- setShowSessionPrompt(hasSessionStorageState() && !state.quiz.skipToResults);
27
+ setShowSessionPrompt(hasSessionStorageState() && !skipToResults);
29
28
  }
30
- if (state.quiz.skipToResults)
31
- hydrateQuiz();
32
29
  // eslint-disable-next-line react-hooks/exhaustive-deps
33
30
  }, []);
34
31
  const contextValue = {
@@ -65,7 +62,7 @@ function CioQuiz(props) {
65
62
  ".cio-quiz ",
66
63
  (0, utils_1.convertPrimaryColorsToString)(primaryColorStyles)),
67
64
  react_1.default.createElement(SessionPromptModal_1.default, { resetStoredState: resetSessionStorageState, continueSession: hydrateQuiz, showSessionPrompt: showSessionPrompt, setShowSessionPrompt: setShowSessionPrompt }),
68
- react_1.default.createElement(context_1.default.Provider, { value: contextValue }, state.quiz.results || state.quiz.skipToResults ? (react_1.default.createElement(ResultContainer_1.default, { options: resultsPageOptions })) : (state.quiz.currentQuestion && (react_1.default.createElement(react_1.default.Fragment, null,
65
+ react_1.default.createElement(context_1.default.Provider, { value: contextValue }, state.quiz.results || skipToResults ? (react_1.default.createElement(ResultContainer_1.default, { options: resultsPageOptions })) : (state.quiz.currentQuestion && (react_1.default.createElement(react_1.default.Fragment, null,
69
66
  react_1.default.createElement(ProgressBar_1.default, null),
70
67
  react_1.default.createElement(QuizQuestions_1.default, null),
71
68
  react_1.default.createElement(ControlBar_1.default, { ctaButtonText: (questionData === null || questionData === void 0 ? void 0 : questionData.cta_text) || undefined })))))));
@@ -8,7 +8,7 @@ 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, _g, _h, _j, _k;
11
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
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, quizCurrentQuestion: undefined, quizResults: undefined });
@@ -17,16 +17,16 @@ function apiReducer(state, action) {
17
17
  case actions_1.QuizAPIActionTypes.SET_CURRENT_QUESTION: {
18
18
  const { isOpenQuestion, isCoverQuestion, isSingleQuestion, isMultipleQuestion, isSelectQuestion, } = (0, utils_1.getQuestionTypes)((_c = (_b = (_a = action.payload) === null || _a === void 0 ? void 0 : _a.quizCurrentQuestion) === null || _b === void 0 ? void 0 : _b.next_question) === null || _c === void 0 ? void 0 : _c.type);
19
19
  const quizFirstQuestion = state.quizFirstQuestion || ((_d = action.payload) === null || _d === void 0 ? void 0 : _d.quizCurrentQuestion);
20
- return Object.assign(Object.assign({}, state), { quizRequestState: constants_1.RequestStates.Success, quizCurrentQuestion: Object.assign(Object.assign({}, (_e = action.payload) === null || _e === void 0 ? void 0 : _e.quizCurrentQuestion), { isFirstQuestion: (quizFirstQuestion === null || quizFirstQuestion === void 0 ? void 0 : quizFirstQuestion.next_question.id) ===
21
- ((_h = (_g = (_f = action.payload) === null || _f === void 0 ? void 0 : _f.quizCurrentQuestion) === null || _g === void 0 ? void 0 : _g.next_question) === null || _h === void 0 ? void 0 : _h.id), isOpenQuestion,
20
+ return Object.assign(Object.assign({}, state), { quizRequestState: constants_1.RequestStates.Success, quizCurrentQuestion: Object.assign(Object.assign({}, (_e = action.payload) === null || _e === void 0 ? void 0 : _e.quizCurrentQuestion), { isFirstQuestion: ((_f = quizFirstQuestion === null || quizFirstQuestion === void 0 ? void 0 : quizFirstQuestion.next_question) === null || _f === void 0 ? void 0 : _f.id) ===
21
+ ((_j = (_h = (_g = action.payload) === null || _g === void 0 ? void 0 : _g.quizCurrentQuestion) === null || _h === void 0 ? void 0 : _h.next_question) === null || _j === void 0 ? void 0 : _j.id), isOpenQuestion,
22
22
  isCoverQuestion,
23
23
  isSingleQuestion,
24
24
  isMultipleQuestion,
25
25
  isSelectQuestion }), quizFirstQuestion, quizResults: undefined, selectedOptionsWithAttributes: undefined });
26
26
  }
27
27
  case actions_1.QuizAPIActionTypes.SET_QUIZ_RESULTS: {
28
- const selectedOptionsWithAttributes = ((_j = action.payload) === null || _j === void 0 ? void 0 : _j.quizResults.quiz_selected_options.filter((option) => option.has_attribute).map((option) => option.value)) || [];
29
- return Object.assign(Object.assign({}, state), { quizRequestState: constants_1.RequestStates.Success, quizResults: (_k = action.payload) === null || _k === void 0 ? void 0 : _k.quizResults, quizCurrentQuestion: undefined, selectedOptionsWithAttributes });
28
+ const selectedOptionsWithAttributes = ((_k = action.payload) === null || _k === void 0 ? void 0 : _k.quizResults.quiz_selected_options.filter((option) => option.has_attribute).map((option) => option.value)) || [];
29
+ return Object.assign(Object.assign({}, state), { quizRequestState: constants_1.RequestStates.Success, quizResults: (_l = action.payload) === null || _l === void 0 ? void 0 : _l.quizResults, quizCurrentQuestion: undefined, selectedOptionsWithAttributes });
30
30
  }
31
31
  case actions_1.QuizAPIActionTypes.RESET_QUIZ:
32
32
  return exports.initialState;
@@ -5,6 +5,7 @@ const actions_1 = require("./actions");
5
5
  exports.initialState = {
6
6
  answers: [],
7
7
  answerInputs: {},
8
+ prevAnswerInputs: {},
8
9
  isLastAnswer: false,
9
10
  isQuizCompleted: false,
10
11
  };
@@ -26,7 +27,7 @@ function quizLocalReducer(state, action) {
26
27
  case actions_1.QuestionTypes.MultipleSelect:
27
28
  return Object.assign(Object.assign({}, state), { answerInputs: answerInputReducer(state.answerInputs, action), isLastAnswer: !!((_d = action.payload) === null || _d === void 0 ? void 0 : _d.isLastQuestion), isQuizCompleted: false });
28
29
  case actions_1.QuestionTypes.Next: {
29
- const { answers } = state;
30
+ const { answers, answerInputs } = state;
30
31
  const newAnswers = [...answers];
31
32
  const lastAnswerInputIndex = answers.length;
32
33
  const currentAnswerInput = (_e = Object.values(state.answerInputs)) === null || _e === void 0 ? void 0 : _e[lastAnswerInputIndex];
@@ -46,11 +47,13 @@ function quizLocalReducer(state, action) {
46
47
  default:
47
48
  newAnswers.push([]);
48
49
  }
49
- return Object.assign(Object.assign({}, state), { answers: newAnswers, isQuizCompleted: false });
50
+ return Object.assign(Object.assign({}, state), {
51
+ // We now commit current answers to prevAnswerInputs
52
+ prevAnswerInputs: answerInputs, answers: newAnswers, isQuizCompleted: false });
50
53
  }
51
54
  case actions_1.QuestionTypes.Back: {
52
- const newAnswerInputs = Object.assign({}, state.answerInputs);
53
- return Object.assign(Object.assign({}, state), { answerInputs: newAnswerInputs, answers: [...state.answers.slice(0, -1)], isLastAnswer: false, isQuizCompleted: false });
55
+ const prevAnswerInputs = Object.assign({}, state.prevAnswerInputs);
56
+ return Object.assign(Object.assign({}, state), { answerInputs: prevAnswerInputs, answers: [...state.answers.slice(0, -1)], isLastAnswer: false, isQuizCompleted: false });
54
57
  }
55
58
  case actions_1.QuestionTypes.Reset:
56
59
  return Object.assign({}, exports.initialState);
@@ -6,7 +6,7 @@ const context_1 = tslib_1.__importDefault(require("../CioQuiz/context"));
6
6
  function ProgressBar() {
7
7
  const { state } = (0, react_1.useContext)(context_1.default);
8
8
  const currentQuestion = state === null || state === void 0 ? void 0 : state.quiz.currentQuestion;
9
- if (!currentQuestion)
9
+ if (!(currentQuestion === null || currentQuestion === void 0 ? void 0 : currentQuestion.next_question))
10
10
  return null;
11
11
  return (react_1.default.createElement("div", { className: 'cio-question-progress-affixed-container' },
12
12
  react_1.default.createElement("div", { className: 'cio-question-progress-progress-container' },
@@ -15,8 +15,8 @@ function SelectTypeQuestion() {
15
15
  let instructions;
16
16
  if (state === null || state === void 0 ? void 0 : state.quiz.currentQuestion) {
17
17
  question = state.quiz.currentQuestion.next_question;
18
- hasImages = question.options.some((option) => option.images);
19
- instructions = question.type === actions_1.QuestionTypes.MultipleSelect && 'Select one or more options';
18
+ hasImages = question === null || question === void 0 ? void 0 : question.options.some((option) => option.images);
19
+ instructions = (question === null || question === void 0 ? void 0 : question.type) === actions_1.QuestionTypes.MultipleSelect && 'Select one or more options';
20
20
  }
21
21
  if (question) {
22
22
  return (react_1.default.createElement("div", { className: 'cio-select-question-container', "data-question-key": question.key },
@@ -3,13 +3,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const react_1 = require("react");
4
4
  function useNextQuestionButtonProps(nextQuestion, quizApiState, quizLocalState) {
5
5
  const getNextQuestionButtonProps = (0, react_1.useCallback)(() => {
6
- var _a, _b, _c, _d, _e;
7
- const currentQuestionId = (_a = quizApiState.quizCurrentQuestion) === null || _a === void 0 ? void 0 : _a.next_question.id;
6
+ var _a, _b, _c, _d, _e, _f;
7
+ const currentQuestionId = (_b = (_a = quizApiState.quizCurrentQuestion) === null || _a === void 0 ? void 0 : _a.next_question) === null || _b === void 0 ? void 0 : _b.id;
8
8
  let buttonDisabled;
9
- if (currentQuestionId && !((_b = quizApiState.quizCurrentQuestion) === null || _b === void 0 ? void 0 : _b.isCoverQuestion)) {
9
+ if (currentQuestionId && !((_c = quizApiState.quizCurrentQuestion) === null || _c === void 0 ? void 0 : _c.isCoverQuestion)) {
10
10
  buttonDisabled =
11
- !((_c = quizLocalState.answerInputs[currentQuestionId]) === null || _c === void 0 ? void 0 : _c.value) ||
12
- !((_e = (_d = quizLocalState.answerInputs[currentQuestionId]) === null || _d === void 0 ? void 0 : _d.value) === null || _e === void 0 ? void 0 : _e.length);
11
+ !((_d = quizLocalState.answerInputs[currentQuestionId]) === null || _d === void 0 ? void 0 : _d.value) ||
12
+ !((_f = (_e = quizLocalState.answerInputs[currentQuestionId]) === null || _e === void 0 ? void 0 : _e.value) === null || _f === void 0 ? void 0 : _f.length);
13
13
  }
14
14
  return {
15
15
  className: buttonDisabled ? 'cio-question-cta-button disabled' : 'cio-question-cta-button',
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const tslib_1 = require("tslib");
4
+ const react_1 = require("react");
4
5
  const useCioClient_1 = tslib_1.__importDefault(require("./useCioClient"));
5
6
  const useConsoleErrors_1 = tslib_1.__importDefault(require("./useConsoleErrors"));
6
7
  const usePropsGetters_1 = tslib_1.__importDefault(require("./usePropsGetters"));
@@ -18,9 +19,15 @@ const useQuiz = (quizOptions) => {
18
19
  // Quiz callback events
19
20
  const quizEvents = (0, useQuizEvents_1.default)(quizOptions, cioClient, quizState);
20
21
  // Props getters
21
- const { quizApiState, quizLocalState, skipToResults } = quizState;
22
+ const { quizApiState, quizLocalState, quizSessionStorageState } = quizState;
23
+ const { skipToResults } = quizSessionStorageState;
22
24
  const propGetters = (0, usePropsGetters_1.default)(quizEvents, quizApiState, quizLocalState);
23
25
  const primaryColorStyles = (0, usePrimaryColorStyles_1.default)(primaryColor);
26
+ (0, react_1.useEffect)(() => {
27
+ if (skipToResults)
28
+ quizEvents.hydrateQuiz();
29
+ // eslint-disable-next-line react-hooks/exhaustive-deps
30
+ }, []);
24
31
  return Object.assign(Object.assign({ cioClient, state: {
25
32
  answers: {
26
33
  inputs: quizLocalState.answerInputs,
@@ -30,11 +37,11 @@ const useQuiz = (quizOptions) => {
30
37
  requestState: quizApiState.quizRequestState,
31
38
  versionId: quizLocalState.quizVersionId,
32
39
  sessionId: quizLocalState.quizSessionId,
33
- skipToResults,
34
40
  currentQuestion: quizApiState.quizCurrentQuestion,
35
41
  results: quizApiState.quizResults,
36
42
  selectedOptionsWithAttributes: quizApiState.selectedOptionsWithAttributes,
37
43
  },
44
+ quizSessionStorageState,
38
45
  }, events: Object.assign({}, quizEvents) }, propGetters), { primaryColorStyles });
39
46
  };
40
47
  exports.default = useQuiz;
@@ -12,7 +12,7 @@ const useHydrateQuizLocalState_1 = tslib_1.__importDefault(require("./useHydrate
12
12
  const utils_1 = require("../../utils");
13
13
  const useQuizAddToFavorites_1 = tslib_1.__importDefault(require("./useQuizAddToFavorites"));
14
14
  const useQuizEvents = (quizOptions, cioClient, quizState) => {
15
- const { quizApiState, dispatchLocalState, dispatchApiState, hasQuizStoredState, quizStateKey, quizLocalState, } = quizState;
15
+ const { quizApiState, dispatchLocalState, dispatchApiState, quizLocalState, quizSessionStorageState, } = quizState;
16
16
  const { resultsPageOptions, callbacks } = quizOptions;
17
17
  const { onAddToCartClick, onQuizResultClick, onQuizResultsLoaded, onAddToFavoritesClick } = resultsPageOptions;
18
18
  // Quiz answer change
@@ -29,9 +29,9 @@ const useQuizEvents = (quizOptions, cioClient, quizState) => {
29
29
  // Quiz results loaded event
30
30
  (0, useQuizResultsLoaded_1.default)(cioClient, quizApiState, onQuizResultsLoaded);
31
31
  // Quiz reset
32
- const resetQuiz = (0, useQuizResetClick_1.default)((0, utils_1.resetQuizSessionStorageState)(quizStateKey), dispatchLocalState, dispatchApiState, quizApiState.quizResults);
32
+ const resetQuiz = (0, useQuizResetClick_1.default)((0, utils_1.resetQuizSessionStorageState)(quizSessionStorageState.key), dispatchLocalState, dispatchApiState, quizApiState.quizResults);
33
33
  // Quiz rehydrate
34
- const hydrateQuizLocalState = (0, useHydrateQuizLocalState_1.default)(quizStateKey, dispatchLocalState);
34
+ const hydrateQuizLocalState = (0, useHydrateQuizLocalState_1.default)(quizSessionStorageState.key, dispatchLocalState);
35
35
  return {
36
36
  addToCart,
37
37
  addToFavorites,
@@ -41,8 +41,7 @@ const useQuizEvents = (quizOptions, cioClient, quizState) => {
41
41
  nextQuestion,
42
42
  resetQuiz,
43
43
  hydrateQuiz: hydrateQuizLocalState,
44
- hasSessionStorageState: hasQuizStoredState,
45
- resetSessionStorageState: (0, utils_1.resetQuizSessionStorageState)(quizStateKey),
44
+ resetSessionStorageState: (0, utils_1.resetQuizSessionStorageState)(quizSessionStorageState.key),
46
45
  };
47
46
  };
48
47
  exports.default = useQuizEvents;
@@ -3,16 +3,16 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const react_1 = require("react");
4
4
  const actions_1 = require("../../components/CioQuiz/actions");
5
5
  const utils_1 = require("../../utils");
6
- const useHydrateQuizLocalState = (quizStateKey, dispatchLocalState) => {
7
- const quizState = (0, utils_1.getStateFromSessionStorage)(quizStateKey);
6
+ const useHydrateQuizLocalState = (quizSessionStorageStateKey, dispatchLocalState) => {
7
+ const sessionStorageQuizState = (0, utils_1.getStateFromSessionStorage)(quizSessionStorageStateKey);
8
8
  const hydrateQuizLocalStateHandler = (0, react_1.useCallback)(() => {
9
- if (quizState) {
9
+ if (sessionStorageQuizState) {
10
10
  dispatchLocalState({
11
11
  type: actions_1.QuestionTypes.Hydrate,
12
- payload: quizState,
12
+ payload: sessionStorageQuizState,
13
13
  });
14
14
  }
15
- }, [dispatchLocalState, quizState]);
15
+ }, [dispatchLocalState, sessionStorageQuizState]);
16
16
  return hydrateQuizLocalStateHandler;
17
17
  };
18
18
  exports.default = useHydrateQuizLocalState;
@@ -4,43 +4,45 @@ const react_1 = require("react");
4
4
  const actions_1 = require("../../components/CioQuiz/actions");
5
5
  const useQuizAnswerChangeHandler = (quizApiState, dispatchLocalState) => {
6
6
  const quizAnswerChangedHandler = (0, react_1.useCallback)((payload) => {
7
- var _a;
8
- const questionType = (_a = quizApiState.quizCurrentQuestion) === null || _a === void 0 ? void 0 : _a.next_question.type;
7
+ var _a, _b;
8
+ const questionType = (_b = (_a = quizApiState.quizCurrentQuestion) === null || _a === void 0 ? void 0 : _a.next_question) === null || _b === void 0 ? void 0 : _b.type;
9
9
  const currentQuestion = quizApiState.quizCurrentQuestion;
10
- switch (questionType) {
11
- case actions_1.QuestionTypes.Cover:
12
- dispatchLocalState({
13
- type: actions_1.QuestionTypes.Cover,
14
- payload: {
15
- questionId: currentQuestion.next_question.id,
16
- input: '',
17
- isLastQuestion: currentQuestion.is_last_question,
18
- },
19
- });
20
- break;
21
- case actions_1.QuestionTypes.OpenText:
22
- dispatchLocalState({
23
- type: actions_1.QuestionTypes.OpenText,
24
- payload: {
25
- questionId: currentQuestion.next_question.id,
26
- input: payload,
27
- isLastQuestion: currentQuestion.is_last_question,
28
- },
29
- });
30
- break;
31
- case actions_1.QuestionTypes.SingleSelect:
32
- case actions_1.QuestionTypes.MultipleSelect:
33
- dispatchLocalState({
34
- type: currentQuestion.next_question.type,
35
- payload: {
36
- questionId: currentQuestion.next_question.id,
37
- input: payload,
38
- isLastQuestion: currentQuestion.is_last_question,
39
- },
40
- });
41
- break;
42
- default:
43
- break;
10
+ if (currentQuestion === null || currentQuestion === void 0 ? void 0 : currentQuestion.next_question) {
11
+ switch (questionType) {
12
+ case actions_1.QuestionTypes.Cover:
13
+ dispatchLocalState({
14
+ type: actions_1.QuestionTypes.Cover,
15
+ payload: {
16
+ questionId: currentQuestion.next_question.id,
17
+ input: '',
18
+ isLastQuestion: currentQuestion.is_last_question,
19
+ },
20
+ });
21
+ break;
22
+ case actions_1.QuestionTypes.OpenText:
23
+ dispatchLocalState({
24
+ type: actions_1.QuestionTypes.OpenText,
25
+ payload: {
26
+ questionId: currentQuestion.next_question.id,
27
+ input: payload,
28
+ isLastQuestion: currentQuestion.is_last_question,
29
+ },
30
+ });
31
+ break;
32
+ case actions_1.QuestionTypes.SingleSelect:
33
+ case actions_1.QuestionTypes.MultipleSelect:
34
+ dispatchLocalState({
35
+ type: currentQuestion.next_question.type,
36
+ payload: {
37
+ questionId: currentQuestion.next_question.id,
38
+ input: payload,
39
+ isLastQuestion: currentQuestion.is_last_question,
40
+ },
41
+ });
42
+ break;
43
+ default:
44
+ break;
45
+ }
44
46
  }
45
47
  }, [quizApiState, dispatchLocalState]);
46
48
  return quizAnswerChangedHandler;
@@ -3,10 +3,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const tslib_1 = require("tslib");
4
4
  const useQuizApiState_1 = tslib_1.__importDefault(require("./useQuizApiState"));
5
5
  const useQuizLocalState_1 = tslib_1.__importDefault(require("./useQuizLocalState"));
6
+ const useSessionStorageState_1 = tslib_1.__importDefault(require("./useSessionStorageState"));
6
7
  const useQuizState = (quizOptions, cioClient) => {
7
- const { sessionStateOptions } = quizOptions;
8
+ const { sessionStateOptions, enableHydration } = quizOptions;
8
9
  // Quiz Local state
9
- const { quizLocalState, dispatchLocalState, hasQuizStoredState, skipToResults, quizStateKey } = (0, useQuizLocalState_1.default)(sessionStateOptions);
10
+ const { quizLocalState, dispatchLocalState } = (0, useQuizLocalState_1.default)();
11
+ // Quiz Session Storage state
12
+ const { skipToResults, quizSessionStorageStateKey, hasSessionStorageState } = (0, useSessionStorageState_1.default)(quizLocalState, sessionStateOptions, enableHydration === undefined ? true : enableHydration);
10
13
  // Quiz API state
11
14
  const { quizApiState, dispatchApiState } = (0, useQuizApiState_1.default)(quizOptions, cioClient, quizLocalState, skipToResults, dispatchLocalState);
12
15
  return {
@@ -14,9 +17,11 @@ const useQuizState = (quizOptions, cioClient) => {
14
17
  quizLocalState,
15
18
  dispatchApiState,
16
19
  dispatchLocalState,
17
- hasQuizStoredState,
18
- skipToResults,
19
- quizStateKey,
20
+ quizSessionStorageState: {
21
+ skipToResults,
22
+ key: quizSessionStorageStateKey,
23
+ hasSessionStorageState,
24
+ },
20
25
  };
21
26
  };
22
27
  exports.default = useQuizState;
@@ -10,36 +10,39 @@ const useQuizApiState = (quizOptions, cioClient, quizLocalState, skipToResults,
10
10
  ) => {
11
11
  const [quizApiState, dispatchApiState] = (0, react_1.useReducer)(quizApiReducer_1.default, quizApiReducer_1.initialState);
12
12
  const { quizId, quizVersionId: quizVersionIdProp, resultsPageOptions } = quizOptions;
13
+ const dispatchQuizResults = () => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
14
+ try {
15
+ const quizResults = yield (0, services_1.getQuizResults)(cioClient, quizId, {
16
+ answers: quizLocalState.answers,
17
+ resultsPerPage: resultsPageOptions === null || resultsPageOptions === void 0 ? void 0 : resultsPageOptions.numResultsToDisplay,
18
+ quizVersionId: quizLocalState.quizVersionId,
19
+ quizSessionId: quizLocalState.quizSessionId,
20
+ });
21
+ // Set quiz results state
22
+ dispatchApiState({
23
+ type: actions_1.QuizAPIActionTypes.SET_QUIZ_RESULTS,
24
+ payload: {
25
+ quizResults,
26
+ },
27
+ });
28
+ if (!quizLocalState.isQuizCompleted)
29
+ dispatchLocalState({
30
+ type: actions_1.QuestionTypes.Complete,
31
+ });
32
+ }
33
+ catch (error) {
34
+ dispatchApiState({
35
+ type: actions_1.QuizAPIActionTypes.SET_IS_ERROR,
36
+ });
37
+ }
38
+ });
13
39
  (0, react_1.useEffect)(() => {
14
40
  (() => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
15
41
  dispatchApiState({
16
42
  type: actions_1.QuizAPIActionTypes.SET_IS_LOADING,
17
43
  });
18
44
  if (quizLocalState.isLastAnswer || skipToResults) {
19
- try {
20
- const quizResults = yield (0, services_1.getQuizResults)(cioClient, quizId, {
21
- answers: quizLocalState.answers,
22
- resultsPerPage: resultsPageOptions === null || resultsPageOptions === void 0 ? void 0 : resultsPageOptions.numResultsToDisplay,
23
- quizVersionId: quizLocalState.quizVersionId,
24
- quizSessionId: quizLocalState.quizSessionId,
25
- });
26
- // Set quiz results state
27
- dispatchApiState({
28
- type: actions_1.QuizAPIActionTypes.SET_QUIZ_RESULTS,
29
- payload: {
30
- quizResults,
31
- },
32
- });
33
- if (!quizLocalState.isQuizCompleted)
34
- dispatchLocalState({
35
- type: actions_1.QuestionTypes.Complete,
36
- });
37
- }
38
- catch (error) {
39
- dispatchApiState({
40
- type: actions_1.QuizAPIActionTypes.SET_IS_ERROR,
41
- });
42
- }
45
+ yield dispatchQuizResults();
43
46
  }
44
47
  else {
45
48
  try {
@@ -50,6 +53,10 @@ const useQuizApiState = (quizOptions, cioClient, quizLocalState, skipToResults,
50
53
  quizVersionId,
51
54
  quizSessionId,
52
55
  });
56
+ if (!questionResult.next_question) {
57
+ yield dispatchQuizResults();
58
+ return;
59
+ }
53
60
  // Update quizSessionId, quizVersionId
54
61
  if ((!quizSessionId && (questionResult === null || questionResult === void 0 ? void 0 : questionResult.quiz_session_id)) ||
55
62
  (!quizVersionId && questionResult.quiz_version_id)) {
@@ -3,32 +3,16 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const tslib_1 = require("tslib");
4
4
  const react_1 = require("react");
5
5
  const quizLocalReducer_1 = tslib_1.__importStar(require("../../components/CioQuiz/quizLocalReducer"));
6
- const constants_1 = require("../../constants");
7
6
  const utils_1 = require("../../utils");
8
- const useQuizLocalState = (sessionStateOptions) => {
9
- var _a;
7
+ const useQuizLocalState = () => {
10
8
  const [quizLocalState, dispatch] = (0, react_1.useReducer)(quizLocalReducer_1.default, quizLocalReducer_1.initialState);
11
- const quizStateKey = (sessionStateOptions === null || sessionStateOptions === void 0 ? void 0 : sessionStateOptions.sessionStateKey) || constants_1.quizSessionStateKey;
12
- (0, react_1.useEffect)(() => {
13
- var _a, _b;
14
- // don't save state if initial state
15
- if ((_a = quizLocalState === null || quizLocalState === void 0 ? void 0 : quizLocalState.answers) === null || _a === void 0 ? void 0 : _a.length) {
16
- (_b = window === null || window === void 0 ? void 0 : window.sessionStorage) === null || _b === void 0 ? void 0 : _b.setItem(quizStateKey, JSON.stringify(quizLocalState));
17
- }
18
- }, [quizLocalState, quizStateKey]);
19
- const hasQuizStoredState = () => (0, utils_1.getStateFromSessionStorage)(quizStateKey) !== null;
20
- const skipToResults = !!((_a = (0, utils_1.getStateFromSessionStorage)(quizStateKey)) === null || _a === void 0 ? void 0 : _a.isQuizCompleted) &&
21
- !(sessionStateOptions === null || sessionStateOptions === void 0 ? void 0 : sessionStateOptions.showSessionModalOnResults);
22
9
  const dispatchLocalState = (0, react_1.useCallback)((action) => {
23
10
  (0, utils_1.logger)(action);
24
11
  dispatch(action);
25
12
  }, []);
26
13
  return {
27
14
  quizLocalState,
28
- hasQuizStoredState,
29
15
  dispatchLocalState,
30
- skipToResults,
31
- quizStateKey,
32
16
  };
33
17
  };
34
18
  exports.default = useQuizLocalState;
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const react_1 = require("react");
4
+ const utils_1 = require("../../utils");
5
+ const constants_1 = require("../../constants");
6
+ const useSessionStorageState = (quizLocalState, sessionStateOptions, enableHydration) => {
7
+ var _a;
8
+ const quizSessionStorageStateKey = (sessionStateOptions === null || sessionStateOptions === void 0 ? void 0 : sessionStateOptions.sessionStateKey) || constants_1.quizSessionStateKey;
9
+ // Save state to session storage
10
+ (0, react_1.useEffect)(() => {
11
+ var _a, _b;
12
+ // don't save state if initial state
13
+ if (enableHydration && ((_a = quizLocalState === null || quizLocalState === void 0 ? void 0 : quizLocalState.answers) === null || _a === void 0 ? void 0 : _a.length)) {
14
+ (_b = window === null || window === void 0 ? void 0 : window.sessionStorage) === null || _b === void 0 ? void 0 : _b.setItem(quizSessionStorageStateKey, JSON.stringify(quizLocalState));
15
+ }
16
+ }, [quizLocalState, quizSessionStorageStateKey, enableHydration]);
17
+ const skipToResults = !!enableHydration &&
18
+ !!((_a = (0, utils_1.getStateFromSessionStorage)(quizSessionStorageStateKey)) === null || _a === void 0 ? void 0 : _a.isQuizCompleted) &&
19
+ !(sessionStateOptions === null || sessionStateOptions === void 0 ? void 0 : sessionStateOptions.showSessionModalOnResults);
20
+ return {
21
+ skipToResults,
22
+ quizSessionStorageStateKey,
23
+ hasSessionStorageState: () => (0, utils_1.getStateFromSessionStorage)(quizSessionStorageStateKey) !== null,
24
+ };
25
+ };
26
+ exports.default = useSessionStorageState;
@@ -55,7 +55,6 @@ const getMockState = (question) => ({
55
55
  requestState: constants_1.RequestStates.Success,
56
56
  versionId: '',
57
57
  sessionId: '',
58
- skipToResults: false,
59
58
  currentQuestion: {
60
59
  next_question: question,
61
60
  isFirstQuestion: false,
@@ -121,6 +120,11 @@ const getMockState = (question) => ({
121
120
  },
122
121
  selectedOptionsWithAttributes: ['Chocolate', 'Medium'],
123
122
  },
123
+ quizSessionStorageState: {
124
+ key: constants_1.quizSessionStateKey,
125
+ skipToResults: true,
126
+ hasSessionStorageState: () => true,
127
+ },
124
128
  });
125
129
  exports.getMockState = getMockState;
126
130
  const mockElementProps = {
@@ -10,21 +10,18 @@ import SessionPromptModal from '../SessionPromptModal/SessionPromptModal';
10
10
  import { convertPrimaryColorsToString, renderImages } from '../../utils';
11
11
  import ProgressBar from '../ProgressBar/ProgressBar';
12
12
  export default function CioQuiz(props) {
13
- const { cioClient, state, events: { hydrateQuiz, hasSessionStorageState, resetSessionStorageState }, getAddToCartButtonProps, getAddToFavoritesButtonProps, getCoverQuestionProps, getHydrateQuizButtonProps, getNextQuestionButtonProps, getOpenTextInputProps, getPreviousQuestionButtonProps, getQuizImageProps, getQuizResultButtonProps, getQuizResultLinkProps, getResetQuizButtonProps, getSelectInputProps, primaryColorStyles, } = useQuiz(props);
13
+ const { cioClient, state, events: { hydrateQuiz, resetSessionStorageState }, getAddToCartButtonProps, getAddToFavoritesButtonProps, getCoverQuestionProps, getHydrateQuizButtonProps, getNextQuestionButtonProps, getOpenTextInputProps, getPreviousQuestionButtonProps, getQuizImageProps, getQuizResultButtonProps, getQuizResultLinkProps, getResetQuizButtonProps, getSelectInputProps, primaryColorStyles, } = useQuiz(props);
14
14
  const [showSessionPrompt, setShowSessionPrompt] = useState(false);
15
15
  const { resultsPageOptions, sessionStateOptions } = props;
16
+ const { quizSessionStorageState: { hasSessionStorageState, skipToResults }, } = state;
16
17
  useEffect(() => {
17
18
  // Respect showSessionModal if defined, else default to true.
18
19
  if (sessionStateOptions?.showSessionModal !== undefined) {
19
- setShowSessionPrompt(sessionStateOptions?.showSessionModal &&
20
- hasSessionStorageState() &&
21
- !state.quiz.skipToResults);
20
+ setShowSessionPrompt(sessionStateOptions?.showSessionModal && hasSessionStorageState() && !skipToResults);
22
21
  }
23
22
  else {
24
- setShowSessionPrompt(hasSessionStorageState() && !state.quiz.skipToResults);
23
+ setShowSessionPrompt(hasSessionStorageState() && !skipToResults);
25
24
  }
26
- if (state.quiz.skipToResults)
27
- hydrateQuiz();
28
25
  // eslint-disable-next-line react-hooks/exhaustive-deps
29
26
  }, []);
30
27
  const contextValue = {
@@ -61,7 +58,7 @@ export default function CioQuiz(props) {
61
58
  ".cio-quiz ",
62
59
  convertPrimaryColorsToString(primaryColorStyles)),
63
60
  React.createElement(SessionPromptModal, { resetStoredState: resetSessionStorageState, continueSession: hydrateQuiz, showSessionPrompt: showSessionPrompt, setShowSessionPrompt: setShowSessionPrompt }),
64
- React.createElement(QuizContext.Provider, { value: contextValue }, state.quiz.results || state.quiz.skipToResults ? (React.createElement(ResultContainer, { options: resultsPageOptions })) : (state.quiz.currentQuestion && (React.createElement(React.Fragment, null,
61
+ React.createElement(QuizContext.Provider, { value: contextValue }, state.quiz.results || skipToResults ? (React.createElement(ResultContainer, { options: resultsPageOptions })) : (state.quiz.currentQuestion && (React.createElement(React.Fragment, null,
65
62
  React.createElement(ProgressBar, null),
66
63
  React.createElement(QuizQuestions, null),
67
64
  React.createElement(ControlBar, { ctaButtonText: questionData?.cta_text || undefined })))))));
@@ -29,7 +29,7 @@ export default function apiReducer(state, action) {
29
29
  quizRequestState: RequestStates.Success,
30
30
  quizCurrentQuestion: {
31
31
  ...action.payload?.quizCurrentQuestion,
32
- isFirstQuestion: quizFirstQuestion?.next_question.id ===
32
+ isFirstQuestion: quizFirstQuestion?.next_question?.id ===
33
33
  action.payload?.quizCurrentQuestion?.next_question?.id,
34
34
  isOpenQuestion,
35
35
  isCoverQuestion,
@@ -2,6 +2,7 @@ import { QuestionTypes } from './actions';
2
2
  export const initialState = {
3
3
  answers: [],
4
4
  answerInputs: {},
5
+ prevAnswerInputs: {},
5
6
  isLastAnswer: false,
6
7
  isQuizCompleted: false,
7
8
  };
@@ -45,7 +46,7 @@ export default function quizLocalReducer(state, action) {
45
46
  isQuizCompleted: false,
46
47
  };
47
48
  case QuestionTypes.Next: {
48
- const { answers } = state;
49
+ const { answers, answerInputs } = state;
49
50
  const newAnswers = [...answers];
50
51
  const lastAnswerInputIndex = answers.length;
51
52
  const currentAnswerInput = Object.values(state.answerInputs)?.[lastAnswerInputIndex];
@@ -67,15 +68,17 @@ export default function quizLocalReducer(state, action) {
67
68
  }
68
69
  return {
69
70
  ...state,
71
+ // We now commit current answers to prevAnswerInputs
72
+ prevAnswerInputs: answerInputs,
70
73
  answers: newAnswers,
71
74
  isQuizCompleted: false,
72
75
  };
73
76
  }
74
77
  case QuestionTypes.Back: {
75
- const newAnswerInputs = { ...state.answerInputs };
78
+ const prevAnswerInputs = { ...state.prevAnswerInputs };
76
79
  return {
77
80
  ...state,
78
- answerInputs: newAnswerInputs,
81
+ answerInputs: prevAnswerInputs,
79
82
  answers: [...state.answers.slice(0, -1)],
80
83
  isLastAnswer: false,
81
84
  isQuizCompleted: false,
@@ -3,7 +3,7 @@ import QuizContext from '../CioQuiz/context';
3
3
  export default function ProgressBar() {
4
4
  const { state } = useContext(QuizContext);
5
5
  const currentQuestion = state?.quiz.currentQuestion;
6
- if (!currentQuestion)
6
+ if (!currentQuestion?.next_question)
7
7
  return null;
8
8
  return (React.createElement("div", { className: 'cio-question-progress-affixed-container' },
9
9
  React.createElement("div", { className: 'cio-question-progress-progress-container' },
@@ -11,8 +11,8 @@ function SelectTypeQuestion() {
11
11
  let instructions;
12
12
  if (state?.quiz.currentQuestion) {
13
13
  question = state.quiz.currentQuestion.next_question;
14
- hasImages = question.options.some((option) => option.images);
15
- instructions = question.type === QuestionTypes.MultipleSelect && 'Select one or more options';
14
+ hasImages = question?.options.some((option) => option.images);
15
+ instructions = question?.type === QuestionTypes.MultipleSelect && 'Select one or more options';
16
16
  }
17
17
  if (question) {
18
18
  return (React.createElement("div", { className: 'cio-select-question-container', "data-question-key": question.key },
@@ -1,7 +1,7 @@
1
1
  import { useCallback } from 'react';
2
2
  export default function useNextQuestionButtonProps(nextQuestion, quizApiState, quizLocalState) {
3
3
  const getNextQuestionButtonProps = useCallback(() => {
4
- const currentQuestionId = quizApiState.quizCurrentQuestion?.next_question.id;
4
+ const currentQuestionId = quizApiState.quizCurrentQuestion?.next_question?.id;
5
5
  let buttonDisabled;
6
6
  if (currentQuestionId && !quizApiState.quizCurrentQuestion?.isCoverQuestion) {
7
7
  buttonDisabled =