From 1012106be2264cd496086398c74457a0756949bc Mon Sep 17 00:00:00 2001 From: Chris Chudzicki Date: Mon, 31 Aug 2026 12:16:02 -0400 Subject: [PATCH 1/2] refactor(mui): migrate off the legacy MUI Grid API Move every remaining `@mui/material/Grid` usage to the v2 Grid API (`@mui/material/Grid2`), which is a prerequisite for the @mui/material v7 bump: v7 removes the `Grid2` import path and repoints `Grid` at the v2 API, renaming the old one to `GridLegacy`. Doing the API move on v6 first keeps the version bump a mechanical rename. The two APIs lay out differently, so this is not purely cosmetic: - Legacy Grid spaces items with a negative container margin plus per-item padding; the v2 Grid uses `gap` and subtracts the gap from item widths. - A legacy `container` carried `width: 100%`. A v2 container does not, so a container that is also a flex item (legacy ``) needs an explicit `size` to stay full-width. The three converted rows in ItemsListingComponent and the one in LearningPathListingPage get `size={12}` for this reason; containers whose parent is not itself a container need nothing, since a block-level flex box already fills its parent. Also drop GridLayout's `GridContainer`/`GridColumn`. Both were already marked `@deprecated` in-repo as predating the site's formal designs, and both wrapped the legacy API; their two consumers now use Grid directly with the column widths inlined. The legacy `Grid` re-export is removed from the ol-components barrel so the old API cannot come back by accident. Co-Authored-By: Claude Opus 5 (1M context) --- .../DashboardPage/ProfileContent.tsx | 2 +- .../LearningPathListingPage.tsx | 15 ++-- .../OnboardingPage/OnboardingPage.tsx | 2 +- .../src/components/GridLayout/GridLayout.tsx | 68 ------------------- .../ItemsListing/ItemsListingComponent.tsx | 27 ++++---- .../src/components/ChoiceBox/ChoiceBox.tsx | 4 +- .../components/ChoiceBox/ChoiceBoxField.tsx | 6 +- .../ChoiceBoxFieldCheckbox.stories.tsx | 2 +- .../ChoiceBox/ChoiceBoxFieldRadio.stories.tsx | 2 +- .../src/components/Logo/Logo.stories.tsx | 12 ++-- .../SelectField/SelectField.stories.tsx | 8 +-- frontends/ol-components/src/index.ts | 2 - 12 files changed, 36 insertions(+), 114 deletions(-) delete mode 100644 frontends/main/src/components/GridLayout/GridLayout.tsx diff --git a/frontends/main/src/app-pages/DashboardPage/ProfileContent.tsx b/frontends/main/src/app-pages/DashboardPage/ProfileContent.tsx index bf9361aea8..3cb94251ba 100644 --- a/frontends/main/src/app-pages/DashboardPage/ProfileContent.tsx +++ b/frontends/main/src/app-pages/DashboardPage/ProfileContent.tsx @@ -135,7 +135,7 @@ const ProfileContent: React.FC = () => { xs: 3, }, }} - gridItemProps={{ xs: 3 }} + gridItemProps={{ size: 3 }} /> { className="learningpaths-page" > - - + + - + Learning Paths { )} - - + + ) diff --git a/frontends/main/src/app-pages/OnboardingPage/OnboardingPage.tsx b/frontends/main/src/app-pages/OnboardingPage/OnboardingPage.tsx index 5ed32e1fa3..c96cb9318e 100644 --- a/frontends/main/src/app-pages/OnboardingPage/OnboardingPage.tsx +++ b/frontends/main/src/app-pages/OnboardingPage/OnboardingPage.tsx @@ -140,7 +140,7 @@ const GridStyle = ( columns: columns, maxWidth: maxWidth, }, - gridItemProps: { xs: 3 }, + gridItemProps: { size: 3 }, } } diff --git a/frontends/main/src/components/GridLayout/GridLayout.tsx b/frontends/main/src/components/GridLayout/GridLayout.tsx deleted file mode 100644 index d18de6aa51..0000000000 --- a/frontends/main/src/components/GridLayout/GridLayout.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import React from "react" -import { Grid } from "ol-components" -import type { GridProps } from "ol-components" - -type GridContainerProps = Omit< - GridProps, - "item" | "container" | "columnSpacing" -> & { component?: React.ElementType } - -type ColumnVariant = - | "single-full" - | "main-2" - | "sidebar-2" - | "main-2-wide-main" - | "sidebar-2-wide-main" -type GridItemProps = Omit< - GridProps, - "item" | "xs" | "sm" | "md" | "lg" | "xl" -> & { variant: ColumnVariant; component?: React.ElementType } - -const columnVariants: Record = { - "single-full": { - xs: 12, - md: 12, - }, - "main-2": { - xs: 12, - md: 9, - }, - "sidebar-2": { - xs: 12, - md: 3, - }, - "main-2-wide-main": { - xs: 12, - md: 8, - }, - "sidebar-2-wide-main": { - xs: 12, - md: 4, - }, -} - -/** - * This is a thin wrapper around MUI's [Grid](https://mui.com/material-ui/react-grid/) - * component specifying some app-specific props. - * @deprecated The grid presets in this component predates formal designs for - * MIT Learn site and should be avoided. - */ -const GridContainer: React.FC = (props) => ( - -) - -/** - * Represents a grid column and accepts a `variant` prop that determines the - * column width. - * - * This is a thin wrapper around MUI's [Grid](https://mui.com/material-ui/react-grid/) - * component. This is a `` with breakpoint widths specified by `variant`. - * - * @deprecated The grid presets in this component predates formal designs for - * MIT Learn site and should be avoided. - */ -const GridColumn: React.FC = ({ variant, ...others }) => ( - -) - -export { GridContainer, GridColumn } diff --git a/frontends/main/src/page-components/ItemsListing/ItemsListingComponent.tsx b/frontends/main/src/page-components/ItemsListing/ItemsListingComponent.tsx index 62fd51a43a..007be9698b 100644 --- a/frontends/main/src/page-components/ItemsListing/ItemsListingComponent.tsx +++ b/frontends/main/src/page-components/ItemsListing/ItemsListingComponent.tsx @@ -1,9 +1,8 @@ import React from "react" -import { Grid, Typography, styled } from "ol-components" +import { Grid2 as Grid, Typography, styled } from "ol-components" import { Button, ButtonLink } from "@mitodl/smoot-design" import { RiArrowLeftLine, RiArrowUpDownLine } from "@remixicon/react" import { useToggle, pluralize } from "ol-utilities" -import { GridColumn, GridContainer } from "@/components/GridLayout/GridLayout" import ItemsListing from "./ItemsListing" import type { LearningResourceListItem } from "./ItemsListing" import { MY_LISTS } from "@/common/urls" @@ -84,17 +83,17 @@ const ItemsListingComponent: React.FC = ({ const count = list?.item_count return ( - - + + - + = ({ - + {list?.title} {list?.description && ( {list.description} )} - + {canEdit ? ( Edit List @@ -126,12 +125,12 @@ const ItemsListingComponent: React.FC = ({ - + {showSort && !!items.length && ( )} - + {count !== undefined && count > 0 ? ( {`${count} ${pluralize("item", count)}`} ) : null} @@ -159,8 +158,8 @@ const ItemsListingComponent: React.FC = ({ emptyMessage="There are no items in this list yet." condensed={condensed} /> - - + + ) } diff --git a/frontends/ol-components/src/components/ChoiceBox/ChoiceBox.tsx b/frontends/ol-components/src/components/ChoiceBox/ChoiceBox.tsx index 4fe50f8e3f..77f738bd03 100644 --- a/frontends/ol-components/src/components/ChoiceBox/ChoiceBox.tsx +++ b/frontends/ol-components/src/components/ChoiceBox/ChoiceBox.tsx @@ -1,6 +1,6 @@ import React from "react" import styled from "@emotion/styled" -import { type GridProps } from "@mui/material/Grid" +import { type Grid2Props } from "@mui/material/Grid2" import { Checkbox } from "@mitodl/smoot-design" import { Radio } from "../Radio/Radio" @@ -110,7 +110,7 @@ interface ChoiceBoxChoice { description?: string } -type FieldGridProps = Omit +type FieldGridProps = Omit interface ChoiceBoxGridProps { gridProps?: FieldGridProps diff --git a/frontends/ol-components/src/components/ChoiceBox/ChoiceBoxField.tsx b/frontends/ol-components/src/components/ChoiceBox/ChoiceBoxField.tsx index eede76afae..3bb74143e5 100644 --- a/frontends/ol-components/src/components/ChoiceBox/ChoiceBoxField.tsx +++ b/frontends/ol-components/src/components/ChoiceBox/ChoiceBoxField.tsx @@ -3,7 +3,7 @@ import styled from "@emotion/styled" import FormControl from "@mui/material/FormControl" import FormGroup from "@mui/material/FormGroup" import FormLabel from "@mui/material/FormLabel" -import Grid, { type GridProps } from "@mui/material/Grid" +import Grid, { type Grid2Props } from "@mui/material/Grid2" import { theme } from "../ThemeProvider/ThemeProvider" import { ChoiceBox } from "./ChoiceBox" import type { @@ -44,7 +44,7 @@ const ChoiceBoxField: React.FC = ({ gridProps, gridItemProps, }: ChoiceBoxFieldProps) => { - const fieldGridProps: GridProps = { + const fieldGridProps: Grid2Props = { spacing: "12px", justifyContent: "center", columns: { @@ -65,7 +65,7 @@ const ChoiceBoxField: React.FC = ({ {choices.map((choice, index) => ( - + { }, }} gridItemProps={{ - xs: 4, + size: 4, }} /> ) diff --git a/frontends/ol-components/src/components/ChoiceBox/ChoiceBoxFieldRadio.stories.tsx b/frontends/ol-components/src/components/ChoiceBox/ChoiceBoxFieldRadio.stories.tsx index 400b22fd61..896ea85166 100644 --- a/frontends/ol-components/src/components/ChoiceBox/ChoiceBoxFieldRadio.stories.tsx +++ b/frontends/ol-components/src/components/ChoiceBox/ChoiceBoxFieldRadio.stories.tsx @@ -37,7 +37,7 @@ const StateWrapper = (props: RadioChoiceBoxFieldProps) => { }, }} gridItemProps={{ - xs: 4, + size: 4, }} /> ) diff --git a/frontends/ol-components/src/components/Logo/Logo.stories.tsx b/frontends/ol-components/src/components/Logo/Logo.stories.tsx index 7a1c257f69..8c9156f9a2 100644 --- a/frontends/ol-components/src/components/Logo/Logo.stories.tsx +++ b/frontends/ol-components/src/components/Logo/Logo.stories.tsx @@ -1,7 +1,7 @@ import React from "react" import type { Meta, StoryObj } from "@storybook/nextjs" import { PlatformLogo, PLATFORM_LOGOS } from "./Logo" -import Grid from "@mui/material/Grid" +import Grid from "@mui/material/Grid2" import styled from "@emotion/styled" import { PlatformEnum } from "api" @@ -22,20 +22,18 @@ const meta: Meta = { render: ({ showIconBackground, iconHeight }) => { return ( - + Note: the showIconBackground and{" "} iconHeight args are only for this story. Not applicable to the actual component. {Object.entries(PLATFORM_LOGOS).map(([platformCode, platform]) => ( - + {platformCode} - - {platform.name} - - + {platform.name} + {STATES.map(({ label, extraProps }) => ( <> - - {label} - - + {label} + {ITEMS.map((item) => ( Date: Mon, 31 Aug 2026 12:40:56 -0400 Subject: [PATCH 2/2] refactor(mui): drop the no-op grid wrapper left by GridContainer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `GridContainer`/`GridColumn` always rendered a container holding a single full-width column, so the pair contributed two divs and no layout: a v2 container spaces items with `gap`, and a gap never renders when there is one item per row. Inlining them in the previous commit preserved that shape to keep the API migration reviewable on its own; this removes it. The wrapper was not quite free. `` is a flex item, which establishes an independent formatting context, so `ListHeaderGrid`'s vertical margins could not collapse out of it — removing the wrapper puts them in `Container`'s block formatting context, where they can. Verified they don't: against the pre-migration render, `/learningpaths` differs only inside a 2x40px region near the page bottom, with the heading and every element above it byte-identical. Collapsing a 1rem margin would have shifted the page. Co-Authored-By: Claude Opus 5 (1M context) --- .../LearningPathListingPage.tsx | 76 ++++----- .../ItemsListing/ItemsListingComponent.tsx | 144 +++++++++--------- 2 files changed, 104 insertions(+), 116 deletions(-) diff --git a/frontends/main/src/app-pages/LearningPathListingPage/LearningPathListingPage.tsx b/frontends/main/src/app-pages/LearningPathListingPage/LearningPathListingPage.tsx index a15c2c5e54..8a14025871 100644 --- a/frontends/main/src/app-pages/LearningPathListingPage/LearningPathListingPage.tsx +++ b/frontends/main/src/app-pages/LearningPathListingPage/LearningPathListingPage.tsx @@ -83,50 +83,40 @@ const LearningPathListingPage: React.FC = () => { className="learningpaths-page" > - - - - - - Learning Paths - - - - {canEdit ? ( - - ) : null} - - -
- - {listingQuery.data && ( - - {listingQuery.data.results?.map((resource) => { - return ( -
  • - - ) : null - } - /> -
  • - ) - })} -
    - )} -
    + + + + Learning Paths + -
    + + {canEdit ? ( + + ) : null} + + +
    + + {listingQuery.data && ( + + {listingQuery.data.results?.map((resource) => { + return ( +
  • + : null + } + /> +
  • + ) + })} +
    + )} +
    ) diff --git a/frontends/main/src/page-components/ItemsListing/ItemsListingComponent.tsx b/frontends/main/src/page-components/ItemsListing/ItemsListingComponent.tsx index 007be9698b..4e8a217bab 100644 --- a/frontends/main/src/page-components/ItemsListing/ItemsListingComponent.tsx +++ b/frontends/main/src/page-components/ItemsListing/ItemsListingComponent.tsx @@ -83,83 +83,81 @@ const ItemsListingComponent: React.FC = ({ const count = list?.item_count return ( - - - - - - } + <> + + + + } + > + My Lists + + + + + + {list?.title} + {list?.description && ( + {list.description} + )} + + + {canEdit ? ( + + Edit List + + ) : null} + + + + + {showSort && !!items.length && ( + + )} - - - {list?.title} - {list?.description && ( - {list.description} - )} - - - {canEdit ? ( - - Edit List - - ) : null} - + + {count !== undefined && count > 0 ? ( + {`${count} ${pluralize("item", count)}`} + ) : null} - - - {showSort && !!items.length && ( - - )} - - - {count !== undefined && count > 0 ? ( - {`${count} ${pluralize("item", count)}`} - ) : null} - - - - + - + + ) }