@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.
Files changed (88) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +88 -0
  3. package/lib/cjs/components/BackButton/BackButton.js +12 -0
  4. package/lib/cjs/components/CTAButton/CTAButton.js +10 -0
  5. package/lib/cjs/components/CioQuiz/actions.js +12 -0
  6. package/lib/cjs/components/CioQuiz/context.js +5 -0
  7. package/lib/cjs/components/CioQuiz/index.js +115 -0
  8. package/lib/cjs/components/CioQuiz/reducer.js +32 -0
  9. package/lib/cjs/components/ControlBar/ControlBar.js +13 -0
  10. package/lib/cjs/components/CoverTypeQuestion/CoverTypeQuestion.js +42 -0
  11. package/lib/cjs/components/OpenTextTypeQuestion/OpenTextTypeQuestion.js +66 -0
  12. package/lib/cjs/components/QuestionDescription/QuestionDescription.js +9 -0
  13. package/lib/cjs/components/QuestionTitle/QuestionTitle.js +9 -0
  14. package/lib/cjs/components/QuizQuestions/index.js +17 -0
  15. package/lib/cjs/components/RedoButton/RedoButton.js +12 -0
  16. package/lib/cjs/components/RedoButton/RedoSVG.js +15 -0
  17. package/lib/cjs/components/ResultCard/ResultCard.js +45 -0
  18. package/lib/cjs/components/ResultContainer/ResultContainer.js +39 -0
  19. package/lib/cjs/components/ResultCtaButton/ResultCtaButton.js +16 -0
  20. package/lib/cjs/components/ResultFilters/ResultFilters.js +30 -0
  21. package/lib/cjs/components/Results/Results.js +16 -0
  22. package/lib/cjs/components/SelectTypeQuestion/SelectTypeQuestion.js +90 -0
  23. package/lib/cjs/components/Spinner/Spinner.js +12 -0
  24. package/lib/cjs/components/ZeroResults/ZeroResults.js +13 -0
  25. package/lib/cjs/constants.js +36 -0
  26. package/lib/cjs/hooks/useCioClient.js +11 -0
  27. package/lib/cjs/index.js +5 -0
  28. package/lib/cjs/stories/Quiz/argTypes.js +30 -0
  29. package/lib/cjs/types.js +2 -0
  30. package/lib/cjs/utils.js +97 -0
  31. package/lib/mjs/components/BackButton/BackButton.js +9 -0
  32. package/lib/mjs/components/CTAButton/CTAButton.js +7 -0
  33. package/lib/mjs/components/CioQuiz/actions.js +9 -0
  34. package/lib/mjs/components/CioQuiz/context.js +2 -0
  35. package/lib/mjs/components/CioQuiz/index.js +111 -0
  36. package/lib/mjs/components/CioQuiz/reducer.js +55 -0
  37. package/lib/mjs/components/ControlBar/ControlBar.js +10 -0
  38. package/lib/mjs/components/CoverTypeQuestion/CoverTypeQuestion.js +37 -0
  39. package/lib/mjs/components/OpenTextTypeQuestion/OpenTextTypeQuestion.js +61 -0
  40. package/lib/mjs/components/QuestionDescription/QuestionDescription.js +6 -0
  41. package/lib/mjs/components/QuestionTitle/QuestionTitle.js +6 -0
  42. package/lib/mjs/components/QuizQuestions/index.js +13 -0
  43. package/lib/mjs/components/RedoButton/RedoButton.js +9 -0
  44. package/lib/mjs/components/RedoButton/RedoSVG.js +12 -0
  45. package/lib/mjs/components/ResultCard/ResultCard.js +37 -0
  46. package/lib/mjs/components/ResultContainer/ResultContainer.js +34 -0
  47. package/lib/mjs/components/ResultCtaButton/ResultCtaButton.js +12 -0
  48. package/lib/mjs/components/ResultFilters/ResultFilters.js +27 -0
  49. package/lib/mjs/components/Results/Results.js +9 -0
  50. package/lib/mjs/components/SelectTypeQuestion/SelectTypeQuestion.js +85 -0
  51. package/lib/mjs/components/Spinner/Spinner.js +8 -0
  52. package/lib/mjs/components/ZeroResults/ZeroResults.js +10 -0
  53. package/lib/mjs/constants.js +33 -0
  54. package/lib/mjs/hooks/useCioClient.js +9 -0
  55. package/lib/mjs/index.js +2 -0
  56. package/lib/mjs/stories/Quiz/argTypes.js +27 -0
  57. package/lib/mjs/types.js +1 -0
  58. package/lib/mjs/utils.js +83 -0
  59. package/lib/styles.css +689 -0
  60. package/lib/types/components/BackButton/BackButton.d.ts +3 -0
  61. package/lib/types/components/CTAButton/CTAButton.d.ts +6 -0
  62. package/lib/types/components/CioQuiz/actions.d.ts +25 -0
  63. package/lib/types/components/CioQuiz/context.d.ts +17 -0
  64. package/lib/types/components/CioQuiz/index.d.ts +14 -0
  65. package/lib/types/components/CioQuiz/reducer.d.ts +12 -0
  66. package/lib/types/components/ControlBar/ControlBar.d.ts +10 -0
  67. package/lib/types/components/CoverTypeQuestion/CoverTypeQuestion.d.ts +2 -0
  68. package/lib/types/components/OpenTextTypeQuestion/OpenTextTypeQuestion.d.ts +7 -0
  69. package/lib/types/components/QuestionDescription/QuestionDescription.d.ts +6 -0
  70. package/lib/types/components/QuestionTitle/QuestionTitle.d.ts +6 -0
  71. package/lib/types/components/QuizQuestions/index.d.ts +5 -0
  72. package/lib/types/components/RedoButton/RedoButton.d.ts +6 -0
  73. package/lib/types/components/RedoButton/RedoSVG.d.ts +3 -0
  74. package/lib/types/components/ResultCard/ResultCard.d.ts +11 -0
  75. package/lib/types/components/ResultContainer/ResultContainer.d.ts +7 -0
  76. package/lib/types/components/ResultCtaButton/ResultCtaButton.d.ts +9 -0
  77. package/lib/types/components/ResultFilters/ResultFilters.d.ts +6 -0
  78. package/lib/types/components/Results/Results.d.ts +10 -0
  79. package/lib/types/components/SelectTypeQuestion/SelectTypeQuestion.d.ts +3 -0
  80. package/lib/types/components/Spinner/Spinner.d.ts +2 -0
  81. package/lib/types/components/ZeroResults/ZeroResults.d.ts +6 -0
  82. package/lib/types/constants.d.ts +11 -0
  83. package/lib/types/hooks/useCioClient.d.ts +8 -0
  84. package/lib/types/index.d.ts +2 -0
  85. package/lib/types/stories/Quiz/argTypes.d.ts +30 -0
  86. package/lib/types/types.d.ts +1 -0
  87. package/lib/types/utils.d.ts +31 -0
  88. 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;
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ const CioQuiz_1 = tslib_1.__importDefault(require("./components/CioQuiz"));
5
+ exports.default = CioQuiz_1.default;
@@ -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
+ };
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -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,2 @@
1
+ import React from 'react';
2
+ export default React.createContext({});
@@ -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;
@@ -0,0 +1,6 @@
1
+ import React from 'react';
2
+ function QuestionDescription(props) {
3
+ const { description } = props;
4
+ return React.createElement("p", { className: 'cio-question-description' }, description);
5
+ }
6
+ export default QuestionDescription;