UNPKG

@instructure/quiz-interactions

Version:

A React UI component Library for quiz interaction types.

272 lines (268 loc) • 11.5 kB
import _classCallCheck from "@babel/runtime/helpers/esm/classCallCheck"; import _createClass from "@babel/runtime/helpers/esm/createClass"; import _possibleConstructorReturn from "@babel/runtime/helpers/esm/possibleConstructorReturn"; import _getPrototypeOf from "@babel/runtime/helpers/esm/getPrototypeOf"; import _inherits from "@babel/runtime/helpers/esm/inherits"; import _defineProperty from "@babel/runtime/helpers/esm/defineProperty"; 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 = _getPrototypeOf(derived); return _possibleConstructorReturn(_this, isNativeReflectConstruct() ? Reflect.construct(derived, args || [], _getPrototypeOf(_this).constructor) : derived.apply(_this, args)); } import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { v4 as uuid } from 'uuid'; import { Text } from '@instructure/ui-text'; import { ScreenReaderContent } from '@instructure/ui-a11y-content'; import { ItemBodyWrapper, RichContentInput, RichContentRenderer, RCE_THREE_LINES_HEIGHT } from '@instructure/quiz-rce'; import { WordCount } from '../helpers/WordCount'; import t from '@instructure/quiz-i18n/es/format-message'; import { DOCUMENT_TREE_DEPTH_LIMIT, CONTENT_SIZE_LIMIT, CONTENT_SIZE_BY_ANSWER, TextArea } from '@instructure/quiz-common'; /** --- category: Essay --- Essay Take component ```jsx_example function Example (props) { const exampleProps = { itemBody: 'Why did the Roman Empire fall?', interactionData: { rce: true, spellCheck: true, wordCount: true, wordLimitEnabled: true, wordLimitMax: 10, wordLimitMin: 1, notes: 'Teachers grading notes' }, userResponse: { value: 'So it could learn to pick itself back up again.' } } return ( <EssayTake {...exampleProps} {...props} /> ) } <SettingsSwitcher locales={LOCALES}> <TakeStateProvider> <Example /> </TakeStateProvider> </SettingsSwitcher> ``` **/ var EssayTake = /*#__PURE__*/function (_Component) { function EssayTake(props) { var _this2; _classCallCheck(this, EssayTake); _this2 = _callSuper(this, EssayTake, [props]); _defineProperty(_this2, "verifyErrors", function (text) { var documentTreeDepthOverLimit = _this2.isDocumentTreeDepthOverLimit(text); var dynamoRecordSize = _this2.getContentSize(text); _this2.setState({ documentTreeDepthOverLimit: documentTreeDepthOverLimit, dynamoRecordSize: dynamoRecordSize }); }); _defineProperty(_this2, "isDocumentTreeDepthOverLimit", function (text) { if (!text) return false; try { var template = document.createElement('template'); template.innerHTML = text; return _this2.isLevelOfChildrenOverLimit(template.content); } catch (e) { console.error(e); return false; } }); _defineProperty(_this2, "isLevelOfChildrenOverLimit", function (element) { var level = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; if (level > DOCUMENT_TREE_DEPTH_LIMIT) return true; if (!element) return false; var children = element.childNodes; if (children.length === 0) return false; var childrenLevel = level + 1; return Array.from(children).some(function (e) { return _this2.isLevelOfChildrenOverLimit(e, childrenLevel); }); }); _defineProperty(_this2, "getContentSize", function (text) { if (!text) return 0; var match = new RegExp('[\&\<\>]', 'g'); // eslint-disable-line no-useless-escape // In backend the whole response is serialized into Unicode // every answer occupies CONTENT_SIZE_BY_ANSWER // every text answer is serialized twice // and then every ampersand, greater than and less than character // when converted to Unicode occupies 5 more bytes twice // Example, an & is turned into \u0026 return CONTENT_SIZE_BY_ANSWER + text.length * 2 + (text.match(match) || []).length * 10; }); _defineProperty(_this2, "handleOnBlur", function (event, rceContent) { if (!_this2.props.readOnly) { var newVal = _this2.props.interactionData.rce ? rceContent.editorContent : event.target.value; if (newVal !== void 0 && newVal !== _this2.state.workingEssayContent) { _this2.props.handleResponseUpdate(newVal); _this2.verifyErrors(newVal); _this2.setState({ workingEssayContent: newVal }); } } }); _defineProperty(_this2, "handleOnChange", function (event, rceContent) { if (!_this2.props.readOnly) { var newVal = _this2.props.interactionData.rce ? rceContent.editorContent : event.target.value; if (newVal !== void 0 && newVal !== _this2.state.workingEssayContent) { _this2.props.handleResponseUpdate(newVal); _this2.setState({ workingEssayContent: newVal }); } } }); _this2.uniqId = uuid(); var workingEssayContent = props.userResponse && props.userResponse.value || ''; var _documentTreeDepthOverLimit = _this2.isDocumentTreeDepthOverLimit(workingEssayContent); var _dynamoRecordSize = _this2.getContentSize(workingEssayContent); _this2.state = { workingEssayContent: workingEssayContent, documentTreeDepthOverLimit: _documentTreeDepthOverLimit, dynamoRecordSize: _dynamoRecordSize, contentId: uuid() }; return _this2; } _inherits(EssayTake, _Component); return _createClass(EssayTake, [{ key: "componentDidUpdate", value: function componentDidUpdate(prevProps) { var _prevProps$userRespon; var userResponse = this.props.userResponse; var prevValue = (_prevProps$userRespon = prevProps.userResponse) === null || _prevProps$userRespon === void 0 ? void 0 : _prevProps$userRespon.value; var currentValue = userResponse === null || userResponse === void 0 ? void 0 : userResponse.value; if (currentValue && prevValue === undefined) { this.setState({ workingEssayContent: currentValue || '', contentId: uuid() }); } } }, { key: "renderEditor", value: function renderEditor() { var commonProps = { label: /*#__PURE__*/React.createElement(ScreenReaderContent, null, /*#__PURE__*/React.createElement(RichContentRenderer, { content: this.props.itemBody })), readOnly: this.props.readOnly, onChange: this.handleOnChange, onBlur: this.handleOnBlur }; if (this.props.interactionData.rce) { var _this$props$interacti = this.props.interactionData, spellCheck = _this$props$interacti.spellCheck, wordCount = _this$props$interacti.wordCount; return /*#__PURE__*/React.createElement(RichContentInput, Object.assign({}, commonProps, { textareaId: "rceTextArea_".concat(this.uniqId), onKeyUp: this.handleOnChange, openImportModal: this.props.openImportModal, defaultContent: this.state.workingEssayContent, stem: this.props.itemBody, height: RCE_THREE_LINES_HEIGHT, editorOptions: { spellCheck: spellCheck, wordCount: wordCount }, key: this.state.contentId, disableDocumentAccess: this.props.disableDocumentAccess })); } return /*#__PURE__*/React.createElement(TextArea, Object.assign({}, commonProps, { value: this.state.workingEssayContent, resize: "vertical", spellCheck: this.props.interactionData.spellCheck, autoGrow: this.context.disableTextAreaAutoGrow ? false : null })); } }, { key: "renderErrors", value: function renderErrors() { var _this$state = this.state, documentTreeDepthOverLimit = _this$state.documentTreeDepthOverLimit, dynamoRecordSize = _this$state.dynamoRecordSize; return /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement(Text, { color: "danger", size: "small" }, documentTreeDepthOverLimit ? t('Formatting error. Please ensure that the text entry has not been pasted from a different source. Type text to submit response.') : '', dynamoRecordSize > CONTENT_SIZE_LIMIT ? t('This submission is too large. Please adjust and try to submit again.') : '')); } }, { key: "renderWordCountMessage", value: function renderWordCountMessage() { var _this$props$interacti2 = this.props.interactionData, wordCount = _this$props$interacti2.wordCount, wordLimitMin = _this$props$interacti2.wordLimitMin, wordLimitEnabled = _this$props$interacti2.wordLimitEnabled, wordLimitMax = _this$props$interacti2.wordLimitMax; if (wordCount || wordLimitEnabled) { return /*#__PURE__*/React.createElement(ScreenReaderContent, null, wordLimitEnabled && wordLimitMin ? t('This essay has a minimum word count of {wordLimitMin}.', { wordLimitMin: wordLimitMin }) : '', wordLimitEnabled && wordLimitMax ? t('This essay has a maximum word count of {wordLimitMax}.', { wordLimitMax: wordLimitMax }) : '', wordCount ? t('There is a word count below the text area.') : ''); } } }, { key: "render", value: function render() { var _this$props = this.props, interactionData = _this$props.interactionData, itemBody = _this$props.itemBody, notifyScreenreader = _this$props.notifyScreenreader; return /*#__PURE__*/React.createElement(ItemBodyWrapper, { itemBody: itemBody }, this.renderWordCountMessage(), /*#__PURE__*/React.createElement("div", { className: "fs-mask" }, this.renderEditor()), /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement(WordCount, { rce: interactionData.rce, isEditing: true, essay: this.state.workingEssayContent, wordCount: interactionData.wordCount, wordLimitEnabled: interactionData.wordLimitEnabled, wordLimitMin: parseInt(interactionData.wordLimitMin, 10), wordLimitMax: parseInt(interactionData.wordLimitMax, 10), notifyScreenreader: notifyScreenreader })), this.renderErrors()); } }]); }(Component); _defineProperty(EssayTake, "propTypes", { handleResponseUpdate: PropTypes.func, interactionData: PropTypes.object.isRequired, itemBody: PropTypes.string.isRequired, userResponse: PropTypes.object, notifyScreenreader: PropTypes.func, openImportModal: PropTypes.func, readOnly: PropTypes.bool, disableDocumentAccess: PropTypes.bool }); _defineProperty(EssayTake, "defaultProps", { handleResponseUpdate: function handleResponseUpdate() {}, openImportModal: function openImportModal() {}, readOnly: false, userResponse: void 0, notifyScreenreader: void 0, disableDocumentAccess: false }); _defineProperty(EssayTake, "contextTypes", { disableTextAreaAutoGrow: PropTypes.bool }); export { EssayTake as default };