@instructure/quiz-interactions
Version:
A React UI component Library for quiz interaction types.
272 lines (268 loc) • 11.5 kB
JavaScript
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 };