diff --git a/package-lock.json b/package-lock.json index e66b8d0818..a8bed3d210 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31,7 +31,7 @@ "@fortawesome/free-solid-svg-icons": "5.15.4", "@fortawesome/react-fontawesome": "^0.1.4", "@openedx/frontend-plugin-framework": "^1.0.2", - "@openedx/paragon": "^22.1.1", + "@openedx/paragon": "^22.3.0", "@popperjs/core": "2.11.8", "@reduxjs/toolkit": "1.8.1", "classnames": "2.3.2", @@ -5076,16 +5076,9 @@ } }, "node_modules/@openedx/paragon": { - "version": "22.2.1", - "resolved": "https://registry.npmjs.org/@openedx/paragon/-/paragon-22.2.1.tgz", - "integrity": "sha512-Dd7PzvHwNnUokqbFkuOpugJZ9dHaUBOcYwqAA2aMoN7tgi4xEZWsfDFyP1+se2UPuR7NvNGammEesLAwGQ0Ylw==", - "workspaces": [ - "example", - "component-generator", - "www", - "icons", - "dependent-usage-analyzer" - ], + "version": "22.3.0", + "resolved": "https://registry.npmjs.org/@openedx/paragon/-/paragon-22.3.0.tgz", + "integrity": "sha512-tyPD14nNHfNPUzlbtspiBYFoGtrYa5+ANAVLA5ZXV1Oqunw4Etf8VMTj0DMII+BlZixBpc3gFuVHNbQBNd42Pw==", "dependencies": { "@fortawesome/fontawesome-svg-core": "^6.1.1", "@fortawesome/react-fontawesome": "^0.1.18", diff --git a/package.json b/package.json index c81bd8b345..314f7d0384 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "@fortawesome/free-solid-svg-icons": "5.15.4", "@fortawesome/react-fontawesome": "^0.1.4", "@openedx/frontend-plugin-framework": "^1.0.2", - "@openedx/paragon": "^22.1.1", + "@openedx/paragon": "^22.3.0", "@popperjs/core": "2.11.8", "@reduxjs/toolkit": "1.8.1", "classnames": "2.3.2", diff --git a/src/course-home/data/__snapshots__/redux.test.js.snap b/src/course-home/data/__snapshots__/redux.test.js.snap index c1999d8c08..99605c5176 100644 --- a/src/course-home/data/__snapshots__/redux.test.js.snap +++ b/src/course-home/data/__snapshots__/redux.test.js.snap @@ -14,7 +14,11 @@ Object { }, "courseware": Object { "courseId": null, + "courseOutline": Object {}, + "courseOutlineShouldUpdate": false, + "courseOutlineStatus": "loading", "courseStatus": "loading", + "coursewareOutlineSidebarSettings": Object {}, "sequenceId": null, "sequenceMightBeUnit": false, "sequenceStatus": "loading", @@ -402,7 +406,11 @@ Object { }, "courseware": Object { "courseId": null, + "courseOutline": Object {}, + "courseOutlineShouldUpdate": false, + "courseOutlineStatus": "loading", "courseStatus": "loading", + "coursewareOutlineSidebarSettings": Object {}, "sequenceId": null, "sequenceMightBeUnit": false, "sequenceStatus": "loading", @@ -671,7 +679,11 @@ Object { }, "courseware": Object { "courseId": null, + "courseOutline": Object {}, + "courseOutlineShouldUpdate": false, + "courseOutlineStatus": "loading", "courseStatus": "loading", + "coursewareOutlineSidebarSettings": Object {}, "sequenceId": null, "sequenceMightBeUnit": false, "sequenceStatus": "loading", diff --git a/src/course-home/outline-tab/SequenceLink.jsx b/src/course-home/outline-tab/SequenceLink.jsx index c83e254bc1..41af780c19 100644 --- a/src/course-home/outline-tab/SequenceLink.jsx +++ b/src/course-home/outline-tab/SequenceLink.jsx @@ -1,4 +1,3 @@ -import React from 'react'; import PropTypes from 'prop-types'; import classNames from 'classnames'; import { Link } from 'react-router-dom'; @@ -96,7 +95,7 @@ const SequenceLink = ({ icon={fasCheckCircle} fixedWidth className="float-left text-success mt-1" - aria-hidden="true" + aria-hidden={complete} title={intl.formatMessage(messages.completedAssignment)} /> ) : ( @@ -104,7 +103,7 @@ const SequenceLink = ({ icon={farCheckCircle} fixedWidth className="float-left text-gray-400 mt-1" - aria-hidden="true" + aria-hidden={complete} title={intl.formatMessage(messages.incompleteAssignment)} /> )} @@ -118,14 +117,14 @@ const SequenceLink = ({ {hideFromTOC && ( -
- - - - {intl.formatMessage(messages.hiddenSequenceLink)} +
+ + + + {intl.formatMessage(messages.hiddenSequenceLink)} + - -
+
)}
diff --git a/src/courseware/course/Course.jsx b/src/courseware/course/Course.jsx index 502e9b666d..131db82e09 100644 --- a/src/courseware/course/Course.jsx +++ b/src/courseware/course/Course.jsx @@ -1,24 +1,23 @@ -import React, { useEffect, useState } from 'react'; +import { useEffect, useState } from 'react'; import PropTypes from 'prop-types'; import { Helmet } from 'react-helmet'; -import { useDispatch } from 'react-redux'; +import { useDispatch, useSelector } from 'react-redux'; import { getConfig } from '@edx/frontend-platform'; import { breakpoints, useWindowSize } from '@openedx/paragon'; -import { AlertList } from '../../generic/user-messages'; - -import Sequence from './sequence'; - -import { CelebrationModal, shouldCelebrateOnSectionLoad, WeeklyGoalCelebrationModal } from './celebration'; +import { AlertList } from '@src/generic/user-messages'; +import { useModel } from '@src/generic/model-store'; +import { getCoursewareOutlineSidebarSettings } from '../data/selectors'; +import { Trigger as CourseOutlineTrigger } from './sidebar/sidebars/course-outline'; import Chat from './chat/Chat'; -import ContentTools from './content-tools'; -import CourseBreadcrumbs from './CourseBreadcrumbs'; import SidebarProvider from './sidebar/SidebarContextProvider'; import SidebarTriggers from './sidebar/SidebarTriggers'; import NewSidebarProvider from './new-sidebar/SidebarContextProvider'; import NewSidebarTriggers from './new-sidebar/SidebarTriggers'; - -import { useModel } from '../../generic/model-store'; +import { CelebrationModal, shouldCelebrateOnSectionLoad, WeeklyGoalCelebrationModal } from './celebration'; +import CourseBreadcrumbs from './CourseBreadcrumbs'; +import ContentTools from './content-tools'; +import Sequence from './sequence'; const Course = ({ courseId, @@ -37,7 +36,8 @@ const Course = ({ } = useModel('courseHomeMeta', courseId); const sequence = useModel('sequences', sequenceId); const section = useModel('sections', sequence ? sequence.sectionId : null); - const navigationDisabled = sequence?.navigationDisabled ?? false; + const { enableNavigationSidebar } = useSelector(getCoursewareOutlineSidebarSettings); + const navigationDisabled = enableNavigationSidebar || (sequence?.navigationDisabled ?? false); const pageTitleBreadCrumbs = [ sequence, @@ -54,7 +54,6 @@ const Course = ({ const [weeklyGoalCelebrationOpen, setWeeklyGoalCelebrationOpen] = useState( celebrations && !celebrations.streakLengthToCelebrate && celebrations.weeklyGoal, ); - const shouldDisplayTriggers = windowWidth >= breakpoints.small.minWidth; const shouldDisplayChat = windowWidth >= breakpoints.medium.minWidth; const daysPerWeek = course?.courseGoals?.selectedGoal?.daysPerWeek; @@ -76,7 +75,7 @@ const Course = ({ {`${pageTitleBreadCrumbs.join(' | ')} | ${getConfig().SITE_NAME}`} -
+
{navigationDisabled || ( <> )} - {shouldDisplayTriggers && ( - <> - {isNewDiscussionSidebarViewEnabled ? : } - - )} +
+ + {isNewDiscussionSidebarViewEnabled ? : } +
diff --git a/src/courseware/course/Course.test.jsx b/src/courseware/course/Course.test.jsx index de8a057874..34d630c9c5 100644 --- a/src/courseware/course/Course.test.jsx +++ b/src/courseware/course/Course.test.jsx @@ -5,7 +5,7 @@ import { Factory } from 'rosie'; import { breakpoints } from '@openedx/paragon'; import { - act, fireEvent, getByRole, initializeTestStore, loadUnit, render, screen, waitFor, + fireEvent, getByRole, initializeTestStore, loadUnit, render, screen, waitFor, } from '../../setupTest'; import * as celebrationUtils from './celebration/utils'; import { handleNextSectionCelebration } from './celebration'; @@ -59,7 +59,7 @@ describe('Course', () => { it('loads learning sequence', async () => { render(, { wrapWithRouter: true }); - expect(screen.getByRole('navigation', { name: 'breadcrumb' })).toBeInTheDocument(); + expect(screen.queryByRole('navigation', { name: 'breadcrumb' })).not.toBeInTheDocument(); expect(await screen.findByText('Loading learning sequence...')).toBeInTheDocument(); expect(screen.queryByRole('alert')).not.toBeInTheDocument(); @@ -142,27 +142,32 @@ describe('Course', () => { const notificationTrigger = screen.getByRole('button', { name: /Show notification tray/i }); expect(notificationTrigger).toBeInTheDocument(); - expect(notificationTrigger.parentNode).not.toHaveClass('mt-3', { exact: true }); + expect(notificationTrigger.parentNode).not.toHaveClass('sidebar-active', { exact: true }); fireEvent.click(notificationTrigger); - expect(notificationTrigger.parentNode).toHaveClass('mt-3'); + expect(notificationTrigger.parentNode).toHaveClass('sidebar-active'); }); it('handles click to open/close discussions sidebar', async () => { await setupDiscussionSidebar(); - const discussionsTrigger = await screen.getByRole('button', { name: /Show discussions tray/i }); - const discussionsSideBar = await waitFor(() => screen.findByTestId('sidebar-DISCUSSIONS')); - expect(discussionsSideBar).not.toHaveClass('d-none'); + await waitFor(() => { + expect(screen.getByTestId('sidebar-DISCUSSIONS')).toBeInTheDocument(); + expect(screen.getByTestId('sidebar-DISCUSSIONS')).not.toHaveClass('d-none'); + }); + + const discussionsTrigger = await screen.getByRole('button', { name: /Show discussions tray/i }); + expect(discussionsTrigger).toBeInTheDocument(); + fireEvent.click(discussionsTrigger); - await act(async () => { - fireEvent.click(discussionsTrigger); + await waitFor(() => { + expect(screen.queryByTestId('sidebar-DISCUSSIONS')).not.toBeInTheDocument(); }); - await expect(discussionsSideBar).toHaveClass('d-none'); - await act(async () => { - fireEvent.click(discussionsTrigger); + fireEvent.click(discussionsTrigger); + + await waitFor(() => { + expect(screen.queryByTestId('sidebar-DISCUSSIONS')).toBeInTheDocument(); }); - await expect(discussionsSideBar).not.toHaveClass('d-none'); }); it('displays discussions sidebar when unit changes', async () => { @@ -192,8 +197,9 @@ describe('Course', () => { it('handles click to open/close notification tray', async () => { await setupDiscussionSidebar(); const notificationShowButton = await screen.findByRole('button', { name: /Show notification tray/i }); - expect(screen.queryByRole('region', { name: /notification tray/i })).toHaveClass('d-none'); + expect(screen.queryByRole('region', { name: /notification tray/i })).not.toBeInTheDocument(); fireEvent.click(notificationShowButton); + expect(screen.queryByRole('region', { name: /notification tray/i })).toBeInTheDocument(); expect(screen.queryByRole('region', { name: /notification tray/i })).not.toHaveClass('d-none'); }); @@ -204,7 +210,9 @@ describe('Course', () => { { type: 'vertical' }, { courseId: courseMetadata.id }, )); - const testStore = await initializeTestStore({ courseMetadata, unitBlocks }, false); + const testStore = await initializeTestStore({ + courseMetadata, unitBlocks, enableNavigationSidebar: { enable_navigation_sidebar: false }, + }, false); const { courseware, models } = testStore.getState(); const { courseId, sequenceId } = courseware; const testData = { diff --git a/src/courseware/course/CourseBreadcrumbs.jsx b/src/courseware/course/CourseBreadcrumbs.jsx index 6b29144fea..ab9d031dbd 100644 --- a/src/courseware/course/CourseBreadcrumbs.jsx +++ b/src/courseware/course/CourseBreadcrumbs.jsx @@ -154,7 +154,7 @@ const CourseBreadcrumbs = ({ }, [courseStatus, sequenceStatus, allSequencesInSections]); return ( -