@constructor-io/constructorio-ui-quizzes 1.0.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.
- package/LICENSE +21 -0
- package/README.md +88 -0
- package/lib/cjs/components/BackButton/BackButton.js +12 -0
- package/lib/cjs/components/CTAButton/CTAButton.js +10 -0
- package/lib/cjs/components/CioQuiz/actions.js +12 -0
- package/lib/cjs/components/CioQuiz/context.js +5 -0
- package/lib/cjs/components/CioQuiz/index.js +115 -0
- package/lib/cjs/components/CioQuiz/reducer.js +32 -0
- package/lib/cjs/components/ControlBar/ControlBar.js +13 -0
- package/lib/cjs/components/CoverTypeQuestion/CoverTypeQuestion.js +42 -0
- package/lib/cjs/components/OpenTextTypeQuestion/OpenTextTypeQuestion.js +66 -0
- package/lib/cjs/components/QuestionDescription/QuestionDescription.js +9 -0
- package/lib/cjs/components/QuestionTitle/QuestionTitle.js +9 -0
- package/lib/cjs/components/QuizQuestions/index.js +17 -0
- package/lib/cjs/components/RedoButton/RedoButton.js +12 -0
- package/lib/cjs/components/RedoButton/RedoSVG.js +15 -0
- package/lib/cjs/components/ResultCard/ResultCard.js +45 -0
- package/lib/cjs/components/ResultContainer/ResultContainer.js +39 -0
- package/lib/cjs/components/ResultCtaButton/ResultCtaButton.js +16 -0
- package/lib/cjs/components/ResultFilters/ResultFilters.js +30 -0
- package/lib/cjs/components/Results/Results.js +16 -0
- package/lib/cjs/components/SelectTypeQuestion/SelectTypeQuestion.js +90 -0
- package/lib/cjs/components/Spinner/Spinner.js +12 -0
- package/lib/cjs/components/ZeroResults/ZeroResults.js +13 -0
- package/lib/cjs/constants.js +36 -0
- package/lib/cjs/hooks/useCioClient.js +11 -0
- package/lib/cjs/index.js +5 -0
- package/lib/cjs/stories/Quiz/argTypes.js +30 -0
- package/lib/cjs/types.js +2 -0
- package/lib/cjs/utils.js +97 -0
- package/lib/mjs/components/BackButton/BackButton.js +9 -0
- package/lib/mjs/components/CTAButton/CTAButton.js +7 -0
- package/lib/mjs/components/CioQuiz/actions.js +9 -0
- package/lib/mjs/components/CioQuiz/context.js +2 -0
- package/lib/mjs/components/CioQuiz/index.js +111 -0
- package/lib/mjs/components/CioQuiz/reducer.js +55 -0
- package/lib/mjs/components/ControlBar/ControlBar.js +10 -0
- package/lib/mjs/components/CoverTypeQuestion/CoverTypeQuestion.js +37 -0
- package/lib/mjs/components/OpenTextTypeQuestion/OpenTextTypeQuestion.js +61 -0
- package/lib/mjs/components/QuestionDescription/QuestionDescription.js +6 -0
- package/lib/mjs/components/QuestionTitle/QuestionTitle.js +6 -0
- package/lib/mjs/components/QuizQuestions/index.js +13 -0
- package/lib/mjs/components/RedoButton/RedoButton.js +9 -0
- package/lib/mjs/components/RedoButton/RedoSVG.js +12 -0
- package/lib/mjs/components/ResultCard/ResultCard.js +37 -0
- package/lib/mjs/components/ResultContainer/ResultContainer.js +34 -0
- package/lib/mjs/components/ResultCtaButton/ResultCtaButton.js +12 -0
- package/lib/mjs/components/ResultFilters/ResultFilters.js +27 -0
- package/lib/mjs/components/Results/Results.js +9 -0
- package/lib/mjs/components/SelectTypeQuestion/SelectTypeQuestion.js +85 -0
- package/lib/mjs/components/Spinner/Spinner.js +8 -0
- package/lib/mjs/components/ZeroResults/ZeroResults.js +10 -0
- package/lib/mjs/constants.js +33 -0
- package/lib/mjs/hooks/useCioClient.js +9 -0
- package/lib/mjs/index.js +2 -0
- package/lib/mjs/stories/Quiz/argTypes.js +27 -0
- package/lib/mjs/types.js +1 -0
- package/lib/mjs/utils.js +83 -0
- package/lib/styles.css +689 -0
- package/lib/types/components/BackButton/BackButton.d.ts +3 -0
- package/lib/types/components/CTAButton/CTAButton.d.ts +6 -0
- package/lib/types/components/CioQuiz/actions.d.ts +25 -0
- package/lib/types/components/CioQuiz/context.d.ts +17 -0
- package/lib/types/components/CioQuiz/index.d.ts +14 -0
- package/lib/types/components/CioQuiz/reducer.d.ts +12 -0
- package/lib/types/components/ControlBar/ControlBar.d.ts +10 -0
- package/lib/types/components/CoverTypeQuestion/CoverTypeQuestion.d.ts +2 -0
- package/lib/types/components/OpenTextTypeQuestion/OpenTextTypeQuestion.d.ts +7 -0
- package/lib/types/components/QuestionDescription/QuestionDescription.d.ts +6 -0
- package/lib/types/components/QuestionTitle/QuestionTitle.d.ts +6 -0
- package/lib/types/components/QuizQuestions/index.d.ts +5 -0
- package/lib/types/components/RedoButton/RedoButton.d.ts +6 -0
- package/lib/types/components/RedoButton/RedoSVG.d.ts +3 -0
- package/lib/types/components/ResultCard/ResultCard.d.ts +11 -0
- package/lib/types/components/ResultContainer/ResultContainer.d.ts +7 -0
- package/lib/types/components/ResultCtaButton/ResultCtaButton.d.ts +9 -0
- package/lib/types/components/ResultFilters/ResultFilters.d.ts +6 -0
- package/lib/types/components/Results/Results.d.ts +10 -0
- package/lib/types/components/SelectTypeQuestion/SelectTypeQuestion.d.ts +3 -0
- package/lib/types/components/Spinner/Spinner.d.ts +2 -0
- package/lib/types/components/ZeroResults/ZeroResults.d.ts +6 -0
- package/lib/types/constants.d.ts +11 -0
- package/lib/types/hooks/useCioClient.d.ts +8 -0
- package/lib/types/index.d.ts +2 -0
- package/lib/types/stories/Quiz/argTypes.d.ts +30 -0
- package/lib/types/types.d.ts +1 -0
- package/lib/types/utils.d.ts +31 -0
- package/package.json +91 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
const react_1 = tslib_1.__importStar(require("react"));
|
|
5
|
+
const context_1 = tslib_1.__importDefault(require("../CioQuiz/context"));
|
|
6
|
+
const ResultCard_1 = tslib_1.__importDefault(require("../ResultCard/ResultCard"));
|
|
7
|
+
function Results(props) {
|
|
8
|
+
var _a, _b;
|
|
9
|
+
const { addToCartCallback, clickItemCallback, resultCardSalePriceKey, resultCardRegularPriceKey, } = props;
|
|
10
|
+
const { resultsResponse } = (0, react_1.useContext)(context_1.default);
|
|
11
|
+
return (react_1.default.createElement("div", { className: 'cio-results' }, (_b = (_a = resultsResponse === null || resultsResponse === void 0 ? void 0 : resultsResponse.response) === null || _a === void 0 ? void 0 : _a.results) === null || _b === void 0 ? void 0 : _b.map((result) => {
|
|
12
|
+
var _a;
|
|
13
|
+
return (react_1.default.createElement(ResultCard_1.default, { result: result, key: (_a = result.data) === null || _a === void 0 ? void 0 : _a.id, salePriceKey: resultCardSalePriceKey, regularPriceKey: resultCardRegularPriceKey, addToCartCallback: addToCartCallback, clickItemCallback: clickItemCallback }));
|
|
14
|
+
})));
|
|
15
|
+
}
|
|
16
|
+
exports.default = Results;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
const react_1 = tslib_1.__importStar(require("react"));
|
|
5
|
+
const QuestionTitle_1 = tslib_1.__importDefault(require("../QuestionTitle/QuestionTitle"));
|
|
6
|
+
const QuestionDescription_1 = tslib_1.__importDefault(require("../QuestionDescription/QuestionDescription"));
|
|
7
|
+
const context_1 = tslib_1.__importDefault(require("../CioQuiz/context"));
|
|
8
|
+
const utils_1 = require("../../utils");
|
|
9
|
+
const actions_1 = require("../CioQuiz/actions");
|
|
10
|
+
const ControlBar_1 = tslib_1.__importDefault(require("../ControlBar/ControlBar"));
|
|
11
|
+
function SelectTypeQuestion() {
|
|
12
|
+
var _a;
|
|
13
|
+
const { questionResponse, state, quizNextHandler, quizBackHandler, isFirstQuestion } = (0, react_1.useContext)(context_1.default);
|
|
14
|
+
let question;
|
|
15
|
+
let type;
|
|
16
|
+
let hasImages = false;
|
|
17
|
+
if (questionResponse) {
|
|
18
|
+
question = questionResponse.next_question;
|
|
19
|
+
type = question.type;
|
|
20
|
+
hasImages = questionResponse.next_question.options.some((option) => option.images);
|
|
21
|
+
}
|
|
22
|
+
const [selected, setSelected] = (0, react_1.useState)({});
|
|
23
|
+
const isDisabled = Object.keys(selected).length === 0;
|
|
24
|
+
(0, react_1.useEffect)(() => {
|
|
25
|
+
var _a, _b;
|
|
26
|
+
if ((_a = questionResponse === null || questionResponse === void 0 ? void 0 : questionResponse.next_question) === null || _a === void 0 ? void 0 : _a.type) {
|
|
27
|
+
const answers = ((_b = state === null || state === void 0 ? void 0 : state.answerInputs) === null || _b === void 0 ? void 0 : _b[questionResponse.next_question.id]) || [];
|
|
28
|
+
const prevSelected = {};
|
|
29
|
+
answers === null || answers === void 0 ? void 0 : answers.forEach((answer) => {
|
|
30
|
+
prevSelected[Number(answer)] = true;
|
|
31
|
+
});
|
|
32
|
+
setSelected(prevSelected);
|
|
33
|
+
}
|
|
34
|
+
}, [questionResponse, state === null || state === void 0 ? void 0 : state.answerInputs]);
|
|
35
|
+
const toggleIdSelected = (id) => {
|
|
36
|
+
if (type === actions_1.QuestionTypes.SingleSelect) {
|
|
37
|
+
setSelected({ [id]: true });
|
|
38
|
+
}
|
|
39
|
+
else if (type === actions_1.QuestionTypes.MultipleSelect) {
|
|
40
|
+
if (selected[id]) {
|
|
41
|
+
const newState = Object.assign({}, selected);
|
|
42
|
+
delete newState[id];
|
|
43
|
+
setSelected(newState);
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
setSelected(Object.assign(Object.assign({}, selected), { [id]: true }));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
const onOptionKeyDown = (event, id) => {
|
|
51
|
+
if ((event === null || event === void 0 ? void 0 : event.key) === ' ' || (event === null || event === void 0 ? void 0 : event.key) === 'Enter') {
|
|
52
|
+
toggleIdSelected(id);
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
const onNextClick = () => {
|
|
56
|
+
if (quizNextHandler && !isDisabled && questionResponse) {
|
|
57
|
+
const questionType = type === actions_1.QuestionTypes.SingleSelect
|
|
58
|
+
? actions_1.QuestionTypes.SingleSelect
|
|
59
|
+
: actions_1.QuestionTypes.MultipleSelect;
|
|
60
|
+
quizNextHandler({
|
|
61
|
+
type: questionType,
|
|
62
|
+
payload: {
|
|
63
|
+
questionId: questionResponse === null || questionResponse === void 0 ? void 0 : questionResponse.next_question.id,
|
|
64
|
+
input: Object.keys(selected).filter((key) => selected[Number(key)]),
|
|
65
|
+
isLastQuestion: questionResponse.is_last_question,
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
if (question) {
|
|
71
|
+
return (react_1.default.createElement("div", { className: 'cio-select-question-container' },
|
|
72
|
+
react_1.default.createElement("div", { className: 'cio-select-question-text' },
|
|
73
|
+
react_1.default.createElement(QuestionTitle_1.default, { title: question.title }),
|
|
74
|
+
(question === null || question === void 0 ? void 0 : question.description) ? react_1.default.createElement(QuestionDescription_1.default, { description: question.description }) : ''),
|
|
75
|
+
react_1.default.createElement("div", { className: `${!hasImages
|
|
76
|
+
? 'cio-question-options-container-text-only'
|
|
77
|
+
: 'cio-question-options-container'}` }, (_a = question === null || question === void 0 ? void 0 : question.options) === null || _a === void 0 ? void 0 : _a.map((option) => (react_1.default.createElement("div", { className: `${!hasImages
|
|
78
|
+
? 'cio-question-option-container-text-only'
|
|
79
|
+
: 'cio-question-option-container'} ${selected[option.id] ? 'selected' : ''}`, onClick: () => {
|
|
80
|
+
toggleIdSelected(option.id);
|
|
81
|
+
}, onKeyDown: (event) => {
|
|
82
|
+
onOptionKeyDown(event, option.id);
|
|
83
|
+
}, role: 'button', tabIndex: 0, key: option.id },
|
|
84
|
+
option.images ? (0, utils_1.renderImages)(option.images, 'cio-question-option-image') : '',
|
|
85
|
+
react_1.default.createElement("div", { className: 'cio-question-option-value' }, option === null || option === void 0 ? void 0 : option.value))))),
|
|
86
|
+
react_1.default.createElement(ControlBar_1.default, { nextButtonHandler: onNextClick, isNextButtonDisabled: isDisabled, backButtonHandler: quizBackHandler, showBackButton: !isFirstQuestion, ctaButtonText: question === null || question === void 0 ? void 0 : question.cta_text })));
|
|
87
|
+
}
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
exports.default = SelectTypeQuestion;
|
|
@@ -0,0 +1,12 @@
|
|
|
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
|
+
function Spinner() {
|
|
6
|
+
return (react_1.default.createElement("div", { className: 'cio-spinner' },
|
|
7
|
+
react_1.default.createElement("div", null),
|
|
8
|
+
react_1.default.createElement("div", null),
|
|
9
|
+
react_1.default.createElement("div", null),
|
|
10
|
+
react_1.default.createElement("div", null)));
|
|
11
|
+
}
|
|
12
|
+
exports.default = Spinner;
|
|
@@ -0,0 +1,13 @@
|
|
|
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 ZeroResults(props) {
|
|
7
|
+
const { onResetClick } = props;
|
|
8
|
+
return (react_1.default.createElement("div", { className: 'cio-zero-results' },
|
|
9
|
+
react_1.default.createElement("h3", { className: 'cio-zero-results-subtitle' }, "Sorry, it seems like we couldn\u2019t find results based on your answers."),
|
|
10
|
+
react_1.default.createElement("p", { className: 'cio-zero-results-description' }, "This is embarrassing \uD83D\uDE22. It might be that some of the questions are not properly set up from our end. Would you give us another try?"),
|
|
11
|
+
react_1.default.createElement(CTAButton_1.default, { ctaText: 'Try Again', onClick: onResetClick })));
|
|
12
|
+
}
|
|
13
|
+
exports.default = ZeroResults;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RequestStates = exports.cioJsClientDescription = exports.basicDescription = exports.componentDescription = exports.quizId = exports.apiKey = void 0;
|
|
4
|
+
// Autocomplete key index
|
|
5
|
+
exports.apiKey = 'key_wJSdZSiesX5hiVLt';
|
|
6
|
+
exports.quizId = 'coffee-quiz';
|
|
7
|
+
/// //////////////////////////////
|
|
8
|
+
// Storybook Folder Descriptions
|
|
9
|
+
/// //////////////////////////////
|
|
10
|
+
exports.componentDescription = `- import \`CioQuiz\` to render in your JSX.
|
|
11
|
+
- This component handles state management, data fetching, and rendering logic.
|
|
12
|
+
- To use this component, \`quizId\`, \`resultsPageOptions\`, and one of \`apiKey\` or \`cioJsClient\` are required.
|
|
13
|
+
- \`resultsPageOptions\` lets you configure the results page
|
|
14
|
+
- \`addToCartCallback\` is a callback function that will be called when the "Add to cart" button is clicked
|
|
15
|
+
- \`clickItemCallback\` is an optional callback function that will be called when the result card is clicked. The default behavior is redirecting the user to the item's URL
|
|
16
|
+
- \`resultCardRegularPriceKey\` is a parameter that specifies the metadata field name for the regular price
|
|
17
|
+
- \`resultCardSalePriceKey\` is an optional parameter that specifies the metadata field name for the sale price
|
|
18
|
+
- Use different props to configure the behavior of this component.
|
|
19
|
+
- The following stories show how different props affect the component's behavior
|
|
20
|
+
|
|
21
|
+
> Note: \`cioJsClient\` refers to an instance of the [constructorio-client-javascript](https://www.npmjs.com/package/@constructor-io/constructorio-client-javascript)
|
|
22
|
+
`;
|
|
23
|
+
/// //////////////////////////////
|
|
24
|
+
// Storybook Stories
|
|
25
|
+
/// //////////////////////////////
|
|
26
|
+
exports.basicDescription = `Pass an \`apiKey\` and a \`quizId\` to request questions and quiz results from Constructor's servers`;
|
|
27
|
+
exports.cioJsClientDescription = `If you are already using an instance of the \`ConstructorIOClient\`, you can pass a \`cioJsClient\` instead of an \`apiKey\` to request results from Constructor's servers
|
|
28
|
+
|
|
29
|
+
> Note: \`cioJsClient\` refers to an instance of the [constructorio-client-javascript](https://www.npmjs.com/package/@constructor-io/constructorio-client-javascript)`;
|
|
30
|
+
var RequestStates;
|
|
31
|
+
(function (RequestStates) {
|
|
32
|
+
RequestStates[RequestStates["Stale"] = 0] = "Stale";
|
|
33
|
+
RequestStates[RequestStates["Loading"] = 1] = "Loading";
|
|
34
|
+
RequestStates[RequestStates["Success"] = 2] = "Success";
|
|
35
|
+
RequestStates[RequestStates["Error"] = 3] = "Error";
|
|
36
|
+
})(RequestStates = exports.RequestStates || (exports.RequestStates = {}));
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const react_1 = require("react");
|
|
4
|
+
const utils_1 = require("../utils");
|
|
5
|
+
const useCioClient = ({ apiKey, cioJsClient }) => {
|
|
6
|
+
if (!apiKey && !cioJsClient) {
|
|
7
|
+
console.error('Either apiKey or cioJsClient is required');
|
|
8
|
+
}
|
|
9
|
+
return (0, react_1.useMemo)(() => cioJsClient || (0, utils_1.getCioClient)(apiKey), [apiKey, cioJsClient]);
|
|
10
|
+
};
|
|
11
|
+
exports.default = useCioClient;
|
package/lib/cjs/index.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.argTypes = void 0;
|
|
4
|
+
// eslint-disable-next-line
|
|
5
|
+
exports.argTypes = {
|
|
6
|
+
apiKey: {
|
|
7
|
+
type: { name: 'string' },
|
|
8
|
+
description: 'Your Constructor API key',
|
|
9
|
+
table: {
|
|
10
|
+
type: {
|
|
11
|
+
summary: 'string',
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
control: {
|
|
15
|
+
type: 'text',
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
quizId: {
|
|
19
|
+
type: { name: 'string' },
|
|
20
|
+
description: 'ID of the quiz',
|
|
21
|
+
table: {
|
|
22
|
+
type: {
|
|
23
|
+
summary: 'string',
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
control: {
|
|
27
|
+
type: 'text',
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
};
|
package/lib/cjs/types.js
ADDED
package/lib/cjs/utils.js
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getPreferredColorScheme = exports.getCioClient = exports.getQuestionTypes = exports.getQuizResults = exports.getNextQuestion = exports.stringify = exports.stringifyWithDefaults = exports.defaultAddToCartCallbackCode = exports.getStoryParams = exports.renderImages = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const react_1 = tslib_1.__importDefault(require("react"));
|
|
6
|
+
const constructorio_client_javascript_1 = tslib_1.__importDefault(require("@constructor-io/constructorio-client-javascript"));
|
|
7
|
+
const actions_1 = require("./components/CioQuiz/actions");
|
|
8
|
+
const renderImages = (images, cssClasses) => {
|
|
9
|
+
const { primary_url: primaryUrl, primary_alt: primaryAlt, secondary_url: secondaryUrl, secondary_alt: secondaryAlt, } = images;
|
|
10
|
+
if (primaryUrl) {
|
|
11
|
+
const windowWidth = window.innerWidth;
|
|
12
|
+
let src = primaryUrl;
|
|
13
|
+
let alt = primaryAlt || 'Quiz Image';
|
|
14
|
+
if (windowWidth > 768 && secondaryUrl) {
|
|
15
|
+
src = secondaryUrl;
|
|
16
|
+
if (secondaryAlt) {
|
|
17
|
+
alt = secondaryAlt;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return (react_1.default.createElement("span", { className: `${cssClasses || ''}`.trim() },
|
|
21
|
+
react_1.default.createElement("img", { className: 'cio-question-image', src: src, alt: alt })));
|
|
22
|
+
}
|
|
23
|
+
return '';
|
|
24
|
+
};
|
|
25
|
+
exports.renderImages = renderImages;
|
|
26
|
+
// More on Story layout: https://storybook.js.org/docs/react/configure/story-layout
|
|
27
|
+
const getStoryParams = (storyCode, templateCode, importCode) => {
|
|
28
|
+
const code = `
|
|
29
|
+
${importCode}
|
|
30
|
+
${storyCode}
|
|
31
|
+
${templateCode}
|
|
32
|
+
`;
|
|
33
|
+
return {
|
|
34
|
+
docs: {
|
|
35
|
+
source: {
|
|
36
|
+
code,
|
|
37
|
+
language: 'jsx',
|
|
38
|
+
format: true,
|
|
39
|
+
type: 'code',
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
exports.getStoryParams = getStoryParams;
|
|
45
|
+
exports.defaultAddToCartCallbackCode = `"addToCartCallback": (item) => console.dir(item)`;
|
|
46
|
+
const stringifyWithDefaults = (obj) => {
|
|
47
|
+
const { addToCartCallback, cioJsClient } = obj, rest = tslib_1.__rest(obj, ["addToCartCallback", "cioJsClient"]);
|
|
48
|
+
let res = JSON.stringify(rest, null, ' ');
|
|
49
|
+
if (cioJsClient) {
|
|
50
|
+
res = res.replace('"resultsPageOptions": {', `"cioJsClient": cioJsClient,
|
|
51
|
+
"resultsPageOptions": {`);
|
|
52
|
+
}
|
|
53
|
+
res = res.replace('"resultsPageOptions": {', `"resultsPageOptions": {
|
|
54
|
+
${exports.defaultAddToCartCallbackCode},`);
|
|
55
|
+
return res;
|
|
56
|
+
};
|
|
57
|
+
exports.stringifyWithDefaults = stringifyWithDefaults;
|
|
58
|
+
const stringify = (obj) => JSON.stringify(obj, null, ' ');
|
|
59
|
+
exports.stringify = stringify;
|
|
60
|
+
const getNextQuestion = (cioClient, quizId, parameters) => cioClient === null || cioClient === void 0 ? void 0 : cioClient.quizzes.getQuizNextQuestion(quizId, parameters);
|
|
61
|
+
exports.getNextQuestion = getNextQuestion;
|
|
62
|
+
const getQuizResults = (cioClient, quizId, parameters) => tslib_1.__awaiter(void 0, void 0, void 0, function* () { return cioClient === null || cioClient === void 0 ? void 0 : cioClient.quizzes.getQuizResults(quizId, parameters); });
|
|
63
|
+
exports.getQuizResults = getQuizResults;
|
|
64
|
+
const getQuestionTypes = (questionType) => {
|
|
65
|
+
const isOpenQuestion = questionType === actions_1.QuestionTypes.OpenText;
|
|
66
|
+
const isCoverQuestion = questionType === actions_1.QuestionTypes.Cover;
|
|
67
|
+
const isSingleQuestion = questionType === actions_1.QuestionTypes.SingleSelect;
|
|
68
|
+
const isMultipleQuestion = questionType === actions_1.QuestionTypes.MultipleSelect;
|
|
69
|
+
const isSelectQuestion = isSingleQuestion || isMultipleQuestion;
|
|
70
|
+
return {
|
|
71
|
+
isOpenQuestion,
|
|
72
|
+
isCoverQuestion,
|
|
73
|
+
isSingleQuestion,
|
|
74
|
+
isMultipleQuestion,
|
|
75
|
+
isSelectQuestion,
|
|
76
|
+
};
|
|
77
|
+
};
|
|
78
|
+
exports.getQuestionTypes = getQuestionTypes;
|
|
79
|
+
const getCioClient = (apiKey) => {
|
|
80
|
+
if (apiKey) {
|
|
81
|
+
return new constructorio_client_javascript_1.default({
|
|
82
|
+
apiKey,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
return null;
|
|
86
|
+
};
|
|
87
|
+
exports.getCioClient = getCioClient;
|
|
88
|
+
function getPreferredColorScheme() {
|
|
89
|
+
var _a;
|
|
90
|
+
let colorScheme = 'light';
|
|
91
|
+
// Check if the dark-mode Media-Query matches
|
|
92
|
+
if ((_a = window.matchMedia('(prefers-color-scheme: dark)')) === null || _a === void 0 ? void 0 : _a.matches) {
|
|
93
|
+
colorScheme = 'dark';
|
|
94
|
+
}
|
|
95
|
+
return colorScheme;
|
|
96
|
+
}
|
|
97
|
+
exports.getPreferredColorScheme = getPreferredColorScheme;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
function BackButton(props) {
|
|
3
|
+
// eslint-disable-next-line
|
|
4
|
+
const { disabled } = props;
|
|
5
|
+
return (React.createElement("button", { type: 'button', className: `cio-question-back-button ${disabled ? 'disabled' : ''}`, ...props },
|
|
6
|
+
React.createElement("svg", { width: '8', height: '12', viewBox: '0 0 8 12', fill: 'none', xmlns: 'http://www.w3.org/2000/svg' },
|
|
7
|
+
React.createElement("path", { d: 'M6.06313 1.06268L0.964522 5.43176C0.882383 5.50218 0.816449 5.58954 0.771245 5.68785C0.726041 5.78615 0.702637 5.89306 0.702637 6.00126C0.702637 6.10946 0.726041 6.21637 0.771245 6.31467C0.816449 6.41297 0.882383 6.50033 0.964522 6.57076L6.06313 10.9398C6.5498 11.3568 7.30153 11.0111 7.30153 10.3703V1.63093C7.30153 0.990168 6.5498 0.644468 6.06313 1.06268Z', fill: 'currentColor' }))));
|
|
8
|
+
}
|
|
9
|
+
export default BackButton;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
function CTAButton(props) {
|
|
3
|
+
const { ctaText = 'Continue', disabled, className = 'cio-button-container', ...rest } = props;
|
|
4
|
+
return (React.createElement("div", { className: `${className || ''}` },
|
|
5
|
+
React.createElement("button", { type: 'button', className: `${disabled ? 'cio-question-cta-button disabled' : 'cio-question-cta-button'}`, ...rest }, ctaText || 'Continue')));
|
|
6
|
+
}
|
|
7
|
+
export default CTAButton;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export var QuestionTypes;
|
|
2
|
+
(function (QuestionTypes) {
|
|
3
|
+
QuestionTypes["OpenText"] = "open";
|
|
4
|
+
QuestionTypes["Cover"] = "cover";
|
|
5
|
+
QuestionTypes["SingleSelect"] = "single";
|
|
6
|
+
QuestionTypes["MultipleSelect"] = "multiple";
|
|
7
|
+
QuestionTypes["Back"] = "back";
|
|
8
|
+
QuestionTypes["Reset"] = "reset";
|
|
9
|
+
})(QuestionTypes || (QuestionTypes = {}));
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import React, { useReducer, useState, useEffect, useCallback } from 'react';
|
|
2
|
+
import QuizContext from './context';
|
|
3
|
+
import reducer, { initialState } from './reducer';
|
|
4
|
+
import { QuestionTypes } from './actions';
|
|
5
|
+
import QuizQuestions from '../QuizQuestions';
|
|
6
|
+
import ResultContainer from '../ResultContainer/ResultContainer';
|
|
7
|
+
import { RequestStates } from '../../constants';
|
|
8
|
+
import { getNextQuestion, getQuizResults } from '../../utils';
|
|
9
|
+
import Spinner from '../Spinner/Spinner';
|
|
10
|
+
import useCioClient from '../../hooks/useCioClient';
|
|
11
|
+
export default function CioQuiz(props) {
|
|
12
|
+
const { quizId, apiKey, cioJsClient, resultsPageOptions, quizVersionId: quizVersionIdProp, } = props;
|
|
13
|
+
if (!quizId) {
|
|
14
|
+
// eslint-disable-next-line no-console
|
|
15
|
+
console.error('quizId is a required field of type string');
|
|
16
|
+
}
|
|
17
|
+
const cioClient = useCioClient({ apiKey, cioJsClient });
|
|
18
|
+
const [state, dispatch] = useReducer(reducer, initialState);
|
|
19
|
+
const [requestState, setRequestState] = useState(RequestStates.Stale);
|
|
20
|
+
const [questionResponse, setQuestionResponse] = useState();
|
|
21
|
+
const [resultsResponse, setResultsResponse] = useState();
|
|
22
|
+
const [firstQuestion, setFirstQuestion] = useState();
|
|
23
|
+
const [quizVersionId, setQuizVersionId] = useState(quizVersionIdProp || '');
|
|
24
|
+
const [quizSessionId, setQuizSessionId] = useState('');
|
|
25
|
+
const isFirstQuestion = firstQuestion?.next_question.id === questionResponse?.next_question.id;
|
|
26
|
+
const quizNextHandler = useCallback((action) => {
|
|
27
|
+
if (action) {
|
|
28
|
+
dispatch(action);
|
|
29
|
+
}
|
|
30
|
+
}, [dispatch]);
|
|
31
|
+
const quizBackHandler = useCallback(() => {
|
|
32
|
+
if (dispatch) {
|
|
33
|
+
dispatch({ type: QuestionTypes.Back });
|
|
34
|
+
}
|
|
35
|
+
}, [dispatch]);
|
|
36
|
+
const contextValue = {
|
|
37
|
+
dispatch,
|
|
38
|
+
questionResponse,
|
|
39
|
+
state,
|
|
40
|
+
resultsResponse,
|
|
41
|
+
isFirstQuestion,
|
|
42
|
+
quizNextHandler,
|
|
43
|
+
quizBackHandler,
|
|
44
|
+
};
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
(async () => {
|
|
47
|
+
if (cioClient) {
|
|
48
|
+
setRequestState(RequestStates.Loading);
|
|
49
|
+
if (state.isLastAnswer) {
|
|
50
|
+
try {
|
|
51
|
+
const quizResults = await getQuizResults(cioClient, quizId, {
|
|
52
|
+
answers: state.answers,
|
|
53
|
+
resultsPerPage: resultsPageOptions?.numResultsToDisplay,
|
|
54
|
+
quizVersionId,
|
|
55
|
+
quizSessionId,
|
|
56
|
+
});
|
|
57
|
+
setResultsResponse(quizResults);
|
|
58
|
+
setRequestState(RequestStates.Success);
|
|
59
|
+
setQuestionResponse(undefined);
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
setResultsResponse(undefined);
|
|
63
|
+
setRequestState(RequestStates.Error);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
try {
|
|
68
|
+
const questionResult = await getNextQuestion(cioClient, quizId, {
|
|
69
|
+
answers: state.answers,
|
|
70
|
+
quizVersionId,
|
|
71
|
+
quizSessionId,
|
|
72
|
+
});
|
|
73
|
+
setQuestionResponse(questionResult);
|
|
74
|
+
setRequestState(RequestStates.Success);
|
|
75
|
+
setResultsResponse(undefined);
|
|
76
|
+
if (!quizVersionId && questionResult?.quiz_version_id) {
|
|
77
|
+
setQuizVersionId(questionResult.quiz_version_id);
|
|
78
|
+
}
|
|
79
|
+
if (!quizSessionId && questionResult?.quiz_session_id) {
|
|
80
|
+
setQuizSessionId(questionResult.quiz_session_id);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
setRequestState(RequestStates.Error);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
})();
|
|
89
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
90
|
+
}, [cioClient, state, quizId, state.isLastAnswer, resultsPageOptions?.numResultsToDisplay]);
|
|
91
|
+
useEffect(() => {
|
|
92
|
+
if (!firstQuestion) {
|
|
93
|
+
setFirstQuestion(questionResponse);
|
|
94
|
+
}
|
|
95
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
96
|
+
}, [questionResponse]);
|
|
97
|
+
const resetQuizSessionId = () => {
|
|
98
|
+
setQuizSessionId('');
|
|
99
|
+
};
|
|
100
|
+
if (requestState === RequestStates.Loading) {
|
|
101
|
+
return (React.createElement("div", { className: 'cio-quiz' },
|
|
102
|
+
React.createElement(Spinner, null)));
|
|
103
|
+
}
|
|
104
|
+
if (requestState === RequestStates.Success) {
|
|
105
|
+
return (React.createElement("div", { className: 'cio-quiz' },
|
|
106
|
+
React.createElement(QuizContext.Provider, { value: contextValue },
|
|
107
|
+
resultsResponse && (React.createElement(ResultContainer, { options: resultsPageOptions, resetQuizSessionId: resetQuizSessionId })),
|
|
108
|
+
questionResponse && React.createElement(QuizQuestions, { questionResponse: questionResponse }))));
|
|
109
|
+
}
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { QuestionTypes, } from './actions';
|
|
2
|
+
export const initialState = {
|
|
3
|
+
answers: [],
|
|
4
|
+
answerInputs: {},
|
|
5
|
+
isLastAnswer: false,
|
|
6
|
+
};
|
|
7
|
+
function answerInputReducer(state, action) {
|
|
8
|
+
return {
|
|
9
|
+
...state,
|
|
10
|
+
[String(action.payload.questionId)]: action.payload.input,
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
export default function reducer(state, action) {
|
|
14
|
+
switch (action.type) {
|
|
15
|
+
case QuestionTypes.OpenText:
|
|
16
|
+
return {
|
|
17
|
+
...state,
|
|
18
|
+
answers: [...state.answers, ['true']],
|
|
19
|
+
answerInputs: answerInputReducer(state.answerInputs, action),
|
|
20
|
+
isLastAnswer: !!action.payload?.isLastQuestion,
|
|
21
|
+
};
|
|
22
|
+
case QuestionTypes.Cover:
|
|
23
|
+
return {
|
|
24
|
+
...state,
|
|
25
|
+
answers: [...state.answers, ['seen']],
|
|
26
|
+
isLastAnswer: !!action.payload?.isLastQuestion,
|
|
27
|
+
};
|
|
28
|
+
case QuestionTypes.SingleSelect:
|
|
29
|
+
return {
|
|
30
|
+
...state,
|
|
31
|
+
answers: [...state.answers, action.payload?.input],
|
|
32
|
+
answerInputs: answerInputReducer(state.answerInputs, action),
|
|
33
|
+
isLastAnswer: !!action.payload?.isLastQuestion,
|
|
34
|
+
};
|
|
35
|
+
case QuestionTypes.MultipleSelect:
|
|
36
|
+
return {
|
|
37
|
+
...state,
|
|
38
|
+
answers: [...state.answers, action.payload?.input],
|
|
39
|
+
answerInputs: answerInputReducer(state.answerInputs, action),
|
|
40
|
+
isLastAnswer: !!action.payload?.isLastQuestion,
|
|
41
|
+
};
|
|
42
|
+
case QuestionTypes.Back:
|
|
43
|
+
return {
|
|
44
|
+
...state,
|
|
45
|
+
answers: [...state.answers.slice(0, -1)],
|
|
46
|
+
isLastAnswer: false,
|
|
47
|
+
};
|
|
48
|
+
case QuestionTypes.Reset:
|
|
49
|
+
return {
|
|
50
|
+
...initialState,
|
|
51
|
+
};
|
|
52
|
+
default:
|
|
53
|
+
return state;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import BackButton from '../BackButton/BackButton';
|
|
3
|
+
import CTAButton from '../CTAButton/CTAButton';
|
|
4
|
+
function ControlBar(props) {
|
|
5
|
+
const { showBackButton, backButtonHandler, ctaButtonText, nextButtonHandler, isNextButtonDisabled, } = props;
|
|
6
|
+
return (React.createElement("div", { className: 'cio-question-buttons-container' },
|
|
7
|
+
showBackButton && React.createElement(BackButton, { onClick: backButtonHandler }),
|
|
8
|
+
React.createElement(CTAButton, { disabled: isNextButtonDisabled, ctaText: ctaButtonText, onClick: nextButtonHandler })));
|
|
9
|
+
}
|
|
10
|
+
export default ControlBar;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import React, { useContext } from 'react';
|
|
2
|
+
import QuestionTitle from '../QuestionTitle/QuestionTitle';
|
|
3
|
+
import QuizContext from '../CioQuiz/context';
|
|
4
|
+
import QuestionDescription from '../QuestionDescription/QuestionDescription';
|
|
5
|
+
import { renderImages } from '../../utils';
|
|
6
|
+
import { QuestionTypes } from '../CioQuiz/actions';
|
|
7
|
+
import ControlBar from '../ControlBar/ControlBar';
|
|
8
|
+
export default function CoverTypeQuestion() {
|
|
9
|
+
const { questionResponse, quizBackHandler, quizNextHandler, isFirstQuestion } = useContext(QuizContext);
|
|
10
|
+
let question;
|
|
11
|
+
if (questionResponse) {
|
|
12
|
+
question = questionResponse.next_question;
|
|
13
|
+
}
|
|
14
|
+
const hasImage = question?.images?.primary_url;
|
|
15
|
+
const onNextClick = () => {
|
|
16
|
+
if (quizNextHandler) {
|
|
17
|
+
quizNextHandler({
|
|
18
|
+
type: QuestionTypes.Cover,
|
|
19
|
+
payload: {
|
|
20
|
+
isLastQuestion: questionResponse?.is_last_question,
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
if (question) {
|
|
26
|
+
return (React.createElement("div", { className: `
|
|
27
|
+
cio-container${hasImage ? '--with-image' : ''}
|
|
28
|
+
cio-cover-question-container${hasImage ? '--with-image' : ''}
|
|
29
|
+
` },
|
|
30
|
+
React.createElement("div", { className: 'cio-question-content' },
|
|
31
|
+
React.createElement(QuestionTitle, { title: question?.title }),
|
|
32
|
+
React.createElement(QuestionDescription, { description: question.description }),
|
|
33
|
+
React.createElement(ControlBar, { nextButtonHandler: onNextClick, backButtonHandler: quizBackHandler, showBackButton: !isFirstQuestion, ctaButtonText: question?.cta_text })),
|
|
34
|
+
hasImage ? renderImages(question.images, 'cio-question-image-container') : ''));
|
|
35
|
+
}
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import React, { useContext, useEffect, useState } from 'react';
|
|
2
|
+
import QuestionTitle from '../QuestionTitle/QuestionTitle';
|
|
3
|
+
import QuestionDescription from '../QuestionDescription/QuestionDescription';
|
|
4
|
+
import { renderImages } from '../../utils';
|
|
5
|
+
import QuizContext from '../CioQuiz/context';
|
|
6
|
+
import { QuestionTypes } from '../CioQuiz/actions';
|
|
7
|
+
import ControlBar from '../ControlBar/ControlBar';
|
|
8
|
+
function OpenTextQuestion(props) {
|
|
9
|
+
const { initialValue = '', onChangeHandler: userDefinedHandler = null } = props;
|
|
10
|
+
const { questionResponse, quizBackHandler, quizNextHandler, isFirstQuestion, state } = useContext(QuizContext);
|
|
11
|
+
const [openTextInput, setOpenTextInput] = useState(initialValue);
|
|
12
|
+
let question;
|
|
13
|
+
if (questionResponse) {
|
|
14
|
+
question = questionResponse.next_question;
|
|
15
|
+
}
|
|
16
|
+
const onChangeHandler = (e) => {
|
|
17
|
+
setOpenTextInput(e.target.value);
|
|
18
|
+
if (userDefinedHandler) {
|
|
19
|
+
userDefinedHandler(e);
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
const onNextClick = () => {
|
|
23
|
+
if (quizNextHandler && openTextInput && questionResponse) {
|
|
24
|
+
quizNextHandler({
|
|
25
|
+
type: QuestionTypes.OpenText,
|
|
26
|
+
payload: {
|
|
27
|
+
questionId: questionResponse.next_question.id,
|
|
28
|
+
input: openTextInput,
|
|
29
|
+
isLastQuestion: questionResponse.is_last_question,
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
const onKeyDownHandler = (e) => {
|
|
35
|
+
const { key } = e;
|
|
36
|
+
if (key === 'Enter') {
|
|
37
|
+
onNextClick();
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
if (questionResponse) {
|
|
42
|
+
const openTextAnswer = state?.answerInputs?.[questionResponse?.next_question.id] || initialValue;
|
|
43
|
+
setOpenTextInput(openTextAnswer);
|
|
44
|
+
}
|
|
45
|
+
}, [questionResponse, state, initialValue]);
|
|
46
|
+
if (question) {
|
|
47
|
+
const hasImage = question?.images?.primary_url;
|
|
48
|
+
return (React.createElement("div", { className: `
|
|
49
|
+
cio-container${hasImage ? '--with-image' : ''}
|
|
50
|
+
cio-open-text-question-container${hasImage ? '--with-image' : ''}
|
|
51
|
+
` },
|
|
52
|
+
hasImage ? renderImages(question.images, 'cio-question-image-container') : '',
|
|
53
|
+
React.createElement("div", { className: 'cio-question-content' },
|
|
54
|
+
React.createElement(QuestionTitle, { title: question.title }),
|
|
55
|
+
React.createElement(QuestionDescription, { description: question.description }),
|
|
56
|
+
React.createElement("input", { className: 'cio-question-input-text', placeholder: question.input_placeholder || 'Answer here...', value: openTextInput, onChange: onChangeHandler, onKeyDown: onKeyDownHandler }),
|
|
57
|
+
React.createElement(ControlBar, { nextButtonHandler: onNextClick, isNextButtonDisabled: !openTextInput, backButtonHandler: quizBackHandler, showBackButton: !isFirstQuestion, ctaButtonText: question?.cta_text }))));
|
|
58
|
+
}
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
export default OpenTextQuestion;
|