[Chakra exit · Phase 3 · PR 13] frontend: remove the @redpanda-data/ui dependency - #2650
Open
SpicyPete wants to merge 6 commits into
Open
[Chakra exit · Phase 3 · PR 13] frontend: remove the @redpanda-data/ui dependency#2650SpicyPete wants to merge 6 commits into
SpicyPete wants to merge 6 commits into
Conversation
…i dependency Unmounts ChakraProvider from the three app roots and both test-harness render helpers, then takes the package and its five @chakra-ui/* peers and @emotion/css out of package.json and both lockfiles. Deleted: utils/redpanda-theme.ts (the Table.baseStyle kebab-case patch), the three build shims (react-onclickoutside, date-fns-tz and its zonedTimeToUtc deep path) with all four resolver aliases across rsbuild.config.ts and rsbuild.config.federation-test.ts, the @redpanda-data/ui aliases in test.shared.ts and rstest.config.unit.ts with tests/mocks/redpanda-ui.ts, and types/react-19-global-jsx.d.ts — no Console file relies on the global JSX namespace, every bare `JSX.` use already imports the type from react. react-router-dom$ goes with them: it was aliased only so @module-federation/bridge-react's v6 shim could not shadow the real package for @redpanda-data/ui's NavLink/Link. Console declares no react-router-dom dependency and imports it nowhere. Also drops the two rstest console suppressions that existed for @redpanda-data/ui's Popover and DataTable, a dead rs.mock in kowl-json-view.test.tsx, and route-utils' createVisibleSidebarItems (the legacy sidebar helper, zero references) — which lets NavLinkProps.icon take the same union SidebarItem declares instead of `any`. Nine libraries collapse to one install each: @tanstack/react-table 9, react-markdown 10, remark-gfm 4, react-syntax-highlighter 16, react-day-picker 9, @hookform/resolvers 5, remark-emoji 5, react-hook-form 7.78, framer-motion 12. react-datepicker, react-icons, yup, date-fns-tz, @textea/json-viewer, chakra-react-select and emotion leave the tree entirely. The Chakra interop CSS stays for PR 14, which is paired with cloud-ui. Gates: type:check clean, build green, 997 unit + 1438 integration passing, both lockfiles stable across repeat installs. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Contributor
✅ Clean — no registry drift, off-token colours, or ad-hoc classesApp:
Generated by lookout audit-changes. |
…viding CI caught what the local gates could not: `shadowlink-create-page.tsx` imports `DevTool` from `@hookform/devtools`, which was never declared in package.json — it resolved only as a transitive of `@redpanda-data/ui@4.2.0`. Removing that package took it with it, so the clean-install build failed on `Module not found: Can't resolve '@hookform/devtools'` and type-check on TS2307. The local run passed because `bun install` leaves the removed packages' directories behind in node_modules; every gate resolved the stale copy. Verified by reinstalling from the lockfile before re-running them. Declared at ^4.4.0 — the version @redpanda-data/ui was resolving, so this changes nothing about what Console has been running against. It goes in `dependencies`, not `devDependencies`, because the DevTool renders unconditionally in the production bundle. Audited the rest of @redpanda-data/ui's dependency set the same way: @hookform/devtools was the only phantom. The other eight packages Console imports from it (@hookform/resolvers, @tanstack/react-table, react-hook-form, react-markdown, remark-gfm, remark-emoji, react-syntax-highlighter, react-day-picker) are all declared directly. Consequence to be explicit about: @hookform/devtools depends on @emotion/react and @emotion/styled, so emotion stays in the dependency tree (13 packages) even though Chakra is gone. Clearing that means not shipping a form-debugging overlay to users — a real bug, older than this branch, filed rather than fixed here. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Self-review caught a factual error I introduced. The earlier commit's note claimed the @hookform/devtools path "left with @redpanda-data/ui" — but the follow-up commit declared @hookform/devtools directly, so that path did not leave, it became direct. uuid@8.3.2 is still in the tree and @hookform/devtools is its only dependant. Wrong statements are worse in a security-policy file than anywhere else, so the note now says where uuid actually comes from, and records that dropping the production <DevTool> render would retire this ignore outright. The reachability conclusion is unchanged: no direct uuid imports, no caller-provided buffers, not reachable from user input. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Eight findings from a review of this branch, all verified against HEAD before applying. .snyk — my own previous commit's note was still wrong, and self-contradictory: it listed "Docker test tooling" as a second uuid path and then said @hookform/devtools is uuid's only dependant. The lockfile settles it — uuid@8.3.2 has exactly one dependant and testcontainers declares no uuid at all. The retirement condition was wrong too: Snyk resolves the path from the dependency graph, so deleting the <DevTool> render alone would not retire the ignore; removing the dependency is what does. rstest.setup.ts — `/Creating Table Instance/` was the only info-channel pattern, so dropping it in the previous commit left the whole console.info interceptor and its originalInfo capture as an unconditional pass-through that still shadowed the global for 1438 tests. Removed both. Verified no test spies on console.info and no surviving pattern targets that channel. route-utils.tsx — NavLinkProps.group was dead (processSidebarItem never sets it; the sidebar groups off SidebarGroupedItems.group), processSidebarItem's doc comment still named the legacy consumer this branch deleted, and the new SidebarItemIcon alias reached for the ambient UMD `React` namespace in a file that already imports its types explicitly — SVGProps now comes from react. Also: field-description.test.tsx asserted `textContent === ''` to work around a hidden node the Chakra render wrapper used to add, which passes for any markup that renders no text — now toBeEmptyDOMElement(). schema-create.tsx's SVG-override comment and the ui-development skill doc both still attributed the unlayered emotion reset to Console's own Chakra; both now name the cloud-ui host, matching how PR 14 re-scoped the globals.css comments. And rsbuild.config.federation-test.ts spreads a single source, so it just assigns it. Gates: type:check clean, build green, 997 unit + 1438 integration + 1 federation passing, no lint drift. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
SpicyPete
marked this pull request as ready for review
September 11, 2026 21:24
SpicyPete
requested review from
a team,
Mateoc,
eblairmckee,
r-vasquez and
yougotashovel
and removed request for
a team
September 11, 2026 21:24
Review of the full Phase 3 stack against master, plus a repo-wide audit for anything still naming the removed packages. rsbuild.config.ts — a dead `legacyUi` splitChunks cacheGroup still tested for `node_modules/@redpanda-data/ui/` at `priority: 40, enforce: true`. It can never match now, and it outranks the monaco group, so it reads as a live constraint to anyone tuning chunk priorities. A full build emits no `lib-redpanda-ui` chunk either way. rstest.setup.ts — 35 lines of global monkey-patching existed only for Chakra: `userEvent.setup` was wrapped app-wide so `makeFocusPatchWritable()` could re-open `HTMLElement.prototype.focus` for assignment, because Chakra's @zag-js/focus-visible assigned to it. @zag-js is no longer in the tree and user-event's own patchFocus is the only other toucher, so the wrapper, the helper and the `_rawUserEvent` import are all gone. The matchMedia mock stays — the Registry theme provider and the responsive hooks read media queries — but its comment no longer credits Chakra's colorMode polling for it. topics/Tab.Messages/constants.ts — `defaultSelectChakraStyles` and `inlineSelectChakraStyles` were chakra-react-select style objects (they use Chakra's `_hover` pseudo-prop syntax) with zero consumers anywhere in src or tests. chakra-react-select left the tree with the package. rsbuild.config.federation-test.ts — restored the defensive copy of `sharedAliases`. An earlier review round simplified it to pass the object directly, but that module-level object is shared with the rstest projects and rspack normalises `resolve.alias` in place; the comment now says so. no-legacy.md — the skill doc still told agents "Chakra components: throughout codebase (migrate on touch)" and framed the packages as deprecated rather than uninstalled, so it now names the real state and the real fence (TS2307, not lint — biome's noRestrictedImports is level "warn"). Gates: type:check clean, build green, 997 unit + 1438 integration + 1 federation passing, no lint drift. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Pre-existing race, unrelated to Chakra, but it failed `test-integration (1/2)` on this branch and it was worth being sure it was not mine before dismissing it: I had just removed a global `userEvent.setup` monkey-patch in rstest.setup.ts, and the symptom — `expected 'c' to be 'custom:0'`, only the first keystroke landing — looks exactly like broken typing. It is not related. That patch only made user-event's getter-only `HTMLElement.prototype.focus` descriptor accept assignment, for the benefit of Chakra's @zag-js/focus-visible; with @zag-js out of the tree nothing assigns, its `override` stays null and the getter passes straight through, so the patch was a no-op before it was deleted. Evidence: the same commit passes on re-run, the test passes 12/12 and its whole file 8/8 locally, and PR 14 — a superset of this branch — went green on the same shard. The real cause is in the test. `NodeConfigForm` reports through `onConfigChange` from a `useEffect` keyed on react-hook-form's watched values, so the final keystroke's propagation can land after `user.type` resolves, and the assertion read `onConfigChange.mock.calls.at(-1)` synchronously. Wrapped it in `waitFor`; it asserts the same thing. No other test in the file uses `waitFor`, so the same latent race sits across the rest of it — this fixes the one that actually trips. Gates: type:check clean, 1438 integration passing, the file 10/10 on repeat. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
SpicyPete
marked this pull request as draft
September 11, 2026 22:21
SpicyPete
marked this pull request as ready for review
September 11, 2026 23:01
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
Phase 3, PR 13 of the Chakra exit: remove
@redpanda-data/uifrom Console. Phase 2 closed on 2026-09-11 with #2645, #2647 and #2649, leaving five importers — all of them the files that mountChakraProviderrather than use it. This unmounts them and takes the dependency out.mastergoes from 5 importers to 0, and the baregit grepfrom 13 to 0.Removed
The provider, from the three app roots (
app.tsx,embedded-app.tsx,federation/federated-providers.tsx) and bothtest-utils.tsxrender helpers.Dependencies —
@redpanda-data/ui, the five@chakra-ui/*peers,@emotion/css; both lockfiles regenerated with the pinned bun 1.4.0.Files —
utils/redpanda-theme.ts(theTable.baseStylekebab-case patch), all three build shims (react-onclickoutside,date-fns-tz, and itszonedTimeToUtcdeep path),types/react-19-global-jsx.d.ts,tests/mocks/redpanda-ui.ts.src/shims/andsrc/utils/vendor/are now empty and gone.All four resolver aliases, which lived in more places than the tracker recorded —
rsbuild.config.ts,rsbuild.config.federation-test.ts,test.shared.tsandrstest.config.unit.ts. Thesrc+testsmetric could not see the last three because they sit at thefrontend/root.Two rstest console suppressions that existed only for this package's Popover (
isInPortal) and DataTable (debugTable: true). The nested-<button>suppression stays —consumers/group-details.tsxnow renders the Registry Accordion, which has the same shape, so only its attribution changed.Three judgment calls worth a reviewer's eye
react-router-dom$went too. It was aliased only so@module-federation/bridge-react's v6 shim could not shadow the real package for@redpanda-data/ui'sNavLink/Link. Console declares noreact-router-domdependency and imports it nowhere, so the alias now points at a path that need not exist.test:federationpasses.react-19-global-jsx.d.tsis safe to delete. Its stated reason was the react-markdown@8 vendored by this package, but Console's own code also uses the globalJSXnamespace — so I checked rather than assumed: all 22 files that write a bareJSX.alreadyimport type { JSX } from 'react', so zero rely on the global shim.type:checkis clean without it.defaultTheme="light"stays. Chakra pinned light mode and the RegistryThemeProvidernow carries that alone. Flipping it tosystemis a user-visible change onto a page with known dark-mode defects (the hard-light rp-connect YAML editor, the near-invisible Builder.io headlines), so it belongs to the dark-theme pass, not here.Deliberately not in this PR
globals.csshacks,body.chakra-ui-dark, the twonav.chakra-*selectors, the dead.ant-*rules. That is PR 14, which is a paired cross-repo change with cloud-ui. A built CSS chunk still carries them, as expected at this step.biome.jsoncnoRestrictedImportsentries for@redpanda-data/uiand@chakra-ui/reactare kept, but not as the regression fence I first called them — review caught that. The rule's level is"warn", and CI's lint gate isultracite fixfailing on a dirty tree, so a warn leaves nothing dirty and gates nothing. The real fence is the one this PR creates for free:type:checknow fails with TS2307 on any re-added import, because the packages are simply not installed. The entries stay as a developer-facing hint inlint:checkoutput. (For the record, all five paths in that rule block — includingmobx,mobx-react-liteandyup— now have zero live importers.)Dependency result
Nine libraries collapse to a single install each —
@tanstack/react-table9 (was 8+9), react-markdown 10 (8+10), remark-gfm 4 (3+4), react-syntax-highlighter 16 (15+16), react-day-picker 9 (8+9),@hookform/resolvers5 (3+5), remark-emoji 5 (3+5), react-hook-form 7.78 (was also 7.66), framer-motion 12 (was also 10).react-datepicker,react-icons,yup,date-fns-tz,@textea/json-viewerandchakra-react-selectleave the tree entirely, along with 78 MB ofnode_modules(54@redpanda-data+ 24@chakra-ui).Emotion does not leave, and CI is what proved it.
shadowlink-create-page.tsximportsDevToolfrom@hookform/devtools— a package never declared inpackage.json, which resolved only as a transitive of@redpanda-data/ui. Removing Chakra took it with it and the clean-install build failed onModule not found. My local gates had passed becausebun installleaves removed packages' directories behind innode_modules, so every one of them resolved a stale copy; I now re-ran them against a reinstall from the lockfile. It is declared at^4.4.0(the version Chakra was resolving, so nothing changes about what Console runs against) independenciesrather thandevDependencies, because that DevTool renders unconditionally in the production bundle — a form-debugging overlay shipping to users, older than this branch. Since@hookform/devtoolsdepends on@emotion/reactand@emotion/styled, emotion stays at 13 packages.So the "emotion absent from the dependency tree" line in the definition of done is now blocked on one line of unrelated code, not on Chakra. Deleting that
<DevTool>render is a small follow-up that fixes a real bug and collects the last of the payoff; I have flagged it rather than folding it in here.I audited the rest of
@redpanda-data/ui's dependency set the same way —@hookform/devtoolswas the only phantom. The other eight packages Console imports from it are all declared directly.A correction to my own earlier claim here, caught in review: I had written that
@emotion/is-prop-validleaves with Chakra and thatmotiontherefore falls back to its own prop filter. That is wrong.mastercarried two copies — Chakra's 0.8.8 and framer-motion's own 1.4.0 — and 1.4.0 is still hoisted top-level, somotionresolves it before and after and no fallback ever happens. The real latent risk runs the other way:motion's prop filtering will change silently on the day@hookform/devtoolsis dropped, which is worth knowing before doing it.Gates
type:checkcleanbun run buildgreen; the bundle contains no@redpanda-data/ui,chakra-uiJS orframer-motion10lint:check(ANSI-stripped, one file per invocation): zero net-new diagnostics, and threenoRestrictedImportseliminated. Compared by swapping master's content in at the same path — a differently-named probe file silently gets a different rule set, becausebiome.jsonckeys overrides on**/*.test.tsxandrstest.setup*.ts.bun installruns, so CI's dirty-tree check will not tripReview
Reviewed at high effort; twelve findings, eight applied on the branch (see
fix(review):commits). The three that mattered most were all in my own additions: the.snyknote I had already "fixed" once was still self-contradictory, dropping theCreating Table Instancepattern left the wholeconsole.infointerceptor as a pass-through no-op, and the two body claims corrected above.Not applied, deliberately:
<DevTool>render itself. Gating it and moving the dependency todevDependenciesis the highest-value follow-up — it retires the Snyk ignore and finally takes emotion out — but it is a behaviour change plus a lockfile regeneration, well outside "remove the dependency".error. It would not gate CI either, sinceultracite fixexits 0 with errors present.Second review, on the full stack
Reviewed again with both PRs as one diff against
master, plus a repo-wide audit for anything still naming the removed packages. Four more pieces of residue in this PR, now fixed:legacyUisplitChunks cacheGroup inrsbuild.config.ts, still testingnode_modules/@redpanda-data/ui/atpriority: 40, enforce: true— outranking the monaco group, so it reads as a live constraint.rstest.setup.ts:userEvent.setupwas wrapped app-wide soHTMLElement.prototype.focuscould be re-opened for assignment, purely because Chakra's@zag-js/focus-visibleassigned to it.@zag-jsis gone. (ThematchMediamock stays — the Registry theme provider needs it — but no longer credits Chakra for it.)defaultSelectChakraStyles/inlineSelectChakraStylesintopics/Tab.Messages/constants.ts—chakra-react-selectstyle objects, zero consumers.no-legacy.mdstill told agents "Chakra components: throughout codebase (migrate on touch)".It also caught that an earlier round's "simplification" of
rsbuild.config.federation-test.tsintroduced a real aliasing bug — passing the module-levelsharedAliasesobject straight to rspack, which normalisesresolve.aliasin place, while the rstest projects share that same object. The defensive copy is back with a comment saying why.One thing the review corrected about my reasoning rather than my code: judgment call 2 above is right, but my grep was lucky —
test-utils.tsxdoes write bareJSX.Element, and type-checks only because it importstype JSXinside a multi-line react import that a single-line pattern misses. The conclusion holds (@types/react@19declares no globalJSX, confirmed with a probe that errors TS2503), but the audit was weaker than I described.Still wants a human
An embedded / Cloud UI pass:
federated-providers.tsxis the host path and nothing in this repo exercises it end to end.🤖 Generated with Claude Code