From 2932d989766a984e86cac2bc07b26d1a28cf2623 Mon Sep 17 00:00:00 2001 From: connorhaugh <49422820+connorhaugh@users.noreply.github.com> Date: Tue, 21 Sep 2021 15:39:52 -0400 Subject: [PATCH] feat: breadcrumb rolloutout flag + analytics (#647) As an addendum to https://openedx.atlassian.net/browse/TNL-7107, we want to hide rollout behind a frontend feature flag added in https://github.com/edx/edx-internal/pull/5489. We also want to report these events to the events api with name `edx.ui.lms.jump_nav.selected`. Doummentation to add this event is listed at the following PR: https://github.com/edx/edx-documentation/pull/1982 --- .env | 1 + .env.development | 1 + .env.test | 1 + README.rst | 6 + src/courseware/course/Course.test.jsx | 25 ++++ src/courseware/course/CourseBreadcrumbs.jsx | 39 ++++-- .../course/CourseBreadcrumbs.test.jsx | 117 ++++++++++++++++++ src/index.jsx | 1 + 8 files changed, 181 insertions(+), 10 deletions(-) create mode 100644 src/courseware/course/CourseBreadcrumbs.test.jsx diff --git a/.env b/.env index a8f64ec2..8864ad88 100644 --- a/.env +++ b/.env @@ -37,3 +37,4 @@ TWITTER_HASHTAG='' TWITTER_URL='' USER_INFO_COOKIE_NAME='' SESSION_COOKIE_DOMAIN='' +ENABLE_JUMPNAV='true' diff --git a/.env.development b/.env.development index 3404a922..61164b00 100644 --- a/.env.development +++ b/.env.development @@ -37,3 +37,4 @@ TWITTER_HASHTAG='myedxjourney' TWITTER_URL='https://twitter.com/edXOnline' USER_INFO_COOKIE_NAME='edx-user-info' SESSION_COOKIE_DOMAIN='localhost' +ENABLE_JUMPNAV='true' diff --git a/.env.test b/.env.test index 4a1445bb..3b844f3c 100644 --- a/.env.test +++ b/.env.test @@ -36,3 +36,4 @@ TERMS_OF_SERVICE_URL='https://www.edx.org/edx-terms-service' TWITTER_HASHTAG='myedxjourney' TWITTER_URL='https://twitter.com/edXOnline' USER_INFO_COOKIE_NAME='edx-user-info' +ENABLE_JUMPNAV='true' diff --git a/README.rst b/README.rst index b19b4d00..4af4a220 100644 --- a/README.rst +++ b/README.rst @@ -109,3 +109,9 @@ TWITTER_URL unless this is set. Optional. Example: https://twitter.com/edXOnline + +ENABLE_JUMPNAV + Enables the new Jump Navigation feature in the course breadcrumbs, defaulted to the string 'true'. + Disable to have simple hyperlinks for breadcrumbs. Setting it to any other value but 'true' ('false','I love flags', 'etc' would disable the Jumpnav). + This feature flag is slated to be removed as jumpnav becomes default. Follow the progress of this ticket here: + https://openedx.atlassian.net/browse/TNL-8678 diff --git a/src/courseware/course/Course.test.jsx b/src/courseware/course/Course.test.jsx index ea454d23..26512c86 100644 --- a/src/courseware/course/Course.test.jsx +++ b/src/courseware/course/Course.test.jsx @@ -91,6 +91,31 @@ describe('Course', () => { expect(notificationTrigger).not.toHaveClass('trigger-active'); }); + it('renders course breadcrumbs as expected', async () => { + const courseMetadata = Factory.build('courseMetadata'); + const unitBlocks = Array.from({ length: 3 }).map(() => Factory.build( + 'block', + { type: 'vertical' }, + { courseId: courseMetadata.id }, + )); + const testStore = await initializeTestStore({ courseMetadata, unitBlocks }, false); + const { courseware, models } = testStore.getState(); + const { courseId, sequenceId } = courseware; + const testData = { + ...mockData, + courseId, + sequenceId, + unitId: Object.values(models.units)[1].id, // Corner cases are already covered in `Sequence` tests. + }; + render(, { store: testStore }); + + loadUnit(); + await waitFor(() => expect(screen.queryByText('Loading learning sequence...')).not.toBeInTheDocument()); + // expect the section and sequence "titles" to be loaded in as breadcrumb labels. + expect(screen.getByText('cdabcdabcdabcdabcdabcdabcdabcd13')).toBeInTheDocument(); + expect(screen.getByText('cdabcdabcdabcdabcdabcdabcdabcd12')).toBeInTheDocument(); + }); + it('passes handlers to the sequence', async () => { const nextSequenceHandler = jest.fn(); const previousSequenceHandler = jest.fn(); diff --git a/src/courseware/course/CourseBreadcrumbs.jsx b/src/courseware/course/CourseBreadcrumbs.jsx index e6b4e85b..7b918bb0 100644 --- a/src/courseware/course/CourseBreadcrumbs.jsx +++ b/src/courseware/course/CourseBreadcrumbs.jsx @@ -7,6 +7,10 @@ import { faHome } from '@fortawesome/free-solid-svg-icons'; import { useSelector } from 'react-redux'; import { Hyperlink, MenuItem, 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'; @@ -16,14 +20,31 @@ function CourseBreadcrumb({ }) { const defaultContent = content.filter(destination => destination.default)[0]; const { administrator } = getAuthenticatedUser(); + 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); + } return ( <> {withSeparator && ( -
  • /
  • +
  • /
  • )} -
  • - {process.env.NODE_ENV !== 'test' || content.length < 2 || !administrator + +
  • + { getConfig().ENABLE_JUMPNAV !== 'true' || content.length < 2 || !administrator ? ( {defaultContent.label} @@ -34,7 +55,8 @@ function CourseBreadcrumb({ {item.label} @@ -46,7 +68,6 @@ function CourseBreadcrumb({ ); } - CourseBreadcrumb.propTypes = { content: PropTypes.arrayOf( PropTypes.shape({ @@ -72,7 +93,7 @@ export default function CourseBreadcrumbs({ }) { const course = useModel('coursewareMeta', courseId); const courseStatus = useSelector(state => state.courseware.courseStatus); - const sections = Object.fromEntries(useModels('sections', course.sectionIds).map(section => [section.id, section])); + 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); @@ -97,13 +118,12 @@ export default function CourseBreadcrumbs({ } return temp; }, [courseStatus, sections, sequences]); - return (