UNPKG

@instructure/quiz-interactions

Version:

A React UI component Library for quiz interaction types.

181 lines (165 loc) • 5.79 kB
/** @jsx jsx */ import {Component} from 'react' import PropTypes from 'prop-types' import map from 'lodash/fp/map' import striptags from 'striptags' import {jsx} from '@instructure/emotion' import {CheckboxGroup, Checkbox} from '@instructure/ui-checkbox' import {IconButton} from '@instructure/ui-buttons' import {IconStrikethroughLine} from '@instructure/ui-icons' 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: MultipleAnswer --- Multiple Answer Take component ```jsx_example function Example (props) { const exampleProps = { itemBody: 'Who was in the first cabinet of the USA?', interactionData: { choices: [ { id: 'uuid1', itemBody: 'Thomas Jefferson', position: 1 }, { id: 'uuid2', itemBody: 'John Marshall', position: 2 }, { id: 'uuid3', itemBody: 'John Knox', position: 3 }, { id: 'uuid4', itemBody: 'Alexander Hamilton', position: 4 }, { id: 'uuid5', itemBody: 'Aaron Burr', position: 5 }, { id: 'uuid6', itemBody: 'Ben Franklin', position: 6 } ] }, scoringData: { value: ['uuid1', 'uuid3', 'uuid4'] }, userResponse: { value: ['uuid1', 'uuid6'] } } return ( <MultipleAnswerTake {...exampleProps} {...props} /> ) } <SettingsSwitcher locales={LOCALES}> <TakeStateProvider> <Example /> </TakeStateProvider> </SettingsSwitcher> ``` **/ @withStyleOverrides(generateStyle, generateComponentTheme) export default class MultipleAnswerTake extends Component { static displayName = 'MultipleAnswerTake' static componentId = `Quizzes${this.displayName}` static propTypes = { choiceElimination: PropTypes.bool, handleResponseUpdate: PropTypes.func.isRequired, itemId: PropTypes.string, itemBody: PropTypes.string.isRequired, interactionData: PropTypes.shape({ choices: PropTypes.arrayOf( PropTypes.shape({ id: PropTypes.string, itemBody: PropTypes.string, }), ), }).isRequired, userResponse: PropTypes.shape({ value: PropTypes.arrayOf(PropTypes.string), properties: PropTypes.shape({ eliminated: PropTypes.array, }), }), readOnly: PropTypes.bool, styles: PropTypes.object, } static defaultProps = { choiceElimination: false, itemId: void 0, readOnly: false, userResponse: {}, handleResponseUpdate: Function.prototype, } getEliminations() { return this.props.userResponse?.properties?.eliminated || [] } handleCheckboxGroupChange = value => { if (!this.props.readOnly) { this.props.handleResponseUpdate(value, {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], }) } } 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 ( <Checkbox key={choice.id} value={choice.id} aria-label={showEliminations ? ariaEliminationLabel : ariaLabel} label={showEliminations ? eliminationLabel : itemBody} /> ) } render() { return ( <ItemBodyWrapper> <div className="fs-mask" css={this.props.styles.fs_mask}> <CheckboxGroup value={this.props.userResponse?.value || []} name={`interaction_${this.props.itemId}`} onChange={this.handleCheckboxGroupChange} readOnly={this.props.readOnly} description={ <RichContentRenderer content={this.props.itemBody} customStyles={this.props.styles.itemDescription} /> } > {map(this.renderChoice, this.props.interactionData.choices)} </CheckboxGroup> </div> </ItemBodyWrapper> ) } }