Skip to content

fix(ui): restore a green Frontend Quality job - #98

Merged
rennerdo30 merged 3 commits into
mainfrom
fix/frontend-lint-react-hooks
Sep 5, 2026
Merged

fix(ui): restore a green Frontend Quality job#98
rennerdo30 merged 3 commits into
mainfrom
fix/frontend-lint-react-hooks

Conversation

@rennerdo30

Copy link
Copy Markdown
Owner

main has been red since late July. Every job except Frontend Quality passes on the current tip (2294805); Frontend Quality fails at its Lint step, which also hides two further breakages in the Test step behind it. This PR fixes all three, one commit per cause.

1. react-hooks lint errors (npm run lint, exit 1)

src/app/[locale]/errors/page.tsx
  43:14  error  Calling setState synchronously within an effect can trigger cascading renders  react-hooks/set-state-in-effect

src/components/file-upload.tsx
  71:58  error  Cannot access variable before it is declared  react-hooks/immutability

✖ 15 problems (2 errors, 13 warnings)

Both rules ship with the react-hooks plugin bundled by eslint-config-next 16.3.x. formatSize was duplicated verbatim in file-upload.tsx and file-browser.tsx, so it moves to lib/utils.ts as formatBytes (module scope, named constants, unit-tested) and both components consume it. The errors page moves to React Query, matching every other data-loading page in the app, which removes the offending effect rather than silencing it.

2. Radix primitives no longer expose displayName

Recent Radix releases (@radix-ui/react-label 2.1.15, react-select 2.3.7, react-tabs 1.1.21, …) dropped displayName from their primitives — grep -c displayName node_modules/@radix-ui/react-label/dist/index.mjs returns 0. Every wrapper that copied it (Label.displayName = LabelPrimitive.Root.displayName) therefore had undefined, failing 8 assertions and leaving anonymous ForwardRef nodes in React DevTools. Each wrapper now declares its own name, as the non-Radix ui components already did.

3. Popover dismissal is deferred to the following click

@radix-ui/react-popover now passes deferPointerDownOutside: true, so the dismissable layer waits for the click after an outside pointerdown (a drag ending outside no longer closes the popover). The test fired only pointerDown and never observed onOpenChange; it now simulates the full interaction.

Verification

Ran exactly what CI runs, from a clean origin/main checkout:

step before after
npm run lint 2 errors, 13 warnings — exit 1 0 errors, 13 warnings
npm run test:run 9 failed / 704 passed 716 passed (28 files)
npm run build never reached succeeds

The 13 remaining no-unused-vars warnings are pre-existing and non-fatal; they are left alone so this change stays scoped to the breakage.

Unblocks the open Dependabot PRs, which currently inherit this failure and cannot be judged on their own merits.

The `Frontend Quality` job fails on main because `npm run lint` reports two
errors from the react-hooks rules that ship with eslint-config-next 16.3.x:

* `react-hooks/immutability` in `file-upload.tsx` - `formatSize` was declared
  after the `handleFileSelect` callback that reads it.
* `react-hooks/set-state-in-effect` in the errors page - the effect called a
  fetch helper that synchronously flipped the loading state.

`formatSize` was duplicated in `file-upload.tsx` and `file-browser.tsx`, so it
moves to `lib/utils.ts` as `formatBytes` (module scope, covered by tests) and
both components use it. The errors page moves to React Query, matching every
other data-loading page in the app, which removes the effect entirely.
Recent Radix UI releases no longer assign `displayName` to their primitives, so
every wrapper that copied it (`Label.displayName = LabelPrimitive.Root.displayName`
and friends) ended up with `undefined`. That is invisible in the browser but
breaks eight assertions in the component suites and leaves React DevTools
showing anonymous `ForwardRef` nodes.

Each wrapper now declares its own name, which is what the rest of the ui
components already did. The scroll bar keeps the name of the exported
component, `ScrollBar`, instead of the Radix-internal `ScrollAreaScrollbar`.
Radix now passes `deferPointerDownOutside: true` for popovers, so an outside
pointerdown no longer dismisses on its own - the layer waits for the click that
follows, which stops a drag that ends outside the popover from closing it. The
test fired only `pointerDown` and therefore never saw `onOpenChange`.
@rennerdo30
rennerdo30 merged commit 7ab7419 into main Sep 5, 2026
6 checks passed
@rennerdo30
rennerdo30 deleted the fix/frontend-lint-react-hooks branch September 5, 2026 06:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant