UNPKG

@instructure/quiz-interactions

Version:

A React UI component Library for quiz interaction types.

200 lines (181 loc) • 6.32 kB
/** @jsx jsx */ import {Component} from 'react' import PropTypes from 'prop-types' import map from 'lodash/map' import find from 'lodash/find' import indexOf from 'lodash/indexOf' import update from 'immutability-helper' import {jsx} from '@instructure/emotion' import Wordbank from '../../fill_blank/drag_components/Wordbank' import TakeStem from '../../fill_blank/Take/take_components/TakeStem' import generateStyle from './styles' import generateComponentTheme from './theme' import {withStyleOverrides} from '@instructure/quiz-common/util/withStyleOverrides' /** --- category: RichFillInTheBlank --- Rich Fill in the Blank Take component ```jsx_example function Example (props) { const exampleProps = { itemBody: `<p><span id="blank_fitb_uuid1"></span> Columbus sailed in <span id="blank_fitb_uuid2"></span> from the country of <span id="blank_fitb_uuid3"></span> on the continent of <span id="blank_fitb_uuid4"></span> across the <span id="blank_fitb_uuid5"></span> and found... <span id="blank_fitb_uuid6"></span>!!! Where the people were " <span id="blank_fitb_uuid7"></span>"</p>`, interactionData: { 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> ``` **/ @withStyleOverrides(generateStyle, generateComponentTheme) export default class RichFillBlankTake extends Component { static displayName = 'RichFillBlankTake' static componentId = `Quizzes${this.displayName}` static propTypes = { handleResponseUpdate: PropTypes.func, interactionData: PropTypes.object.isRequired, itemBody: PropTypes.string.isRequired, userResponse: PropTypes.object.isRequired, readOnly: PropTypes.bool, styles: PropTypes.object, } static defaultProps = { handleResponseUpdate: void 0, readOnly: false, } moveChoiceToWordbank = (blankId, choiceId) => { const responses = this.props.userResponse.value const blank = find(responses, {id: blankId}) // Removes the answer for blank if (blank) { const blankIndex = indexOf(map(this.props.userResponse.value, 'id'), blankId) const newResponses = update(responses, { [blankIndex]: { $merge: {value: null}, }, }) this.props.handleResponseUpdate(newResponses) } } allWordBankChoices = () => { if (this.props.interactionData.wordBankChoices) { return this.props.interactionData.wordBankChoices } const wordbankBlanks = this.props.interactionData.blanks.filter( blank => blank.answerType === 'wordbank', ) return wordbankBlanks.map(blank => blank.choices).flat() } remainingWordBankChoices() { let choices = [...this.allWordBankChoices()] if (this.props.interactionData.reuseWordBankChoices) { // only unique choices (by itemBody) choices = choices.filter( (value, index, array) => array.findIndex(choice => choice.itemBody === value.itemBody) === index, ) } else { // filter the choices which are already used this.props.userResponse.value.forEach(response => { const choiceIndex = choices.findIndex(choice => [choice.id, choice.itemBody].includes(response.value), ) if (choiceIndex > -1) { choices.splice(choiceIndex, 1) } }) } return choices } renderWordbank() { const blanks = this.props.interactionData.blanks || [] if (blanks.find(blank => ['wordbank', 'dropdown'].includes(blank.answerType))) { return ( <Wordbank blanks={blanks} response={this.props.userResponse.value} reuseWordBankChoices={this.props.interactionData.reuseWordBankChoices} returnChoiceToWordbank={this.moveChoiceToWordbank} wordBankChoices={this.remainingWordBankChoices()} /> ) } } render() { return ( <div css={this.props.styles.question}> <div css={this.props.styles.headerSection}> <TakeStem onUpdate={this.props.handleResponseUpdate} itemBody={this.props.itemBody} interactionData={this.props.interactionData} readOnly={this.props.readOnly} returnChoiceToWordbank={this.moveChoiceToWordbank} richFITB userResponses={this.props.userResponse.value} wordBankChoices={this.remainingWordBankChoices()} /> </div> {this.renderWordbank()} </div> ) } }