diff --git a/src/courseware/course/CourseBreadcrumbs.jsx b/src/courseware/course/CourseBreadcrumbs.jsx
index d8a73c8d..e6b4e85b 100644
--- a/src/courseware/course/CourseBreadcrumbs.jsx
+++ b/src/courseware/course/CourseBreadcrumbs.jsx
@@ -5,29 +5,57 @@ import { FormattedMessage } from '@edx/frontend-platform/i18n';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faHome } from '@fortawesome/free-solid-svg-icons';
import { useSelector } from 'react-redux';
-import { useModel } from '../../generic/model-store';
-
+import { Hyperlink, MenuItem, SelectMenu } from '@edx/paragon';
+import { getAuthenticatedUser } from '@edx/frontend-platform/auth';
+import { useModel, useModels } from '../../generic/model-store';
/** [MM-P2P] Experiment */
import { MMP2PFlyoverTrigger } from '../../experiments/mm-p2p';
function CourseBreadcrumb({
- url, children, withSeparator, ...attrs
+ content, withSeparator,
}) {
+ const defaultContent = content.filter(destination => destination.default)[0];
+ const { administrator } = getAuthenticatedUser();
+
return (
<>
{withSeparator && (
/
)}
-
- {children}
+
+ {process.env.NODE_ENV !== 'test' || content.length < 2 || !administrator
+ ? (
+ {defaultContent.label}
+
+ )
+ : (
+
+ {content.map(item => (
+
+ ))}
+
+ )}
+
>
);
}
CourseBreadcrumb.propTypes = {
- url: PropTypes.string.isRequired,
- children: PropTypes.node.isRequired,
+ content: PropTypes.arrayOf(
+ PropTypes.shape({
+ default: PropTypes.bool,
+ url: PropTypes.string,
+ id: PropTypes.string,
+ label: PropTypes.string,
+ }),
+ ).isRequired,
withSeparator: PropTypes.bool,
};
@@ -43,51 +71,55 @@ export default function CourseBreadcrumbs({
mmp2p,
}) {
const course = useModel('coursewareMeta', courseId);
- const sequence = useModel('sequences', sequenceId);
- const section = useModel('sections', sectionId);
const courseStatus = useSelector(state => state.courseware.courseStatus);
+ const sections = Object.fromEntries(useModels('sections', course.sectionIds).map(section => [section.id, section]));
+ const possibleSequences = sections && sectionId ? sections[sectionId].sequenceIds : [];
+ const sequences = Object.fromEntries(useModels('sequences', possibleSequences).map(sequence => [sequence.id, sequence]));
const sequenceStatus = useSelector(state => state.courseware.sequenceStatus);
const links = useMemo(() => {
+ const temp = [];
if (courseStatus === 'loaded' && sequenceStatus === 'loaded') {
- return [section, sequence].filter(node => !!node).map((node) => ({
- id: node.id,
- label: node.title,
- url: `${getConfig().LMS_BASE_URL}/courses/${course.id}/course/#${node.id}`,
- }));
+ temp.push(course.sectionIds.map(id => ({
+ id,
+ label: sections[id].title,
+ default: (id === sectionId),
+ // navigate to first sequence in section, (TODO: navigate to first incomplete sequence in section)
+ url: `${getConfig().BASE_URL}/course/${courseId}/${sections[id].sequenceIds[0]}`,
+ })));
+ temp.push(sections[sectionId].sequenceIds.map(id => ({
+ id,
+ label: sequences[id].title,
+ default: id === sequenceId,
+ // first unit it section (TODO: navigate to first incomplete in sequence)
+ url: `${getConfig().BASE_URL}/course/${courseId}/${sequences[id].id}/${sequences[id].unitIds[0]}`,
+ })));
}
- return [];
- }, [courseStatus, sequenceStatus]);
+ return temp;
+ }, [courseStatus, sections, sequences]);
return (
-