fix: ensure full-height layout (#1724)
When content within a sequence was shorter than the height of the browser viewport, the `.sequence-container > .outline-sidebar-wrapper` element does not expand appropriately. This caused the course outline sidebar to be partially or completely hidden from view.
This commit is contained in:
committed by
GitHub
parent
73eaf61261
commit
ab3f5fd7bc
8
package-lock.json
generated
8
package-lock.json
generated
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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