Skip to content

fix(frontend): keep the filter controls on the screen on a phone - #120

Merged
pdevito3 merged 9 commits into
mainfrom
fix/issue-114-filter-submenu-phone
Oct 25, 2026
Merged

pdevito3 merged 9 commits into
mainfrom
fix/issue-114-filter-submenu-phone

Conversation

@pdevito3

Copy link
Copy Markdown
Owner

Why

The Add Filter menu opened the date, number, boolean, and multiselect controls in a submenu beside the menu. On a phone there is no room beside the menu, so the controls went off the screen and the page scrolled sideways. React Aria flips a side popover to the other side, but it never moves the popover along that axis, so no collision option can keep a side submenu on the screen. When the screen has no room for a submenu, the menu now opens the control in the filter dialog that the text control already uses. On a phone that dialog is a bottom sheet.

Closes #114

What changed

  • FilterPropertyMenu shows a submenu only at (width >= 64rem). The widest control, date Between, needs about 911 px beside the sidebar. Below that width each control opens in FilterControlDialog.
  • The submenu popover scrolls inside the max height that React Aria gives it. Before, the popover ignored that height, so on a short window a tall control went below the screen (Scheduled At (Local) at 1280x512: bottom at 530 px).
  • useMediaQuery reads the query on the first render, with useSyncExternalStore, and useIsMobile uses it with (width < 48rem), the md breakpoint of Tailwind. Before, useIsMobile returned false on the first render. So on a phone each ResponsiveDialog mounted with the blur of the desktop dialog animation, became a sheet, and stayed blurred. The text filter sheet on main has this fault.
  • The number, boolean, and multiselect controls fill the sheet below md.

Scope

  • Covers the Add Filter menu at all screen sizes. From 1024 px wide, the submenus do not change, except that a submenu taller than the window now scrolls.
  • Leaves the label inside the boolean control, which repeats the dialog title.

Blast Radius

useIsMobile also drives ResponsiveDialog and the sidebar. Both now get the right value on the first render, not one render later. From 768 px to 1023 px, and on a phone on its side, the controls now open in a centered dialog in place of a submenu. At 1024 px and 1280 px every rect matches main.

Verification

  • New functional spec frontend/tests/features/filter-demo/. On main, the controls fail toBeInViewport at 390x844 (Created Date ratio 0.354), 844x390 (0.737), and 820x1180 (0.815), the five phone sheets have blur(4px), and at 1280x512 the Scheduled At (Local) control ends at 530.28 px, below the 512 px window. With the fix, 10 pass, and 100 pass with --repeat-each=10.
  • A probe in light and dark at 390x844, 667x375, 844x390, 768x1024, 820x1180, 1024x768, 1280x800, and 1280x512. Created Date at 390 px: x=256 to 634 and scrollWidth 634 before, x=0 to 390 and scrollWidth 390 after. At 844x390 and 820x1180, scrollWidth was 890 before and equals the viewport after. At 1280x800 every submenu rect matches main. At 1280x512 every submenu ends at y=500.
  • pnpm lint, pnpm build, pnpm test (80 passed), and pnpm test:e2e (21 passed).
Screen Before After
390 px, Created Date, light
390 px, Created Date, dark
390 px, Comment Count, dark
390 px, First Name (blur)
844x390, Created Date, dark
820 px, Created Date, light
1280 px, Created Date, light

Is Blocked, Status, and the menu at 390 px after the fix:

667x375 before, after, and after with the sheet scrolled to Add Filter:

A control in a submenu beside the Add Filter menu has no room on a phone.
React Aria flips a side popover to the other side, but it never moves it
along that axis, so the date, number, boolean, and multiselect controls
went off the screen and the page scrolled sideways. On a phone, the menu
now opens each control in the filter dialog, as it already did for text.
useIsMobile returned false on the first render and read the media query
in an effect. So on a phone, ResponsiveDialog mounted with the animation
of the desktop dialog, which starts from a blur, and then became a bottom
sheet. The sheet animation has no filter target, so the sheet stayed
blurred. useIsMobile now reads the media query with useSyncExternalStore.
…ltiselect controls

These controls have the fixed width of a submenu. In the bottom sheet on
a phone they filled only part of the width. Below the md breakpoint they
now fill the sheet.
… has no room for a submenu

From 768 px to about 906 px wide, and on a phone on its side, the date
submenu still went off the screen. The menu now shows a submenu only at
64rem wide and 32rem high or more. useMediaQuery reads the query on the
first render, and useIsMobile uses it with the md breakpoint of Tailwind
in rem.
React Aria sets the max height of a popover to the room on the screen,
but the submenu popover let the control overflow it. So on a short
window, a tall control such as Scheduled At went below the screen. The
submenu popover now scrolls. The height limit of the media query is
not necessary any more, so the menu checks only the width.
@pdevito3

Copy link
Copy Markdown
Owner Author

Open findings from the two review rounds, with the reason each stays open:

  • Tests do not pin the exact 64rem threshold. The specs check 820x1180 and 844x390 (dialog) and 1280x800 and 1280x512 (submenu), but not 1023 px against 1024 px. The threshold comes from the width of the date Between control (about 911 px beside the sidebar). A test at the edge would pin a number that changes with the sidebar, not a behavior.
  • No test for the w-full md:w-* width of the number, boolean, and multiselect controls. The phone specs check that each sheet is on the screen and that the page does not scroll sideways, but not that the control fills the sheet. The screenshots in the PR body show it.
  • useMediaQuery makes a new MediaQueryList on each snapshot read. matchMedia is cheap, and the hook returns a boolean, so React does not re-render for it. Kept as is.
  • The dialog-surface item shows the arrow icon on a desktop only. On a phone it shows the text icon, the same as the text control, because the item opens a sheet, not a submenu. Kept by design.
  • The last commit (overflow-y-auto on the submenu popover) was not re-reviewed. It came from the medium finding of the second round. The 1280x512 spec proves it red (control bottom at 530.28 px) and green. Firefox, WebKit, and Chromium show no scrollbar on the desktop submenus at 1280x800.

Other faults seen, not fixed here:

  • The boolean control's inner "Is Blocked" label repeats the dialog title.
  • The TanStack devtools badge covers the top right corner on a phone.

@pdevito3

Copy link
Copy Markdown
Owner Author

Correction to item 4 of the earlier comment: on a phone and a tablet, a submenu-type item that opens as a dialog shows the arrow icon (ArrowRight01Icon, filter-property-menu.tsx:143-148), the same as DropdownMenuSubTrigger. It does not show the text icon. The code is right.

@pdevito3
pdevito3 merged commit 55c158c into main Oct 25, 2026
16 checks passed
@pdevito3
pdevito3 deleted the fix/issue-114-filter-submenu-phone branch October 25, 2026 11:24
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.

fix(frontend): the Created Date filter submenu goes off the screen on a phone

1 participant