@constructor-io/constructorio-ui-quizzes 1.17.24 → 1.17.26
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 +12 -13
- package/lib/cjs/components/CioQuiz/actions.js +2 -0
- package/lib/cjs/components/CioQuiz/index.js +4 -1
- package/lib/cjs/components/CioQuiz/quizApiReducer.js +4 -2
- package/lib/cjs/components/CioQuiz/quizLocalReducer.js +33 -22
- package/lib/cjs/components/SelectTypeQuestion/SelectTypeQuestion.js +4 -1
- package/lib/cjs/hooks/usePropsGetters/useSelectInputProps.js +18 -6
- package/lib/cjs/hooks/useQuizEvents/useQuizAnswerChangeHandler.js +2 -0
- package/lib/cjs/utils.js +5 -1
- package/lib/cjs/version.js +1 -1
- package/lib/mjs/components/CioQuiz/actions.js +2 -0
- package/lib/mjs/components/CioQuiz/index.js +4 -1
- package/lib/mjs/components/CioQuiz/quizApiReducer.js +3 -1
- package/lib/mjs/components/CioQuiz/quizLocalReducer.js +41 -22
- package/lib/mjs/components/SelectTypeQuestion/SelectTypeQuestion.js +4 -1
- package/lib/mjs/hooks/usePropsGetters/useSelectInputProps.js +19 -5
- package/lib/mjs/hooks/useQuizEvents/useQuizAnswerChangeHandler.js +2 -0
- package/lib/mjs/utils.js +5 -1
- package/lib/mjs/version.js +1 -1
- package/lib/types/components/CioQuiz/actions.d.ts +3 -1
- package/lib/types/types.d.ts +4 -2
- package/lib/types/utils.d.ts +2 -0
- package/lib/types/version.d.ts +1 -1
- package/package.json +8 -5
|
@@ -8,6 +8,8 @@ var QuestionTypes;
|
|
|
8
8
|
QuestionTypes["Cover"] = "cover";
|
|
9
9
|
QuestionTypes["SingleSelect"] = "single";
|
|
10
10
|
QuestionTypes["MultipleSelect"] = "multiple";
|
|
11
|
+
QuestionTypes["SingleFilterValue"] = "single_filter_value";
|
|
12
|
+
QuestionTypes["MultipleFilterValues"] = "multiple_filter_values";
|
|
11
13
|
QuestionTypes["Next"] = "next";
|
|
12
14
|
QuestionTypes["Skip"] = "skip";
|
|
13
15
|
QuestionTypes["Back"] = "back";
|
|
@@ -13,6 +13,7 @@ const SessionPromptModal_1 = tslib_1.__importDefault(require("../SessionPromptMo
|
|
|
13
13
|
const ShareResultsModal_1 = tslib_1.__importDefault(require("../ShareResultsModal/ShareResultsModal"));
|
|
14
14
|
const utils_1 = require("../../utils");
|
|
15
15
|
const ProgressBar_1 = tslib_1.__importDefault(require("../ProgressBar/ProgressBar"));
|
|
16
|
+
const actions_1 = require("./actions");
|
|
16
17
|
function CioQuiz(props) {
|
|
17
18
|
var _a;
|
|
18
19
|
const { cioClient, state, events: { hydrateQuiz, resetSessionStorageState }, getAddToCartButtonProps, getAddToFavoritesButtonProps, getCoverQuestionProps, getHydrateQuizButtonProps, getNextQuestionButtonProps, getSkipQuestionButtonProps, getOpenTextInputProps, getPreviousQuestionButtonProps, getQuizImageProps, getQuizResultButtonProps, getQuizResultLinkProps, getResetQuizButtonProps, getSelectInputProps, primaryColorStyles, getShareResultsButtonProps, } = (0, useQuiz_1.default)(props);
|
|
@@ -70,7 +71,9 @@ function CioQuiz(props) {
|
|
|
70
71
|
const questionData = (_a = state.quiz.currentQuestion) === null || _a === void 0 ? void 0 : _a.next_question;
|
|
71
72
|
const questionType = questionData === null || questionData === void 0 ? void 0 : questionData.type;
|
|
72
73
|
const questionImages = questionData === null || questionData === void 0 ? void 0 : questionData.images;
|
|
73
|
-
const displayBackgroundImage = (questionType ===
|
|
74
|
+
const displayBackgroundImage = (questionType === actions_1.QuestionTypes.SingleSelect ||
|
|
75
|
+
questionType === actions_1.QuestionTypes.MultipleSelect) &&
|
|
76
|
+
questionImages;
|
|
74
77
|
if (state.quiz.requestState === constants_1.RequestStates.Success) {
|
|
75
78
|
return (react_1.default.createElement("div", { className: 'cio-quiz', style: { overflow: showShareModal || showSessionPrompt ? 'hidden' : undefined } },
|
|
76
79
|
displayBackgroundImage && (0, utils_1.renderImages)(questionImages, 'cio-question-background-image'),
|
|
@@ -15,14 +15,16 @@ function apiReducer(state, action) {
|
|
|
15
15
|
case actions_1.QuizAPIActionTypes.SET_IS_ERROR:
|
|
16
16
|
return Object.assign(Object.assign({}, state), { quizRequestState: constants_1.RequestStates.Error, quizCurrentQuestion: undefined, quizResults: undefined, selectedOptionsWithAttributes: undefined, matchedOptions: undefined });
|
|
17
17
|
case actions_1.QuizAPIActionTypes.SET_CURRENT_QUESTION: {
|
|
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);
|
|
18
|
+
const { isOpenQuestion, isCoverQuestion, isSingleQuestion, isMultipleQuestion, isSelectQuestion, isSingleFilterQuestion, isMultipleFilterQuestion, } = (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
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
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
|
-
isSelectQuestion
|
|
25
|
+
isSelectQuestion,
|
|
26
|
+
isSingleFilterQuestion,
|
|
27
|
+
isMultipleFilterQuestion }), quizFirstQuestion, quizResults: undefined, selectedOptionsWithAttributes: undefined, matchedOptions: undefined });
|
|
26
28
|
}
|
|
27
29
|
case actions_1.QuizAPIActionTypes.SET_QUIZ_RESULTS: {
|
|
28
30
|
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)) || [];
|
|
@@ -14,8 +14,33 @@ function answerInputReducer(state, action) {
|
|
|
14
14
|
value: action.payload.input,
|
|
15
15
|
} });
|
|
16
16
|
}
|
|
17
|
-
function
|
|
17
|
+
function handleNextQuestion(state) {
|
|
18
18
|
var _a;
|
|
19
|
+
const { answers, answerInputs } = state;
|
|
20
|
+
const newAnswers = [...answers];
|
|
21
|
+
const lastAnswerInputIndex = answers.length;
|
|
22
|
+
const currentAnswerInput = (_a = Object.values(state.answerInputs)) === null || _a === void 0 ? void 0 : _a[lastAnswerInputIndex];
|
|
23
|
+
switch (currentAnswerInput.type) {
|
|
24
|
+
case actions_1.QuestionTypes.OpenText:
|
|
25
|
+
newAnswers.push(['true']);
|
|
26
|
+
break;
|
|
27
|
+
case actions_1.QuestionTypes.Cover:
|
|
28
|
+
newAnswers.push(['seen']);
|
|
29
|
+
break;
|
|
30
|
+
case actions_1.QuestionTypes.SingleSelect:
|
|
31
|
+
case actions_1.QuestionTypes.MultipleSelect:
|
|
32
|
+
case actions_1.QuestionTypes.SingleFilterValue:
|
|
33
|
+
case actions_1.QuestionTypes.MultipleFilterValues:
|
|
34
|
+
newAnswers.push(currentAnswerInput.value.map((answer) => answer.id));
|
|
35
|
+
break;
|
|
36
|
+
default:
|
|
37
|
+
newAnswers.push([]);
|
|
38
|
+
}
|
|
39
|
+
return Object.assign(Object.assign({}, state), {
|
|
40
|
+
// We now commit current answers to prevAnswerInputs
|
|
41
|
+
prevAnswerInputs: answerInputs, answers: newAnswers, isQuizCompleted: false });
|
|
42
|
+
}
|
|
43
|
+
function quizLocalReducer(state, action) {
|
|
19
44
|
switch (action.type) {
|
|
20
45
|
case actions_1.QuestionTypes.OpenText:
|
|
21
46
|
return Object.assign(Object.assign({}, state), { answerInputs: answerInputReducer(state.answerInputs, action), isQuizCompleted: false });
|
|
@@ -25,28 +50,12 @@ function quizLocalReducer(state, action) {
|
|
|
25
50
|
return Object.assign(Object.assign({}, state), { answerInputs: answerInputReducer(state.answerInputs, action), isQuizCompleted: false });
|
|
26
51
|
case actions_1.QuestionTypes.MultipleSelect:
|
|
27
52
|
return Object.assign(Object.assign({}, state), { answerInputs: answerInputReducer(state.answerInputs, action), isQuizCompleted: false });
|
|
53
|
+
case actions_1.QuestionTypes.SingleFilterValue:
|
|
54
|
+
return Object.assign(Object.assign({}, state), { answerInputs: answerInputReducer(state.answerInputs, action), isQuizCompleted: false });
|
|
55
|
+
case actions_1.QuestionTypes.MultipleFilterValues:
|
|
56
|
+
return Object.assign(Object.assign({}, state), { answerInputs: answerInputReducer(state.answerInputs, action), isQuizCompleted: false });
|
|
28
57
|
case actions_1.QuestionTypes.Next: {
|
|
29
|
-
|
|
30
|
-
const newAnswers = [...answers];
|
|
31
|
-
const lastAnswerInputIndex = answers.length;
|
|
32
|
-
const currentAnswerInput = (_a = Object.values(state.answerInputs)) === null || _a === void 0 ? void 0 : _a[lastAnswerInputIndex];
|
|
33
|
-
switch (currentAnswerInput.type) {
|
|
34
|
-
case actions_1.QuestionTypes.OpenText:
|
|
35
|
-
newAnswers.push(['true']);
|
|
36
|
-
break;
|
|
37
|
-
case actions_1.QuestionTypes.Cover:
|
|
38
|
-
newAnswers.push(['seen']);
|
|
39
|
-
break;
|
|
40
|
-
case actions_1.QuestionTypes.SingleSelect:
|
|
41
|
-
case actions_1.QuestionTypes.MultipleSelect:
|
|
42
|
-
newAnswers.push(currentAnswerInput.value.map((answer) => answer.id));
|
|
43
|
-
break;
|
|
44
|
-
default:
|
|
45
|
-
newAnswers.push([]);
|
|
46
|
-
}
|
|
47
|
-
return Object.assign(Object.assign({}, state), {
|
|
48
|
-
// We now commit current answers to prevAnswerInputs
|
|
49
|
-
prevAnswerInputs: answerInputs, answers: newAnswers, isQuizCompleted: false });
|
|
58
|
+
return handleNextQuestion(state);
|
|
50
59
|
}
|
|
51
60
|
case actions_1.QuestionTypes.Skip: {
|
|
52
61
|
const { answers, answerInputs } = state;
|
|
@@ -62,6 +71,8 @@ function quizLocalReducer(state, action) {
|
|
|
62
71
|
break;
|
|
63
72
|
case actions_1.QuestionTypes.SingleSelect:
|
|
64
73
|
case actions_1.QuestionTypes.MultipleSelect:
|
|
74
|
+
case actions_1.QuestionTypes.SingleFilterValue:
|
|
75
|
+
case actions_1.QuestionTypes.MultipleFilterValues:
|
|
65
76
|
default:
|
|
66
77
|
newAnswers.push([]);
|
|
67
78
|
}
|
|
@@ -16,7 +16,10 @@ function SelectTypeQuestion() {
|
|
|
16
16
|
if (state === null || state === void 0 ? void 0 : state.quiz.currentQuestion) {
|
|
17
17
|
question = state.quiz.currentQuestion.next_question;
|
|
18
18
|
hasImages = question === null || question === void 0 ? void 0 : question.options.some((option) => option.images);
|
|
19
|
-
instructions =
|
|
19
|
+
instructions =
|
|
20
|
+
((question === null || question === void 0 ? void 0 : question.type) === actions_1.QuestionTypes.MultipleSelect ||
|
|
21
|
+
question.type === actions_1.QuestionTypes.MultipleFilterValues) &&
|
|
22
|
+
'Select one or more options';
|
|
20
23
|
}
|
|
21
24
|
if (question) {
|
|
22
25
|
return (react_1.default.createElement("div", { className: 'cio-select-question-container', "data-cnstrc-question-type": question.type, "data-question-key": question.key },
|
|
@@ -10,11 +10,12 @@ function useSelectInputProps(quizAnswerChanged, nextQuestion, currentQuestionDat
|
|
|
10
10
|
const [selected, setSelected] = (0, react_1.useState)({});
|
|
11
11
|
const singleSelectClicked = (0, react_1.useRef)({});
|
|
12
12
|
const toggleIdSelected = (0, react_1.useCallback)((id) => {
|
|
13
|
-
if (type === actions_1.QuestionTypes.SingleSelect) {
|
|
13
|
+
if (type === actions_1.QuestionTypes.SingleSelect || type === actions_1.QuestionTypes.SingleFilterValue) {
|
|
14
14
|
singleSelectClicked.current = true;
|
|
15
15
|
setSelected({ [id]: true });
|
|
16
|
+
return;
|
|
16
17
|
}
|
|
17
|
-
|
|
18
|
+
if (type === actions_1.QuestionTypes.MultipleSelect || type === actions_1.QuestionTypes.MultipleFilterValues) {
|
|
18
19
|
if (selected[id]) {
|
|
19
20
|
const newState = Object.assign({}, selected);
|
|
20
21
|
delete newState[id];
|
|
@@ -39,7 +40,7 @@ function useSelectInputProps(quizAnswerChanged, nextQuestion, currentQuestionDat
|
|
|
39
40
|
const prevSelected = {};
|
|
40
41
|
if (Array.isArray(currentAnswer === null || currentAnswer === void 0 ? void 0 : currentAnswer.value)) {
|
|
41
42
|
(_a = currentAnswer === null || currentAnswer === void 0 ? void 0 : currentAnswer.value) === null || _a === void 0 ? void 0 : _a.forEach((answer) => {
|
|
42
|
-
prevSelected[
|
|
43
|
+
prevSelected[answer.id] = true;
|
|
43
44
|
setSelected(prevSelected);
|
|
44
45
|
});
|
|
45
46
|
}
|
|
@@ -52,11 +53,20 @@ function useSelectInputProps(quizAnswerChanged, nextQuestion, currentQuestionDat
|
|
|
52
53
|
}, [currentQuestionData === null || currentQuestionData === void 0 ? void 0 : currentQuestionData.id]);
|
|
53
54
|
// Update global state
|
|
54
55
|
(0, react_1.useEffect)(() => {
|
|
55
|
-
var _a, _b;
|
|
56
|
-
if ((currentQuestionData === null || currentQuestionData === void 0 ? void 0 : currentQuestionData.type) ===
|
|
56
|
+
var _a, _b, _c, _d;
|
|
57
|
+
if ((currentQuestionData === null || currentQuestionData === void 0 ? void 0 : currentQuestionData.type) === actions_1.QuestionTypes.MultipleSelect ||
|
|
58
|
+
(currentQuestionData === null || currentQuestionData === void 0 ? void 0 : currentQuestionData.type) === actions_1.QuestionTypes.SingleSelect) {
|
|
57
59
|
const selectedAnswers = (_b = (_a = currentQuestionData === null || currentQuestionData === void 0 ? void 0 : currentQuestionData.options) === null || _a === void 0 ? void 0 : _a.filter((opt) => selected[Number(opt.id)])) === null || _b === void 0 ? void 0 : _b.map((opt) => ({ id: opt.id, value: opt.value }));
|
|
58
60
|
quizAnswerChanged(selectedAnswers);
|
|
59
61
|
}
|
|
62
|
+
if ((currentQuestionData === null || currentQuestionData === void 0 ? void 0 : currentQuestionData.type) === actions_1.QuestionTypes.MultipleFilterValues ||
|
|
63
|
+
(currentQuestionData === null || currentQuestionData === void 0 ? void 0 : currentQuestionData.type) === actions_1.QuestionTypes.SingleFilterValue) {
|
|
64
|
+
const selectedAnswers = (_d = (_c = currentQuestionData === null || currentQuestionData === void 0 ? void 0 : currentQuestionData.options) === null || _c === void 0 ? void 0 : _c.filter((opt) => selected[String(opt.id)])) === null || _d === void 0 ? void 0 : _d.map((opt) => ({
|
|
65
|
+
id: opt.id,
|
|
66
|
+
value: opt.value,
|
|
67
|
+
}));
|
|
68
|
+
quizAnswerChanged(selectedAnswers);
|
|
69
|
+
}
|
|
60
70
|
}, [
|
|
61
71
|
selected,
|
|
62
72
|
currentQuestionData === null || currentQuestionData === void 0 ? void 0 : currentQuestionData.id,
|
|
@@ -67,7 +77,9 @@ function useSelectInputProps(quizAnswerChanged, nextQuestion, currentQuestionDat
|
|
|
67
77
|
// Go to next question only every time answerInputs (answers input state) changes...
|
|
68
78
|
// and it's a singleSelectQuestion and user has just clicked on an option
|
|
69
79
|
(0, react_1.useEffect)(() => {
|
|
70
|
-
if ((currentQuestionData === null || currentQuestionData === void 0 ? void 0 : currentQuestionData.type) ===
|
|
80
|
+
if (((currentQuestionData === null || currentQuestionData === void 0 ? void 0 : currentQuestionData.type) === actions_1.QuestionTypes.SingleSelect ||
|
|
81
|
+
(currentQuestionData === null || currentQuestionData === void 0 ? void 0 : currentQuestionData.type) === actions_1.QuestionTypes.SingleFilterValue) &&
|
|
82
|
+
singleSelectClicked.current) {
|
|
71
83
|
nextQuestion();
|
|
72
84
|
}
|
|
73
85
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
@@ -29,6 +29,8 @@ const useQuizAnswerChangeHandler = (quizApiState, dispatchLocalState) => {
|
|
|
29
29
|
break;
|
|
30
30
|
case actions_1.QuestionTypes.SingleSelect:
|
|
31
31
|
case actions_1.QuestionTypes.MultipleSelect:
|
|
32
|
+
case actions_1.QuestionTypes.SingleFilterValue:
|
|
33
|
+
case actions_1.QuestionTypes.MultipleFilterValues:
|
|
32
34
|
dispatchLocalState({
|
|
33
35
|
type: currentQuestion.next_question.type,
|
|
34
36
|
payload: {
|
package/lib/cjs/utils.js
CHANGED
|
@@ -77,13 +77,17 @@ const getQuestionTypes = (questionType) => {
|
|
|
77
77
|
const isCoverQuestion = questionType === actions_1.QuestionTypes.Cover;
|
|
78
78
|
const isSingleQuestion = questionType === actions_1.QuestionTypes.SingleSelect;
|
|
79
79
|
const isMultipleQuestion = questionType === actions_1.QuestionTypes.MultipleSelect;
|
|
80
|
-
const
|
|
80
|
+
const isSingleFilterQuestion = questionType === actions_1.QuestionTypes.SingleFilterValue;
|
|
81
|
+
const isMultipleFilterQuestion = questionType === actions_1.QuestionTypes.MultipleFilterValues;
|
|
82
|
+
const isSelectQuestion = isSingleQuestion || isMultipleQuestion || isSingleFilterQuestion || isMultipleFilterQuestion;
|
|
81
83
|
return {
|
|
82
84
|
isOpenQuestion,
|
|
83
85
|
isCoverQuestion,
|
|
84
86
|
isSingleQuestion,
|
|
85
87
|
isMultipleQuestion,
|
|
86
88
|
isSelectQuestion,
|
|
89
|
+
isSingleFilterQuestion,
|
|
90
|
+
isMultipleFilterQuestion,
|
|
87
91
|
};
|
|
88
92
|
};
|
|
89
93
|
exports.getQuestionTypes = getQuestionTypes;
|
package/lib/cjs/version.js
CHANGED
|
@@ -5,6 +5,8 @@ export var QuestionTypes;
|
|
|
5
5
|
QuestionTypes["Cover"] = "cover";
|
|
6
6
|
QuestionTypes["SingleSelect"] = "single";
|
|
7
7
|
QuestionTypes["MultipleSelect"] = "multiple";
|
|
8
|
+
QuestionTypes["SingleFilterValue"] = "single_filter_value";
|
|
9
|
+
QuestionTypes["MultipleFilterValues"] = "multiple_filter_values";
|
|
8
10
|
QuestionTypes["Next"] = "next";
|
|
9
11
|
QuestionTypes["Skip"] = "skip";
|
|
10
12
|
QuestionTypes["Back"] = "back";
|
|
@@ -10,6 +10,7 @@ import SessionPromptModal from '../SessionPromptModal/SessionPromptModal';
|
|
|
10
10
|
import ShareResultsModal from '../ShareResultsModal/ShareResultsModal';
|
|
11
11
|
import { convertPrimaryColorsToString, renderImages } from '../../utils';
|
|
12
12
|
import ProgressBar from '../ProgressBar/ProgressBar';
|
|
13
|
+
import { QuestionTypes } from './actions';
|
|
13
14
|
export default function CioQuiz(props) {
|
|
14
15
|
const { cioClient, state, events: { hydrateQuiz, resetSessionStorageState }, getAddToCartButtonProps, getAddToFavoritesButtonProps, getCoverQuestionProps, getHydrateQuizButtonProps, getNextQuestionButtonProps, getSkipQuestionButtonProps, getOpenTextInputProps, getPreviousQuestionButtonProps, getQuizImageProps, getQuizResultButtonProps, getQuizResultLinkProps, getResetQuizButtonProps, getSelectInputProps, primaryColorStyles, getShareResultsButtonProps, } = useQuiz(props);
|
|
15
16
|
const [showSessionPrompt, setShowSessionPrompt] = useState(false);
|
|
@@ -66,7 +67,9 @@ export default function CioQuiz(props) {
|
|
|
66
67
|
const questionData = state.quiz.currentQuestion?.next_question;
|
|
67
68
|
const questionType = questionData?.type;
|
|
68
69
|
const questionImages = questionData?.images;
|
|
69
|
-
const displayBackgroundImage = (questionType ===
|
|
70
|
+
const displayBackgroundImage = (questionType === QuestionTypes.SingleSelect ||
|
|
71
|
+
questionType === QuestionTypes.MultipleSelect) &&
|
|
72
|
+
questionImages;
|
|
70
73
|
if (state.quiz.requestState === RequestStates.Success) {
|
|
71
74
|
return (React.createElement("div", { className: 'cio-quiz', style: { overflow: showShareModal || showSessionPrompt ? 'hidden' : undefined } },
|
|
72
75
|
displayBackgroundImage && renderImages(questionImages, 'cio-question-background-image'),
|
|
@@ -23,7 +23,7 @@ export default function apiReducer(state, action) {
|
|
|
23
23
|
matchedOptions: undefined,
|
|
24
24
|
};
|
|
25
25
|
case QuizAPIActionTypes.SET_CURRENT_QUESTION: {
|
|
26
|
-
const { isOpenQuestion, isCoverQuestion, isSingleQuestion, isMultipleQuestion, isSelectQuestion, } = getQuestionTypes(action.payload?.quizCurrentQuestion?.next_question?.type);
|
|
26
|
+
const { isOpenQuestion, isCoverQuestion, isSingleQuestion, isMultipleQuestion, isSelectQuestion, isSingleFilterQuestion, isMultipleFilterQuestion, } = getQuestionTypes(action.payload?.quizCurrentQuestion?.next_question?.type);
|
|
27
27
|
const quizFirstQuestion = state.quizFirstQuestion || action.payload?.quizCurrentQuestion;
|
|
28
28
|
return {
|
|
29
29
|
...state,
|
|
@@ -37,6 +37,8 @@ export default function apiReducer(state, action) {
|
|
|
37
37
|
isSingleQuestion,
|
|
38
38
|
isMultipleQuestion,
|
|
39
39
|
isSelectQuestion,
|
|
40
|
+
isSingleFilterQuestion,
|
|
41
|
+
isMultipleFilterQuestion,
|
|
40
42
|
},
|
|
41
43
|
quizFirstQuestion,
|
|
42
44
|
quizResults: undefined,
|
|
@@ -14,6 +14,35 @@ function answerInputReducer(state, action) {
|
|
|
14
14
|
},
|
|
15
15
|
};
|
|
16
16
|
}
|
|
17
|
+
function handleNextQuestion(state) {
|
|
18
|
+
const { answers, answerInputs } = state;
|
|
19
|
+
const newAnswers = [...answers];
|
|
20
|
+
const lastAnswerInputIndex = answers.length;
|
|
21
|
+
const currentAnswerInput = Object.values(state.answerInputs)?.[lastAnswerInputIndex];
|
|
22
|
+
switch (currentAnswerInput.type) {
|
|
23
|
+
case QuestionTypes.OpenText:
|
|
24
|
+
newAnswers.push(['true']);
|
|
25
|
+
break;
|
|
26
|
+
case QuestionTypes.Cover:
|
|
27
|
+
newAnswers.push(['seen']);
|
|
28
|
+
break;
|
|
29
|
+
case QuestionTypes.SingleSelect:
|
|
30
|
+
case QuestionTypes.MultipleSelect:
|
|
31
|
+
case QuestionTypes.SingleFilterValue:
|
|
32
|
+
case QuestionTypes.MultipleFilterValues:
|
|
33
|
+
newAnswers.push(currentAnswerInput.value.map((answer) => answer.id));
|
|
34
|
+
break;
|
|
35
|
+
default:
|
|
36
|
+
newAnswers.push([]);
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
...state,
|
|
40
|
+
// We now commit current answers to prevAnswerInputs
|
|
41
|
+
prevAnswerInputs: answerInputs,
|
|
42
|
+
answers: newAnswers,
|
|
43
|
+
isQuizCompleted: false,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
17
46
|
export default function quizLocalReducer(state, action) {
|
|
18
47
|
switch (action.type) {
|
|
19
48
|
case QuestionTypes.OpenText:
|
|
@@ -40,32 +69,20 @@ export default function quizLocalReducer(state, action) {
|
|
|
40
69
|
answerInputs: answerInputReducer(state.answerInputs, action),
|
|
41
70
|
isQuizCompleted: false,
|
|
42
71
|
};
|
|
43
|
-
case QuestionTypes.
|
|
44
|
-
const { answers, answerInputs } = state;
|
|
45
|
-
const newAnswers = [...answers];
|
|
46
|
-
const lastAnswerInputIndex = answers.length;
|
|
47
|
-
const currentAnswerInput = Object.values(state.answerInputs)?.[lastAnswerInputIndex];
|
|
48
|
-
switch (currentAnswerInput.type) {
|
|
49
|
-
case QuestionTypes.OpenText:
|
|
50
|
-
newAnswers.push(['true']);
|
|
51
|
-
break;
|
|
52
|
-
case QuestionTypes.Cover:
|
|
53
|
-
newAnswers.push(['seen']);
|
|
54
|
-
break;
|
|
55
|
-
case QuestionTypes.SingleSelect:
|
|
56
|
-
case QuestionTypes.MultipleSelect:
|
|
57
|
-
newAnswers.push(currentAnswerInput.value.map((answer) => answer.id));
|
|
58
|
-
break;
|
|
59
|
-
default:
|
|
60
|
-
newAnswers.push([]);
|
|
61
|
-
}
|
|
72
|
+
case QuestionTypes.SingleFilterValue:
|
|
62
73
|
return {
|
|
63
74
|
...state,
|
|
64
|
-
|
|
65
|
-
prevAnswerInputs: answerInputs,
|
|
66
|
-
answers: newAnswers,
|
|
75
|
+
answerInputs: answerInputReducer(state.answerInputs, action),
|
|
67
76
|
isQuizCompleted: false,
|
|
68
77
|
};
|
|
78
|
+
case QuestionTypes.MultipleFilterValues:
|
|
79
|
+
return {
|
|
80
|
+
...state,
|
|
81
|
+
answerInputs: answerInputReducer(state.answerInputs, action),
|
|
82
|
+
isQuizCompleted: false,
|
|
83
|
+
};
|
|
84
|
+
case QuestionTypes.Next: {
|
|
85
|
+
return handleNextQuestion(state);
|
|
69
86
|
}
|
|
70
87
|
case QuestionTypes.Skip: {
|
|
71
88
|
const { answers, answerInputs } = state;
|
|
@@ -81,6 +98,8 @@ export default function quizLocalReducer(state, action) {
|
|
|
81
98
|
break;
|
|
82
99
|
case QuestionTypes.SingleSelect:
|
|
83
100
|
case QuestionTypes.MultipleSelect:
|
|
101
|
+
case QuestionTypes.SingleFilterValue:
|
|
102
|
+
case QuestionTypes.MultipleFilterValues:
|
|
84
103
|
default:
|
|
85
104
|
newAnswers.push([]);
|
|
86
105
|
}
|
|
@@ -12,7 +12,10 @@ function SelectTypeQuestion() {
|
|
|
12
12
|
if (state?.quiz.currentQuestion) {
|
|
13
13
|
question = state.quiz.currentQuestion.next_question;
|
|
14
14
|
hasImages = question?.options.some((option) => option.images);
|
|
15
|
-
instructions =
|
|
15
|
+
instructions =
|
|
16
|
+
(question?.type === QuestionTypes.MultipleSelect ||
|
|
17
|
+
question.type === QuestionTypes.MultipleFilterValues) &&
|
|
18
|
+
'Select one or more options';
|
|
16
19
|
}
|
|
17
20
|
if (question) {
|
|
18
21
|
return (React.createElement("div", { className: 'cio-select-question-container', "data-cnstrc-question-type": question.type, "data-question-key": question.key },
|
|
@@ -7,11 +7,12 @@ export default function useSelectInputProps(quizAnswerChanged, nextQuestion, cur
|
|
|
7
7
|
const [selected, setSelected] = useState({});
|
|
8
8
|
const singleSelectClicked = useRef({});
|
|
9
9
|
const toggleIdSelected = useCallback((id) => {
|
|
10
|
-
if (type === QuestionTypes.SingleSelect) {
|
|
10
|
+
if (type === QuestionTypes.SingleSelect || type === QuestionTypes.SingleFilterValue) {
|
|
11
11
|
singleSelectClicked.current = true;
|
|
12
12
|
setSelected({ [id]: true });
|
|
13
|
+
return;
|
|
13
14
|
}
|
|
14
|
-
|
|
15
|
+
if (type === QuestionTypes.MultipleSelect || type === QuestionTypes.MultipleFilterValues) {
|
|
15
16
|
if (selected[id]) {
|
|
16
17
|
const newState = { ...selected };
|
|
17
18
|
delete newState[id];
|
|
@@ -35,7 +36,7 @@ export default function useSelectInputProps(quizAnswerChanged, nextQuestion, cur
|
|
|
35
36
|
const prevSelected = {};
|
|
36
37
|
if (Array.isArray(currentAnswer?.value)) {
|
|
37
38
|
currentAnswer?.value?.forEach((answer) => {
|
|
38
|
-
prevSelected[
|
|
39
|
+
prevSelected[answer.id] = true;
|
|
39
40
|
setSelected(prevSelected);
|
|
40
41
|
});
|
|
41
42
|
}
|
|
@@ -48,12 +49,23 @@ export default function useSelectInputProps(quizAnswerChanged, nextQuestion, cur
|
|
|
48
49
|
}, [currentQuestionData?.id]);
|
|
49
50
|
// Update global state
|
|
50
51
|
useEffect(() => {
|
|
51
|
-
if (currentQuestionData?.type ===
|
|
52
|
+
if (currentQuestionData?.type === QuestionTypes.MultipleSelect ||
|
|
53
|
+
currentQuestionData?.type === QuestionTypes.SingleSelect) {
|
|
52
54
|
const selectedAnswers = currentQuestionData?.options
|
|
53
55
|
?.filter((opt) => selected[Number(opt.id)])
|
|
54
56
|
?.map((opt) => ({ id: opt.id, value: opt.value }));
|
|
55
57
|
quizAnswerChanged(selectedAnswers);
|
|
56
58
|
}
|
|
59
|
+
if (currentQuestionData?.type === QuestionTypes.MultipleFilterValues ||
|
|
60
|
+
currentQuestionData?.type === QuestionTypes.SingleFilterValue) {
|
|
61
|
+
const selectedAnswers = currentQuestionData?.options
|
|
62
|
+
?.filter((opt) => selected[String(opt.id)])
|
|
63
|
+
?.map((opt) => ({
|
|
64
|
+
id: opt.id,
|
|
65
|
+
value: opt.value,
|
|
66
|
+
}));
|
|
67
|
+
quizAnswerChanged(selectedAnswers);
|
|
68
|
+
}
|
|
57
69
|
}, [
|
|
58
70
|
selected,
|
|
59
71
|
currentQuestionData?.id,
|
|
@@ -64,7 +76,9 @@ export default function useSelectInputProps(quizAnswerChanged, nextQuestion, cur
|
|
|
64
76
|
// Go to next question only every time answerInputs (answers input state) changes...
|
|
65
77
|
// and it's a singleSelectQuestion and user has just clicked on an option
|
|
66
78
|
useEffect(() => {
|
|
67
|
-
if (currentQuestionData?.type ===
|
|
79
|
+
if ((currentQuestionData?.type === QuestionTypes.SingleSelect ||
|
|
80
|
+
currentQuestionData?.type === QuestionTypes.SingleFilterValue) &&
|
|
81
|
+
singleSelectClicked.current) {
|
|
68
82
|
nextQuestion();
|
|
69
83
|
}
|
|
70
84
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
@@ -26,6 +26,8 @@ const useQuizAnswerChangeHandler = (quizApiState, dispatchLocalState) => {
|
|
|
26
26
|
break;
|
|
27
27
|
case QuestionTypes.SingleSelect:
|
|
28
28
|
case QuestionTypes.MultipleSelect:
|
|
29
|
+
case QuestionTypes.SingleFilterValue:
|
|
30
|
+
case QuestionTypes.MultipleFilterValues:
|
|
29
31
|
dispatchLocalState({
|
|
30
32
|
type: currentQuestion.next_question.type,
|
|
31
33
|
payload: {
|
package/lib/mjs/utils.js
CHANGED
|
@@ -70,13 +70,17 @@ export const getQuestionTypes = (questionType) => {
|
|
|
70
70
|
const isCoverQuestion = questionType === QuestionTypes.Cover;
|
|
71
71
|
const isSingleQuestion = questionType === QuestionTypes.SingleSelect;
|
|
72
72
|
const isMultipleQuestion = questionType === QuestionTypes.MultipleSelect;
|
|
73
|
-
const
|
|
73
|
+
const isSingleFilterQuestion = questionType === QuestionTypes.SingleFilterValue;
|
|
74
|
+
const isMultipleFilterQuestion = questionType === QuestionTypes.MultipleFilterValues;
|
|
75
|
+
const isSelectQuestion = isSingleQuestion || isMultipleQuestion || isSingleFilterQuestion || isMultipleFilterQuestion;
|
|
74
76
|
return {
|
|
75
77
|
isOpenQuestion,
|
|
76
78
|
isCoverQuestion,
|
|
77
79
|
isSingleQuestion,
|
|
78
80
|
isMultipleQuestion,
|
|
79
81
|
isSelectQuestion,
|
|
82
|
+
isSingleFilterQuestion,
|
|
83
|
+
isMultipleFilterQuestion,
|
|
80
84
|
};
|
|
81
85
|
};
|
|
82
86
|
export function getPreferredColorScheme() {
|
package/lib/mjs/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export default '1.17.
|
|
1
|
+
export default '1.17.26';
|
|
@@ -5,6 +5,8 @@ export declare enum QuestionTypes {
|
|
|
5
5
|
Cover = "cover",
|
|
6
6
|
SingleSelect = "single",
|
|
7
7
|
MultipleSelect = "multiple",
|
|
8
|
+
SingleFilterValue = "single_filter_value",
|
|
9
|
+
MultipleFilterValues = "multiple_filter_values",
|
|
8
10
|
Next = "next",
|
|
9
11
|
Skip = "skip",
|
|
10
12
|
Back = "back",
|
|
@@ -23,7 +25,7 @@ interface Action<Type, Payload = {}> {
|
|
|
23
25
|
type: Type;
|
|
24
26
|
payload?: Payload;
|
|
25
27
|
}
|
|
26
|
-
export type ActionAnswerInputQuestion = Action<QuestionTypes.OpenText, OpenTextQuestionPayload> | Action<QuestionTypes.SingleSelect, SelectQuestionPayload> | Action<QuestionTypes.MultipleSelect, SelectQuestionPayload> | Action<QuestionTypes.Cover, CoverQuestionPayload>;
|
|
28
|
+
export type ActionAnswerInputQuestion = Action<QuestionTypes.OpenText, OpenTextQuestionPayload> | Action<QuestionTypes.SingleSelect, SelectQuestionPayload> | Action<QuestionTypes.MultipleSelect, SelectQuestionPayload> | Action<QuestionTypes.SingleFilterValue, SelectQuestionPayload> | Action<QuestionTypes.MultipleFilterValues, SelectQuestionPayload> | Action<QuestionTypes.Cover, CoverQuestionPayload>;
|
|
27
29
|
export type ActionAnswerQuestion = ActionAnswerInputQuestion | Action<QuestionTypes.Next, CurrentQuestion> | Action<QuestionTypes.Skip, CurrentQuestion> | Action<QuestionTypes.Back, CurrentQuestion> | Action<QuestionTypes.Reset> | Action<QuestionTypes.Complete> | Action<QuestionTypes.Hydrate, Partial<QuizLocalReducerState>>;
|
|
28
30
|
export declare enum QuizAPIActionTypes {
|
|
29
31
|
SET_IS_LOADING = 0,
|
package/lib/types/types.d.ts
CHANGED
|
@@ -105,13 +105,15 @@ export interface QuizSessionStorageState {
|
|
|
105
105
|
skipToResults: boolean;
|
|
106
106
|
hasSessionStorageState: () => boolean;
|
|
107
107
|
}
|
|
108
|
-
export type InputQuestionsTypes = QuestionTypes.OpenText | QuestionTypes.Cover | QuestionTypes.SingleSelect | QuestionTypes.MultipleSelect;
|
|
108
|
+
export type InputQuestionsTypes = QuestionTypes.OpenText | QuestionTypes.Cover | QuestionTypes.SingleSelect | QuestionTypes.MultipleSelect | QuestionTypes.SingleFilterValue | QuestionTypes.MultipleFilterValues;
|
|
109
109
|
export type CurrentQuestion = NextQuestionResponse & {
|
|
110
110
|
isFirstQuestion: boolean;
|
|
111
111
|
isOpenQuestion: boolean;
|
|
112
112
|
isCoverQuestion: boolean;
|
|
113
113
|
isSingleQuestion: boolean;
|
|
114
114
|
isMultipleQuestion: boolean;
|
|
115
|
+
isSingleFilterQuestion: boolean;
|
|
116
|
+
isMultipleFilterQuestion: boolean;
|
|
115
117
|
isSelectQuestion: boolean;
|
|
116
118
|
};
|
|
117
119
|
export declare namespace QuizEventsReturn {
|
|
@@ -217,7 +219,7 @@ export interface SelectInputProps {
|
|
|
217
219
|
onKeyDown: React.KeyboardEventHandler<HTMLElement>;
|
|
218
220
|
role: 'button';
|
|
219
221
|
tabIndex: number;
|
|
220
|
-
key: number;
|
|
222
|
+
key: number | string;
|
|
221
223
|
}
|
|
222
224
|
export type GetOpenTextInputProps = () => OpenTextInputProps;
|
|
223
225
|
export type GetCoverQuestionProps = () => CoverQuestionProps;
|
package/lib/types/utils.d.ts
CHANGED
|
@@ -27,6 +27,8 @@ export declare const getQuestionTypes: (questionType?: `${QuestionTypes}`) => {
|
|
|
27
27
|
isSingleQuestion: boolean;
|
|
28
28
|
isMultipleQuestion: boolean;
|
|
29
29
|
isSelectQuestion: boolean;
|
|
30
|
+
isSingleFilterQuestion: boolean;
|
|
31
|
+
isMultipleFilterQuestion: boolean;
|
|
30
32
|
};
|
|
31
33
|
export declare function getPreferredColorScheme(): string;
|
|
32
34
|
export declare function isFunction(fn: any): boolean;
|
package/lib/types/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare const _default: "1.17.
|
|
1
|
+
declare const _default: "1.17.26";
|
|
2
2
|
export default _default;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@constructor-io/constructorio-ui-quizzes",
|
|
3
|
-
"version": "1.17.
|
|
3
|
+
"version": "1.17.26",
|
|
4
4
|
"description": "Constructor.io Quizzes UI library for web applications",
|
|
5
5
|
"author": "Constructor.io Corporation",
|
|
6
6
|
"license": "MIT",
|
|
@@ -25,11 +25,13 @@
|
|
|
25
25
|
},
|
|
26
26
|
".": {
|
|
27
27
|
"import": "./lib/mjs/index.js",
|
|
28
|
-
"require": "./lib/cjs/index.js"
|
|
28
|
+
"require": "./lib/cjs/index.js",
|
|
29
|
+
"types": "./lib/types/index.d.ts"
|
|
29
30
|
},
|
|
30
31
|
"./cjs": {
|
|
31
32
|
"import": "./lib/cjs/index.js",
|
|
32
|
-
"require": "./lib/cjs/index.js"
|
|
33
|
+
"require": "./lib/cjs/index.js",
|
|
34
|
+
"types": "./lib/types/index.d.ts"
|
|
33
35
|
}
|
|
34
36
|
},
|
|
35
37
|
"files": [
|
|
@@ -59,7 +61,7 @@
|
|
|
59
61
|
"check-license": "license-checker --production --onlyAllow 'Apache-2.0;BSD-3-Clause;MIT;0BSD;BSD-2-Clause' --excludePackages 'picocolors@1.0.0'"
|
|
60
62
|
},
|
|
61
63
|
"peerDependencies": {
|
|
62
|
-
"@constructor-io/constructorio-client-javascript": "^2.
|
|
64
|
+
"@constructor-io/constructorio-client-javascript": "^2.65.1",
|
|
63
65
|
"react": ">=16.12.0",
|
|
64
66
|
"react-dom": ">=16.12.0",
|
|
65
67
|
"tslib": "^2.4.0"
|
|
@@ -112,6 +114,7 @@
|
|
|
112
114
|
"typescript": "^4.9.4",
|
|
113
115
|
"vite": "^4.2.1",
|
|
114
116
|
"vite-plugin-css-injected-by-js": "^3.1.0",
|
|
115
|
-
"webpack": "^5.75.0"
|
|
117
|
+
"webpack": "^5.75.0",
|
|
118
|
+
"whatwg-fetch": "^3.6.20"
|
|
116
119
|
}
|
|
117
120
|
}
|