@instructure/quiz-interactions
Version:
A React UI component Library for quiz interaction types.
134 lines (116 loc) • 3.73 kB
JavaScript
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import {Decimal} from '@instructure/quiz-i18n/util/Decimal'
import t from '@instructure/quiz-i18n/format-message'
import {ItemBodyWrapper} from '@instructure/quiz-rce/components/ItemBodyWrapper/index'
import {FeedbackWrapper, NoResponse} from '@instructure/quiz-results-feedback'
import {parseSeparators} from '@instructure/quiz-common/util/parseSeparators'
import {ApplyLocaleContext} from '@instructure/ui-i18n'
/**
---
category: Numeric
---
Numeric Result component
```jsx_example
function Example (props) {
const exampleProps = {
itemBody: 'x is an integer and 9 < x^2 < 99. What\'s the max value of x, minus the minimum value of x',
scoredData: {
value: {
userResponse: '3.14159265',
correctAnswer: '9999.9999',
resultScore: 0
}
}
}
return <NumericResult {...exampleProps} {...props} />
}
<SettingsSwitcher locales={LOCALES}>
<Example />
</SettingsSwitcher>
```
**/
export default class NumericResult extends Component {
static propTypes = {
itemBody: PropTypes.string.isRequired,
scoredData: PropTypes.shape({
value: PropTypes.shape({
userResponse: PropTypes.string,
correctAnswer: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.string)]),
resultScore: PropTypes.number,
}),
}).isRequired,
locale: PropTypes.string,
separatorConfig: PropTypes.shape({
decimalSeparator: PropTypes.string,
thousandSeparator: PropTypes.string,
}),
}
static contextType = ApplyLocaleContext
static defaultProps = {
locale: void 0,
}
componentDidMount() {
Decimal.accountSettingDelimiters = this.props.separatorConfig
? parseSeparators(this.props.separatorConfig)
: null
}
get locale() {
return this.props.locale || this.context.locale || 'en-US'
}
correctAnswerString() {
const {correctAnswer} = this.props.scoredData.value || {}
if (Array.isArray(correctAnswer)) {
const [minimum, maximum] = correctAnswer
return t('{minimum} to {maximum} inclusive', {
minimum: this.formatNumber(minimum),
maximum: this.formatNumber(maximum),
})
} else if (correctAnswer) {
return this.formatNumber(correctAnswer)
}
return ''
}
isDecimal(value) {
return /^-?\d+(\.\d+)?$/.test(String(value).trim())
}
formatNumber(value) {
// Only format decimal numbers, if a user used scientific notation or something else,
// we want to display it as they entered it
return this.isDecimal(value)
? Decimal.toLocaleString(value.toString().trim(), this.locale)
: value
}
render() {
const {itemBody} = this.props
const {resultScore, userResponse} = this.props.scoredData.value || {}
const responseHidden = typeof userResponse === 'undefined'
let status
if (resultScore > 0) {
status = 'correct'
} else if (resultScore <= 0) {
status = 'incorrect'
} else {
status = 'unknown'
}
return (
<ItemBodyWrapper itemBody={itemBody}>
{userResponse == null ? (
<NoResponse
correctAnswerLabel={this.correctAnswerString()}
responseHidden={responseHidden}
status={status}
/>
) : (
<FeedbackWrapper
status={status}
userResponse={this.formatNumber(userResponse)}
correctAnswer={this.correctAnswerString()}
hiddenCorrectAnswerText={t('Correct answer: ')}
hiddenIncorrectAnswerText={t('Incorrect answer: ')}
/>
)}
</ItemBodyWrapper>
)
}
}