@instructure/quiz-interactions
Version:
A React UI component Library for quiz interaction types.
291 lines (287 loc) • 12.5 kB
JavaScript
/** @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_without_holes(arr) {
if (Array.isArray(arr)) return _array_like_to_array(arr);
}
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 _iterable_to_array(iter) {
if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
}
function _non_iterable_spread() {
throw new TypeError("Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
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 _to_consumable_array(arr) {
return _array_without_holes(arr) || _iterable_to_array(arr) || _unsupported_iterable_to_array(arr) || _non_iterable_spread();
}
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);
}
import React, { useCallback, useMemo } from 'react';
import PropTypes from 'prop-types';
import { Alert } from '@instructure/ui-alerts';
import { Text } from '@instructure/ui-text';
import { jsx } from '@instructure/emotion';
import { ItemBodyWrapper } from '@instructure/quiz-rce/components/ItemBodyWrapper/index';
import t from '@instructure/quiz-i18n/format-message';
import Wordbank from '../drag_components/Wordbank';
import TakeStem from './take_components/TakeStem';
import generateStyle from './styles';
import generateComponentTheme from './theme';
import { withStyleOverrides } from '@instructure/quiz-common/util/withStyleOverrides';
/**
---
category: FillInTheBlank
---
Fill in the Blank Take component
```jsx_example
function Example (props) {
const exampleProps = {
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: ' !!! ' },
{ id: 'stem_uuid12', position: 14, type: 'text', value: ' Where the people were " ' },
{ id: 'stem_uuid13', position: 15, type: 'blank', blankId: 'fitb_uuid7' },
{ id: 'stem_uuid14', position: 16, 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'
}]
},
userResponse: {
value: [
{ id: 'fitb_uuid1', value: 'Christopher', type: 'Text'},
{ id: 'fitb_uuid2', value: '1493', type: 'Text'},
{ id: 'fitb_uuid3', value: 'Span', type: 'Text'},
{ id: 'fitb_uuid4', value: 'Europe', type: 'Text'},
{ id: 'fitb_uuid5', value: 'Atlantic', type: 'Text'},
{ id: 'fitb_uuid6', value: 'choice_uuid12_austria', type: 'Text'},
{ id: 'fitb_uuid7', value: 'choice_uuid13_confused', type: 'Text'}
]
}
}
return (
<DndProvider backend={HTML5Backend}>
<FillBlankTake {...exampleProps} {...props} />
</DndProvider>
)
}
<SettingsSwitcher locales={LOCALES}>
<TakeStateProvider>
<Example />
</TakeStateProvider>
</SettingsSwitcher>
```
**/ // ============================================
// Sub-components
// ============================================
function DeprecationWarning() {
return /*#__PURE__*/ jsx(Alert, {
variant: "error"
}, /*#__PURE__*/ jsx(Text, {
color: "primary"
}, /*#__PURE__*/ jsx("b", null, t('This question type is deprecated'))), /*#__PURE__*/ jsx("ul", null, /*#__PURE__*/ jsx("li", null, t('Quizzes containing deprecated question types will fail to copy.')), /*#__PURE__*/ jsx("li", null, t('This question was unable to be automatically upgraded due to malformed data.')), /*#__PURE__*/ jsx("li", null, t('Please delete this question and recreate it using the current Fill in the Blank question type.'))));
}
function WordbankSection(param) {
var blanks = param.blanks, response = param.response, returnChoiceToWordbank = param.returnChoiceToWordbank, wordBankChoices = param.wordBankChoices;
if (blanks.find(function(blank) {
return [
'wordbank',
'dropdown'
].includes(blank.answerType);
})) {
return /*#__PURE__*/ jsx(Wordbank, {
blanks: blanks,
response: response,
returnChoiceToWordbank: returnChoiceToWordbank,
wordBankChoices: wordBankChoices
});
}
return null;
}
// ============================================
// Main Component
// ============================================
var FillBlankTake = /*#__PURE__*/ React.forwardRef(function(props, ref) {
var handleResponseUpdate = props.handleResponseUpdate, interactionData = props.interactionData, userResponse = props.userResponse, _props_readOnly = props.readOnly, readOnly = _props_readOnly === void 0 ? false : _props_readOnly, styles = props.styles;
var blanks = useMemo(function() {
return (interactionData === null || interactionData === void 0 ? void 0 : interactionData.blanks) || [];
}, [
interactionData === null || interactionData === void 0 ? void 0 : interactionData.blanks
]);
var moveChoiceToWordbank = useCallback(function(blankId, choiceId) {
var responses = userResponse.value;
var blank = responses.find(function(r) {
return r.id === blankId;
});
// Removes the answer for blank only if choice is selected response for blank
if ((blank === null || blank === void 0 ? void 0 : blank.value) === choiceId) {
var blankIndex = userResponse.value.findIndex(function(r) {
return r.id === blankId;
});
var newResponses = responses.map(function(r, i) {
return i === blankIndex ? _object_spread_props(_object_spread({}, r), {
value: null
}) : r;
});
handleResponseUpdate(newResponses);
}
}, [
userResponse.value,
handleResponseUpdate
]);
var remainingWordBankChoices = useMemo(function() {
var // filter the choices which are already used
_userResponse_value;
var wordbankBlanks = interactionData.blanks.filter(function(blank) {
return blank.answerType === 'wordbank';
});
var choices = _to_consumable_array(wordbankBlanks.map(function(blank) {
return blank.choices;
}).flat());
userResponse === null || userResponse === void 0 ? void 0 : (_userResponse_value = userResponse.value) === null || _userResponse_value === void 0 ? void 0 : _userResponse_value.forEach(function(response) {
var choiceIndex = choices.findIndex(function(choice) {
return choice.id === response.value;
});
if (choiceIndex > -1) {
choices.splice(choiceIndex, 1);
}
});
return choices;
}, [
interactionData.blanks,
userResponse.value
]);
return /*#__PURE__*/ jsx(ItemBodyWrapper, {
itemBody: interactionData.prompt
}, readOnly && /*#__PURE__*/ jsx(DeprecationWarning, null), /*#__PURE__*/ jsx("div", {
css: styles.question
}, /*#__PURE__*/ jsx("div", {
css: styles.headerSection
}, /*#__PURE__*/ jsx(TakeStem, {
onUpdate: handleResponseUpdate,
interactionData: interactionData,
returnChoiceToWordbank: moveChoiceToWordbank,
userResponses: userResponse.value,
readOnly: readOnly,
wordBankChoices: remainingWordBankChoices
})), /*#__PURE__*/ jsx(WordbankSection, {
blanks: blanks,
response: userResponse.value,
returnChoiceToWordbank: moveChoiceToWordbank,
wordBankChoices: remainingWordBankChoices
})));
});
FillBlankTake.displayName = 'FillBlankTake';
FillBlankTake.propTypes = {
handleResponseUpdate: PropTypes.func,
interactionData: PropTypes.object.isRequired,
userResponse: PropTypes.object.isRequired,
readOnly: PropTypes.bool,
styles: PropTypes.object
};
var FillBlankTakeWithStyles = withStyleOverrides(generateStyle, generateComponentTheme)(FillBlankTake);
FillBlankTakeWithStyles.displayName = 'FillBlankTake';
FillBlankTakeWithStyles.componentId = "Quizzes".concat(FillBlankTake.displayName);
FillBlankTakeWithStyles.originalType = FillBlankTake;
export default FillBlankTakeWithStyles;