Skip to content

Commit

Permalink
lk/grade implementation (#67)
Browse files Browse the repository at this point in the history
* feat: implement grade route

* chore: update assessment view for multiple peers

* chore: fix assessment

* chore: upgrade requested change

* chore: add keys to array of nodes

* chore: add toggle function

* chore: add popover text to header

* chore: update assessmentCriterions shape

* chore: parse feedback correctly

* chore: update message

* chore: update logic for final steps
  • Loading branch information
leangseu-edx authored Oct 30, 2023
1 parent 6bf9d90 commit c0890f9
Show file tree
Hide file tree
Showing 26 changed files with 605 additions and 72 deletions.
3 changes: 3 additions & 0 deletions src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import SelfAssessmentView from 'views/SelfAssessmentView';
import StudentTrainingView from 'views/StudentTrainingView';
import SubmissionView from 'views/SubmissionView';
import XBlockView from 'views/XBlockView';
import GradeView from 'views/GradeView';

import AppContainer from 'components/AppContainer';
import ModalContainer from 'components/ModalContainer';
Expand Down Expand Up @@ -49,6 +50,7 @@ const RouterRoot = () => {
modalRoute(routes.selfAssessmentEmbed, SelfAssessmentView, 'ORA Self Assessment'),
modalRoute(routes.studentTrainingEmbed, StudentTrainingView, 'ORA Student Training'),
modalRoute(routes.submissionEmbed, SubmissionView, 'ORA Submission'),
modalRoute(routes.gradedEmbed, GradeView, 'My Grade'),
<Route path={routes.rootEmbed} element={<ErrorPage message={formatMessage(messages.error404Message)} />} />,
];
const baseRoutes = [
Expand All @@ -57,6 +59,7 @@ const RouterRoot = () => {
modalRoute(routes.selfAssessment, SelfAssessmentView, 'Assess yourself'),
modalRoute(routes.studentTraining, StudentTrainingView, 'Practice grading'),
modalRoute(routes.submission, SubmissionView, 'Your response'),
modalRoute(routes.graded, GradeView, 'My Grade'),
<Route path={routes.root} element={<ErrorPage message={formatMessage(messages.error404Message)} />} />,
];

Expand Down
52 changes: 52 additions & 0 deletions src/components/CollapsibleFeedback/AssessmentCriterion.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import React from 'react';
import PropTypes from 'prop-types';

import { useIntl } from '@edx/frontend-platform/i18n';
import Feedback from './Feedback';
import messages from './messages';
import { useORAConfigData } from 'data/services/lms/hooks/selectors';

const AssessmentCriterion = ({
assessmentCriterions,
overallFeedback,
stepLabel,
}) => {
const { formatMessage } = useIntl();
const { rubricConfig } = useORAConfigData();
return (
<>
{rubricConfig.criteria.map((criterion, i) => {
const assessmentCriterion = assessmentCriterions[i];
const option = criterion.options[assessmentCriterion.selectedOption];
return (
<Feedback
key={criterion.name}
criterionName={criterion.name}
criterionDescription={criterion.description}
selectedOption={option.name}
selectedPoints={option.points}
commentHeader={stepLabel}
commentBody={assessmentCriterion.feedback}
/>
);
})}
<Feedback
criterionName={formatMessage(messages.overallFeedback)}
commentHeader={stepLabel}
commentBody={overallFeedback}
/>
</>
);
};
AssessmentCriterion.defaultProps = {};
AssessmentCriterion.propTypes = {
assessmentCriterions: PropTypes.arrayOf(PropTypes.shape({
selectedOption: PropTypes.number,
// selectedPoints: PropTypes.number,
feedback: PropTypes.string,
})),
overallFeedback: PropTypes.string,
stepLabel: PropTypes.string.isRequired,
};

export default AssessmentCriterion;
40 changes: 40 additions & 0 deletions src/components/CollapsibleFeedback/CollapsibleFeedback.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import React from 'react';
import PropTypes from 'prop-types';

import { Collapsible } from '@edx/paragon';
import { useIntl } from '@edx/frontend-platform/i18n';
import messages from './messages';

const CollapsibleFeedback = ({ children, stepScore, stepLabel, defaultOpen }) => {
const { formatMessage } = useIntl();
const [open, setOpen] = React.useState(defaultOpen);

const toggle = () => setOpen(!open);

return (
<Collapsible
title={
<h3>
{formatMessage(messages.grade, { stepLabel })}
{stepScore && formatMessage(messages.gradePoints, stepScore)}
</h3>
}
open={open}
onToggle={toggle}
>
{children}
</Collapsible>
);
};
CollapsibleFeedback.defaultProps = {};
CollapsibleFeedback.propTypes = {
stepLabel: PropTypes.string.isRequired,
stepScore: PropTypes.shape({
earned: PropTypes.number,
possible: PropTypes.number,
}),
children: PropTypes.node.isRequired,
defaultOpen: PropTypes.bool,
};

export default CollapsibleFeedback;
78 changes: 78 additions & 0 deletions src/components/CollapsibleFeedback/Feedback.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import React from 'react';
import PropTypes from 'prop-types';

import { Collapsible, Icon } from '@edx/paragon';
import { ExpandMore, ExpandLess } from '@edx/paragon/icons';
import { useIntl } from '@edx/frontend-platform/i18n';
import messages from './messages';
import InfoPopover from 'components/InfoPopover';

const Feedback = ({
criterionName,
criterionDescription,
selectedOption,
selectedPoints,
commentHeader,
commentBody,
defaultOpen,
}) => {
const [isExpanded, setIsExpanded] = React.useState(defaultOpen);
const { formatMessage } = useIntl();

const toggle = () => setIsExpanded(!isExpanded);

return (
<>
<div className='mt-2'>
<div className='d-flex justify-content-between align-items-center'>
<h5 className='mb-0'>{criterionName}</h5>
{criterionDescription && (
<InfoPopover onClick={() => {}}>
<p>{criterionDescription}</p>
</InfoPopover>
)}
</div>
{selectedOption && (
<p>
{selectedOption} -- {selectedPoints} points
</p>
)}
</div>
<div className='bg-gray-100 p-3'>
<Collapsible.Advanced open={isExpanded} onToggle={toggle}>
<Collapsible.Trigger className='d-flex justify-content-between'>
<h5 className='mb-0'>{commentHeader} Comment</h5>
{isExpanded ? (
<div className='d-flex mb-0 small'>
<span>{formatMessage(messages.readLess)}</span>
<Icon src={ExpandLess} />
</div>
) : (
<div className='d-flex mb-0 small'>
<span>{formatMessage(messages.readMore)}</span>
<Icon src={ExpandMore} />
</div>
)}
</Collapsible.Trigger>
<Collapsible.Body className='pt-2'>
<p>{commentBody}</p>
</Collapsible.Body>
</Collapsible.Advanced>
</div>
</>
);
};
Feedback.defaultProps = {
defaultOpen: false,
};
Feedback.propTypes = {
defaultOpen: PropTypes.bool,
criterionName: PropTypes.string.isRequired,
criterionDescription: PropTypes.string,
selectedOption: PropTypes.string,
selectedPoints: PropTypes.number,
commentHeader: PropTypes.string.isRequired,
commentBody: PropTypes.string.isRequired,
};

export default Feedback;
48 changes: 48 additions & 0 deletions src/components/CollapsibleFeedback/MultipleAssessmentStep.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import React, { Fragment } from 'react';
import PropTypes from 'prop-types';

import CollapsibleFeedback from './CollapsibleFeedback';
import AssessmentCriterion from './AssessmentCriterion';

const MultipleAssessmentStep = ({
stepLabel,
step,
stepScore,
assessments,
defaultOpen,
}) => (
<div className='my-2' key='peer'>
<CollapsibleFeedback stepLabel={stepLabel} stepScore={stepScore} defaultOpen={defaultOpen}>
{assessments?.map((assessment, index) => (
<Fragment key={index}>
<p className='mb-0'>
{stepLabel} {index + 1}:
</p>
<AssessmentCriterion {...assessment} stepLabel={stepLabel} />
<hr className='my-4' />
</Fragment>
))}
</CollapsibleFeedback>
</div>
);

MultipleAssessmentStep.defaultProps = {
defaultOpen: false,
};
MultipleAssessmentStep.propTypes = {
stepLabel: PropTypes.string.isRequired,
stepScore: PropTypes.shape({
earned: PropTypes.number,
possible: PropTypes.number,
}),
assessments: PropTypes.arrayOf(
PropTypes.shape({
selectedOption: PropTypes.number,
// selectedPoints: PropTypes.number,
feedback: PropTypes.string,
})
),
defaultOpen: PropTypes.bool,
};

export default MultipleAssessmentStep;
40 changes: 40 additions & 0 deletions src/components/CollapsibleFeedback/SingleAssessmentStep.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import React from 'react';
import PropTypes from 'prop-types';

import CollapsibleFeedback from './CollapsibleFeedback';
import AssessmentCriterion from './AssessmentCriterion';

const SingleAssessmentStep = ({
stepLabel,
step,
stepScore,
assessment,
defaultOpen,
}) => (
<CollapsibleFeedback
stepLabel={stepLabel}
stepScore={stepScore}
defaultOpen={defaultOpen}
>
<AssessmentCriterion {...assessment} stepLabel={stepLabel} />
</CollapsibleFeedback>
);

SingleAssessmentStep.defaultProps = {
defaultOpen: false,
};
SingleAssessmentStep.propTypes = {
stepLabel: PropTypes.string.isRequired,
stepScore: PropTypes.shape({
earned: PropTypes.number,
possible: PropTypes.number,
}),
assessment: PropTypes.shape({
selectedOption: PropTypes.number,
// selectedPoints: PropTypes.number,
feedback: PropTypes.string,
}),
defaultOpen: PropTypes.bool,
};

export default SingleAssessmentStep;
5 changes: 5 additions & 0 deletions src/components/CollapsibleFeedback/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export { default as CollapsibleFeedback } from './CollapsibleFeedback';
export { default as MultipleAssessmentStep } from './MultipleAssessmentStep';
export { default as SingleAssessmentStep } from './SingleAssessmentStep';
export { default as AssessmentCriterion } from './AssessmentCriterion';
export { default as Feedback } from './Feedback';
36 changes: 36 additions & 0 deletions src/components/CollapsibleFeedback/messages.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { defineMessages } from '@edx/frontend-platform/i18n';

const messages = defineMessages({
readMore: {
id: 'ora-collapsible-comment.readMore',
defaultMessage: 'Read more',
description: 'Read more button text',
},
readLess: {
id: 'ora-collapsible-comment.readLess',
defaultMessage: 'Read less',
description: 'Read less button text',
},
grade: {
id: 'ora-collapsible-comment.grade',
defaultMessage: '{stepLabel} Grade:',
description: 'Grade button text',
},
gradePoints: {
id: 'ora-collapsible-comment.gradePoints',
defaultMessage: '{earned} / {possible}',
description: 'Grade points button text',
},
notWeightedGradeLabel: {
id: 'ora-collapsible-comment.notWeightedGradeLabel',
defaultMessage: '(Not weighted toward final grade))',
description: 'Not weighted grade label',
},
overallFeedback: {
id: 'ora-collapsible-comment.overallFeedback',
defaultMessage: 'Overall Feedback',
description: 'Overall feedback label',
},
});

export default messages;
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ import './FileCard.scss';
/**
* <FileCard />
*/
const FileCard = ({ file, children }) => (
const FileCard = ({ file, children, defaultOpen }) => (
<Card className="file-card" key={file.fileName}>
<Collapsible
className="file-collapsible"
defaultOpen
defaultOpen={defaultOpen}
title={<h3 className="file-card-title">{file.fileName}</h3>}
>
<div className="preview-panel">
Expand All @@ -24,6 +24,7 @@ const FileCard = ({ file, children }) => (
FileCard.propTypes = {
file: PropTypes.shape({ fileName: PropTypes.string.isRequired }).isRequired,
children: PropTypes.node.isRequired,
defaultOpen: PropTypes.bool.isRequired,
};

export default FileCard;
9 changes: 6 additions & 3 deletions src/components/FilePreview/components/FileRenderer/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { useRenderData } from './hooks';
/**
* <FileRenderer />
*/
export const FileRenderer = ({ file }) => {
export const FileRenderer = ({ file, defaultOpen }) => {
const { formatMessage } = useIntl();
const {
Renderer,
Expand All @@ -21,7 +21,7 @@ export const FileRenderer = ({ file }) => {
} = useRenderData({ file, formatMessage });

return (
<FileCard key={file.fileUrl} file={file}>
<FileCard defaultOpen={defaultOpen} file={file}>
{isLoading && <LoadingBanner />}
{errorStatus ? (
<ErrorBanner {...error} />
Expand All @@ -32,12 +32,15 @@ export const FileRenderer = ({ file }) => {
);
};

FileRenderer.defaultProps = {};
FileRenderer.defaultProps = {
defaultOpen: true,
};
FileRenderer.propTypes = {
file: PropTypes.shape({
fileName: PropTypes.string,
fileUrl: PropTypes.string,
}).isRequired,
defaultOpen: PropTypes.bool,
// injected
// intl: intlShape.isRequired,
};
Expand Down
Loading

0 comments on commit c0890f9

Please sign in to comment.