UNPKG

@instructure/quiz-interactions

Version:

A React UI component Library for quiz interaction types.

348 lines (343 loc) • 15.8 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports["default"] = void 0; var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray")); var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck")); var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass")); var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn")); var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf")); var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits")); var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); var _react = require("react"); var _propTypes = _interopRequireDefault(require("prop-types")); var _get = _interopRequireDefault(require("lodash/fp/get")); var _uiText = require("@instructure/ui-text"); var _emotion = require("@instructure/emotion"); var _formatMessage = _interopRequireDefault(require("@instructure/quiz-i18n/es/format-message")); var _quizRce = require("@instructure/quiz-rce"); var _quizResultsFeedback = require("@instructure/quiz-results-feedback"); var _CategoriesContainer = _interopRequireDefault(require("../common/CategoriesContainer")); var _utils = require("../common/utils"); var _styles = _interopRequireDefault(require("./styles")); var _theme = _interopRequireDefault(require("./theme")); var _quizCommon = require("@instructure/quiz-common"); var _dec, _class, _CategorizationResult; /** @jsx jsx */ function _callSuper(_this, derived, args) { function isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { return !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (e) { return false; } } derived = (0, _getPrototypeOf2["default"])(derived); return (0, _possibleConstructorReturn2["default"])(_this, isNativeReflectConstruct() ? Reflect.construct(derived, args || [], (0, _getPrototypeOf2["default"])(_this).constructor) : derived.apply(_this, args)); } /** --- category: Categorization --- Categorization Result component ```jsx_example <SettingsSwitcher locales={LOCALES}> <CategorizationResult itemBody="Match the name of the celestial body on the left with its correct classification:" interactionData={{ categoryOrder: ['category_uuid1', 'category_uuid2', 'category_uuid3', 'category_uuid4'], categories: { category_uuid1: { id: 'category_uuid1', itemBody: 'Planet' }, category_uuid2: { id: 'category_uuid2', itemBody: 'Moon' }, category_uuid3: { id: 'category_uuid3', itemBody: 'Quasar' }, category_uuid4: { id: 'category_uuid4', itemBody: 'Stars' }, }, distractors: { answer_uuid3: { id: 'answer_uuid3', itemBody: 'Mars' }, answer_uuid4: { id: 'answer_uuid4', itemBody: 'Europa' }, answer_uuid5: { id: 'answer_uuid5', itemBody: 'Venus' }, answer_uuid6: { id: 'answer_uuid6', itemBody: 'Phobos' }, answer_uuid7: { id: 'answer_uuid7', itemBody: 'Deimos' }, answer_uuid8: { id: 'answer_uuid8', itemBody: 'Jupiter' }, answer_uuid9: { id: 'answer_uuid9', itemBody: 'America' }, answer_uuid10: { id: 'answer_uuid10', itemBody: 'Asia' }, answer_uuid11: { id: 'answer_uuid11', itemBody: 'The Sun' } } }} scoredData={{ value: { category_uuid1: { value: { answer_uuid3: { resultScore: 1, userResponded: false }, answer_uuid5: { resultScore: 1, userResponded: true }, answer_uuid8: { resultScore: 1, userResponded: false }, answer_uuid7: { resultScore: 0, userResponded: true } } }, category_uuid3: { value: {} }, category_uuid4: { value: { answer_uuid11: { resultScore: 1, userResponded: false } } }, category_uuid2: { value: { answer_uuid4: { resultScore: 1, userResponded: true }, answer_uuid6: { resultScore: 1, userResponded: true }, answer_uuid7: { resultScore: 1, userResponded: false }, answer_uuid8: { resultScore: 0, userResponded: true }, answer_uuid9: { resultScore: 0, userResponded: true } } } } }} /> </SettingsSwitcher> ``` **/ var CategorizationResult = exports["default"] = (_dec = (0, _quizCommon.withStyleOverrides)(_styles["default"], _theme["default"]), _dec(_class = (_CategorizationResult = /*#__PURE__*/function (_Component) { function CategorizationResult(props) { var _this2; (0, _classCallCheck2["default"])(this, CategorizationResult); _this2 = _callSuper(this, CategorizationResult, [props]); (0, _defineProperty2["default"])(_this2, "renderCategoryBody", function (categoryId) { var categoryValue = (0, _get["default"])("scoredData.value[".concat(categoryId, "].value"), _this2.props) || {}; var answers = Object.entries(categoryValue).filter(function (_ref) { var _ref2 = (0, _slicedToArray2["default"])(_ref, 2), _ = _ref2[0], answer = _ref2[1]; return answer.userResponded; }); if (answers.length) { return (0, _emotion.jsx)("div", { className: "categoryBody fs-mask", css: _this2.props.styles.categoryBody }, answers.map(function (_ref3) { var _ref4 = (0, _slicedToArray2["default"])(_ref3, 2), answerId = _ref4[0], resultScore = _ref4[1].resultScore; return _this2.renderCategoryAnswer(answerId, resultScore, categoryId); })); } return (0, _emotion.jsx)("div", { className: "categoryBody fs-mask", css: _this2.props.styles.categoryBody }, (0, _emotion.jsx)("div", { css: _this2.props.styles.noChoicesText }, (0, _emotion.jsx)(_uiText.Text, { color: "primary" }, _this2.responseHidden() ? (0, _formatMessage["default"])('Choices not displayed') : (0, _formatMessage["default"])('No Choices Selected')))); }); (0, _defineProperty2["default"])(_this2, "renderUnansweredChoice", function (answerId) { var itemBody = _this2.props.interactionData.distractors[answerId].itemBody; var status = 'unknown', correctAnswerText; if (_this2.correctnessKnown()) { var scoredData = _this2.props.scoredData; correctAnswerText = _this2.getCorrectAnswer(answerId); if (correctAnswerText) { status = 'incorrect'; } else if (!scoredData.uncategorized) { if (_this2.includesAllCorrectAnswers()) { status = 'correct'; // for backward-compatibility } else { status = 'unknown'; } } else { status = (0, _get["default"])("uncategorized[".concat(answerId, "].resultScore"), scoredData) ? 'correct' : 'incorrect'; } } return (0, _emotion.jsx)("div", { key: answerId, css: _this2.props.styles.unanswered }, (0, _emotion.jsx)("div", { css: _this2.props.styles.feedbackWrapper }, (0, _emotion.jsx)(_quizResultsFeedback.FeedbackWrapper, { correctAnswer: correctAnswerText, hiddenCorrectAnswerText: (0, _formatMessage["default"])('Correct answer: '), hiddenIncorrectAnswerText: (0, _formatMessage["default"])('Incorrect answer: '), status: status }, (0, _emotion.jsx)("div", { css: _this2.props.styles.userResponse }, (0, _emotion.jsx)(_uiText.Text, { color: "primary" }, itemBody))))); }); var _props$interactionDat = props.interactionData, categories = _props$interactionDat.categories, categoryOrder = _props$interactionDat.categoryOrder; _this2.state = { sortedCategories: (0, _utils.getSortedCategories)(categories, categoryOrder) }; return _this2; } // =========== // UTILS // =========== (0, _inherits2["default"])(CategorizationResult, _Component); return (0, _createClass2["default"])(CategorizationResult, [{ key: "hasMissedCorrectAnswerInObject", value: function hasMissedCorrectAnswerInObject(answers) { return answers && Object.keys(answers).some(function (answerId) { return answers[answerId] && answers[answerId].resultScore > 0 && answers[answerId].userResponded === false; }); } }, { key: "hasMissedCorrectAnswerInUncategorized", value: function hasMissedCorrectAnswerInUncategorized() { var uncategorized = this.props.scoredData.uncategorized; return this.hasMissedCorrectAnswerInObject(uncategorized); } }, { key: "hasMissedCorrectAnswerInCategories", value: function hasMissedCorrectAnswerInCategories() { var _this3 = this; var categories = this.props.scoredData.value; return categories && Object.keys(categories).some(function (categoryKey) { var answers = categories[categoryKey].value; return _this3.hasMissedCorrectAnswerInObject(answers); }); } }, { key: "includesAllCorrectAnswers", value: function includesAllCorrectAnswers() { // we know a response includes all the correct answers if it is either // correct, or it contains correct responses not chosen by the user var scoredData = this.props.scoredData; if (scoredData.correct) { return true; } return this.hasMissedCorrectAnswerInUncategorized() || this.hasMissedCorrectAnswerInCategories(); } }, { key: "getCorrectAnswer", value: function getCorrectAnswer(answerId, categoryId) { var scoredData = this.props.scoredData; var correctCategoryId = scoredData.value && Object.keys(scoredData.value).find(function (categoryKey) { if (categoryKey !== categoryId) { return (0, _get["default"])("value[".concat(categoryKey, "].value[").concat(answerId, "].resultScore"), scoredData); } else { return null; } }); if (correctCategoryId) { return this.props.interactionData.categories[correctCategoryId].itemBody; } } }, { key: "getUnansweredAnswersIds", value: function getUnansweredAnswersIds() { var _this4 = this; if (this.responseHidden()) return []; var distractorsArray = Object.keys(this.props.interactionData.distractors); return distractorsArray.filter(function (answerId) { var category = Object.values(_this4.props.scoredData.value).find(function (scoredDataCateg) { return (0, _get["default"])("value[".concat(answerId, "].userResponded"), scoredDataCateg); }); return category === void 0; }); } }, { key: "responseHidden", value: function responseHidden() { var scoredData = this.props.scoredData; return !scoredData.value || Object.keys(scoredData.value).length === 0; } }, { key: "correctnessKnown", value: function correctnessKnown() { return this.props.scoredData.correct != null; } // =========== // RENDER // =========== }, { key: "renderCategoryAnswer", value: function renderCategoryAnswer(answerId, resultScore, categoryId) { var status, correctAnswerText; if (resultScore > 0) { status = 'correct'; } else if (resultScore <= 0) { status = 'incorrect'; if (this.correctnessKnown()) { correctAnswerText = this.getCorrectAnswer(answerId); if (!correctAnswerText) { // If a correct answer can't be found, it means that either the // answer should have been left uncategorized, or the results are // restricted and the correct answer is hidden. In the first case, we // want to display "No Category" feedback, but in the latter case we // don't want to provide any feedback beyond whether the answer was // correct. To distinguish between these 2 cases, we check whether // any missed correct answers are present in the response, and whether // the `uncategorized` scoredData attribute exists, which is essentially a // category for answers that should remain uncategorized. If the // `uncategorized` attribute is absent, but we show other correct answers, // we display the "No Category" feedback for backward-compatibility. var uncategorized = this.props.scoredData.uncategorized; if (this.includesAllCorrectAnswers() && (!uncategorized || (0, _get["default"])("".concat(answerId, ".resultScore"), uncategorized))) { correctAnswerText = (0, _formatMessage["default"])('No Category'); } } } } else { status = 'unknown'; } return (0, _emotion.jsx)("div", { key: answerId, css: this.props.styles.feedbackWrapper }, (0, _emotion.jsx)(_quizResultsFeedback.FeedbackWrapper, { correctAnswer: correctAnswerText, hiddenCorrectAnswerText: (0, _formatMessage["default"])('Correct answer: '), hiddenIncorrectAnswerText: (0, _formatMessage["default"])('Incorrect answer: '), status: status }, (0, _emotion.jsx)("div", { css: this.props.styles.userResponse }, (0, _emotion.jsx)(_uiText.Text, { color: "primary" }, this.props.interactionData.distractors[answerId].itemBody)))); } }, { key: "render", value: function render() { var unansweredList = this.getUnansweredAnswersIds(); return (0, _emotion.jsx)(_quizRce.ItemBodyWrapper, { itemBody: this.props.itemBody }, (0, _emotion.jsx)(_CategoriesContainer["default"], { sortedCategories: this.state.sortedCategories, categoryBody: this.renderCategoryBody, distractors: this.props.interactionData.distractors }), unansweredList.length === 0 ? null : (0, _emotion.jsx)("div", { css: this.props.styles.mainContainer }, (0, _emotion.jsx)(_uiText.Text, { color: "primary" }, (0, _formatMessage["default"])('Uncategorized answers')), (0, _emotion.jsx)("div", { className: "categoryBody fs-mask", css: this.props.styles.categoryBody }, unansweredList.map(this.renderUnansweredChoice)))); } }]); }(_react.Component), (0, _defineProperty2["default"])(_CategorizationResult, "displayName", 'CategorizationResult'), (0, _defineProperty2["default"])(_CategorizationResult, "componentId", "Quizzes".concat(_CategorizationResult.displayName)), (0, _defineProperty2["default"])(_CategorizationResult, "propTypes", { interactionData: _propTypes["default"].object.isRequired, itemBody: _propTypes["default"].string.isRequired, scoredData: _propTypes["default"].shape({ correct: _propTypes["default"].bool, uncategorized: _propTypes["default"].objectOf(_propTypes["default"].shape({ resultScore: _propTypes["default"].number, userResponded: _propTypes["default"].bool })), value: _propTypes["default"].objectOf(_propTypes["default"].shape({ value: _propTypes["default"].objectOf(_propTypes["default"].shape({ resultScore: _propTypes["default"].number, userResponded: _propTypes["default"].bool })) })) }).isRequired, styles: _propTypes["default"].object }), _CategorizationResult)) || _class);