@instructure/quiz-interactions
Version:
A React UI component Library for quiz interaction types.
370 lines (366 loc) • 16.4 kB
JavaScript
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 _object_spread(target) {
for(var i = 1; i < arguments.length; i++){
var source = arguments[i] != null ? arguments[i] : {};
var ownKeys = Object.keys(source);
if (typeof Object.getOwnPropertySymbols === "function") {
ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function(sym) {
return Object.getOwnPropertyDescriptor(source, sym).enumerable;
}));
}
ownKeys.forEach(function(key) {
_define_property(target, key, source[key]);
});
}
return target;
}
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 _object_spread_props(target, source) {
source = source != null ? source : {};
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 _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 _type_of(obj) {
"@swc/helpers - typeof";
return obj && typeof Symbol !== "undefined" && obj.constructor === Symbol ? "symbol" : typeof obj;
}
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;
})();
}
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/format-message';
import { DOCUMENT_TREE_DEPTH_LIMIT, CONTENT_SIZE_LIMIT, CONTENT_SIZE_BY_ANSWER, TextArea } from '@instructure/quiz-common';
var EssayTake = /*#__PURE__*/ function(Component) {
"use strict";
_inherits(EssayTake, Component);
function EssayTake(props) {
_class_call_check(this, EssayTake);
var _this;
_this = _call_super(this, EssayTake, [
props
]), _define_property(_this, "verifyErrors", function(text) {
var documentTreeDepthOverLimit = _this.isDocumentTreeDepthOverLimit(text);
var dynamoRecordSize = _this.getContentSize(text);
_this.setState({
documentTreeDepthOverLimit: documentTreeDepthOverLimit,
dynamoRecordSize: dynamoRecordSize
});
}), _define_property(_this, "isDocumentTreeDepthOverLimit", function(text) {
if (!text) return false;
try {
var template = document.createElement('template');
template.innerHTML = text;
return _this.isLevelOfChildrenOverLimit(template.content);
} catch (e) {
console.error(e);
return false;
}
}), _define_property(_this, "isLevelOfChildrenOverLimit", function(element) {
var level = arguments.length > 1 && arguments[1] !== void 0 ? 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 _this.isLevelOfChildrenOverLimit(e, childrenLevel);
});
}), _define_property(_this, "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;
}), _define_property(_this, "handleOnBlur", function(event, rceContent) {
if (!_this.props.readOnly) {
var newVal = _this.props.interactionData.rce ? rceContent.editorContent : event.target.value;
if (newVal !== void 0 && newVal !== _this.state.workingEssayContent) {
_this.props.handleResponseUpdate(newVal);
_this.verifyErrors(newVal);
_this.setState({
workingEssayContent: newVal
});
}
}
}), _define_property(_this, "handleOnChange", function(event, rceContent) {
if (!_this.props.readOnly) {
var newVal = _this.props.interactionData.rce ? rceContent.editorContent : event.target.value;
if (newVal !== void 0 && newVal !== _this.state.workingEssayContent) {
_this.props.handleResponseUpdate(newVal);
_this.setState({
workingEssayContent: newVal
});
}
}
});
_this.uniqId = uuid();
var workingEssayContent = props.userResponse && props.userResponse.value || '';
var documentTreeDepthOverLimit = _this.isDocumentTreeDepthOverLimit(workingEssayContent);
var dynamoRecordSize = _this.getContentSize(workingEssayContent);
_this.state = {
workingEssayContent: workingEssayContent,
documentTreeDepthOverLimit: documentTreeDepthOverLimit,
dynamoRecordSize: dynamoRecordSize,
contentId: uuid()
};
return _this;
}
_create_class(EssayTake, [
{
key: "componentDidUpdate",
value: function componentDidUpdate(prevProps) {
var _prevProps_userResponse;
var userResponse = this.props.userResponse;
var prevValue = (_prevProps_userResponse = prevProps.userResponse) === null || _prevProps_userResponse === void 0 ? void 0 : _prevProps_userResponse.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_interactionData = this.props.interactionData, spellCheck = _this_props_interactionData.spellCheck, wordCount = _this_props_interactionData.wordCount;
return /*#__PURE__*/ React.createElement(RichContentInput, _object_spread_props(_object_spread({}, 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_spread_props(_object_spread({}, 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_interactionData = this.props.interactionData, wordCount = _this_props_interactionData.wordCount, wordLimitMin = _this_props_interactionData.wordLimitMin, wordLimitEnabled = _this_props_interactionData.wordLimitEnabled, wordLimitMax = _this_props_interactionData.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());
}
}
]);
return EssayTake;
}(Component);
_define_property(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
});
_define_property(EssayTake, "defaultProps", {
handleResponseUpdate: function() {},
openImportModal: function() {},
readOnly: false,
userResponse: void 0,
notifyScreenreader: void 0,
disableDocumentAccess: false
});
_define_property(EssayTake, "contextTypes", {
disableTextAreaAutoGrow: PropTypes.bool
});
/**
---
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>
```
**/ export { EssayTake as default };