UNPKG

@instructure/quiz-grading

Version:

The Quiz React SDK by Instructure Inc.

232 lines (200 loc) • 7.39 kB
/** @jsx jsx */ import {Component} from 'react' import PropTypes from 'prop-types' import ImmutablePropTypes from 'react-immutable-proptypes' import {Map} from 'immutable' import {jsx} from '@instructure/emotion' import {CustomPropTypes} from '@instructure/quiz-core/common/util/CustomPropTypes' import {SessionItemResult} from '@instructure/quiz-core/common/components/resources/sessionItemResult/SessionItemResult/index' import GradingBar from '../GradingBar' import RegradeOverride from '../../quizEntry/RegradeOverride' import {CLEAR_ALL_INPUT_VALIDATION_ERRORS} from '@instructure/quiz-common/constants' import {withStyleOverrides} from '@instructure/quiz-common/util/withStyleOverrides' import generateStyle from './styles' import generateComponentTheme from './theme' class BaseSessionItemResultWrapper extends Component { static displayName = 'SessionItemResultWrapper' static componentId = `Quizzes${this.displayName}` static propTypes = { activeItemId: PropTypes.string, appContainer: CustomPropTypes.selectors.isRequired, handleChangeFeedback: PropTypes.func.isRequired, handleChangeGradeStatus: PropTypes.func.isRequired, handleChangePoints: PropTypes.func.isRequired, gradeByQuestionEnabled: PropTypes.bool, graderFeedback: ImmutablePropTypes.map.isRequired, isEditing: PropTypes.bool.isRequired, isRegrading: PropTypes.bool.isRequired, item: ImmutablePropTypes.record.isRequired, itemResultsModifications: ImmutablePropTypes.map.isRequired, quizSessionId: PropTypes.string.isRequired, regradingKey: PropTypes.string.isRequired, renderUpdateButton: PropTypes.func.isRequired, sessionItem: ImmutablePropTypes.record.isRequired, sessionItemResult: ImmutablePropTypes.record.isRequired, setActiveItem: PropTypes.func.isRequired, shouldRenderTopBorder: PropTypes.bool.isRequired, switchOffEditing: PropTypes.func.isRequired, switchOnEditing: PropTypes.func.isRequired, toggleRegradingEditMode: PropTypes.func.isRequired, makeStyles: PropTypes.func, styles: PropTypes.object, regradingFromSpeedGrader: PropTypes.bool, isSurvey: PropTypes.bool.isRequired, } static defaultProps = { activeItemId: null, gradeByQuestionEnabled: false, } state = { graderFeedback: this.props.graderFeedback, validationErrorsFromApi: {}, } componentDidMount() { this.props.makeStyles() } componentDidUpdate() { this.props.makeStyles() } gradingBar = null _timeouts = [] componentWillUnmount() { this._timeouts.forEach(clearTimeout) } isAutogradeable() { const notAutogradeableTypes = ['essay', 'file-upload'] return !notAutogradeableTypes.includes(this.props.item.getInteractionType().slug) } isSubjective() { return this.props.item.getInteractionType().isSubjective } startFeedbackEdit = () => { if (!this.props.isEditing) { this.props.switchOnEditing() } } closeFeedbackEdit = () => { this.props.switchOffEditing() this.gradingBar?.focusFeedbackButton() } submitFeedbackEdit = () => { this.props.handleChangeFeedback(this.props.sessionItem.id, this.props.item.id, { graderFeedback: this.state.graderFeedback.toJS(), }) this.closeFeedbackEdit() } updateFeedbackEdit = (e, {editorContent}) => { this.setState({ graderFeedback: Map({content: editorContent}), }) } // ============= // HANDLERS // ============= setActiveItem = () => { if (this.props.gradeByQuestionEnabled) { this.props.setActiveItem(this.props.item.id) } } handleRegradingEditModeToggle = () => { this.props.toggleRegradingEditMode(this.props.regradingKey) } handleRegradingOnCancel = sessionItemId => { this.clearValidationErrorsFromApi(CLEAR_ALL_INPUT_VALIDATION_ERRORS) this._timeouts = this._timeouts.concat(setTimeout(() => this.gradingBar?.focusRegradeButton())) } handleGradingBarRef = node => { this.gradingBar = node } handleValidationErrorsFromApi = errors => { this.setState({validationErrorsFromApi: errors}) } clearValidationErrorsFromApi = inputId => { if (inputId === CLEAR_ALL_INPUT_VALIDATION_ERRORS) { this.setState({validationErrorsFromApi: {}}) } else if (this.state.validationErrorsFromApi[inputId]) { const newErrors = {...this.state.validationErrorsFromApi} delete newErrors[inputId] this.setState({validationErrorsFromApi: newErrors}) } } // ============= // RENDERING // ============= renderRegradeOverride = (sessionItem, item) => ( <RegradeOverride displayPosition={sessionItem.position} guid={sessionItem.id} onCancel={this.handleRegradingOnCancel} itemId={item.id} quizSessionId={this.props.quizSessionId} sessionItem={sessionItem} validationErrorsFromApi={this.state.validationErrorsFromApi} handleValidationErrorsFromApi={this.handleValidationErrorsFromApi} clearValidationErrorsFromApi={this.clearValidationErrorsFromApi} /> ) renderResult(sessionItem, item) { const { handleChangeGradeStatus, handleChangePoints, itemResultsModifications, sessionItemResult, shouldRenderTopBorder, } = this.props return ( <div css={this.props.styles.root} data-automation="sdk-grading-result-wrapper" tabIndex="-1" onFocus={this.setActiveItem} > <div css={this.props.styles.result}> <SessionItemResult appContainer={this.props.appContainer} editGraderFeedback={this.startFeedbackEdit} hideGraderFeedback={this.props.isEditing} item={item} sessionItem={sessionItem} sessionItemResult={sessionItemResult} shouldRenderTopBorder={shouldRenderTopBorder} /> </div> <div css={this.props.styles.gradingBar}> <GradingBar appContainer={this.props.appContainer} ref={this.handleGradingBarRef} cancelFeedbackEdit={this.closeFeedbackEdit} graderFeedback={this.state.graderFeedback} handleChangeGradeStatus={handleChangeGradeStatus} handleChangePoints={handleChangePoints} isAutogradeable={this.isAutogradeable()} isEditing={this.props.isEditing} isSubjective={this.isSubjective()} itemResultsModifications={itemResultsModifications} regradeToggleAction={this.handleRegradingEditModeToggle} renderUpdateButton={this.props.renderUpdateButton} sessionItem={sessionItem} sessionItemResult={sessionItemResult} startFeedbackEdit={this.startFeedbackEdit} submitFeedbackEdit={this.submitFeedbackEdit} updateFeedbackEdit={this.updateFeedbackEdit} /> </div> </div> ) } render() { const {sessionItem, item, regradingFromSpeedGrader, isRegrading} = this.props if (isRegrading && regradingFromSpeedGrader) { return this.renderRegradeOverride(sessionItem, item) } else { return this.renderResult(sessionItem, item) } } } export const SessionItemResultWrapper = withStyleOverrides( generateStyle, generateComponentTheme, )(BaseSessionItemResultWrapper) export default SessionItemResultWrapper