Skip to content

Commit b764038

Browse files
fix: update Progress Bar component
1 parent 0d35aaf commit b764038

File tree

5 files changed

+158
-109
lines changed

5 files changed

+158
-109
lines changed
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
import classNames from 'classnames';
4+
5+
import { StrictDict } from '@edx/react-unit-test-utils';
6+
import { Nav, Icon } from '@edx/paragon';
7+
import {
8+
CheckCircle,
9+
Edit,
10+
Error,
11+
Highlight,
12+
Rule,
13+
} from '@edx/paragon/icons';
14+
15+
import { stepNames } from 'data/services/lms/constants';
16+
import { useProgressStepData } from './hooks';
17+
18+
export const stepIcons = StrictDict({
19+
[stepNames.submisson]: Edit,
20+
[stepNames.studentTraining]: Highlight,
21+
[stepNames.self]: Highlight,
22+
[stepNames.peer]: Highlight,
23+
[stepNames.myGrades]: Rule,
24+
});
25+
26+
const ProgressStep = ({
27+
step,
28+
canRevisit,
29+
label,
30+
}) => {
31+
const {
32+
href,
33+
isActive,
34+
isEnabled,
35+
isComplete,
36+
isPastDue,
37+
myGrade,
38+
} = useProgressStepData({ step, canRevisit });
39+
let iconSrc = stepIcons[step];
40+
let subLabel = null;
41+
let colorClass = null;
42+
if (isPastDue) {
43+
colorClass = 'text-danger-500';
44+
iconSrc = Error;
45+
subLabel = 'Past due!';
46+
} else if (isComplete) {
47+
iconSrc = CheckCircle;
48+
if (step === stepNames.myGrades) {
49+
subLabel = `${myGrade.earned} / ${myGrade.possible}`;
50+
}
51+
}
52+
return (
53+
<Nav.Link
54+
href={href}
55+
disabled={!isEnabled}
56+
className={classNames(
57+
'ora-progress-nav',
58+
'px-4',
59+
{ 'is-active': isActive },
60+
)}
61+
>
62+
<Icon className={classNames('nav-icon', colorClass)} src={iconSrc} />
63+
<div className="d-inline-block">
64+
{label}
65+
{subLabel && (
66+
<p className={classNames('x-small', colorClass)}>{subLabel}</p>
67+
)}
68+
</div>
69+
</Nav.Link>
70+
);
71+
};
72+
ProgressStep.propTypes = {
73+
label: PropTypes.node.isRequired,
74+
step: PropTypes.string.isRequired,
75+
canRevisit: PropTypes.bool.isRequired,
76+
};
77+
78+
export default ProgressStep;

src/components/ProgressBar/hooks.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { useParams } from 'react-router-dom';
2+
import { useActiveView, useIsEmbedded } from 'hooks';
3+
import { useStepState, useEffectiveGrade } from 'data/services/lms/hooks/selectors';
4+
import {
5+
routeSteps,
6+
stepRoutes,
7+
stepStates,
8+
} from 'data/services/lms/constants';
9+
10+
export const useProgressStepData = ({ step, canRevisit = false }) => {
11+
const { xblockId } = useParams();
12+
const isEmbedded = useIsEmbedded();
13+
const activeView = useActiveView();
14+
const viewStep = routeSteps[activeView];
15+
const stepState = useStepState({ step });
16+
17+
const href = `/${stepRoutes[step]}${isEmbedded ? '/embedded' : ''}/${xblockId}`;
18+
const isEnabled = (
19+
(stepState === stepStates.inProgress)
20+
|| (canRevisit && stepState === stepStates.completed)
21+
);
22+
const myGrade = useEffectiveGrade();
23+
24+
return {
25+
href,
26+
isEnabled,
27+
isActive: viewStep === step,
28+
isComplete: stepState === stepStates.completed,
29+
inProgress: stepState === stepStates.inProgress,
30+
isPastDue: stepState === stepStates.closed,
31+
myGrade,
32+
/*
33+
myGrade: { earned: 8, possible: 10 },
34+
isPastDue: step === 'self',
35+
*/
36+
};
37+
};
38+
39+
export default { useProgressStepData };

src/components/ProgressBar/index.jsx

Lines changed: 36 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -1,135 +1,64 @@
11
import React from 'react';
2-
import PropTypes from 'prop-types';
3-
import { useLocation } from 'react-router-dom';
4-
import classNames from 'classnames';
52

63
import { useIntl } from '@edx/frontend-platform/i18n';
7-
import { Navbar, Nav, Icon } from '@edx/paragon';
8-
import { Locked, CheckCircle, Error } from '@edx/paragon/icons';
4+
import { Navbar } from '@edx/paragon';
95

106
import {
11-
useAssessmentStepConfig,
12-
useProgressData,
7+
useAssessmentStepOrder,
138
useIsPageDataLoaded,
149
} from 'data/services/lms/hooks/selectors';
10+
import { stepNames } from 'data/services/lms/constants';
11+
12+
import ProgressStep from './ProgressStep';
1513

1614
import messages from './messages';
1715
import './index.scss';
1816

19-
export const ProgressStep = ({
20-
children,
21-
href,
22-
isActive,
23-
isEnabled,
24-
isComplete,
25-
isError,
26-
number,
27-
}) => {
28-
let icon = <Icon className="nav-icon" src={Locked} />;
29-
if (isComplete) {
30-
icon = <Icon className="nav-icon" src={CheckCircle} />;
31-
} else if (isError) {
32-
icon = <Icon className="nav-icon text-danger-300" src={Error} />;
33-
} else if (number) {
34-
icon = <span className="nav-icon number-icon">{number}</span>;
35-
}
36-
return (
37-
<Nav.Link
38-
href={href}
39-
disabled={!isEnabled}
40-
className={classNames(
41-
'ora-progress-nav',
42-
'px-4',
43-
{ 'is-active': isActive },
44-
)}
45-
>
46-
{icon}
47-
{children}
48-
</Nav.Link>
49-
);
50-
};
51-
ProgressStep.defaultProps = {
52-
href: '#',
53-
isActive: false,
54-
isEnabled: false,
55-
isComplete: false,
56-
isError: false,
57-
number: null,
58-
};
59-
ProgressStep.propTypes = {
60-
children: PropTypes.node.isRequired,
61-
href: PropTypes.string,
62-
isActive: PropTypes.bool,
63-
isEnabled: PropTypes.bool,
64-
isError: PropTypes.bool,
65-
isComplete: PropTypes.bool,
66-
number: PropTypes.number,
17+
export const stepLabels = {
18+
[stepNames.submission]: messages.createSubmission,
19+
[stepNames.peer]: messages.peerAssess,
20+
[stepNames.studentTraining]: messages.studentTraining,
21+
[stepNames.self]: messages.selfAssess,
22+
[stepNames.myGrades]: messages.myGrade,
6723
};
6824

69-
export const SubmissionStep = () => {
70-
const { formatMessage } = useIntl();
71-
return (
72-
<ProgressStep>{formatMessage(messages.createSubmission)}</ProgressStep>
73-
);
25+
export const stepCanRevisit = {
26+
[stepNames.submission]: true,
27+
[stepNames.peer]: true,
28+
[stepNames.studentTraining]: false,
29+
[stepNames.self]: false,
30+
[stepNames.myGrades]: false,
7431
};
7532

76-
export const TrainingStep = () => {
77-
const { formatMessage } = useIntl();
78-
return (
79-
<ProgressStep>{formatMessage(messages.practice)}</ProgressStep>
80-
);
81-
};
82-
83-
export const SelfAssessStep = () => {
84-
const { formatMessage } = useIntl();
85-
return (
86-
<ProgressStep>{formatMessage(messages.selfAssess)}</ProgressStep>
87-
);
88-
};
89-
90-
export const PeerAssessStep = () => {
91-
const { formatMessage } = useIntl();
92-
return (
93-
<ProgressStep>{formatMessage(messages.peerAssess)}</ProgressStep>
94-
);
95-
};
33+
export const ProgressBar = () => {
34+
const isLoaded = useIsPageDataLoaded();
9635

97-
export const MyGradeStep = () => {
36+
const stepOrder = useAssessmentStepOrder();
9837
const { formatMessage } = useIntl();
99-
return (
100-
<ProgressStep>{formatMessage(messages.myGrade)}</ProgressStep>
101-
);
102-
};
10338

104-
export const ProgressBar = () => {
105-
const stepConfig = useAssessmentStepConfig();
106-
const progress = useProgressData();
107-
const isLoaded = useIsPageDataLoaded();
108-
const location = useLocation();
10939
if (!isLoaded) {
11040
return null;
11141
}
112-
console.log({
113-
stepConfig, progress, location,
114-
});
42+
43+
const stepEl = (step) => (
44+
stepLabels[step]
45+
? (
46+
<ProgressStep
47+
step={step}
48+
key={step}
49+
label={formatMessage(stepLabels[step])}
50+
canRevisit={stepCanRevisit[step]}
51+
/>
52+
) : null
53+
);
54+
11555
return (
11656
<Navbar>
11757
<Navbar.Collapse className="ora-progress-nav-group">
11858
<hr className="ora-progress-divider" />
119-
<SubmissionStep />
120-
{stepConfig.order.map(step => {
121-
if (step === 'peer') {
122-
return <PeerAssessStep key="peer" />;
123-
}
124-
if (step === 'training') {
125-
return <TrainingStep key="training" />;
126-
}
127-
if (step === 'self') {
128-
return <SelfAssessStep key="self" />;
129-
}
130-
return null;
131-
})}
132-
<MyGradeStep />
59+
{stepEl(stepNames.submission)}
60+
{stepOrder.map(stepEl)}
61+
{stepEl(stepNames.myGrades)}
13362
</Navbar.Collapse>
13463
</Navbar>
13564
);

src/components/ProgressBar/index.scss

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,7 @@
2323
color: white;
2424
border-radius: 100%;
2525
}
26+
.active {
27+
border-bottom: 2px solid black;
28+
}
2629
}

src/components/ProgressBar/messages.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ const messages = defineMessages({
66
defaultMessage: 'Create your response',
77
description: 'Create response progress indicator',
88
},
9-
practice: {
10-
id: 'ora-grading.ProgressBar.practice',
9+
studentTraining: {
10+
id: 'ora-grading.ProgressBar.studentTraining',
1111
defaultMessage: 'Practice grading',
1212
description: 'Student training step progress indicator',
1313
},

0 commit comments

Comments
 (0)