Migrate off the legacy MUI Grid API (prerequisite for #2770) - #3867
Merged
Merged
Conversation
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 `<Grid item container>`) 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) <noreply@anthropic.com>
`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. `<Grid size={12}>` 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) <noreply@anthropic.com>
OpenAPI ChangesNo changes detected Unexpected changes? Ensure your branch is up-to-date with |
ChristopherChudzicki
marked this pull request as ready for review
August 31, 2026 19:16
Contributor
There was a problem hiding this comment.
Pull request overview
Migrates remaining legacy MUI Grid usage to Grid v2, preparing for the Material UI major upgrade in #2770.
Changes:
- Converts legacy Grid props and imports to Grid2.
- Updates ChoiceBox grid-prop consumers.
- Removes deprecated layout wrappers and legacy barrel exports.
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
frontends/ol-components/src/index.ts |
Removes legacy Grid exports. |
frontends/ol-components/src/components/SelectField/SelectField.stories.tsx |
Migrates story layout to Grid2. |
frontends/ol-components/src/components/Logo/Logo.stories.tsx |
Migrates logo story to Grid2. |
frontends/ol-components/src/components/ChoiceBox/ChoiceBoxFieldRadio.stories.tsx |
Updates radio story sizing. |
frontends/ol-components/src/components/ChoiceBox/ChoiceBoxFieldCheckbox.stories.tsx |
Updates checkbox story sizing. |
frontends/ol-components/src/components/ChoiceBox/ChoiceBoxField.tsx |
Migrates ChoiceBox field layout to Grid2. |
frontends/ol-components/src/components/ChoiceBox/ChoiceBox.tsx |
Updates public grid-prop types. |
frontends/main/src/page-components/ItemsListing/ItemsListingComponent.tsx |
Replaces deprecated wrappers with Grid2. |
frontends/main/src/components/GridLayout/GridLayout.tsx |
Deletes deprecated legacy Grid wrappers. |
frontends/main/src/app-pages/OnboardingPage/OnboardingPage.tsx |
Updates item sizing syntax. |
frontends/main/src/app-pages/LearningPathListingPage/LearningPathListingPage.tsx |
Simplifies layout using Grid2 directly. |
frontends/main/src/app-pages/DashboardPage/ProfileContent.tsx |
Updates profile item sizing syntax. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What are the relevant tickets?
N/A. Prerequisite for #2770 (Renovate's
Update material-ui monorepo (major)).Description (What does it do?)
@mui/material/Gridusages to the v2 Grid API, so Update material-ui monorepo (major) #2770 can be a rename instead of an API migration bundled into a version bump.GridLayout'sGridContainer/GridColumn— already@deprecatedin-repo, and both wrapped the legacy API. Their two consumers now useGriddirectly.Grid/GridPropsre-exports from theol-componentsbarrel, so the old API can't come back by accident.mainis clean on every surface except one invisible 16px shift on/learningpaths, which is staff-only. See Automated Checks.Implementation details
Why v7 forces it. Checked against the published tarballs, since the v6 JSDoc is wrong about v7 — it annotates props "will be removed in v7" that survive to 9.4.0.
7.3.11has noGrid2directory, itsGridtakessize, and the old API is renamedGridLegacy. So on v7 theGrid2path vanishes andGridsilently becomes the v2 API.9.4.0dropsGridLegacyentirely.Gridtherefore doesn't fail to compile on v7 — it changes layout. That's what makes Grid the one hard gate in the bump.@mitodl/smoot-design6.33.4 declares@mui/material: ^7.0.0as a peer while this repo hoists one copy at 6.4.5.The difference that mattered. A legacy
containercarrieswidth: 100%; a v2 container does not. So a container that is also a flex item (legacy<Grid item container>) drops to content width on conversion. The four rows that hit this all usejustifyContent="space-between", which is meaningless at content width, so they get an explicitsize={12}. Containers whose parent isn't itself a container need nothing —display: flexalready makes them fill the parent. (Spacing also moves from negative-margin + padding togap.)Scope. Seven files. Grep by tag name over-reports here:
DepartmentListingPage,TopicsListingPage,SearchDisplayandNewsEventsSectionalready importGrid2 as Grid, andRelatedPlaylist.tsxhas a localstyled.divnamedGridthat isn't MUI at all.ChoiceBoxFieldforwards caller-supplied grid props, so four call sites also moved from{ xs: 3 }to{ size: 3 }— onlytsccaught those, the test suite passed without them.Commit 2, and the one real side effect.
GridContainer/GridColumnalways rendered a container holding one full-width column, which in v2 is two divs and no layout (agapnever renders with one item per row). But<Grid size={12}>was a flex item, which containedListHeaderGrid'smargin: 1rem 0; attached straight toContainer(horizontal padding, nopadding-top) that top margin now collapses through. On/learningpaths,Containerstarts 16px lower and ends 16px shorter — same bottom edge, and the header row's own box is identical, so nothing visible moves. Shipping as-is: any fix just relocates the invisible 16px to a different box. Worth knowing it's there if that edge ever gets a background or a border.Screenshots (if appropriate):
Thumbnails are scaled down — click through for full size. These are for eyeballing; the no-movement claim rests on Automated Checks below.
Public UI — any signed-in learner (`Permission.Authenticated`)
/onboarding/dashboard/profile/dashboard/my-lists/[id]Staff UI — learning-path editors only (`Permission.LearningPathEditor`)
/learningpaths/learningpaths/[id]Automated Checks
Screenshot Comparison. The before/after sets above cover 5 surfaces at 3 viewports. The residual pixel differences are measurement noise rather than layout — re-shooting the same commit reproduces them at the same magnitude, in the same pixels.
Bounding Box Comparison. Dumped
getBoundingClientRect()for every rendered element on both branches: nothing moves anywhere except/learningpaths, whereContainerstarts 16px lower and ends 16px shorter, same bottom edge. Nothing renders differently and the route is staff-only, so that is acceptable.The measurements
Same-commit screenshot controls, worst pair of 4 repeats: 78px at Δ3/255 on
/learningpaths/[id]at 2560x1440, and 36px at Δ1 on/learningpathsmobile — against before/after figures of 200px and 31px, overlapping 85-100% of the same pixel coordinates. Every differing pixel sits on the antialiasedborder-radiuscorner of a card thumbnail. Hardening the capture (--reduced-motion, software rasterization, animations disabled, awaitingdocument.fonts.readyandimg.decode()) closed it at mobile widths but not at 2560x1440.The bounding-box dump is exact by comparison: 3 repeat dumps at one commit gave 0 differing rows of 318 and 568 elements. Boxes are diffed as a multiset rather than keyed by DOM path, because removing a wrapper reparents every descendant and would otherwise swamp the diff. It proves nothing moved — it would not catch a color or z-order change, which is what the screenshots are for.
main/learningpaths/learningpaths/[id]/dashboard/my-lists/[id]/onboarding/dashboard/profileIdentical at all three viewports. The two list-detail pages lose exactly the
margin-left: -48px/padding-left: 48pxwrapper pair, which cancels — content sat at x854 before and after, and nothing new appears./onboardingand/dashboard/profileswap theChoiceBoxFieldgrid from negative-margin spacing togap, so every box moves +12,+12 and shrinks 12x12; the item's content box onmain(656+12, 272+12, 309-12, 71-12) is exactly the branch's border box, which is why those two surfaces are byte-identical in the screenshots. The "different CSS" rows aregap: normal->gap: 0px, equivalent on a flex container, rects identical to the hundredth of a pixel.How can this be tested?
yarn installhas runmainvs this branch at each of these paths, at desktop and mobile widths./dashboard/my-lists/[id],/onboarding,/dashboard/profile/learningpaths,/learningpaths/[id]The two list pages are where
size={12}restores a container width, so a regression would look like a header row hugging its content instead of spanning.