UNPKG

@instructure/quiz-interactions

Version:

A React UI component Library for quiz interaction types.

140 lines (119 loc) 4.15 kB
import React, {Component} from 'react' import PropTypes from 'prop-types' import {SELECT_EMPTY_OPTION, SELECT_EMPTY_OPTION_TEXT} from '@instructure/quiz-common/constants' import {Select} from '@instructure/quiz-common/components/Select/index' export default class MatchSelect extends Component { static propTypes = { questionId: PropTypes.string.isRequired, options: PropTypes.arrayOf(PropTypes.string).isRequired, handleResponseUpdate: PropTypes.func, selectedAnswers: PropTypes.shape({ value: PropTypes.objectOf(PropTypes.string), }).isRequired, interaction: PropTypes.string, renderLabel: PropTypes.element.isRequired, } static defaultProps = { handleResponseUpdate: void 0, interaction: 'enabled', } state = { inputValue: this.props.selectedAnswers.value[this.props.questionId] || '', isShowingOptions: false, highlightedOptionId: null, filteredOptions: this.props.options, } filterOptions = value => { return this.props.options.filter(option => option.startsWith(value)) } handleShowOptions = () => { this.setState({ isShowingOptions: true, }) } handleHideOptions = () => { const {filteredOptions, inputValue} = this.state const index = filteredOptions.findIndex(option => option === inputValue) this.handleSelectOption(null, { id: index === -1 ? undefined : this.getIdFromText(filteredOptions[index]), }) } handleBlur = () => { this.setState({highlightedOptionId: null}) } handleHighlightOption = (event, option) => { event.persist() if (!option || !option.id) return // prevent highlighting of empty option this.setState({ highlightedOptionId: option.id, inputValue: event.type === 'keydown' ? this.getTextFromId(option.id) : this.state.inputValue, }) } handleSelectOption = (event, option) => { if (!option || option.id === SELECT_EMPTY_OPTION) return const value = option.id ? this.getTextFromId(option.id) : '' this.props.handleResponseUpdate({ ...(this.props.selectedAnswers && this.props.selectedAnswers.value), [this.props.questionId]: option && value, }) this.setState({ inputValue: value, isShowingOptions: false, filteredOptions: this.props.options, }) } handleInputChange = event => { const value = event.target.value const newOptions = this.filterOptions(value) this.setState({ isShowingOptions: true, inputValue: value, highlightedOptionId: newOptions.length > 0 ? this.getIdFromText(newOptions[0]) : null, }) } getIdFromText = text => { return btoa(encodeURIComponent(text)) } getTextFromId = id => { return decodeURIComponent(atob(id)) } render() { const {inputValue, isShowingOptions, highlightedOptionId, filteredOptions} = this.state return ( <Select interaction={this.props.interaction} renderLabel={this.props.renderLabel} inputValue={inputValue} isShowingOptions={isShowingOptions} onBlur={this.handleBlur} optionsMaxHeight="50vh" onInputChange={this.handleInputChange} onRequestShowOptions={this.handleShowOptions} onRequestHideOptions={this.handleHideOptions} onRequestHighlightOption={this.handleHighlightOption} onRequestSelectOption={this.handleSelectOption} mountNode={() => document.body} > {filteredOptions.length > 0 ? ( filteredOptions.map(option => { const normalized = this.getIdFromText(option) return ( <Select.Option id={normalized} key={option} isHighlighted={normalized === highlightedOptionId} isSelected={option === this.props.selectedAnswers.value[this.props.questionId]} > {option} </Select.Option> ) }) ) : ( <Select.Option id={SELECT_EMPTY_OPTION} key={SELECT_EMPTY_OPTION}> {SELECT_EMPTY_OPTION_TEXT} </Select.Option> )} </Select> ) } }