UNPKG

@instructure/quiz-grading

Version:

The Quiz React SDK by Instructure Inc.

295 lines (264 loc) • 8.06 kB
/** @jsx jsx */ import {Component} from 'react' import PropTypes from 'prop-types' import {Alert} from '@instructure/ui-alerts' import {Button, CloseButton} from '@instructure/ui-buttons' import {Text} from '@instructure/ui-text' import {Heading} from '@instructure/ui-heading' import {RadioInput, RadioInputGroup} from '@instructure/ui-radio-input' import {jsx, InstUISettingsProvider} from '@instructure/emotion' import { Modal, ModalHeader, ModalBody, ModalFooter, } from '@instructure/quiz-common/components/Modal/index' import {XSMALL_SIDE_MARGIN} from '@instructure/quiz-common/constants' import {withStyleOverrides} from '@instructure/quiz-common/util/withStyleOverrides' import generateStyle from './styles' import generateComponentTheme from './theme' import t from '@instructure/quiz-i18n/format-message' class BaseRegradingModal extends Component { static displayName = 'RegradingModal' static componentId = `Quizzes${this.displayName}` static propTypes = { addSuccess: PropTypes.func.isRequired, closeModal: PropTypes.func.isRequired, createQuizEntryRegrade: PropTypes.func.isRequired, modalOpen: PropTypes.bool.isRequired, quizId: PropTypes.string.isRequired, regradingPayload: PropTypes.shape({ quizEntryId: PropTypes.string.isRequired, scoringAlgorithm: PropTypes.string, scoringData: PropTypes.object, }).isRequired, scoreReconciliation: PropTypes.string.isRequired, setScoreReconciliation: PropTypes.func.isRequired, toggleRegradingEditMode: PropTypes.func.isRequired, handleValidationErrorsFromApi: PropTypes.func, styles: PropTypes.object, } state = { hasGenericErrorFromApi: false, } static defaultProps = { handleValidationErrorsFromApi: Function.prototype, } // Getters get optionInputs() { return { new_score: { hint: t('some scores may be reduced, some may be raised'), text: t('award points for correct answer only'), }, old_score: { hint: t('no scores will be reduced'), text: t('award points for both current and previous answers'), }, any_response: { hint: t('everyone who answers gets full credit'), text: t('award points for any submitted answer'), }, thrown_out: { hint: t('full credit awarded to all who have submitted the quiz'), text: t('give everyone full credit'), }, } } get scoreReconciliation() { return this.props.scoreReconciliation } get selectHint() { const hint = this.optionInputs[this.scoreReconciliation].hint return [ { text: t('Result: {hint}', {hint}), type: 'hint', }, ] } // Handlers clearGenericErrorFromApi = () => { this.setState({hasGenericErrorFromApi: false}) } cancelAction = () => { this.clearGenericErrorFromApi() this.props.closeModal() } onSpeedgraderOptionChange = (event, newValue) => { this.props.setScoreReconciliation(newValue) } onSubmit = () => { const {createQuizEntryRegrade, quizId, regradingPayload, scoreReconciliation} = this.props this.clearGenericErrorFromApi() createQuizEntryRegrade(quizId, {...regradingPayload, scoreReconciliation}) .then(() => { this.props.toggleRegradingEditMode('') this.props.addSuccess( t('Regrade scheduled successfully; check back later to see it applied.'), ) }) .catch(e => { if (e.validationErrorsFromApi) { // Close the modal so the user can view the input validation errors and correct them this.cancelAction() this.props.handleValidationErrorsFromApi(e.validationErrorsFromApi) } else { // Some other type of error occurred this.setState({hasGenericErrorFromApi: true}) } throw e }) } // Rendering cancelButton() { return ( <Button margin={XSMALL_SIDE_MARGIN} onClick={this.cancelAction} ref={this.setRef('cancelImportButton')} > {t('Cancel')} </Button> ) } regradeButton() { return ( <Button margin={XSMALL_SIDE_MARGIN} onClick={this.onSubmit} ref={this.setRef('confirmButton')} color="primary" data-automation="sdk-regrade-button" > {t('Regrade')} </Button> ) } setRef(refName) { return node => { this[refName] = node } } renderOptionInputs() { return Object.entries(this.optionInputs).map(optionInput => { return ( <option key={optionInput[0]} value={optionInput[0]}> {optionInput[1].text} </option> ) }) } renderRadioInputs() { return Object.entries(this.optionInputs).map(optionInput => { return ( <RadioInput label={optionInput[1].text} value={optionInput[0]} key={optionInput[0]} data-automation={`sdk-regrade-option-${optionInput[0]}`} /> ) }) } renderModalBody() { return ( <div> <Alert margin="small 0" transition="none" variant="info"> <div css={this.props.styles.alert}> <span> {t.rich( ` <0>Regrading only applies to completed submissions.</0> <1>If all students are affected please wait for all submissions before Regrading.</1> `, [ ({children}) => ( <Text as="div" weight="bold" key="0"> {children} </Text> ), ({children}) => ( <Text as="span" key="1"> {children} </Text> ), ], )} </span> </div> </Alert> <div css={this.props.styles.radioInputGroup} role="form"> <RadioInputGroup description={t('Select how you would like to apply this regrade.')} name="scoring_reconciliation" onChange={this.onSpeedgraderOptionChange} messages={this.selectHint} value={this.scoreReconciliation} > {this.renderRadioInputs()} </RadioInputGroup> </div> </div> ) } renderErrorFromApi() { if (this.state.hasGenericErrorFromApi) { return ( <div css={this.props.styles.errorAlert}> <InstUISettingsProvider theme={{ componentOverrides: { Alert: { contentPadding: '0.4rem 1.5rem', }, }, }} > <Alert margin="0 0" transition="fade" variant="error"> <Text size="small"> {t('Regrade did not run due to server error. Please try again later!')} </Text> </Alert> </InstUISettingsProvider> </div> ) } } render() { return ( <Modal open={this.props.modalOpen} size="medium" onDismiss={this.cancelAction} label={t('Question Regrade Options')} ref={this.setRef('modal')} data-automation="sdk-regrade-modal" shouldCloseOnDocumentClick={false} > <ModalHeader> <Heading level="reset" as="h2"> {t('Question Regrade Options')} </Heading> <CloseButton onClick={this.cancelAction} placement="end" offset="medium" screenReaderLabel={t('Close')} /> </ModalHeader> <ModalBody>{this.renderModalBody()}</ModalBody> <ModalFooter> {this.renderErrorFromApi()} {this.cancelButton()} {this.regradeButton()} </ModalFooter> </Modal> ) } } export const RegradingModal = withStyleOverrides( generateStyle, generateComponentTheme, )(BaseRegradingModal) export default RegradingModal