Merge branch 'openedx:master' into master
This commit is contained in:
@@ -34,188 +34,192 @@ exports[`app registry subscribe: APP_READY. links App to root element 1`] = `
|
||||
<PathFixesProvider>
|
||||
<NoticesProvider>
|
||||
<UserMessagesProvider>
|
||||
<Routes>
|
||||
<Route
|
||||
element={
|
||||
<PageWrap>
|
||||
<Page Not Found />
|
||||
</PageWrap>
|
||||
}
|
||||
path="*"
|
||||
/>
|
||||
<Route
|
||||
element={
|
||||
<PageWrap>
|
||||
<Goal Unsubscribe />
|
||||
</PageWrap>
|
||||
}
|
||||
path="/goal-unsubscribe/:token"
|
||||
/>
|
||||
<Route
|
||||
element={
|
||||
<PageWrap>
|
||||
<Courseware Redirect Landing Page />
|
||||
</PageWrap>
|
||||
}
|
||||
path="/redirect/*"
|
||||
/>
|
||||
<Route
|
||||
element={
|
||||
<PageWrap>
|
||||
<Preferences Unsubscribe />
|
||||
</PageWrap>
|
||||
}
|
||||
path="/preferences-unsubscribe/:userToken/:updatePatch"
|
||||
/>
|
||||
<Route
|
||||
element={
|
||||
<DecodePageRoute>
|
||||
<Course Access Error Page />
|
||||
</DecodePageRoute>
|
||||
}
|
||||
path="/course/:courseId/access-denied"
|
||||
/>
|
||||
<Route
|
||||
element={
|
||||
<DecodePageRoute>
|
||||
<Tab Container
|
||||
fetch={[Function]}
|
||||
slice="courseHome"
|
||||
tab="outline"
|
||||
>
|
||||
<Outline Tab />
|
||||
</Tab Container>
|
||||
</DecodePageRoute>
|
||||
}
|
||||
path="/course/:courseId/home"
|
||||
/>
|
||||
<Route
|
||||
element={
|
||||
<DecodePageRoute>
|
||||
<Tab Container
|
||||
fetch={[Function]}
|
||||
slice="courseHome"
|
||||
tab="lti_live"
|
||||
>
|
||||
<Live Tab />
|
||||
</Tab Container>
|
||||
</DecodePageRoute>
|
||||
}
|
||||
path="/course/:courseId/live"
|
||||
/>
|
||||
<Route
|
||||
element={
|
||||
<DecodePageRoute>
|
||||
<Tab Container
|
||||
fetch={[Function]}
|
||||
slice="courseHome"
|
||||
tab="dates"
|
||||
>
|
||||
<Dates Tab />
|
||||
</Tab Container>
|
||||
</DecodePageRoute>
|
||||
}
|
||||
path="/course/:courseId/dates"
|
||||
/>
|
||||
<Route
|
||||
element={
|
||||
<DecodePageRoute>
|
||||
<Tab Container
|
||||
fetch={[Function]}
|
||||
slice="courseHome"
|
||||
tab="discussion"
|
||||
>
|
||||
<Discussion Tab />
|
||||
</Tab Container>
|
||||
</DecodePageRoute>
|
||||
}
|
||||
path="/course/:courseId/discussion/:path/*"
|
||||
/>
|
||||
<Route
|
||||
element={
|
||||
<DecodePageRoute>
|
||||
<Tab Container
|
||||
fetch={[Function]}
|
||||
isProgressTab={true}
|
||||
slice="courseHome"
|
||||
tab="progress"
|
||||
>
|
||||
<Progress Tab />
|
||||
</Tab Container>
|
||||
</DecodePageRoute>
|
||||
}
|
||||
path="/course/:courseId/progress/:targetUserId/"
|
||||
/>
|
||||
<Route
|
||||
element={
|
||||
<DecodePageRoute>
|
||||
<Tab Container
|
||||
fetch={[Function]}
|
||||
isProgressTab={true}
|
||||
slice="courseHome"
|
||||
tab="progress"
|
||||
>
|
||||
<Progress Tab />
|
||||
</Tab Container>
|
||||
</DecodePageRoute>
|
||||
}
|
||||
path="/course/:courseId/progress"
|
||||
/>
|
||||
<Route
|
||||
element={
|
||||
<DecodePageRoute>
|
||||
<Tab Container
|
||||
fetch={[Function]}
|
||||
slice="courseware"
|
||||
tab="courseware"
|
||||
>
|
||||
<Course Exit />
|
||||
</Tab Container>
|
||||
</DecodePageRoute>
|
||||
}
|
||||
path="/course/:courseId/course-end"
|
||||
/>
|
||||
<Route
|
||||
element={
|
||||
<DecodePageRoute>
|
||||
<Courseware Container />
|
||||
</DecodePageRoute>
|
||||
}
|
||||
path="/course/:courseId/:sequenceId/:unitId"
|
||||
/>
|
||||
<Route
|
||||
element={
|
||||
<DecodePageRoute>
|
||||
<Courseware Container />
|
||||
</DecodePageRoute>
|
||||
}
|
||||
path="/course/:courseId/:sequenceId"
|
||||
/>
|
||||
<Route
|
||||
element={
|
||||
<DecodePageRoute>
|
||||
<Courseware Container />
|
||||
</DecodePageRoute>
|
||||
}
|
||||
path="/course/:courseId"
|
||||
/>
|
||||
<Route
|
||||
element={
|
||||
<DecodePageRoute>
|
||||
<Courseware Container />
|
||||
</DecodePageRoute>
|
||||
}
|
||||
path="/preview/course/:courseId/:sequenceId/:unitId"
|
||||
/>
|
||||
<Route
|
||||
element={
|
||||
<DecodePageRoute>
|
||||
<Courseware Container />
|
||||
</DecodePageRoute>
|
||||
}
|
||||
path="/preview/course/:courseId/:sequenceId"
|
||||
/>
|
||||
</Routes>
|
||||
<div
|
||||
className="app-container"
|
||||
>
|
||||
<Routes>
|
||||
<Route
|
||||
element={
|
||||
<PageWrap>
|
||||
<Page Not Found />
|
||||
</PageWrap>
|
||||
}
|
||||
path="*"
|
||||
/>
|
||||
<Route
|
||||
element={
|
||||
<PageWrap>
|
||||
<Goal Unsubscribe />
|
||||
</PageWrap>
|
||||
}
|
||||
path="/goal-unsubscribe/:token"
|
||||
/>
|
||||
<Route
|
||||
element={
|
||||
<PageWrap>
|
||||
<Courseware Redirect Landing Page />
|
||||
</PageWrap>
|
||||
}
|
||||
path="/redirect/*"
|
||||
/>
|
||||
<Route
|
||||
element={
|
||||
<PageWrap>
|
||||
<Preferences Unsubscribe />
|
||||
</PageWrap>
|
||||
}
|
||||
path="/preferences-unsubscribe/:userToken/:updatePatch"
|
||||
/>
|
||||
<Route
|
||||
element={
|
||||
<DecodePageRoute>
|
||||
<Course Access Error Page />
|
||||
</DecodePageRoute>
|
||||
}
|
||||
path="/course/:courseId/access-denied"
|
||||
/>
|
||||
<Route
|
||||
element={
|
||||
<DecodePageRoute>
|
||||
<Tab Container
|
||||
fetch={[Function]}
|
||||
slice="courseHome"
|
||||
tab="outline"
|
||||
>
|
||||
<Outline Tab />
|
||||
</Tab Container>
|
||||
</DecodePageRoute>
|
||||
}
|
||||
path="/course/:courseId/home"
|
||||
/>
|
||||
<Route
|
||||
element={
|
||||
<DecodePageRoute>
|
||||
<Tab Container
|
||||
fetch={[Function]}
|
||||
slice="courseHome"
|
||||
tab="lti_live"
|
||||
>
|
||||
<Live Tab />
|
||||
</Tab Container>
|
||||
</DecodePageRoute>
|
||||
}
|
||||
path="/course/:courseId/live"
|
||||
/>
|
||||
<Route
|
||||
element={
|
||||
<DecodePageRoute>
|
||||
<Tab Container
|
||||
fetch={[Function]}
|
||||
slice="courseHome"
|
||||
tab="dates"
|
||||
>
|
||||
<Dates Tab />
|
||||
</Tab Container>
|
||||
</DecodePageRoute>
|
||||
}
|
||||
path="/course/:courseId/dates"
|
||||
/>
|
||||
<Route
|
||||
element={
|
||||
<DecodePageRoute>
|
||||
<Tab Container
|
||||
fetch={[Function]}
|
||||
slice="courseHome"
|
||||
tab="discussion"
|
||||
>
|
||||
<Discussion Tab />
|
||||
</Tab Container>
|
||||
</DecodePageRoute>
|
||||
}
|
||||
path="/course/:courseId/discussion/:path/*"
|
||||
/>
|
||||
<Route
|
||||
element={
|
||||
<DecodePageRoute>
|
||||
<Tab Container
|
||||
fetch={[Function]}
|
||||
isProgressTab={true}
|
||||
slice="courseHome"
|
||||
tab="progress"
|
||||
>
|
||||
<Progress Tab />
|
||||
</Tab Container>
|
||||
</DecodePageRoute>
|
||||
}
|
||||
path="/course/:courseId/progress/:targetUserId/"
|
||||
/>
|
||||
<Route
|
||||
element={
|
||||
<DecodePageRoute>
|
||||
<Tab Container
|
||||
fetch={[Function]}
|
||||
isProgressTab={true}
|
||||
slice="courseHome"
|
||||
tab="progress"
|
||||
>
|
||||
<Progress Tab />
|
||||
</Tab Container>
|
||||
</DecodePageRoute>
|
||||
}
|
||||
path="/course/:courseId/progress"
|
||||
/>
|
||||
<Route
|
||||
element={
|
||||
<DecodePageRoute>
|
||||
<Tab Container
|
||||
fetch={[Function]}
|
||||
slice="courseware"
|
||||
tab="courseware"
|
||||
>
|
||||
<Course Exit />
|
||||
</Tab Container>
|
||||
</DecodePageRoute>
|
||||
}
|
||||
path="/course/:courseId/course-end"
|
||||
/>
|
||||
<Route
|
||||
element={
|
||||
<DecodePageRoute>
|
||||
<Courseware Container />
|
||||
</DecodePageRoute>
|
||||
}
|
||||
path="/course/:courseId/:sequenceId/:unitId"
|
||||
/>
|
||||
<Route
|
||||
element={
|
||||
<DecodePageRoute>
|
||||
<Courseware Container />
|
||||
</DecodePageRoute>
|
||||
}
|
||||
path="/course/:courseId/:sequenceId"
|
||||
/>
|
||||
<Route
|
||||
element={
|
||||
<DecodePageRoute>
|
||||
<Courseware Container />
|
||||
</DecodePageRoute>
|
||||
}
|
||||
path="/course/:courseId"
|
||||
/>
|
||||
<Route
|
||||
element={
|
||||
<DecodePageRoute>
|
||||
<Courseware Container />
|
||||
</DecodePageRoute>
|
||||
}
|
||||
path="/preview/course/:courseId/:sequenceId/:unitId"
|
||||
/>
|
||||
<Route
|
||||
element={
|
||||
<DecodePageRoute>
|
||||
<Courseware Container />
|
||||
</DecodePageRoute>
|
||||
}
|
||||
path="/preview/course/:courseId/:sequenceId"
|
||||
/>
|
||||
</Routes>
|
||||
</div>
|
||||
</UserMessagesProvider>
|
||||
</NoticesProvider>
|
||||
</PathFixesProvider>
|
||||
|
||||
@@ -54,6 +54,8 @@ const SidebarProvider: React.FC<Props> = ({
|
||||
}, [courseId]);
|
||||
|
||||
useEffect(() => {
|
||||
window.sessionStorage.setItem('hideCourseOutlineSidebar', 'true');
|
||||
window.sessionStorage.setItem(`notificationTrayStatus.${courseId}`, 'open');
|
||||
setHideDiscussionbar(!isDiscussionbarAvailable);
|
||||
setHideNotificationbar(!isNotificationbarAvailable);
|
||||
if (initialSidebar && currentSidebar !== initialSidebar) {
|
||||
|
||||
@@ -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(() => <DiscussionsNotificationsTrigger onClick={onClickMock} />);
|
||||
|
||||
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(() => <DiscussionsNotificationsTrigger onClick={onClickMock} />);
|
||||
|
||||
const button = screen.getByRole('button');
|
||||
fireEvent.click(button);
|
||||
|
||||
expect(setSessionStorage).toHaveBeenCalledWith(
|
||||
`notificationTrayStatus.${courseId}`,
|
||||
'open',
|
||||
);
|
||||
expect(onClickMock).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -218,18 +218,20 @@ const Sequence = ({
|
||||
|
||||
if (sequenceStatus === 'loaded') {
|
||||
return (
|
||||
<div>
|
||||
<SequenceExamWrapper
|
||||
sequence={sequence}
|
||||
courseId={courseId}
|
||||
isStaff={isStaff}
|
||||
originalUserIsStaff={originalUserIsStaff}
|
||||
canAccessProctoredExams={canAccessProctoredExams}
|
||||
>
|
||||
{defaultContent}
|
||||
</SequenceExamWrapper>
|
||||
<>
|
||||
<div className="d-flex flex-column flex-grow-1 justify-content-center">
|
||||
<SequenceExamWrapper
|
||||
sequence={sequence}
|
||||
courseId={courseId}
|
||||
isStaff={isStaff}
|
||||
originalUserIsStaff={originalUserIsStaff}
|
||||
canAccessProctoredExams={canAccessProctoredExams}
|
||||
>
|
||||
{defaultContent}
|
||||
</SequenceExamWrapper>
|
||||
</div>
|
||||
<CourseLicense license={license || undefined} />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -68,6 +68,7 @@ export const useCourseOutlineSidebar = () => {
|
||||
} else {
|
||||
toggleSidebar(ID);
|
||||
window.sessionStorage.removeItem('hideCourseOutlineSidebar');
|
||||
window.sessionStorage.setItem(`notificationTrayStatus.${courseId}`, 'closed');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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 (
|
||||
<div>
|
||||
{isRedirected === true ? null : children}
|
||||
</div>
|
||||
);
|
||||
return isRedirected === true ? null : children;
|
||||
};
|
||||
|
||||
NoticesProvider.propTypes = {
|
||||
|
||||
168
src/index.jsx
168
src/index.jsx
@@ -49,100 +49,102 @@ subscribe(APP_READY, () => {
|
||||
<PathFixesProvider>
|
||||
<NoticesProvider>
|
||||
<UserMessagesProvider>
|
||||
<Routes>
|
||||
<Route path="*" element={<PageWrap><PageNotFound /></PageWrap>} />
|
||||
<Route path={ROUTES.UNSUBSCRIBE} element={<PageWrap><GoalUnsubscribe /></PageWrap>} />
|
||||
<Route path={ROUTES.REDIRECT} element={<PageWrap><CoursewareRedirectLandingPage /></PageWrap>} />
|
||||
<Route
|
||||
path={ROUTES.PREFERENCES_UNSUBSCRIBE}
|
||||
element={
|
||||
<PageWrap><PreferencesUnsubscribe /></PageWrap>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path={DECODE_ROUTES.ACCESS_DENIED}
|
||||
element={<DecodePageRoute><CourseAccessErrorPage /></DecodePageRoute>}
|
||||
/>
|
||||
<Route
|
||||
path={DECODE_ROUTES.HOME}
|
||||
element={(
|
||||
<DecodePageRoute>
|
||||
<TabContainer tab="outline" fetch={fetchOutlineTab} slice="courseHome">
|
||||
<OutlineTab />
|
||||
</TabContainer>
|
||||
</DecodePageRoute>
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path={DECODE_ROUTES.LIVE}
|
||||
element={(
|
||||
<DecodePageRoute>
|
||||
<TabContainer tab="lti_live" fetch={fetchLiveTab} slice="courseHome">
|
||||
<LiveTab />
|
||||
</TabContainer>
|
||||
</DecodePageRoute>
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path={DECODE_ROUTES.DATES}
|
||||
element={(
|
||||
<DecodePageRoute>
|
||||
<TabContainer tab="dates" fetch={fetchDatesTab} slice="courseHome">
|
||||
<DatesTab />
|
||||
</TabContainer>
|
||||
</DecodePageRoute>
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path={DECODE_ROUTES.DISCUSSION}
|
||||
element={(
|
||||
<DecodePageRoute>
|
||||
<TabContainer tab="discussion" fetch={fetchDiscussionTab} slice="courseHome">
|
||||
<DiscussionTab />
|
||||
</TabContainer>
|
||||
</DecodePageRoute>
|
||||
)}
|
||||
/>
|
||||
{DECODE_ROUTES.PROGRESS.map((route) => (
|
||||
<div className="app-container">
|
||||
<Routes>
|
||||
<Route path="*" element={<PageWrap><PageNotFound /></PageWrap>} />
|
||||
<Route path={ROUTES.UNSUBSCRIBE} element={<PageWrap><GoalUnsubscribe /></PageWrap>} />
|
||||
<Route path={ROUTES.REDIRECT} element={<PageWrap><CoursewareRedirectLandingPage /></PageWrap>} />
|
||||
<Route
|
||||
key={route}
|
||||
path={route}
|
||||
path={ROUTES.PREFERENCES_UNSUBSCRIBE}
|
||||
element={
|
||||
<PageWrap><PreferencesUnsubscribe /></PageWrap>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path={DECODE_ROUTES.ACCESS_DENIED}
|
||||
element={<DecodePageRoute><CourseAccessErrorPage /></DecodePageRoute>}
|
||||
/>
|
||||
<Route
|
||||
path={DECODE_ROUTES.HOME}
|
||||
element={(
|
||||
<DecodePageRoute>
|
||||
<TabContainer
|
||||
tab="progress"
|
||||
fetch={fetchProgressTab}
|
||||
slice="courseHome"
|
||||
isProgressTab
|
||||
>
|
||||
<ProgressTab />
|
||||
<TabContainer tab="outline" fetch={fetchOutlineTab} slice="courseHome">
|
||||
<OutlineTab />
|
||||
</TabContainer>
|
||||
</DecodePageRoute>
|
||||
)}
|
||||
)}
|
||||
/>
|
||||
))}
|
||||
<Route
|
||||
path={DECODE_ROUTES.COURSE_END}
|
||||
element={(
|
||||
<DecodePageRoute>
|
||||
<TabContainer tab="courseware" fetch={fetchCourse} slice="courseware">
|
||||
<CourseExit />
|
||||
</TabContainer>
|
||||
</DecodePageRoute>
|
||||
)}
|
||||
/>
|
||||
{DECODE_ROUTES.COURSEWARE.map((route) => (
|
||||
<Route
|
||||
key={route}
|
||||
path={route}
|
||||
path={DECODE_ROUTES.LIVE}
|
||||
element={(
|
||||
<DecodePageRoute>
|
||||
<CoursewareContainer />
|
||||
<TabContainer tab="lti_live" fetch={fetchLiveTab} slice="courseHome">
|
||||
<LiveTab />
|
||||
</TabContainer>
|
||||
</DecodePageRoute>
|
||||
)}
|
||||
)}
|
||||
/>
|
||||
))}
|
||||
</Routes>
|
||||
<Route
|
||||
path={DECODE_ROUTES.DATES}
|
||||
element={(
|
||||
<DecodePageRoute>
|
||||
<TabContainer tab="dates" fetch={fetchDatesTab} slice="courseHome">
|
||||
<DatesTab />
|
||||
</TabContainer>
|
||||
</DecodePageRoute>
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path={DECODE_ROUTES.DISCUSSION}
|
||||
element={(
|
||||
<DecodePageRoute>
|
||||
<TabContainer tab="discussion" fetch={fetchDiscussionTab} slice="courseHome">
|
||||
<DiscussionTab />
|
||||
</TabContainer>
|
||||
</DecodePageRoute>
|
||||
)}
|
||||
/>
|
||||
{DECODE_ROUTES.PROGRESS.map((route) => (
|
||||
<Route
|
||||
key={route}
|
||||
path={route}
|
||||
element={(
|
||||
<DecodePageRoute>
|
||||
<TabContainer
|
||||
tab="progress"
|
||||
fetch={fetchProgressTab}
|
||||
slice="courseHome"
|
||||
isProgressTab
|
||||
>
|
||||
<ProgressTab />
|
||||
</TabContainer>
|
||||
</DecodePageRoute>
|
||||
)}
|
||||
/>
|
||||
))}
|
||||
<Route
|
||||
path={DECODE_ROUTES.COURSE_END}
|
||||
element={(
|
||||
<DecodePageRoute>
|
||||
<TabContainer tab="courseware" fetch={fetchCourse} slice="courseware">
|
||||
<CourseExit />
|
||||
</TabContainer>
|
||||
</DecodePageRoute>
|
||||
)}
|
||||
/>
|
||||
{DECODE_ROUTES.COURSEWARE.map((route) => (
|
||||
<Route
|
||||
key={route}
|
||||
path={route}
|
||||
element={(
|
||||
<DecodePageRoute>
|
||||
<CoursewareContainer />
|
||||
</DecodePageRoute>
|
||||
)}
|
||||
/>
|
||||
))}
|
||||
</Routes>
|
||||
</div>
|
||||
</UserMessagesProvider>
|
||||
</NoticesProvider>
|
||||
</PathFixesProvider>
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user