From 0889b17e85cad578ed4459ce5f413894f7b76b26 Mon Sep 17 00:00:00 2001
From: connorhaugh <49422820+connorhaugh@users.noreply.github.com>
Date: Wed, 6 Oct 2021 11:14:24 -0400
Subject: [PATCH] fix: course home button error. (#669)
In order to finish off TNL-7107 I needed to meet the acceptance criteria: When learners or educators select a section dropdown item they are taken to the first subsection within that section that is not completed by default. If all subsections are completed they should be taken to the first(subsection) in that section.
This reimagining of Jumpnav does that by lazy loading in the menuItem's destinations and routing the user using React-Router.
---
src/courseware/course/Course.jsx | 1 +
src/courseware/course/CourseBreadcrumbs.jsx | 108 +++++++++---------
.../course/CourseBreadcrumbs.test.jsx | 26 +++--
src/courseware/course/JumpNavMenuItem.jsx | 80 +++++++++++++
.../course/JumpNavMenuItem.test.jsx | 69 +++++++++++
src/courseware/data/thunks.js | 4 +-
6 files changed, 224 insertions(+), 64 deletions(-)
create mode 100644 src/courseware/course/JumpNavMenuItem.jsx
create mode 100644 src/courseware/course/JumpNavMenuItem.test.jsx
diff --git a/src/courseware/course/Course.jsx b/src/courseware/course/Course.jsx
index 748d8a40..7458fe7a 100644
--- a/src/courseware/course/Course.jsx
+++ b/src/courseware/course/Course.jsx
@@ -90,6 +90,7 @@ function Course({
courseId={courseId}
sectionId={section ? section.id : null}
sequenceId={sequenceId}
+ unitId={unitId}
//* * [MM-P2P] Experiment */
mmp2p={MMP2P}
/>
diff --git a/src/courseware/course/CourseBreadcrumbs.jsx b/src/courseware/course/CourseBreadcrumbs.jsx
index 14d76d31..41d4d2f7 100644
--- a/src/courseware/course/CourseBreadcrumbs.jsx
+++ b/src/courseware/course/CourseBreadcrumbs.jsx
@@ -5,32 +5,18 @@ 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 { Hyperlink, MenuItem, SelectMenu } from '@edx/paragon';
+import { SelectMenu } from '@edx/paragon';
import { getAuthenticatedUser } from '@edx/frontend-platform/auth';
-import {
- sendTrackingLogEvent,
- sendTrackEvent,
-} from '@edx/frontend-platform/analytics';
import { useModel, useModels } from '../../generic/model-store';
/** [MM-P2P] Experiment */
import { MMP2PFlyoverTrigger } from '../../experiments/mm-p2p';
+import JumpNavMenuItem from './JumpNavMenuItem';
function CourseBreadcrumb({
- content, withSeparator,
+ content, withSeparator, courseId, unitId,
}) {
- const defaultContent = content.filter(destination => destination.default)[0];
- const administrator = getAuthenticatedUser() ? getAuthenticatedUser().administrator : false;
- function logEvent(target) {
- const eventName = 'edx.ui.lms.jump_nav.selected';
- const payload = {
- target_name: target.label,
- id: target.id,
- current_id: defaultContent.id,
- widget_placement: 'breadcrumb',
- };
- sendTrackEvent(eventName, payload);
- sendTrackingLogEvent(eventName, payload);
- }
+ const defaultContent = content.filter(destination => destination.default)[0] || { id: courseId, label: '' };
+ const { administrator } = getAuthenticatedUser();
return (
<>
@@ -46,20 +32,20 @@ function CourseBreadcrumb({
>
{ getConfig().ENABLE_JUMPNAV !== 'true' || content.length < 2 || !administrator
? (
- {defaultContent.label}
+
+ {defaultContent.label}
)
: (
{content.map(item => (
-
+
))}
)}
@@ -72,58 +58,71 @@ CourseBreadcrumb.propTypes = {
content: PropTypes.arrayOf(
PropTypes.shape({
default: PropTypes.bool,
- url: PropTypes.string,
id: PropTypes.string,
label: PropTypes.string,
}),
).isRequired,
+ unitId: PropTypes.string,
withSeparator: PropTypes.bool,
+ courseId: PropTypes.string,
};
CourseBreadcrumb.defaultProps = {
withSeparator: false,
+ unitId: null,
+ courseId: null,
};
export default function CourseBreadcrumbs({
courseId,
sectionId,
sequenceId,
+ unitId,
/** [MM-P2P] Experiment */
mmp2p,
}) {
const course = useModel('coursewareMeta', courseId);
const courseStatus = useSelector(state => state.courseware.courseStatus);
- const sections = course ? Object.fromEntries(useModels('sections', course.sectionIds).map(section => [section.id, section])) : null;
- 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 allSequencesInSections = Object.fromEntries(useModels('sections', course.sectionIds).map(section => [section.id, {
+ default: section.id === sectionId,
+ title: section.title,
+ sequences: useModels('sequences', section.sequenceIds),
+ }]));
+
const links = useMemo(() => {
- const temp = [];
+ const chapters = [];
+ const sequentials = [];
if (courseStatus === 'loaded' && sequenceStatus === 'loaded') {
- 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]}`,
- })));
+ Object.entries(allSequencesInSections).forEach(([id, section]) => {
+ chapters.push({
+ id,
+ label: section.title,
+ default: section.default,
+ sequences: section.sequences,
+ });
+ if (section.default) {
+ section.sequences.forEach(sequence => {
+ sequentials.push({
+ id: sequence.id,
+ label: sequence.title,
+ default: sequence.id === sequenceId,
+ sequences: [sequence],
+ });
+ });
+ }
+ });
}
- return temp;
- }, [courseStatus, sections, sequences]);
+ return [chapters, sequentials];
+ }, [courseStatus, sequenceStatus, allSequencesInSections]);
+
return (