@instructure/quiz-grading
Version:
The Quiz React SDK by Instructure Inc.
232 lines (200 loc) • 7.39 kB
JavaScript
/** @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