Skip to content

Commit ef3d1ed

Browse files
Improve Dashboard Layout / Fix card Overflows (#2365)
* fix key conflict issues * Reduce minimum margins on dashboard layout Previously, dashboard layout minimum margin was effectively 108px on each side. This changes the minimum margin to 24px on both sides, in-line with the rest of the application.
1 parent b68a48d commit ef3d1ed

File tree

9 files changed

+120
-113
lines changed

9 files changed

+120
-113
lines changed

frontends/main/src/app-pages/DashboardPage/CoursewareDisplay/EnrollmentDisplay.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ const EnrollmentExpandCollapse: React.FC<EnrollmentExpandCollapseProps> = ({
139139
<EnrollmentsList itemSpacing={"16px"}>
140140
{shownEnrollments.map((course) => (
141141
<DashboardCardStyled
142-
key={course.id}
142+
key={course.key}
143143
Component="li"
144144
dashboardResource={course}
145145
showNotComplete={false}
@@ -153,7 +153,7 @@ const EnrollmentExpandCollapse: React.FC<EnrollmentExpandCollapseProps> = ({
153153
<HiddenEnrollmentsList itemSpacing={"16px"}>
154154
{hiddenEnrollments.map((course) => (
155155
<DashboardCardStyled
156-
key={course.id}
156+
key={course.key}
157157
Component="li"
158158
dashboardResource={course}
159159
showNotComplete={false}

frontends/main/src/app-pages/DashboardPage/CoursewareDisplay/test-utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ const makeGrade = factories.enrollment.grade
1919
const dashboardCourse: PartialFactory<DashboardCourse> = (...overrides) => {
2020
return mergeOverrides<DashboardCourse>(
2121
{
22-
id: faker.string.uuid(),
22+
key: faker.string.uuid(),
2323
coursewareId: faker.string.uuid(),
2424
type: DashboardResourceType.Course,
2525
title: faker.commerce.productName(),

frontends/main/src/app-pages/DashboardPage/CoursewareDisplay/transform.test.tsx

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ describe("Transforming mitxonline enrollment data to DashboardResource", () => {
2929
const transformed = transform.mitxonlineEnrollments([apiData])
3030
expect(transformed).toHaveLength(1)
3131
expect(transformed[0]).toEqual({
32-
id: `mitxonline-course-${apiData.run.course.id}`,
32+
key: `mitxonline-course-${apiData.run.course.id}-${apiData.run.id}`,
3333
coursewareId: apiData.run.courseware_id ?? null,
3434
type: DashboardResourceType.Course,
3535
title: apiData.run.title,
@@ -79,16 +79,20 @@ describe("Transforming mitxonline enrollment data to DashboardResource", () => {
7979
}),
8080
]
8181

82+
const transformedCourses = mitxonlineCourses({
83+
courses: coursesA,
84+
enrollments,
85+
})
8286
const sortedCourses = sortDashboardCourses(
8387
mitxonlineProgram(programA),
84-
mitxonlineCourses({ courses: coursesA, enrollments }),
88+
transformedCourses,
8589
)
8690

87-
expect(sortedCourses.map((course) => course.id)).toEqual([
88-
`mitxonline-course-${coursesA[1].id}`, // Enrolled course
89-
`mitxonline-course-${coursesA[0].id}`, // Completed course
90-
`mitxonline-course-${coursesA[2].id}`, // Not enrolled course
91-
`mitxonline-course-${coursesA[3].id}`, // Not enrolled course
91+
expect(sortedCourses).toEqual([
92+
transformedCourses[1], // Enrolled course
93+
transformedCourses[0], // Completed course
94+
transformedCourses[2], // Not enrolled course
95+
transformedCourses[3], // Not enrolled course
9296
])
9397
})
9498
})

frontends/main/src/app-pages/DashboardPage/CoursewareDisplay/transform.ts

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,26 @@ import { groupBy } from "lodash"
1717
const sources = {
1818
mitxonline: "mitxonline",
1919
}
20-
const getId = (
21-
source: string,
22-
resourceType: DashboardResourceType,
23-
id: number,
24-
) => `${source}-${resourceType}-${id}`
20+
type KeyOpts = {
21+
source: string
22+
resourceType: DashboardResourceType
23+
id: number
24+
runId?: number
25+
}
26+
const getKey = ({ source, resourceType, id, runId }: KeyOpts) => {
27+
const base = `${source}-${resourceType}-${id}`
28+
return runId ? `${base}-${runId}` : base
29+
}
2530

2631
const mitxonlineEnrollment = (raw: CourseRunEnrollment): DashboardCourse => {
2732
const course = raw.run.course
2833
return {
29-
id: getId(sources.mitxonline, DashboardResourceType.Course, course.id),
34+
key: getKey({
35+
source: sources.mitxonline,
36+
resourceType: DashboardResourceType.Course,
37+
id: course.id,
38+
runId: raw.run.id,
39+
}),
3040
coursewareId: raw.run.courseware_id ?? null,
3141
type: DashboardResourceType.Course,
3242
title: course.title,
@@ -57,7 +67,12 @@ const mitxonlineUnenrolledCourse = (
5767
): DashboardCourse => {
5868
const run = course.courseruns.find((run) => run.id === course.next_run_id)
5969
return {
60-
id: getId(sources.mitxonline, DashboardResourceType.Course, course.id),
70+
key: getKey({
71+
source: sources.mitxonline,
72+
resourceType: DashboardResourceType.Course,
73+
id: course.id,
74+
runId: run?.id,
75+
}),
6176
coursewareId: run?.courseware_id ?? null,
6277
type: DashboardResourceType.Course,
6378
title: course.title,
@@ -100,7 +115,11 @@ const mitxonlineCourses = (raw: {
100115

101116
const mitxonlineProgram = (raw: V2Program): DashboardProgram => {
102117
return {
103-
id: getId(sources.mitxonline, DashboardResourceType.Program, raw.id),
118+
key: getKey({
119+
source: sources.mitxonline,
120+
resourceType: DashboardResourceType.Program,
121+
id: raw.id,
122+
}),
104123
type: DashboardResourceType.Program,
105124
title: raw.title,
106125
programType: raw.program_type,
@@ -113,7 +132,7 @@ const sortDashboardCourses = (
113132
program: DashboardProgram,
114133
courses: DashboardCourse[],
115134
) => {
116-
return courses.sort((a, b) => {
135+
return [...courses].sort((a, b) => {
117136
const aCompleted = a.enrollment?.status === EnrollmentStatus.Completed
118137
const bCompleted = b.enrollment?.status === EnrollmentStatus.Completed
119138
const aEnrolled = a.enrollment?.status === EnrollmentStatus.Enrolled

frontends/main/src/app-pages/DashboardPage/CoursewareDisplay/types.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ const EnrollmentMode = {
1919
type EnrollmentMode = (typeof EnrollmentMode)[keyof typeof EnrollmentMode]
2020

2121
type DashboardCourse = {
22-
id: string
22+
key: string
2323
coursewareId: string | null
2424
title: string
2525
type: typeof DashboardResourceType.Course
@@ -47,7 +47,7 @@ type DashboardCourseEnrollment = {
4747
}
4848

4949
type DashboardProgram = {
50-
id: string
50+
key: string
5151
type: typeof DashboardResourceType.Program
5252
title: string
5353
programType?: string | null

frontends/main/src/app-pages/DashboardPage/DashboardLayout.tsx

Lines changed: 22 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -77,28 +77,14 @@ const Background = styled.div(({ theme }) => ({
7777
},
7878
}))
7979

80-
const Page = styled.div(({ theme }) => ({
81-
display: "flex",
82-
flexDirection: "column",
83-
alignItems: "flex-start",
84-
padding: "40px 84px 80px 84px",
85-
gap: "80px",
86-
height: "100%",
87-
[theme.breakpoints.down("md")]: {
88-
padding: "0",
89-
gap: "24px",
90-
},
91-
}))
92-
93-
const DashboardContainer = styled(Container)(({ theme }) => ({
94-
[theme.breakpoints.down("md")]: {
95-
padding: "24px 16px",
96-
gap: "24px",
97-
},
98-
}))
80+
const PageContainer = styled(Container)({
81+
marginTop: "40px",
82+
marginBottom: "80px",
83+
})
9984

10085
const DashboardGrid = styled.div(({ theme }) => ({
10186
display: "grid",
87+
width: "100%",
10288
gridTemplateColumns: "300px minmax(0, 1fr)",
10389
gap: "48px",
10490
[theme.breakpoints.down("md")]: {
@@ -114,17 +100,15 @@ const DashboardGridItem = styled.div({
114100
},
115101
})
116102

117-
const ProfileSidebar = styled(Card)(({ theme }) => ({
103+
const ProfileSidebar = styled(Card)({
118104
position: "fixed",
119105
display: "flex",
120106
flexDirection: "column",
121107
alignItems: "flex-start",
122108
width: "300px",
123109
boxShadow: "-4px 4px 0px 0px #A31F34",
124-
[theme.breakpoints.down("md")]: {
125-
position: "relative",
126-
},
127-
}))
110+
transform: "translateX(4px)", // keep solid shadow from bleeding into page margins
111+
})
128112

129113
const ProfilePhotoContainer = styled.div(({ theme }) => ({
130114
display: "flex",
@@ -382,22 +366,20 @@ const DashboardPage: React.FC<{
382366

383367
return (
384368
<Background>
385-
<Page>
386-
<LearningResourceDrawer />
387-
<DashboardContainer>
388-
<DashboardGrid>
389-
<TabContext value={tabValue}>
390-
<DashboardGridItem>
391-
<MobileOnly>{mobileTabs}</MobileOnly>
392-
<DesktopOnly>{desktopTabs}</DesktopOnly>
393-
</DashboardGridItem>
394-
<DashboardGridItem>
395-
<TabPanelStyled value={tabValue}>{children}</TabPanelStyled>
396-
</DashboardGridItem>
397-
</TabContext>
398-
</DashboardGrid>
399-
</DashboardContainer>
400-
</Page>
369+
<PageContainer>
370+
<DashboardGrid>
371+
<TabContext value={tabValue}>
372+
<DashboardGridItem>
373+
<MobileOnly>{mobileTabs}</MobileOnly>
374+
<DesktopOnly>{desktopTabs}</DesktopOnly>
375+
</DashboardGridItem>
376+
<DashboardGridItem>
377+
<TabPanelStyled value={tabValue}>{children}</TabPanelStyled>
378+
</DashboardGridItem>
379+
</TabContext>
380+
</DashboardGrid>
381+
</PageContainer>
382+
<LearningResourceDrawer />
401383
</Background>
402384
)
403385
}

frontends/main/src/app-pages/DashboardPage/OrganizationContent.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ const OrgProgramDisplay: React.FC<{
120120
{transform.sortDashboardCourses(program, courses).map((course) => (
121121
<DashboardCardStyled
122122
Component="li"
123-
key={course.id}
123+
key={course.key}
124124
dashboardResource={course}
125125
courseNoun="Module"
126126
offerUpgrade={false}
@@ -178,7 +178,7 @@ const OrganizationContentInternal: React.FC<
178178
const programLoading = courseGroups[index].isLoading
179179
return (
180180
<OrgProgramDisplay
181-
key={program.id}
181+
key={program.key}
182182
program={program}
183183
courses={courses}
184184
coursesLoading={courseGroups[index].isLoading}

frontends/main/src/components/GridLayout/GridLayout.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ const columnVariants: Record<ColumnVariant, GridProps> = {
4444
/**
4545
* This is a thin wrapper around MUI's [Grid](https://mui.com/material-ui/react-grid/)
4646
* component specifying some app-specific props.
47+
* @deprecated The grid presets in this component predates formal designs for
48+
* MIT Learn site and should be avoided.
4749
*/
4850
const GridContainer: React.FC<GridContainerProps> = (props) => (
4951
<Grid container columnSpacing={6} {...props} />
@@ -55,6 +57,9 @@ const GridContainer: React.FC<GridContainerProps> = (props) => (
5557
*
5658
* This is a thin wrapper around MUI's [Grid](https://mui.com/material-ui/react-grid/)
5759
* component. This is a `<Grid item />` with breakpoint widths specified by `variant`.
60+
*
61+
* @deprecated The grid presets in this component predates formal designs for
62+
* MIT Learn site and should be avoided.
5863
*/
5964
const GridColumn: React.FC<GridItemProps> = ({ variant, ...others }) => (
6065
<Grid item {...columnVariants[variant]} {...others} />

frontends/main/src/page-components/UserListListing/UserListListing.tsx

Lines changed: 47 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import {
1212
import { Button } from "@mitodl/smoot-design"
1313
import { RiListCheck3 } from "@remixicon/react"
1414
import { useUserListList } from "api/hooks/userLists"
15-
import { GridColumn, GridContainer } from "@/components/GridLayout/GridLayout"
1615
import { manageListDialogs } from "@/page-components/ManageListDialogs/ManageListDialogs"
1716
import { userListView } from "@/common/urls"
1817
import UserListCardCondensed from "@/page-components/UserListCard/UserListCardCondensed"
@@ -71,55 +70,53 @@ const UserListListingComponent: React.FC<UserListListingComponentProps> = (
7170
}, [])
7271

7372
return (
74-
<GridContainer>
75-
<GridColumn variant="single-full">
76-
<Header component="h1" variant="h3">
77-
{title}
78-
</Header>
79-
<section>
80-
<LoadingSpinner loading={isLoading} />
81-
{!data?.results.length && !isLoading ? (
82-
<EmptyListCard>
83-
<Card.Content>
84-
<EmptyList>
85-
<IconContainer>
86-
<RiListCheck3 />
87-
</IconContainer>
88-
<Typography variant="body2">
89-
Create lists to save your courses and materials.
90-
</Typography>
91-
<Button variant="primary" size="large" onClick={handleCreate}>
92-
Create new list
93-
</Button>
94-
</EmptyList>
95-
</Card.Content>
96-
</EmptyListCard>
97-
) : null}
98-
{data ? (
99-
<PlainList itemSpacing={3}>
100-
{data.results?.map((list) => {
101-
return (
102-
<li
103-
key={list.id}
104-
data-testid={`user-list-card-condensed-${list.id}`}
105-
>
106-
<UserListCardCondensed
107-
href={userListView(list.id)}
108-
userList={list}
109-
/>
110-
</li>
111-
)
112-
})}
113-
</PlainList>
114-
) : null}
115-
{data?.results.length && !isLoading ? (
116-
<NewListButton variant="primary" onClick={handleCreate}>
117-
Create new list
118-
</NewListButton>
119-
) : null}
120-
</section>
121-
</GridColumn>
122-
</GridContainer>
73+
<>
74+
<Header component="h1" variant="h3">
75+
{title}
76+
</Header>
77+
<section>
78+
<LoadingSpinner loading={isLoading} />
79+
{!data?.results.length && !isLoading ? (
80+
<EmptyListCard>
81+
<Card.Content>
82+
<EmptyList>
83+
<IconContainer>
84+
<RiListCheck3 />
85+
</IconContainer>
86+
<Typography variant="body2">
87+
Create lists to save your courses and materials.
88+
</Typography>
89+
<Button variant="primary" size="large" onClick={handleCreate}>
90+
Create new list
91+
</Button>
92+
</EmptyList>
93+
</Card.Content>
94+
</EmptyListCard>
95+
) : null}
96+
{data ? (
97+
<PlainList itemSpacing={3}>
98+
{data.results?.map((list) => {
99+
return (
100+
<li
101+
key={list.id}
102+
data-testid={`user-list-card-condensed-${list.id}`}
103+
>
104+
<UserListCardCondensed
105+
href={userListView(list.id)}
106+
userList={list}
107+
/>
108+
</li>
109+
)
110+
})}
111+
</PlainList>
112+
) : null}
113+
{data?.results.length && !isLoading ? (
114+
<NewListButton variant="primary" onClick={handleCreate}>
115+
Create new list
116+
</NewListButton>
117+
) : null}
118+
</section>
119+
</>
123120
)
124121
}
125122

0 commit comments

Comments
 (0)