UNPKG

@instructure/quiz-interactions

Version:

A React UI component Library for quiz interaction types.

207 lines (189 loc) • 6.5 kB
/** @jsx jsx */ import {Component} from 'react' import PropTypes from 'prop-types' import map from 'lodash/fp/map' import sortBy from 'lodash/fp/sortBy' import flow from 'lodash/fp/flow' import striptags from 'striptags' import {RadioInputGroup, RadioInput} from '@instructure/ui-radio-input' import {CondensedButton, IconButton} from '@instructure/ui-buttons' import {IconStrikethroughLine} from '@instructure/ui-icons' import {jsx} from '@instructure/emotion' import {ItemBodyWrapper} from '@instructure/quiz-rce/components/ItemBodyWrapper/index' import {RichContentRenderer} from '@instructure/quiz-rce/components/RichContentRenderer/index' import generateStyle from './styles' import generateComponentTheme from './theme' import t from '@instructure/quiz-i18n/format-message' import {withStyleOverrides} from '@instructure/quiz-common/util/withStyleOverrides' /** --- category: MultipleChoice --- Multiple Choice Take component ```jsx_example function Example (props) { const exampleProps = { itemId: 'mc_type', itemBody: 'Who was the first President of the United States?', interactionData: { choices: [ { id: 'uuid1', itemBody: 'George Washington', position: 1 }, { id: 'uuid2', itemBody: 'Alexander Hamilton.', position: 2 }, { id: 'uuid3', itemBody: ` <p> John Adams <iframe src="//www.youtube.com/embed/nrvpZxMfKaU" width="560" height="314" allowfullscreen="allowfullscreen" ></iframe> </p> `, position: 3 }, { id: 'uuid4', itemBody: 'Thomas Jefferson', position: 4 } ] }, userResponse: { value: 'uuid1' } } return ( <MultipleChoiceTake {...exampleProps} {...props} /> ) } <SettingsSwitcher locales={LOCALES}> <TakeStateProvider> <Example /> </TakeStateProvider> </SettingsSwitcher> ``` **/ @withStyleOverrides(generateStyle, generateComponentTheme) export default class MultipleChoiceTake extends Component { static displayName = 'MultipleChoiceTake' static componentId = `Quizzes${this.displayName}` static propTypes = { allowClearMcSelection: PropTypes.bool, choiceElimination: PropTypes.bool, handleResponseUpdate: PropTypes.func, interactionData: PropTypes.shape({ choices: PropTypes.arrayOf( PropTypes.shape({ id: PropTypes.string, itemBody: PropTypes.string, position: PropTypes.number, }), ), }).isRequired, itemBody: PropTypes.string.isRequired, itemId: PropTypes.string, userResponse: PropTypes.shape({ value: PropTypes.string, properties: PropTypes.shape({ eliminated: PropTypes.array, }), }).isRequired, readOnly: PropTypes.bool, styles: PropTypes.object, } static defaultProps = { allowClearMcSelection: false, choiceElimination: false, handleResponseUpdate: Function.prototype, itemId: void 0, readOnly: false, } getEliminations() { return this.props.userResponse?.properties?.eliminated || [] } handleChosenValChange = (event, choiceId) => { this.props.handleResponseUpdate(choiceId, {eliminated: this.getEliminations()}) } toggleElimination = choiceId => event => { event.preventDefault() const currEliminations = this.getEliminations() if (currEliminations.includes(choiceId)) { const choiceIndex = currEliminations.indexOf(choiceId) this.props.handleResponseUpdate(this.props.userResponse?.value, { eliminated: [ ...currEliminations.slice(0, choiceIndex), ...currEliminations.slice(choiceIndex + 1), ], }) } else { this.props.handleResponseUpdate(this.props.userResponse?.value, { eliminated: [...currEliminations, choiceId], }) } } clearSelection = () => { this.props.handleResponseUpdate(null) } renderChoice = choice => { const {readOnly, choiceElimination} = this.props const eliminated = this.getEliminations().includes(choice.id) const itemBody = ( <RichContentRenderer customStyles={this.props.styles.itemBody} content={choice.itemBody} /> ) const showEliminations = !readOnly && choiceElimination const screenReaderLabel = eliminated ? t('Undo elimination of answer {choice}', {choice: striptags(choice.itemBody)}) : t('Eliminate answer {choice}', {choice: striptags(choice.itemBody)}) const eliminationLabel = ( <div css={this.props.styles.labelWrapper}> {eliminated ? <s>{itemBody}</s> : itemBody} <IconButton withBackground={false} withBorder={false} onClick={this.toggleElimination(choice.id)} screenReaderLabel={screenReaderLabel} > <IconStrikethroughLine /> </IconButton> </div> ) const ariaLabel = t('Answer: {choice}', {choice: striptags(choice.itemBody)}) const ariaEliminationLabel = eliminated ? t('Eliminated Answer: {choice}', {choice: striptags(choice.itemBody)}) : ariaLabel return ( <RadioInput key={choice.id} value={choice.id} aria-label={showEliminations ? ariaEliminationLabel : ariaLabel} label={showEliminations ? eliminationLabel : itemBody} /> ) } render() { const chosenVal = this.props.userResponse?.value return ( <ItemBodyWrapper> <div className="fs-mask" css={this.props.styles.fs_mask}> <RadioInputGroup defaultValue={chosenVal} value={chosenVal} onChange={this.handleChosenValChange} name={`interaction_${this.props.itemId}`} readOnly={this.props.readOnly} description={ <RichContentRenderer content={this.props.itemBody} customStyles={this.props.styles.itemDescription} /> } > {flow(sortBy('position'), map(this.renderChoice))(this.props.interactionData.choices)} </RadioInputGroup> {this.props.allowClearMcSelection && ( <CondensedButton onClick={this.clearSelection} margin="small 0 0 medium"> {t('Clear my selection')} </CondensedButton> )} </div> </ItemBodyWrapper> ) } }