UNPKG

@instructure/quiz-grading

Version:

The Quiz React SDK by Instructure Inc.

178 lines (154 loc) • 5.59 kB
import React, {Component} from 'react' import PropTypes from 'prop-types' import ImmutablePropTypes from 'react-immutable-proptypes' import extractTextFromHtml from '@instructure/quiz-interactions/util/extractTextFromHtml' import {substituteVars} from '@instructure/quiz-interactions/components/formula/common/util' import {generateDisplayPositions} from '@instructure/quiz-core/common/util/generateDisplayPositions' import {Stimulus} from '@instructure/quiz-core/common/components/layout/sidebar/Stimulus/index' import {Sidebar} from '@instructure/quiz-core/common/components/layout/sidebar/Sidebar/index' import {onPhone} from '@instructure/quiz-core/common/util/windowChecks' import SidebarItem from './SidebarItem' import {SIDEBAR_ITEM_REF, FORMULA_SLUG} from '@instructure/quiz-common/constants' import {withI18nSupport} from '@instructure/quiz-common/with-i18n-support' export class GradingSidebar extends withI18nSupport(Component) { static propTypes = { quizTitle: PropTypes.string.isRequired, role: PropTypes.string, screenreaderNotification: PropTypes.func.isRequired, scrollToItem: PropTypes.func.isRequired, sessionItems: ImmutablePropTypes.list.isRequired, sessionNotLoaded: PropTypes.bool.isRequired, sidebarOpen: PropTypes.bool, toggleSidebar: PropTypes.func.isRequired, locale: PropTypes.string, } static defaultProps = { role: 'navigation', sidebarOpen: false, locale: null, } static contextTypes = { locale: PropTypes.string, } generateItemKey(id) { return `${SIDEBAR_ITEM_REF}_${id}` } setRef(key) { return node => { this[key] = node } } scrollToItem = position => itemId => { this.props.scrollToItem(itemId) if (onPhone() && this.props.sidebarOpen) { this.props.toggleSidebar() } } getItemBody(item, locale) { let itemBody = item.getItemBody() if (item.getInteractionType().slug === FORMULA_SLUG) { const variables = item.toJS().interactionData.variables itemBody = substituteVars(itemBody, variables, locale) } return extractTextFromHtml(itemBody || '') } // Helper to determine if a sessionItem is a textblock isTextblock(sessionItem) { // Prefer interactionType if available const item = sessionItem.getItem ? sessionItem.getItem() : sessionItem.item if (item && typeof item.getInteractionType === 'function') { const type = item.getInteractionType() if (type && type.slug === 'textblock') return true } // Fallback to questionNumber === 0 return sessionItem.questionNumber === 0 } renderSidebarItem = (sessionItem, displayPosition) => { const item = sessionItem.getItem() const key = this.generateItemKey(item.id) const sidebarOpen = this.props.sidebarOpen // Use helper for textblock detection const isTextblock = this.isTextblock(sessionItem) const position = isTextblock ? null : displayPosition !== null ? displayPosition : sessionItem.position return ( <SidebarItem inStimulus={sessionItem.isStimulus} isGraded={sessionItem.sessionItemResult.get('score') !== null} itemBody={this.getItemBody(item, this.locale)} itemId={item.id} itemName={item.getInteractionType().name} key={key} pointsPossible={sessionItem.pointsPossible} position={position} sessionItemId={sessionItem.id} ref={this.generateItemKey(item.id)} scrollToItem={this.scrollToItem} sidebarOpen={sidebarOpen} /> ) } renderSidebarItems() { const {sessionItems, sidebarOpen} = this.props const allItems = generateDisplayPositions(sessionItems) // Recalculate display positions starting from 1, skipping textblocks let displayPosition = 1 return allItems.map(si => { const sessionItem = si.record // Use helper for textblock detection const isTextblock = this.isTextblock(sessionItem) if (sessionItem.isStimulus) { const currentDisplayPosition = isTextblock ? null : displayPosition const nestedCount = sessionItem.getNestedEntries().size // Only increment position for non-textblock stimuli if (!isTextblock) { displayPosition += nestedCount } const key = this.generateItemKey(si.record.id) return ( <Stimulus displayPosition={currentDisplayPosition} itemId={sessionItem.id} key={key} ref={this.setRef(key)} renderSidebarItem={this.renderSidebarItem} scrollToItem={this.scrollToItem(sessionItem.position)} sidebarOpen={sidebarOpen} stimulusParent={sessionItem} /> ) } const currentDisplayPosition = isTextblock ? null : displayPosition // Only increment position for non-textblock items if (!isTextblock) { displayPosition++ } return ( <div key={sessionItem.id}> {this.renderSidebarItem(sessionItem, currentDisplayPosition)} </div> ) }) } render() { if (this.props.sessionNotLoaded) { return null } return ( <Sidebar grading role={this.props.role} scrollToItem={this.scrollToItem} sidebarOpen={this.props.sidebarOpen} toggleSidebar={this.props.toggleSidebar} screenreaderNotification={this.props.screenreaderNotification} > {this.renderSidebarItems()} </Sidebar> ) } } export default GradingSidebar