diff --git a/package-lock.json b/package-lock.json index 93a4c117..26004885 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,7 @@ "@edx/frontend-component-footer": "^14.6.0", "@edx/frontend-component-header": "^6.2.0", "@edx/frontend-lib-learning-assistant": "^2.20.0", - "@edx/frontend-lib-special-exams": "^3.5.0", + "@edx/frontend-lib-special-exams": "^4.0.0", "@edx/frontend-platform": "^8.3.1", "@edx/openedx-atlas": "^0.7.0", "@edx/react-unit-test-utils": "^4.0.0", @@ -2287,9 +2287,9 @@ } }, "node_modules/@edx/frontend-lib-special-exams": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/@edx/frontend-lib-special-exams/-/frontend-lib-special-exams-3.5.0.tgz", - "integrity": "sha512-lRKD3K+XAuoKAaxbZxb7QLTWkSlV9yIy08XflYoHh/weClesVTETU3+NtJ5YRsC/kYHZrzSYIpMZnBnkKTGTww==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@edx/frontend-lib-special-exams/-/frontend-lib-special-exams-4.0.0.tgz", + "integrity": "sha512-mJdrxebdKO9NxDFkQZ1vyWVUvWCk393pIVHJyz9vH42Kvn08LC5db8/gYk37srCfsA4Dl78pMLa408CoT14JMA==", "license": "AGPL-3.0", "dependencies": { "@fortawesome/fontawesome-svg-core": "1.2.34", @@ -7884,9 +7884,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001720", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001720.tgz", - "integrity": "sha512-Ec/2yV2nNPwb4DnTANEV99ZWwm3ZWfdlfkQbWSDDt+PsXEVYwlhPH8tdMaPunYTKKmz7AnHi2oNEi1GcmKCD8g==", + "version": "1.0.30001721", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001721.tgz", + "integrity": "sha512-cOuvmUVtKrtEaoKiO0rSc29jcjwMwX5tOHDy4MgVFEWiUXj4uBMJkwI8MDySkgXidpMiHUcviogAvFi4pA2hDQ==", "funding": [ { "type": "opencollective", diff --git a/package.json b/package.json index 14a2e49e..32411cd3 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "@edx/frontend-component-footer": "^14.6.0", "@edx/frontend-component-header": "^6.2.0", "@edx/frontend-lib-learning-assistant": "^2.20.0", - "@edx/frontend-lib-special-exams": "^3.5.0", + "@edx/frontend-lib-special-exams": "^4.0.0", "@edx/frontend-platform": "^8.3.1", "@edx/openedx-atlas": "^0.7.0", "@edx/react-unit-test-utils": "^4.0.0", @@ -98,4 +98,4 @@ ], "normalizeFilenames": "^.+?(\\..+?)\\.\\w+$" } -} \ No newline at end of file +} diff --git a/src/__snapshots__/index.test.jsx.snap b/src/__snapshots__/index.test.jsx.snap index e5b15a22..7eb0886b 100644 --- a/src/__snapshots__/index.test.jsx.snap +++ b/src/__snapshots__/index.test.jsx.snap @@ -34,188 +34,192 @@ exports[`app registry subscribe: APP_READY. links App to root element 1`] = ` - - - - - } - path="*" - /> - - - - } - path="/goal-unsubscribe/:token" - /> - - - - } - path="/redirect/*" - /> - - - - } - path="/preferences-unsubscribe/:userToken/:updatePatch" - /> - - - - } - path="/course/:courseId/access-denied" - /> - - - - - - } - path="/course/:courseId/home" - /> - - - - - - } - path="/course/:courseId/live" - /> - - - - - - } - path="/course/:courseId/dates" - /> - - - - - - } - path="/course/:courseId/discussion/:path/*" - /> - - - - - - } - path="/course/:courseId/progress/:targetUserId/" - /> - - - - - - } - path="/course/:courseId/progress" - /> - - - - - - } - path="/course/:courseId/course-end" - /> - - - - } - path="/course/:courseId/:sequenceId/:unitId" - /> - - - - } - path="/course/:courseId/:sequenceId" - /> - - - - } - path="/course/:courseId" - /> - - - - } - path="/preview/course/:courseId/:sequenceId/:unitId" - /> - - - - } - path="/preview/course/:courseId/:sequenceId" - /> - +
+ + + + + } + path="*" + /> + + + + } + path="/goal-unsubscribe/:token" + /> + + + + } + path="/redirect/*" + /> + + + + } + path="/preferences-unsubscribe/:userToken/:updatePatch" + /> + + + + } + path="/course/:courseId/access-denied" + /> + + + + + + } + path="/course/:courseId/home" + /> + + + + + + } + path="/course/:courseId/live" + /> + + + + + + } + path="/course/:courseId/dates" + /> + + + + + + } + path="/course/:courseId/discussion/:path/*" + /> + + + + + + } + path="/course/:courseId/progress/:targetUserId/" + /> + + + + + + } + path="/course/:courseId/progress" + /> + + + + + + } + path="/course/:courseId/course-end" + /> + + + + } + path="/course/:courseId/:sequenceId/:unitId" + /> + + + + } + path="/course/:courseId/:sequenceId" + /> + + + + } + path="/course/:courseId" + /> + + + + } + path="/preview/course/:courseId/:sequenceId/:unitId" + /> + + + + } + path="/preview/course/:courseId/:sequenceId" + /> + +
diff --git a/src/courseware/course/new-sidebar/SidebarContextProvider.tsx b/src/courseware/course/new-sidebar/SidebarContextProvider.tsx index 7ea8ffde..f916b916 100644 --- a/src/courseware/course/new-sidebar/SidebarContextProvider.tsx +++ b/src/courseware/course/new-sidebar/SidebarContextProvider.tsx @@ -54,6 +54,8 @@ const SidebarProvider: React.FC = ({ }, [courseId]); useEffect(() => { + window.sessionStorage.setItem('hideCourseOutlineSidebar', 'true'); + window.sessionStorage.setItem(`notificationTrayStatus.${courseId}`, 'open'); setHideDiscussionbar(!isDiscussionbarAvailable); setHideNotificationbar(!isNotificationbarAvailable); if (initialSidebar && currentSidebar !== initialSidebar) { diff --git a/src/courseware/course/new-sidebar/sidebars/discussions-notifications/discussions/DiscussionsWidget.test.tsx b/src/courseware/course/new-sidebar/sidebars/discussions-notifications/discussions/DiscussionsWidget.test.tsx index 3b35d8c2..38eaf00f 100644 --- a/src/courseware/course/new-sidebar/sidebars/discussions-notifications/discussions/DiscussionsWidget.test.tsx +++ b/src/courseware/course/new-sidebar/sidebars/discussions-notifications/discussions/DiscussionsWidget.test.tsx @@ -1,11 +1,13 @@ import React from 'react'; +import { fireEvent } from '@testing-library/react'; import MockAdapter from 'axios-mock-adapter'; import { getConfig } from '@edx/frontend-platform'; -import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth'; import { sendTrackEvent } from '@edx/frontend-platform/analytics'; +import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth'; +import { getSessionStorage, setSessionStorage } from '../../../../../../data/sessionStorage'; import { initializeMockApp, initializeTestStore, render, screen, } from '../../../../../../setupTest'; @@ -14,11 +16,19 @@ import { buildTopicsFromUnits } from '../../../../../data/__factories__/discussi import { getCourseDiscussionTopics } from '../../../../../data/thunks'; import SidebarContext from '../../../SidebarContext'; import DiscussionsNotificationsSidebar from '../DiscussionsNotificationsSidebar'; +import DiscussionsNotificationsTrigger from '../DiscussionsNotificationsTrigger'; import DiscussionsWidget from './DiscussionsWidget'; initializeMockApp(); jest.mock('@edx/frontend-platform/analytics'); +jest.mock('../../../../../../data/sessionStorage', () => ({ + getSessionStorage: jest.fn(), + setSessionStorage: jest.fn(), +})); + +const onClickMock = jest.fn(); + describe('DiscussionsWidget', () => { let axiosMock; let mockData; @@ -81,4 +91,34 @@ describe('DiscussionsWidget', () => { expect(screen.queryByText('Back to course')).toBeInTheDocument(); expect(sendTrackEvent).toHaveBeenCalledTimes(1); }); + + it('should open notification tray if closed', () => { + (getSessionStorage as jest.Mock).mockReturnValue('closed'); + + renderWithProvider(() => ); + + const button = screen.getByRole('button'); + fireEvent.click(button); + + expect(setSessionStorage).toHaveBeenCalledWith( + `notificationTrayStatus.${courseId}`, + 'open', + ); + expect(onClickMock).toHaveBeenCalled(); + }); + + it('should close notification tray if open', () => { + (getSessionStorage as jest.Mock).mockReturnValue('open'); + + renderWithProvider(() => ); + + const button = screen.getByRole('button'); + fireEvent.click(button); + + expect(setSessionStorage).toHaveBeenCalledWith( + `notificationTrayStatus.${courseId}`, + 'open', + ); + expect(onClickMock).toHaveBeenCalled(); + }); }); diff --git a/src/courseware/course/sequence/Sequence.jsx b/src/courseware/course/sequence/Sequence.jsx index e0b3c0a8..30d51cf8 100644 --- a/src/courseware/course/sequence/Sequence.jsx +++ b/src/courseware/course/sequence/Sequence.jsx @@ -218,18 +218,20 @@ const Sequence = ({ if (sequenceStatus === 'loaded') { return ( -
- - {defaultContent} - + <> +
+ + {defaultContent} + +
-
+ ); } diff --git a/src/courseware/course/sidebar/sidebars/course-outline/CourseOutlineTray.scss b/src/courseware/course/sidebar/sidebars/course-outline/CourseOutlineTray.scss index 8376f4aa..0c61035a 100644 --- a/src/courseware/course/sidebar/sidebars/course-outline/CourseOutlineTray.scss +++ b/src/courseware/course/sidebar/sidebars/course-outline/CourseOutlineTray.scss @@ -1,14 +1,12 @@ .outline-sidebar-wrapper { width: 32.125rem; max-width: 100%; - overflow: auto; position: relative; flex-shrink: 0; } .outline-sidebar { @media (min-width: map-get($grid-breakpoints, "xl")) { - position: absolute; left: 0; top: 0; } diff --git a/src/courseware/course/sidebar/sidebars/course-outline/hooks.js b/src/courseware/course/sidebar/sidebars/course-outline/hooks.js index 6d4ea8ca..71ed4bff 100644 --- a/src/courseware/course/sidebar/sidebars/course-outline/hooks.js +++ b/src/courseware/course/sidebar/sidebars/course-outline/hooks.js @@ -68,6 +68,7 @@ export const useCourseOutlineSidebar = () => { } else { toggleSidebar(ID); window.sessionStorage.removeItem('hideCourseOutlineSidebar'); + window.sessionStorage.setItem(`notificationTrayStatus.${courseId}`, 'closed'); } }; diff --git a/src/generic/notices/NoticesProvider.jsx b/src/generic/notices/NoticesProvider.jsx index ec163e2d..acbad187 100644 --- a/src/generic/notices/NoticesProvider.jsx +++ b/src/generic/notices/NoticesProvider.jsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react'; +import { useEffect, useState } from 'react'; import { getConfig } from '@edx/frontend-platform'; import PropTypes from 'prop-types'; import { getNotices } from './api'; @@ -25,11 +25,7 @@ const NoticesProvider = ({ children }) => { getData(); }, []); - return ( -
- {isRedirected === true ? null : children} -
- ); + return isRedirected === true ? null : children; }; NoticesProvider.propTypes = { diff --git a/src/index.jsx b/src/index.jsx index 72c4992a..b3748ca6 100755 --- a/src/index.jsx +++ b/src/index.jsx @@ -49,100 +49,102 @@ subscribe(APP_READY, () => { - - } /> - } /> - } /> - - } - /> - } - /> - - - - - - )} - /> - - - - - - )} - /> - - - - - - )} - /> - - - - - - )} - /> - {DECODE_ROUTES.PROGRESS.map((route) => ( +
+ + } /> + } /> + } /> + } + /> + } + /> + - - + + - )} + )} /> - ))} - - - - - - )} - /> - {DECODE_ROUTES.COURSEWARE.map((route) => ( - + + + - )} + )} /> - ))} - + + + + + + )} + /> + + + + + + )} + /> + {DECODE_ROUTES.PROGRESS.map((route) => ( + + + + + + )} + /> + ))} + + + + + + )} + /> + {DECODE_ROUTES.COURSEWARE.map((route) => ( + + + + )} + /> + ))} + +
diff --git a/src/index.scss b/src/index.scss index 29304d60..4edd1ff1 100755 --- a/src/index.scss +++ b/src/index.scss @@ -8,13 +8,20 @@ #root { - display: flex; - flex-direction: column; - min-height: 100vh; - + .app-container { + display: flex; + flex-direction: column; + min-height: 100svh; + } + main { flex-grow: 1; } + #main-content { + flex-grow: 1; + display: flex; + flex-direction: column; + } header { flex: 0 0 auto;