Use Rubric component for summary

Fixes OUT-2268

Test Plan:
- Open speedgrader, navigate to an assessment of a free-form rubric.
  Verify:
  - the new UI is present before clicking "view rubric"
  - the summary rubric only has two columns
  - each criteria lists points assessed out of points possible right after the
    comments.
- Now go to an assignment with a points-based rubric. Verify:
  - the spiky UI is present
  - the summary rubric only has two columns
  - each criteria lists the points assessment and comments right after
    the description of the rating.
- Navigate to a student without an assessment using the top right arrow
  keys
- Verify there is no summary rubric shown
- Navigate back to the student with an assessment, verify the summary is
  shown again.

Change-Id: Idae819264c507e42d67a6731367d2ddef5cd176d
Reviewed-on: https://gerrit.instructure.com/152226
Tested-by: Jenkins
Reviewed-by: Augusto Callejas <acallejas@instructure.com>
Reviewed-by: Michael Brewer-Davis <mbd@instructure.com>
QA-Review: Matt Berns <mberns@instructure.com>
Product-Review: Sidharth Oberoi <soberoi@instructure.com>
This commit is contained in:
Frank Murphy 2018-06-01 11:34:13 -04:00
parent e2b0a33311
commit 3ceaed757f
18 changed files with 323 additions and 106 deletions

View File

@ -28,7 +28,7 @@ exports[`renders the AlignmentResult component 1`] = `
<FlexItem borderWidth=\\"small\\" as=\\"span\\" grow={false} shrink={false}>
<div className=\\"react-rubric\\">
<div className=\\"ratings\\">
<Ratings tiers={{...}} points={1} onPointChange={[Function]} />
<Ratings tiers={{...}} points={1} footer={{...}} onPointChange={[Function]} />
</div>
</div>
</FlexItem>

View File

@ -104,7 +104,7 @@ CommentText.defaultProps = {
}
const Comments = (props) => {
const { assessing, assessment, ...commentProps } = props
const { assessing, assessment, footer, ...commentProps } = props
if (!assessing || assessment === null) {
return (
<div className="rubric-freeform">
@ -113,6 +113,7 @@ const Comments = (props) => {
placeholder={I18n.t("This area will be used by the assessor to leave comments related to this criterion.")}
weight="normal"
/>
{footer}
</div>
)
}
@ -129,9 +130,13 @@ const Comments = (props) => {
Comments.propTypes = {
assessing: PropTypes.bool.isRequired,
assessment: PropTypes.shape(assessmentShape),
footer: PropTypes.node,
savedComments: PropTypes.arrayOf(PropTypes.string).isRequired,
setComments: PropTypes.func.isRequired,
setSaveLater: PropTypes.func.isRequired
}
Comments.defaultProps = {
footer: null
}
export default Comments

View File

@ -116,10 +116,11 @@ export default class Criterion extends React.Component {
const {
assessment,
criterion,
customRatings,
freeForm,
onAssessmentChange,
savedComments,
customRatings
isSummary
} = this.props
const { dialogOpen } = this.state
const isOutcome = criterion.learning_outcome_id !== undefined
@ -135,24 +136,49 @@ export default class Criterion extends React.Component {
}
const onPointChange = assessing ? updatePoints : undefined
const pointsPossible = criterion.points
const pointsElement = () => (
<Points
key="points"
assessing={assessing}
assessment={assessment}
onPointChange={onPointChange}
pointsPossible={pointsPossible}
/>
)
const pointsComment = () => (
<CommentText key="comment" assessment={assessment} weight="light" />
)
const pointsFooter = () => [
pointsComment(),
pointsElement()
]
const commentRating = (
<Comments
assessing={assessing}
assessment={assessment}
footer={isSummary ? pointsElement() : null}
savedComments={savedComments}
setSaveLater={(saveCommentsForLater) => onAssessmentChange({ saveCommentsForLater })}
setComments={(comments) => onAssessmentChange({ comments })}
/>
)
const ratings = freeForm ? commentRating : (
<Ratings
assessing={assessing}
customRatings={customRatings}
footer={isSummary ? pointsFooter() : null}
tiers={criterion.ratings}
onPointChange={onPointChange}
points={_.get(assessment, 'points')}
useRange={useRange}
pointsPossible={pointsPossible}
defaultMasteryThreshold={isOutcome ? criterion.mastery_points : criterion.points}
customRatings={customRatings}
isSummary={isSummary}
useRange={useRange}
/>
)
@ -181,7 +207,6 @@ export default class Criterion extends React.Component {
) : null
const noComments = (_.get(assessment, 'comments') || '').length > 0
const pointsPossible = criterion.points
const longDescription = criterion.long_description
const threshold = criterion.mastery_points
@ -213,7 +238,7 @@ export default class Criterion extends React.Component {
(freeForm || assessing || noComments) ? null : (
<div className="assessment-comments">
<Text size="x-small" weight="normal">{I18n.t('Instructor Comments')}</Text>
<CommentText assessment={assessment} weight="light" />
{pointsComment()}
</div>
)
}
@ -221,30 +246,31 @@ export default class Criterion extends React.Component {
<td className="ratings">
{ratings}
</td>
<td>
<Points
assessing={assessing}
assessment={assessment}
onPointChange={onPointChange}
pointsPossible={pointsPossible}
/>
{assessing && !freeForm ? commentInput : null}
</td>
{
!isSummary ? (
<td>
{pointsElement()}
{assessing && !freeForm ? commentInput : null}
</td>
) : null
}
</tr>
)
}
}
Criterion.propTypes = {
assessment: PropTypes.shape(assessmentShape),
customRatings: PropTypes.arrayOf(PropTypes.object),
criterion: PropTypes.shape(criterionShape).isRequired,
freeForm: PropTypes.bool.isRequired,
onAssessmentChange: PropTypes.func,
savedComments: PropTypes.arrayOf(PropTypes.string),
customRatings: PropTypes.arrayOf(PropTypes.object)
isSummary: PropTypes.bool
}
Criterion.defaultProps = {
assessment: null,
customRatings: [],
onAssessmentChange: null,
savedComments: [],
customRatings: []
isSummary: false
}

View File

@ -27,7 +27,7 @@ import { assessmentShape } from './types'
export const roundIfWhole = (n) => (
I18n.toNumber(n, { precision: Math.floor(n) === n ? 0 : 1 })
)
const pointString = (n) => n !== null ? roundIfWhole(n) : ''
const pointString = (n) => n !== undefined ? roundIfWhole(n) : '--'
export const possibleString = (possible) =>
I18n.t('%{possible} pts', {
@ -36,13 +36,13 @@ export const possibleString = (possible) =>
export const scoreString = (points, possible) =>
I18n.t('%{points} / %{possible}', {
points: roundIfWhole(points),
points: pointString(points),
possible: possibleString(possible)
})
const invalid = () => [{ text: I18n.t('Invalid value'), type: 'error' }]
const messages = (points, pointsText) =>
(points === null && pointsText) ? invalid() : undefined
(_.isNil(points) && pointsText) ? invalid() : undefined
const Points = (props) => {
const {
@ -68,6 +68,7 @@ const Points = (props) => {
</div>
)
} else {
const usePointsText = pointsText !== null && pointsText !== undefined
return (
<div className="container graded-points">
<TextInput
@ -75,7 +76,7 @@ const Points = (props) => {
label={<ScreenReaderContent>{I18n.t('Points')}</ScreenReaderContent>}
messages={messages(points, pointsText)}
onChange={(e) => onPointChange(e.target.value)}
value={pointsText || pointString(points)}
value={usePointsText ? pointsText : pointString(points)}
width="4rem"
/> {`/ ${possibleString(pointsPossible)}`}
</div>

View File

@ -42,17 +42,27 @@ const pointString = (points, endOfRangePoints) => {
export const Rating = (props) => {
const {
assessing,
classes,
description,
endOfRangePoints,
footer,
long_description,
points,
onClick,
endOfRangePoints,
classes,
isSummary,
tierColor
} = props
const shaderStyle = {backgroundColor: tierColor}
const triangleStyle = {borderBottomColor: tierColor}
const shaderStyle = { backgroundColor: tierColor }
const triangleStyle = { borderBottomColor: tierColor }
const ratingPoints = () => (
<div className="rating-points">
<Text size="x-small">
{pointString(points, endOfRangePoints)}
</Text>
</div>
)
return (
<div
@ -62,11 +72,7 @@ export const Rating = (props) => {
role="button"
tabIndex={assessing ? 0 : null}
>
<div className="rating-points">
<Text size="x-small">
{pointString(points, endOfRangePoints)}
</Text>
</div>
{isSummary ? null : ratingPoints()}
<div className="rating-description">
<Text size="x-small" lineHeight="condensed">
{description}
@ -75,6 +81,13 @@ export const Rating = (props) => {
<Text size="x-small" fontStyle="italic" lineHeight="condensed">
{long_description}
</Text>
{
footer !== null ? (
<div className="rating-footer">
{footer}
</div>
) : null
}
<div className='shader' style={shaderStyle}>
<div className="triangle" style={triangleStyle}/>
</div>
@ -105,9 +118,12 @@ const getCustomColor = (points, customRatings) => {
Rating.propTypes = {
...tierShape,
assessing: PropTypes.bool.isRequired,
selected: PropTypes.bool
footer: PropTypes.node,
selected: PropTypes.bool,
isSummary: PropTypes.bool.isRequired
}
Rating.defaultProps = {
footer: null,
selected: false,
endOfRangePoints: null // eslint-disable-line react/default-props-match-prop-types
}
@ -115,12 +131,14 @@ Rating.defaultProps = {
const Ratings = (props) => {
const {
assessing,
customRatings,
defaultMasteryThreshold,
footer,
tiers,
points,
onPointChange,
defaultMasteryThreshold,
useRange,
customRatings
isSummary,
useRange
} = props
const pairs = tiers.map((tier, index) => {
@ -154,38 +172,55 @@ const Ratings = (props) => {
}
const selectedIndex = points !== undefined ? currentIndex() : null
const ratings = tiers.map((tier, index) => {
const selected = selectedIndex === index
if (isSummary && !selected) return null
const classes = classNames({
'rating-tier': true,
'selected': selected,
})
return (
<Rating
key={index} // eslint-disable-line react/no-array-index-key
assessing={assessing}
classes={classes}
endOfRangePoints={useRange ? getRangePoints(tier.points, tiers[index + 1]) : null}
footer={footer}
isSummary={isSummary}
onClick={() => onPointChange(tier.points)}
tierColor={getTierColor(selected)}
{...tier}
/>
)
}).filter((v) => v !== null)
const defaultRating = () => (
<Rating
key={0}
classes="rating-tier"
description={I18n.t('No details')}
footer={footer}
points={0}
isSummary
/>
)
return (
<div className={classNames("rating-tier-list", { 'react-assessing': assessing })}>
{
tiers.map((tier, index) => {
const selected = selectedIndex === index
const classes = classNames({
'rating-tier': true,
'selected': selected,
})
return (
<Rating
key={index} // eslint-disable-line react/no-array-index-key
assessing={assessing}
onClick={() => onPointChange(tier.points)}
classes={classes}
endOfRangePoints={useRange ? getRangePoints(tier.points, tiers[index + 1]) : null}
tierColor={getTierColor(selected)}
{...tier}
/>
)
})
}
{ratings.length > 0 || !isSummary ? ratings : defaultRating()}
</div>
)
}
Ratings.propTypes = {
...ratingShape,
assessing: PropTypes.bool.isRequired,
onPointChange: PropTypes.func
footer: PropTypes.node,
onPointChange: PropTypes.func,
isSummary: PropTypes.bool.isRequired
}
Ratings.defaultProps = {
footer: null,
onPointChange: () => { }
}

View File

@ -37,7 +37,15 @@ const totalAssessingString = (score, possible) =>
possible: I18n.toNumber(possible, { precision: 1 })
})
const Rubric = ({ onAssessmentChange, rubric, rubricAssessment, rubricAssociation, customRatings }) => {
const Rubric = (props) => {
const {
customRatings,
onAssessmentChange,
rubric,
rubricAssessment,
rubricAssociation,
isSummary
} = props
const assessing = onAssessmentChange !== null
const priorData = _.get(rubricAssessment, 'data', [])
const byCriteria = _.keyBy(priorData, (ra) => ra.criterion_id)
@ -59,10 +67,11 @@ const Rubric = ({ onAssessmentChange, rubric, rubricAssessment, rubricAssociatio
key={criterion.id}
assessment={assessment}
criterion={criterion}
customRatings={customRatings}
freeForm={rubric.free_form_criterion_comments}
isSummary={isSummary}
onAssessmentChange={assessing ? onCriteriaChange(criterion.id) : undefined}
savedComments={allComments[criterion.id]}
customRatings={customRatings}
/>
)
})
@ -80,7 +89,11 @@ const Rubric = ({ onAssessmentChange, rubric, rubricAssessment, rubricAssociatio
<tr>
<th scope="col">{I18n.t('Criteria')}</th>
<th scope="col">{I18n.t('Ratings')}</th>
<th scope="col">{I18n.t('Pts')}</th>
{
isSummary ? null : (
<th scope="col">{I18n.t('Pts')}</th>
)
}
</tr>
</thead>
<tbody className="criterions">
@ -100,6 +113,7 @@ const Rubric = ({ onAssessmentChange, rubric, rubricAssessment, rubricAssociatio
)
}
Rubric.propTypes = {
customRatings: PropTypes.arrayOf(PropTypes.object),
onAssessmentChange: PropTypes.func,
rubric: PropTypes.shape(rubricShape).isRequired,
rubricAssessment: (props) => {
@ -108,13 +122,14 @@ Rubric.propTypes = {
return PropTypes.checkPropTypes({ rubricAssessment }, props, 'prop', 'Rubric')
},
rubricAssociation: PropTypes.shape(rubricAssociationShape),
customRatings: PropTypes.arrayOf(PropTypes.object)
isSummary: PropTypes.bool
}
Rubric.defaultProps = {
customRatings: [],
onAssessmentChange: null,
rubricAssessment: null,
rubricAssociation: {},
customRatings: []
isSummary: false
}
export default Rubric

View File

@ -74,4 +74,10 @@ describe('The Comments component', () => {
expect(setSaveLater.args).toEqual([[true]])
})
it('renders a footer after the comment when provided', () => {
const el = component({ assessing: false, footer: <div>this is a footer</div> })
expect(el.shallow().debug()).toMatchSnapshot()
})
})

View File

@ -95,4 +95,17 @@ describe('Criterion', () => {
dialog.prop('close')()
expectState(false)
})
it('does not have a points column in summary mode', () => {
const el = shallow(
<Criterion
assessment={assessments.points.data[1]}
criterion={rubrics.points.criteria[1]}
freeForm={false}
isSummary
/>
)
expect(el.find('td')).toHaveLength(1)
})
})

View File

@ -40,7 +40,7 @@ describe('The Points component', () => {
it('renders blank when points are undefined', () => {
expect(component({
assessing: true,
assessment: id,
assessment: { ...id, pointsText: '' },
pointsPossible: 2
}).debug()).toMatchSnapshot()
})
@ -57,7 +57,7 @@ describe('The Points component', () => {
assessing: true,
assessment: {
...id,
points: points === undefined ? null : points,
points,
pointsText,
},
pointsPossible: 2

View File

@ -23,13 +23,15 @@ import Ratings, { Rating } from '../Ratings'
describe('The Ratings component', () => {
const props = {
assessing: false,
footer: null,
tiers: [
{ description: 'Superb', points: 10 },
{ description: 'Meh', long_description: 'More Verbosity', points: 5 },
{ description: 'Subpar', points: 1 }
],
points: 5,
defaultMasteryThreshold: 10,
points: 5,
isSummary: false,
useRange: false
}
@ -89,23 +91,41 @@ describe('The Ratings component', () => {
expect(ratings(0, true)).toEqual(['transparent', 'transparent', '#F8971C'])
})
describe('Rating component', () => {
it('is navigable and clickable when assessing', () => {
const onClick = sinon.spy()
const wrapper = shallow(<Rating {...props.tiers[0]} assessing onClick={onClick} />)
const div = wrapper.find('div').at(0)
expect(div.prop('tabIndex')).toEqual(0)
div.simulate('click')
expect(onClick.called).toBe(true)
})
it('is navigable and clickable when assessing', () => {
const onClick = sinon.spy()
const wrapper = shallow(<Rating {...props.tiers[0]} assessing onClick={onClick} />)
const div = wrapper.find('div').at(0)
expect(div.prop('tabIndex')).toEqual(0)
div.simulate('click')
expect(onClick.called).toBe(true)
})
it('is not navigable or clickable when not assessing', () => {
const onClick = sinon.spy()
const wrapper = shallow(<Rating {...props.tiers[0]} assessing={false} onClick={onClick} />)
const div = wrapper.find('div').at(0)
expect(div.prop('tabIndex')).toBeNull()
div.simulate('click')
expect(onClick.called).toBe(false)
})
it('is not navigable or clickable when not assessing', () => {
const onClick = sinon.spy()
const wrapper = shallow(<Rating {...props.tiers[0]} assessing={false} onClick={onClick} />)
const div = wrapper.find('div').at(0)
expect(div.prop('tabIndex')).toBeNull()
div.simulate('click')
expect(onClick.called).toBe(false)
})
it('only renders the single selected Rating with a footer in summary mode', () => {
const el = component({ points: 5, isSummary: true, footer: <div>ow my foot</div> })
const ratings = el.find('Rating')
expect(ratings).toHaveLength(1)
const rating = ratings.at(0)
expect(rating.shallow().debug()).toMatchSnapshot()
})
it('renders a default rating if none of the ratings are selected', () => {
const el = component({ points: 6, isSummary: true, footer: <div>ow my foot</div> })
const ratings = el.find('Rating')
expect(ratings).toHaveLength(1)
const rating = ratings.at(0)
expect(rating.shallow().debug()).toMatchSnapshot()
})
})

View File

@ -82,4 +82,17 @@ describe('the Rubric component', () => {
expect(renderAssessing(onAssessmentChange.args[0][0]).debug()).toMatchSnapshot()
})
it('does not have a points column in summary mode', () => {
const el = shallow(
<Rubric
rubric={rubric}
rubricAssessment={pointsAssessment}
rubricAssociation={pointsAssessment.rubric_association}
isSummary
/>
)
expect(el.find('th')).toHaveLength(2)
})
})

View File

@ -2,6 +2,15 @@
exports[`The Comments component directly renders comments_html 1`] = `"<div>I award you <b>no</b> points, and may God have mercy on your soul.</div>"`;
exports[`The Comments component renders a footer after the comment when provided 1`] = `
"<div className=\\"rubric-freeform\\">
<CommentText assessment={{...}} placeholder=\\"This area will be used by the assessor to leave comments related to this criterion.\\" weight=\\"normal\\" />
<div>
this is a footer
</div>
</div>"
`;
exports[`The Comments component renders a placeholder when no assessment provided 1`] = `
"<Text size=\\"x-small\\" weight=\\"normal\\" as=\\"span\\" letterSpacing=\\"normal\\">
This area will be used by the assessor to leave comments related to this criterion.

View File

@ -33,7 +33,7 @@ exports[`Free-form Rubric with a custom criterion by default renders the root co
</div>
</th>
<td className=\\"ratings\\">
<Comments assessing={false} assessment={{...}} savedComments={{...}} setSaveLater={[Function]} setComments={[Function]} />
<Comments assessing={false} assessment={{...}} footer={{...}} savedComments={{...}} setSaveLater={[Function]} setComments={[Function]} />
</td>
<td>
<Points assessing={false} assessment={{...}} onPointChange={{...}} pointsPossible={8} />
@ -72,7 +72,7 @@ exports[`Free-form Rubric with a custom criterion when assessing renders the roo
</div>
</th>
<td className=\\"ratings\\">
<Comments assessing={true} assessment={{...}} savedComments={{...}} setSaveLater={[Function]} setComments={[Function]} />
<Comments assessing={true} assessment={{...}} footer={{...}} savedComments={{...}} setSaveLater={[Function]} setComments={[Function]} />
</td>
<td>
<Points assessing={true} assessment={{...}} onPointChange={[Function]} pointsPossible={8} />
@ -111,7 +111,7 @@ exports[`Free-form Rubric with a custom criterion without an assessment renders
</div>
</th>
<td className=\\"ratings\\">
<Comments assessing={false} assessment={{...}} savedComments={{...}} setSaveLater={[Function]} setComments={[Function]} />
<Comments assessing={false} assessment={{...}} footer={{...}} savedComments={{...}} setSaveLater={[Function]} setComments={[Function]} />
</td>
<td>
<Points assessing={false} assessment={{...}} onPointChange={{...}} pointsPossible={8} />
@ -177,7 +177,7 @@ exports[`Free-form Rubric with a outcome criterion by default renders the root c
<Threshold threshold={3} />
</th>
<td className=\\"ratings\\">
<Comments assessing={false} assessment={{...}} savedComments={{...}} setSaveLater={[Function]} setComments={[Function]} />
<Comments assessing={false} assessment={{...}} footer={{...}} savedComments={{...}} setSaveLater={[Function]} setComments={[Function]} />
</td>
<td>
<Points assessing={false} assessment={{...}} onPointChange={{...}} pointsPossible={3} />
@ -243,7 +243,7 @@ exports[`Free-form Rubric with a outcome criterion when assessing renders the ro
<Threshold threshold={3} />
</th>
<td className=\\"ratings\\">
<Comments assessing={true} assessment={{...}} savedComments={{...}} setSaveLater={[Function]} setComments={[Function]} />
<Comments assessing={true} assessment={{...}} footer={{...}} savedComments={{...}} setSaveLater={[Function]} setComments={[Function]} />
</td>
<td>
<Points assessing={true} assessment={{...}} onPointChange={[Function]} pointsPossible={3} />
@ -309,7 +309,7 @@ exports[`Free-form Rubric with a outcome criterion without an assessment renders
<Threshold threshold={3} />
</th>
<td className=\\"ratings\\">
<Comments assessing={false} assessment={{...}} savedComments={{...}} setSaveLater={[Function]} setComments={[Function]} />
<Comments assessing={false} assessment={{...}} footer={{...}} savedComments={{...}} setSaveLater={[Function]} setComments={[Function]} />
</td>
<td>
<Points assessing={false} assessment={{...}} onPointChange={{...}} pointsPossible={3} />
@ -348,7 +348,7 @@ exports[`Point Rubric with a custom criterion by default renders the root compon
</div>
</th>
<td className=\\"ratings\\">
<Ratings assessing={false} tiers={{...}} onPointChange={[Function]} points={3.2} useRange={false} defaultMasteryThreshold={8} customRatings={{...}} />
<Ratings assessing={false} customRatings={{...}} footer={{...}} tiers={{...}} onPointChange={[Function]} points={3.2} pointsPossible={8} defaultMasteryThreshold={8} isSummary={false} useRange={false} />
</td>
<td>
<Points assessing={false} assessment={{...}} onPointChange={{...}} pointsPossible={8} />
@ -387,7 +387,7 @@ exports[`Point Rubric with a custom criterion when assessing renders the root co
</div>
</th>
<td className=\\"ratings\\">
<Ratings assessing={true} tiers={{...}} onPointChange={[Function]} points={3.2} useRange={false} defaultMasteryThreshold={8} customRatings={{...}} />
<Ratings assessing={true} customRatings={{...}} footer={{...}} tiers={{...}} onPointChange={[Function]} points={3.2} pointsPossible={8} defaultMasteryThreshold={8} isSummary={false} useRange={false} />
</td>
<td>
<Points assessing={true} assessment={{...}} onPointChange={[Function]} pointsPossible={8} />
@ -433,7 +433,7 @@ exports[`Point Rubric with a custom criterion without an assessment renders the
</div>
</th>
<td className=\\"ratings\\">
<Ratings assessing={false} tiers={{...}} onPointChange={[Function]} points={[undefined]} useRange={false} defaultMasteryThreshold={8} customRatings={{...}} />
<Ratings assessing={false} customRatings={{...}} footer={{...}} tiers={{...}} onPointChange={[Function]} points={[undefined]} pointsPossible={8} defaultMasteryThreshold={8} isSummary={false} useRange={false} />
</td>
<td>
<Points assessing={false} assessment={{...}} onPointChange={{...}} pointsPossible={8} />
@ -499,7 +499,7 @@ exports[`Point Rubric with a outcome criterion by default renders the root compo
<Threshold threshold={3} />
</th>
<td className=\\"ratings\\">
<Ratings assessing={false} tiers={{...}} onPointChange={[Function]} points={3} useRange={false} defaultMasteryThreshold={3} customRatings={{...}} />
<Ratings assessing={false} customRatings={{...}} footer={{...}} tiers={{...}} onPointChange={[Function]} points={3} pointsPossible={3} defaultMasteryThreshold={3} isSummary={false} useRange={false} />
</td>
<td>
<Points assessing={false} assessment={{...}} onPointChange={{...}} pointsPossible={3} />
@ -565,7 +565,7 @@ exports[`Point Rubric with a outcome criterion when assessing renders the root c
<Threshold threshold={3} />
</th>
<td className=\\"ratings\\">
<Ratings assessing={true} tiers={{...}} onPointChange={[Function]} points={3} useRange={false} defaultMasteryThreshold={3} customRatings={{...}} />
<Ratings assessing={true} customRatings={{...}} footer={{...}} tiers={{...}} onPointChange={[Function]} points={3} pointsPossible={3} defaultMasteryThreshold={3} isSummary={false} useRange={false} />
</td>
<td>
<Points assessing={true} assessment={{...}} onPointChange={[Function]} pointsPossible={3} />
@ -638,7 +638,7 @@ exports[`Point Rubric with a outcome criterion without an assessment renders the
</div>
</th>
<td className=\\"ratings\\">
<Ratings assessing={false} tiers={{...}} onPointChange={[Function]} points={[undefined]} useRange={false} defaultMasteryThreshold={3} customRatings={{...}} />
<Ratings assessing={false} customRatings={{...}} footer={{...}} tiers={{...}} onPointChange={[Function]} points={[undefined]} pointsPossible={3} defaultMasteryThreshold={3} isSummary={false} useRange={false} />
</td>
<td>
<Points assessing={false} assessment={{...}} onPointChange={{...}} pointsPossible={3} />

View File

@ -1,5 +1,45 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`The Ratings component only renders the single selected Rating with a footer in summary mode 1`] = `
"<div className=\\"rating-tier selected\\" onClick={{...}} onKeyPress={[Function]} role=\\"button\\" tabIndex={{...}}>
<div className=\\"rating-description\\">
<Text size=\\"x-small\\" lineHeight=\\"condensed\\" as=\\"span\\" letterSpacing=\\"normal\\">
Meh
</Text>
</div>
<Text size=\\"x-small\\" fontStyle=\\"italic\\" lineHeight=\\"condensed\\" as=\\"span\\" letterSpacing=\\"normal\\">
More Verbosity
</Text>
<div className=\\"rating-footer\\">
<div>
ow my foot
</div>
</div>
<div className=\\"shader\\" style={{...}}>
<div className=\\"triangle\\" style={{...}} />
</div>
</div>"
`;
exports[`The Ratings component renders a default rating if none of the ratings are selected 1`] = `
"<div className=\\"rating-tier\\" onClick={{...}} onKeyPress={[Function]} role=\\"button\\" tabIndex={{...}}>
<div className=\\"rating-description\\">
<Text size=\\"x-small\\" lineHeight=\\"condensed\\" as=\\"span\\" letterSpacing=\\"normal\\">
No details
</Text>
</div>
<Text size=\\"x-small\\" fontStyle=\\"italic\\" lineHeight=\\"condensed\\" as=\\"span\\" letterSpacing=\\"normal\\" />
<div className=\\"rating-footer\\">
<div>
ow my foot
</div>
</div>
<div className=\\"shader\\" style={{...}}>
<div className=\\"triangle\\" style={{...}} />
</div>
</div>"
`;
exports[`The Ratings component renders the Rating sub-components as expected when range rating enabled 1`] = `
"<div className=\\"rating-tier\\" onClick={{...}} onKeyPress={[Function]} role=\\"button\\" tabIndex={{...}}>
<div className=\\"rating-points\\">
@ -61,8 +101,8 @@ exports[`The Ratings component renders the Rating sub-components as expected whe
exports[`The Ratings component renders the root component as expected 1`] = `
"<div className=\\"rating-tier-list\\">
<Rating assessing={false} onClick={[Function]} classes=\\"rating-tier\\" endOfRangePoints={{...}} tierColor=\\"transparent\\" description=\\"Superb\\" points={10} selected={false} />
<Rating assessing={false} onClick={[Function]} classes=\\"rating-tier selected\\" endOfRangePoints={{...}} tierColor=\\"#e0d773\\" description=\\"Meh\\" long_description=\\"More Verbosity\\" points={5} selected={false} />
<Rating assessing={false} onClick={[Function]} classes=\\"rating-tier\\" endOfRangePoints={{...}} tierColor=\\"transparent\\" description=\\"Subpar\\" points={1} selected={false} />
<Rating assessing={false} classes=\\"rating-tier\\" endOfRangePoints={{...}} footer={{...}} isSummary={false} onClick={[Function]} tierColor=\\"transparent\\" description=\\"Superb\\" points={10} selected={false} />
<Rating assessing={false} classes=\\"rating-tier selected\\" endOfRangePoints={{...}} footer={{...}} isSummary={false} onClick={[Function]} tierColor=\\"#e0d773\\" description=\\"Meh\\" long_description=\\"More Verbosity\\" points={5} selected={false} />
<Rating assessing={false} classes=\\"rating-tier\\" endOfRangePoints={{...}} footer={{...}} isSummary={false} onClick={[Function]} tierColor=\\"transparent\\" description=\\"Subpar\\" points={1} selected={false} />
</div>"
`;

View File

@ -17,8 +17,8 @@ exports[`the Rubric component hides the score total when needed 1`] = `
</tr>
</thead>
<tbody className=\\"criterions\\">
<Criterion assessment={{...}} criterion={{...}} freeForm={false} onAssessmentChange={{...}} savedComments={{...}} customRatings={{...}} />
<Criterion assessment={{...}} criterion={{...}} freeForm={false} onAssessmentChange={{...}} savedComments={{...}} customRatings={{...}} />
<Criterion assessment={{...}} criterion={{...}} customRatings={{...}} freeForm={false} isSummary={false} onAssessmentChange={{...}} savedComments={{...}} />
<Criterion assessment={{...}} criterion={{...}} customRatings={{...}} freeForm={false} isSummary={false} onAssessmentChange={{...}} savedComments={{...}} />
<tr>
<td colSpan=\\"3\\">
<Flex justifyItems=\\"end\\" as=\\"span\\" direction=\\"row\\" alignItems=\\"center\\" inline={false} visualDebug={false} wrapItems={false}>
@ -48,8 +48,8 @@ exports[`the Rubric component renders as expected 1`] = `
</tr>
</thead>
<tbody className=\\"criterions\\">
<Criterion assessment={{...}} criterion={{...}} freeForm={false} onAssessmentChange={{...}} savedComments={{...}} customRatings={{...}} />
<Criterion assessment={{...}} criterion={{...}} freeForm={false} onAssessmentChange={{...}} savedComments={{...}} customRatings={{...}} />
<Criterion assessment={{...}} criterion={{...}} customRatings={{...}} freeForm={false} isSummary={false} onAssessmentChange={{...}} savedComments={{...}} />
<Criterion assessment={{...}} criterion={{...}} customRatings={{...}} freeForm={false} isSummary={false} onAssessmentChange={{...}} savedComments={{...}} />
<tr>
<td colSpan=\\"3\\">
<Flex justifyItems=\\"end\\" as=\\"span\\" direction=\\"row\\" alignItems=\\"center\\" inline={false} visualDebug={false} wrapItems={false}>
@ -81,8 +81,8 @@ exports[`the Rubric component renders properly with no assessment 1`] = `
</tr>
</thead>
<tbody className=\\"criterions\\">
<Criterion assessment={{...}} criterion={{...}} freeForm={false} onAssessmentChange={{...}} savedComments={{...}} customRatings={{...}} />
<Criterion assessment={{...}} criterion={{...}} freeForm={false} onAssessmentChange={{...}} savedComments={{...}} customRatings={{...}} />
<Criterion assessment={{...}} criterion={{...}} customRatings={{...}} freeForm={false} isSummary={false} onAssessmentChange={{...}} savedComments={{...}} />
<Criterion assessment={{...}} criterion={{...}} customRatings={{...}} freeForm={false} isSummary={false} onAssessmentChange={{...}} savedComments={{...}} />
<tr>
<td colSpan=\\"3\\">
<Flex justifyItems=\\"end\\" as=\\"span\\" direction=\\"row\\" alignItems=\\"center\\" inline={false} visualDebug={false} wrapItems={false}>
@ -114,8 +114,8 @@ exports[`the Rubric component updates the total score when an individual criteri
</tr>
</thead>
<tbody className=\\"criterions\\">
<Criterion assessment={{...}} criterion={{...}} freeForm={false} onAssessmentChange={[Function]} savedComments={{...}} customRatings={{...}} />
<Criterion assessment={{...}} criterion={{...}} freeForm={false} onAssessmentChange={[Function]} savedComments={{...}} customRatings={{...}} />
<Criterion assessment={{...}} criterion={{...}} customRatings={{...}} freeForm={false} isSummary={false} onAssessmentChange={[Function]} savedComments={{...}} />
<Criterion assessment={{...}} criterion={{...}} customRatings={{...}} freeForm={false} isSummary={false} onAssessmentChange={[Function]} savedComments={{...}} />
<tr>
<td colSpan=\\"3\\">
<Flex justifyItems=\\"end\\" as=\\"span\\" direction=\\"row\\" alignItems=\\"center\\" inline={false} visualDebug={false} wrapItems={false}>

View File

@ -109,6 +109,10 @@
position: relative;
}
.react-rubric .rating-tier .rating-footer {
padding-bottom: 1rem;
}
.react-rubric .rating-tier.selected {
.shader {
width: 100%;
@ -137,4 +141,5 @@
.react-rubric .graded-points {
white-space: nowrap;
text-align: end;
}

View File

@ -276,11 +276,16 @@ window.rubricAssessment = {
},
fillAssessment: function(rubric, partialAssessment) {
const fillText = (c) => ({
pointsText: _.isNil(c.points) && _.isUndefined(c.pointsText) ? '--' : c.pointsText,
...c
})
const defaultCriteria = (id) => ({ criterion_id: id, pointsText: '' })
const prior = _.keyBy(_.cloneDeep(partialAssessment.data), (c) => c.criterion_id)
return {
score: 0,
...partialAssessment,
data: rubric.criteria.map((c) => (prior[c.id] || { criterion_id: c.id, score: null }))
data: rubric.criteria.map((c) => fillText(prior[c.id] || defaultCriteria(c.id)))
}
},
@ -374,6 +379,29 @@ window.rubricAssessment = {
}
},
populateNewRubricSummary: function(container, assessment, rubricAssociation, editData) {
if (ENV.nonScoringRubrics && ENV.rubric) {
if(assessment) {
const filled = rubricAssessment.fillAssessment(ENV.rubric, assessment || {})
ReactDOM.render(React.createElement(Rubric, {
customRatings: ENV.outcome_proficiency ? ENV.outcome_proficiency.ratings : [],
rubric: ENV.rubric,
rubricAssessment: filled,
rubricAssociation,
isSummary: true
}, null), container.get(0))
} else {
container.get(0).innerHTML = ''
}
} else {
rubricAssessment.populateRubricSummary(
container,
assessment,
editData
)
}
},
populateRubricSummary: function($rubricSummary, data, editing_data) {
$rubricSummary.find(".criterion_points").text("").end()
.find(".rating_custom").text("");

View File

@ -837,9 +837,10 @@ function initRubricStuff(){
selectors.get('#rubric_assessments_select').change(() => {
const editingData = rubricAssessment.assessmentData($("#rubric_full"))
var selectedAssessment = getSelectedAssessment();
rubricAssessment.populateRubricSummary(
rubricAssessment.populateNewRubricSummary(
$("#rubric_summary_holder .rubric_summary"),
selectedAssessment,
jsonData.rubric_association,
editingData
);
});