UNPKG

@instructure/quiz-interactions

Version:

A React UI component Library for quiz interaction types.

370 lines (368 loc) • 17.1 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports["default"] = void 0; 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 _reactDom = _interopRequireDefault(require("react-dom")); var _propTypes = _interopRequireDefault(require("prop-types")); var _uuid = require("uuid"); 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 _punctuation = require("../helpers/punctuation"); var _styles = _interopRequireDefault(require("./styles")); var _theme = _interopRequireDefault(require("./theme")); var _quizCommon = require("@instructure/quiz-common"); var _dec, _class, _FillBlankResult; /** @jsx jsx */ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { (0, _defineProperty2["default"])(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } 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: FillInTheBlank --- Fill in the Blank Result component ```jsx_example <SettingsSwitcher locales={LOCALES}> <FillBlankResult interactionData={{ prompt: '<p><strong>Please</strong> fill in all the blanks</p>', stemItems: [ { id: 'stem_uuid-1', position: 1, type: 'text', value: ' ' }, { id: 'stem_uuid0', position: 2, type: 'blank', blankId: 'fitb_uuid1' }, { id: 'stem_uuid1', position: 3, type: 'text', value: ' Columbus sailed in ' }, { id: 'stem_uuid2', position: 4, type: 'blank', blankId: 'fitb_uuid2' }, { id: 'stem_uuid3', position: 5, type: 'text', value: ' , from the country of ' }, { id: 'stem_uuid4', position: 6, type: 'blank', blankId: 'fitb_uuid3' }, { id: 'stem_uuid5', position: 7, type: 'text', value: ' , on the continent of ' }, { id: 'stem_uuid6', position: 8, type: 'blank', blankId: 'fitb_uuid4' }, { id: 'stem_uuid7', position: 9, type: 'text', value: ' , across the ' }, { id: 'stem_uuid8', position: 10, type: 'blank', blankId: 'fitb_uuid5' }, { id: 'stem_uuid9', position: 11, type: 'text', value: ' , and found ' }, { id: 'stem_uuid10', position: 12, type: 'blank', blankId: 'fitb_uuid6' }, { id: 'stem_uuid11', position: 13, type: 'text', value: ' ! Where the people were ' }, { id: 'stem_uuid12', position: 14, type: 'blank', blankId: 'fitb_uuid7' }, { id: 'stem_uuid13', position: 15, type: 'text', value: ' ... ' } ], blanks: [ { id: 'fitb_uuid1', answerType: 'openEntry' }, { id: 'fitb_uuid2', answerType: 'openEntry' }, { id: 'fitb_uuid3', answerType: 'openEntry' }, { id: 'fitb_uuid4', answerType: 'openEntry' }, { id: 'fitb_uuid5', answerType: 'openEntry' }, { id: 'fitb_uuid6', choices: [ { id: 'choice_uuid11_brazil', position: 1, itemBody: 'Brazil' }, { id: 'choice_uuid12_austria', position: 2, itemBody: 'Austria' }, { id: 'choice_uuid13_america', position: 3, itemBody: 'America' } ], answerType: 'wordbank' }, { id: 'fitb_uuid7', choices: [ { id: 'choice_uuid11_peaceful', position: 1, itemBody: 'peaceful' }, { id: 'choice_uuid12_war-torn', position: 2, itemBody: 'war-torn' }, { id: 'choice_uuid13_confused', position: 3, itemBody: 'confused' } ], answerType: 'dropdown' } ] }} scoredData={{ value: { fitb_uuid1: { resultScore: 1, userResponse: 'Christopfer', correctAnswer: 'Christopher' }, fitb_uuid2: { resultScore: 1, userResponse: '1492' }, fitb_uuid3: { value: { Spain: { resultScore: 1, userResponded: false }, Espana: { resultScore: 1, userResponded: false }, 'Kingdom of Spain': { resultScore: 1, userResponded: false }, Portugal: { resultScore: 0, userResponded: true } } }, fitb_uuid4: { value:{ Europe: { resultScore: 1, userResponded: false }, Asia: { resultScore: 0, userResponded: true } } }, fitb_uuid5: { resultScore: 1, correctAnswer: 'Atlantic', userResponse: 'Atlantic Ocean' }, fitb_uuid6: { value: { choice_uuid13_america: { resultScore: 1, userResponded: true } } }, fitb_uuid7: { value: { choice_uuid13_confused: { resultScore: 1, userResponded: false }, choice_uuid11_peaceful: { resultScore: 0, userResponded: true } } } } }} /> </SettingsSwitcher> ``` **/ var FillBlankResult = exports["default"] = (_dec = (0, _quizCommon.withStyleOverrides)(_styles["default"], _theme["default"]), _dec(_class = (_FillBlankResult = /*#__PURE__*/function (_Component) { function FillBlankResult() { var _this2; (0, _classCallCheck2["default"])(this, FillBlankResult); for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } _this2 = _callSuper(this, FillBlankResult, [].concat(args)); (0, _defineProperty2["default"])(_this2, "state", { isMounted: false }); (0, _defineProperty2["default"])(_this2, "itemBodyWrapperId", (0, _uuid.v4)()); // ============= // RENDER // ============= (0, _defineProperty2["default"])(_this2, "getBlankStatus", function (blankId) { var blankResult = _this2.__getBlankScoredData(blankId); var status; if (blankResult && blankResult.resultScore > 0) { status = 'correct'; } else if (blankResult && blankResult.resultScore <= 0 || _this2.__correctnessKnown()) { status = 'incorrect'; } else { status = 'unknown'; } return status; }); (0, _defineProperty2["default"])(_this2, "renderBlank", function (blankId) { var renderBefore = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null; var renderAfer = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null; var blankResult = _this2.__getBlankScoredData(blankId); var status = _this2.getBlankStatus(blankId); var innerStyle = _objectSpread({}, _this2.props.styles.feedbackWrapper); var mergedStyle = status === 'incorrect' ? _objectSpread(_objectSpread({}, innerStyle), _this2.props.styles.incorrectFeedbackWrapper) : innerStyle; var responseText = blankResult && blankResult.userResponse; return (0, _emotion.jsx)("div", { key: blankId, css: _this2.props.styles.blank }, renderBefore, (0, _emotion.jsx)("div", { css: mergedStyle }, (0, _emotion.jsx)(_quizResultsFeedback.FeedbackWrapper, { userResponse: blankResult && blankResult.userResponse, correctAnswer: blankResult && blankResult.correctAnswer, 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, { wrap: "break-word", color: "primary" }, responseText || (_this2.__responseHidden() ? (0, _formatMessage["default"])('(response not displayed)') : (0, _formatMessage["default"])('(no answer)')))))), renderAfer); }); (0, _defineProperty2["default"])(_this2, "renderStemItemBlank", function (stemItem) { var punctuation = (0, _punctuation.getTrimmedPunctuation)(stemItem, _this2.props.interactionData.stemItems); return _this2.renderBlank(stemItem.blankId, punctuation.start, punctuation.end); }); (0, _defineProperty2["default"])(_this2, "renderRichBlanks", function () { var itemBodyWrapper = document.getElementById(_this2.itemBodyWrapperId); if (!itemBodyWrapper) return null; return _this2.props.interactionData.blanks.map(function (blank) { var targetEl = itemBodyWrapper.querySelector("#blank_".concat(blank.id)); return targetEl ? _reactDom["default"].createPortal(_this2.renderBlank(blank.id), targetEl, blank.id) : null; }).filter(Boolean); }); (0, _defineProperty2["default"])(_this2, "renderStemItem", function (stemItem) { if (stemItem.type === 'text') { return (0, _emotion.jsx)(_uiText.Text, { wrap: "break-word", color: "primary", key: stemItem.id }, (0, _punctuation.trimPunctuation)(stemItem, _this2.props.interactionData.stemItems)); } return _this2.renderStemItemBlank(stemItem); }); return _this2; } (0, _inherits2["default"])(FillBlankResult, _Component); return (0, _createClass2["default"])(FillBlankResult, [{ key: "componentDidUpdate", value: function componentDidUpdate(prevProps) { this.props.makeStyles(); if (this.props.interactionData.blanks !== prevProps.interactionData.blanks) { this.renderRichBlanks(); } } }, { key: "componentDidMount", value: function componentDidMount() { this.props.makeStyles(); this.setState({ isMounted: true }); } }, { key: "__multipleChoiceResult", value: function __multipleChoiceResult(blank, blankResultValue, choiceResult) { var pickedChoice = choiceResult.id && this.__getChoiceById(choiceResult.id); var userResponseItemBody = pickedChoice && pickedChoice.itemBody; var correctAnswerItemBody; // For dropdown and wordbank the resultScore is not present if the question was not answered. if (!choiceResult.resultScore || choiceResult.resultScore <= 0) { var correctAnswerKey = Object.keys(blankResultValue).find(function (choice) { return blankResultValue[choice].resultScore === 1; }); var correctAnswer = correctAnswerKey && this.__getChoiceById(correctAnswerKey); correctAnswerItemBody = correctAnswer && correctAnswer.itemBody; } return { resultScore: choiceResult.resultScore, userResponse: userResponseItemBody, correctAnswer: correctAnswerItemBody }; } }, { key: "__specifiedAnswersResult", value: function __specifiedAnswersResult(blank, blankResultValue, choiceResult) { var correctAnswers = Object.keys(blankResultValue).filter(function (answer) { return blankResultValue[answer].resultScore === 1; }); return { resultScore: choiceResult.resultScore, userResponse: choiceResult.id, correctAnswer: correctAnswers.join((0, _formatMessage["default"])(', ')) }; } }, { key: "__getUserResponseFromChoices", value: function __getUserResponseFromChoices(blankResultValue) { if (!blankResultValue) return {}; var userResponseKey = Object.keys(blankResultValue).find(function (choiceKey) { return blankResultValue[choiceKey].userResponded; }); return Object.assign({}, blankResultValue[userResponseKey], { id: userResponseKey }); } }, { key: "__getBlankScoredData", value: function __getBlankScoredData(blankId) { var scoredData = this.props.scoredData; var blankResult = scoredData.value ? scoredData.value[blankId] : {}; var blank = this.props.interactionData.blanks.find(function (blank) { return blank.id === blankId; }); var blankResultValue = blankResult && blankResult.value; if (!blankResultValue) { if (blankResult && Array.isArray(blankResult.correctAnswer)) { blankResult.correctAnswer = blankResult.correctAnswer.join((0, _formatMessage["default"])(', ')); } return blankResult; } var choiceResult = this.__getUserResponseFromChoices(blankResultValue); if (blank.answerType !== 'openEntry' && !this.__malformedWordBankBlankResult(blank, blankResult)) { return this.__multipleChoiceResult(blank, blankResultValue, choiceResult); } return this.__specifiedAnswersResult(blank, blankResultValue, choiceResult); } }, { key: "__malformedWordBankBlankResult", value: function __malformedWordBankBlankResult(blank, blankResult) { return blank.answerType === 'wordbank' && 'value' in blankResult && !blank['choices']; } }, { key: "__getChoiceById", value: function __getChoiceById(choiceId) { var blanks = this.props.interactionData.blanks || []; var allChoices = blanks.reduce(function (choices, blank) { return choices.concat(blank.choices || []); }, []); return allChoices.find(function (blank) { return blank.id === choiceId; }); } }, { key: "__responseHidden", value: function __responseHidden() { return !this.props.scoredData.value; } }, { key: "__correctnessKnown", value: function __correctnessKnown() { return typeof this.props.scoredData.correct !== 'undefined'; } }, { key: "render", value: function render() { if (this.props.richFITB) { return (0, _emotion.jsx)(_quizRce.ItemBodyWrapper, { id: this.itemBodyWrapperId, itemBody: this.props.itemBody }, this.renderRichBlanks()); } return (0, _emotion.jsx)(_quizRce.ItemBodyWrapper, { itemBody: this.props.interactionData.prompt }, (0, _emotion.jsx)("div", { css: this.props.styles.stem }, (this.props.interactionData.stemItems || []).slice().sort(function (a, b) { return a.position - b.position; }).map(this.renderStemItem))); } }]); }(_react.Component), (0, _defineProperty2["default"])(_FillBlankResult, "displayName", 'FillBlankResult'), (0, _defineProperty2["default"])(_FillBlankResult, "componentId", "Quizzes".concat(_FillBlankResult.displayName)), (0, _defineProperty2["default"])(_FillBlankResult, "propTypes", { interactionData: _propTypes["default"].object.isRequired, itemBody: _propTypes["default"].string, richFITB: _propTypes["default"].bool, scoredData: _propTypes["default"].shape({ correct: _propTypes["default"].bool, value: _propTypes["default"].objectOf(_propTypes["default"].object) }).isRequired, makeStyles: _propTypes["default"].func, styles: _propTypes["default"].object }), (0, _defineProperty2["default"])(_FillBlankResult, "defaultProps", { itemBody: '', richFITB: false }), _FillBlankResult)) || _class);