UNPKG

@instructure/quiz-interactions

Version:

A React UI component Library for quiz interaction types.

170 lines (152 loc) • 5.32 kB
import React, {useState} from 'react' import PropTypes from 'prop-types' import {SimpleSelect} from '@instructure/quiz-common/components/SimpleSelect/index' import {Flex} from '@instructure/quiz-common/components/Flex/index' import {Decimal} from '@instructure/quiz-i18n/util/Decimal' import {ScientificNumberInput} from '@instructure/quiz-number-input/components/ScientificNumberInput' import {decimals, significantDigits} from 'instructure-validations' import t from '@instructure/quiz-i18n/format-message' import {DECIMALS, SIGNIFICANT_DIGITS} from './constants' function PreciseResponse({ id, locale, messages, numericTypeSelect, precision, precisionType, onChange, value: valueProp, }) { const [value, setValue] = useState(() => { if (valueProp) { try { return Decimal.toLocaleString(valueProp, locale) } catch { return valueProp } } return '' }) const parsedValue = () => { return Decimal.parse(valueProp) } const minPrecision = () => { const val = parsedValue() if (precisionType === SIGNIFICANT_DIGITS) { return val.isNaN() ? 1 : significantDigits(val.toString()) } return val.isNaN() ? 0 : decimals(val.toString()) } const maxPrecision = () => { // These are the limits for toPrecision and toFixed in Chrome if (precisionType === SIGNIFICANT_DIGITS) { const val = parseFloat(valueProp) return val === 0 ? 1 : 21 } return 100 } const handlePrecisionChange = (event, _, normalized) => { // When precision changes, update the value to match the new precision (add // or remove trailing zeros) let newPrecision = normalized let newValue = valueProp const valueDecimal = parsedValue() if (newPrecision != null && !valueDecimal.isNaN()) { const precisionNumber = parseInt(newPrecision, 10) newValue = precisionType === SIGNIFICANT_DIGITS ? valueDecimal.toPrecision(precisionNumber, locale) : valueDecimal.toFixed(precisionNumber, locale) } setValue(newValue) onChange(event, {id, precision: newPrecision, value: newValue}) } const handlePrecisionTypeChange = (event, {value: newValue}) => { const newPrecisionType = newValue let newPrecision = precision if (valueProp) { newPrecision = precisionFromValue(valueProp, newPrecisionType).toString() } onChange(event, {id, precision: newPrecision, precisionType: newPrecisionType}) } const handleValueChange = (event, newValue, normalized) => { // When the answer value changes, update the precision to match the // precision of the answer let newPrecision = precision if (normalized != null) { newPrecision = precisionFromValue(normalized, precisionType).toString() } setValue(newValue) onChange(event, {id, precision: newPrecision, value: normalized}) } return ( <Flex alignItems="start"> <Flex.Item shouldGrow shouldShrink margin="0 xx-small"> {numericTypeSelect} </Flex.Item> <Flex.Item shouldGrow shouldShrink margin="0 xx-small"> <ScientificNumberInput renderLabel={t('Answer')} locale={locale} messages={messages.value} onChange={handleValueChange} value={value} aria-valuetext={`${value} ${t('Answer')}`} data-automation="sdk-numeric-precise-answer-number-input" isRequired={true} /> </Flex.Item> <Flex.Item shouldGrow shouldShrink margin="0 xx-small"> <ScientificNumberInput renderLabel={t('Precision')} locale={locale} max={maxPrecision()} messages={messages.precision} min={minPrecision()} onChange={handlePrecisionChange} step={1} value={precision} aria-valuetext={`${precision} ${t('Precision')}`} data-automation="sdk-numeric-precise-precision-number-input" isRequired={true} /> </Flex.Item> <Flex.Item shouldGrow shouldShrink margin="0 xx-small"> <SimpleSelect renderLabel={t('Type')} onChange={handlePrecisionTypeChange} value={precisionType} > <SimpleSelect.Option id="precise-response-select-option-significant-digits" value={SIGNIFICANT_DIGITS} > {t('Significant digits')} </SimpleSelect.Option> <SimpleSelect.Option id="precise-response-select-option-decimals" value={DECIMALS}> {t('Decimal places')} </SimpleSelect.Option> </SimpleSelect> </Flex.Item> </Flex> ) } PreciseResponse.propTypes = { id: PropTypes.string.isRequired, locale: PropTypes.string.isRequired, messages: PropTypes.objectOf(PropTypes.array), numericTypeSelect: PropTypes.node.isRequired, precision: PropTypes.string, precisionType: PropTypes.oneOf([SIGNIFICANT_DIGITS, DECIMALS]).isRequired, onChange: PropTypes.func.isRequired, value: PropTypes.string, } PreciseResponse.defaultProps = { messages: void 0, precision: void 0, value: void 0, } export default PreciseResponse function precisionFromValue(value, precisionType) { return precisionType === SIGNIFICANT_DIGITS ? significantDigits(value) : decimals(value) }