UNPKG

@instructure/quiz-interactions

Version:

A React UI component Library for quiz interaction types.

404 lines (403 loc) • 18.3 kB
/** @jsx jsx */ function _array_like_to_array(arr, len) { if (len == null || len > arr.length) len = arr.length; for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i]; return arr2; } function _array_with_holes(arr) { if (Array.isArray(arr)) return arr; } function _assert_this_initialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } function _call_super(_this, derived, args) { derived = _get_prototype_of(derived); return _possible_constructor_return(_this, _is_native_reflect_construct() ? Reflect.construct(derived, args || [], _get_prototype_of(_this).constructor) : derived.apply(_this, args)); } function _class_call_check(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _defineProperties(target, props) { for(var i = 0; i < props.length; i++){ var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } function _create_class(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } function _define_property(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } function _get_prototype_of(o) { _get_prototype_of = Object.setPrototypeOf ? Object.getPrototypeOf : function getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _get_prototype_of(o); } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _set_prototype_of(subClass, superClass); } function _iterable_to_array_limit(arr, i) { var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]; if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for(_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true){ _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally{ try { if (!_n && _i["return"] != null) _i["return"](); } finally{ if (_d) throw _e; } } return _arr; } function _non_iterable_rest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _possible_constructor_return(self, call) { if (call && (_type_of(call) === "object" || typeof call === "function")) { return call; } return _assert_this_initialized(self); } function _set_prototype_of(o, p) { _set_prototype_of = Object.setPrototypeOf || function setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _set_prototype_of(o, p); } function _sliced_to_array(arr, i) { return _array_with_holes(arr) || _iterable_to_array_limit(arr, i) || _unsupported_iterable_to_array(arr, i) || _non_iterable_rest(); } function _type_of(obj) { "@swc/helpers - typeof"; return obj && typeof Symbol !== "undefined" && obj.constructor === Symbol ? "symbol" : typeof obj; } function _unsupported_iterable_to_array(o, minLen) { if (!o) return; if (typeof o === "string") return _array_like_to_array(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(n); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array(o, minLen); } function _is_native_reflect_construct() { try { var result = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function() {})); } catch (_) {} return (_is_native_reflect_construct = function() { return !!result; })(); } function _ts_decorate(decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; } import { Component } from 'react'; import PropTypes from 'prop-types'; import get from 'lodash/fp/get'; import { Text } from '@instructure/ui-text'; import { jsx } from '@instructure/emotion'; import t from '@instructure/quiz-i18n/format-message'; import { ItemBodyWrapper } from '@instructure/quiz-rce'; import { FeedbackWrapper } from '@instructure/quiz-results-feedback'; import CategoriesContainer from '../common/CategoriesContainer'; import { getSortedCategories } from '../common/utils'; import generateStyle from './styles'; import generateComponentTheme from './theme'; import { withStyleOverrides } from '@instructure/quiz-common'; var CategorizationResult = /*#__PURE__*/ function(Component) { "use strict"; _inherits(CategorizationResult, Component); function CategorizationResult(props) { _class_call_check(this, CategorizationResult); var _this; _this = _call_super(this, CategorizationResult, [ props ]), _define_property(_this, "renderCategoryBody", function(categoryId) { var categoryValue = get("scoredData.value[".concat(categoryId, "].value"), _this.props) || {}; var answers = Object.entries(categoryValue).filter(function(param) { var _param = _sliced_to_array(param, 2), _ = _param[0], answer = _param[1]; return answer.userResponded; }); if (answers.length) { return /*#__PURE__*/ jsx("div", { className: "categoryBody fs-mask", css: _this.props.styles.categoryBody }, answers.map(function(param) { var _param = _sliced_to_array(param, 2), answerId = _param[0], resultScore = _param[1].resultScore; return _this.renderCategoryAnswer(answerId, resultScore, categoryId); })); } return /*#__PURE__*/ jsx("div", { className: "categoryBody fs-mask", css: _this.props.styles.categoryBody }, /*#__PURE__*/ jsx("div", { css: _this.props.styles.noChoicesText }, /*#__PURE__*/ jsx(Text, { color: "primary" }, _this.responseHidden() ? t('Choices not displayed') : t('No Choices Selected')))); }), _define_property(_this, "renderUnansweredChoice", function(answerId) { var itemBody = _this.props.interactionData.distractors[answerId].itemBody; var status = 'unknown', correctAnswerText; if (_this.correctnessKnown()) { var scoredData = _this.props.scoredData; correctAnswerText = _this.getCorrectAnswer(answerId); if (correctAnswerText) { status = 'incorrect'; } else if (!scoredData.uncategorized) { if (_this.includesAllCorrectAnswers()) { status = 'correct'; // for backward-compatibility } else { status = 'unknown'; } } else { status = get("uncategorized[".concat(answerId, "].resultScore"), scoredData) ? 'correct' : 'incorrect'; } } return /*#__PURE__*/ jsx("div", { key: answerId, css: _this.props.styles.unanswered }, /*#__PURE__*/ jsx("div", { css: _this.props.styles.feedbackWrapper }, /*#__PURE__*/ jsx(FeedbackWrapper, { correctAnswer: correctAnswerText, hiddenCorrectAnswerText: t('Correct answer: '), hiddenIncorrectAnswerText: t('Incorrect answer: '), status: status }, /*#__PURE__*/ jsx("div", { css: _this.props.styles.userResponse }, /*#__PURE__*/ jsx(Text, { color: "primary" }, itemBody))))); }); var _props_interactionData = props.interactionData, categories = _props_interactionData.categories, categoryOrder = _props_interactionData.categoryOrder; _this.state = { sortedCategories: getSortedCategories(categories, categoryOrder) }; return _this; } _create_class(CategorizationResult, [ { // =========== // UTILS // =========== 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 _this = this; var categories = this.props.scoredData.value; return categories && Object.keys(categories).some(function(categoryKey) { var answers = categories[categoryKey].value; return _this.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 get("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 _this = this; if (this.responseHidden()) return []; var distractorsArray = Object.keys(this.props.interactionData.distractors); return distractorsArray.filter(function(answerId) { var category = Object.values(_this.props.scoredData.value).find(function(scoredDataCateg) { return get("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 || get("".concat(answerId, ".resultScore"), uncategorized))) { correctAnswerText = t('No Category'); } } } } else { status = 'unknown'; } return /*#__PURE__*/ jsx("div", { key: answerId, css: this.props.styles.feedbackWrapper }, /*#__PURE__*/ jsx(FeedbackWrapper, { correctAnswer: correctAnswerText, hiddenCorrectAnswerText: t('Correct answer: '), hiddenIncorrectAnswerText: t('Incorrect answer: '), status: status }, /*#__PURE__*/ jsx("div", { css: this.props.styles.userResponse }, /*#__PURE__*/ jsx(Text, { color: "primary" }, this.props.interactionData.distractors[answerId].itemBody)))); } }, { key: "render", value: function render() { var unansweredList = this.getUnansweredAnswersIds(); return /*#__PURE__*/ jsx(ItemBodyWrapper, { itemBody: this.props.itemBody }, /*#__PURE__*/ jsx(CategoriesContainer, { sortedCategories: this.state.sortedCategories, categoryBody: this.renderCategoryBody, distractors: this.props.interactionData.distractors }), unansweredList.length === 0 ? null : /*#__PURE__*/ jsx("div", { css: this.props.styles.mainContainer }, /*#__PURE__*/ jsx(Text, { color: "primary" }, t('Uncategorized answers')), /*#__PURE__*/ jsx("div", { className: "categoryBody fs-mask", css: this.props.styles.categoryBody }, unansweredList.map(this.renderUnansweredChoice)))); } } ]); return CategorizationResult; }(Component); _define_property(CategorizationResult, "displayName", 'CategorizationResult'); _define_property(CategorizationResult, "componentId", "Quizzes".concat(CategorizationResult.displayName)); _define_property(CategorizationResult, "propTypes", { interactionData: PropTypes.object.isRequired, itemBody: PropTypes.string.isRequired, scoredData: PropTypes.shape({ correct: PropTypes.bool, uncategorized: PropTypes.objectOf(PropTypes.shape({ resultScore: PropTypes.number, userResponded: PropTypes.bool })), value: PropTypes.objectOf(PropTypes.shape({ value: PropTypes.objectOf(PropTypes.shape({ resultScore: PropTypes.number, userResponded: PropTypes.bool })) })) }).isRequired, styles: PropTypes.object }); export { CategorizationResult as default }; CategorizationResult = _ts_decorate([ withStyleOverrides(generateStyle, generateComponentTheme) ], CategorizationResult);