Mcpcatalog filters adjustments - #40
Conversation
The filters panel was a fixed w-80 with a height-capped tag list, so a catalog with many tags scrolled a 160px box while the panel itself had room to grow. Widen it in steps and let the tag list take the remaining height, then clamp the panel to what Radix measured between the trigger and the viewport edge (--radix-popover-content-available-height, plus collisionPadding) so it can never run off screen at any width. The tag list moves from a fieldset to role=group: a rendered legend is not subtracted from the height flex assigns its fieldset, so the scroll box overflowed it. It also lays out as a grid rather than CSS columns, because a height-capped multi-column box overflows sideways into new columns instead of scrolling down. Tag labels fill their row so the tap target clears 44px on touch. The popover primitive gains the clamp for every consumer, which is the right default — HeaderQuickNav, VisibilityInfoPopover, TeamsTable and TokensTable are all narrow enough to be unaffected today. scrollbar-thin gives the tag list a transparent track so the bar does not paint over the rounded corners of the box it scrolls in. Signed-off-by: Anna Effort <anna.effort@ibm.com>
The docked sidebar took 16rem out of a 768px viewport, leaving list toolbars roughly 30rem to lay out in — enough that content had to wrap across the whole md range. Move the dock to lg so tablet widths get the sheet instead, which is the arrangement they had room for anyway. useIsMobile takes the breakpoint as a parameter so the sidebar can ask for 1024 without moving the 768 default every other caller relies on. SIDEBAR_COLLAPSE_BREAKPOINT has to stay in step with the lg: utilities that show the docked sidebar, or the sheet and the dock render at once. color-scheme tells the browser which palette to paint native UI with — scrollbars, form controls, the caret. Without it the .dark class swaps our custom properties but leaves that chrome light, which is most visible on the scrollbars the sidebar and page share. Signed-off-by: Anna Effort <anna.effort@ibm.com>
Signed-off-by: Anna Effort <anna.effort@ibm.com>
b721613 to
ea154bb
Compare
There was a problem hiding this comment.
Pull request overview
Reworks the MCP Server Catalog filtering UX to match the Plugins catalog pattern by switching from an “apply” dialog to an immediate-commit popover, while also tightening responsive layout and shared popover containment behavior.
Changes:
- Replace the Server Catalog filters dialog with a popover that commits filter selections immediately (plus per-section and “Clear all” behavior).
- Improve shared UI primitives/responsiveness (popover height clamping, sidebar docking breakpoint, configurable
useIsMobilebreakpoint). - Update styling and translations to support the new filter UI and dark/native UI theming.
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| src/pages/ServerCatalog.tsx | Updates filter state management to commit section changes immediately and adds clear-section/clear-all actions. |
| src/pages/ServerCatalog.test.tsx | Refactors tests to match the new popover behavior (immediate URL commits, single-section expansion, clear-all). |
| src/index.css | Adds color-scheme for native UI theming and introduces a scrollbar-thin utility. |
| src/i18n/locales/pt-BR/mcpServer.json | Updates/introduces strings for the new popover labels (e.g., “Clear all”, “Select”). |
| src/i18n/locales/es-ES/mcpServer.json | Updates/introduces strings for the new popover labels (e.g., “Clear all”, “Select”). |
| src/i18n/locales/en-US/mcpServer.json | Updates/introduces strings for the new popover labels (e.g., “Clear all”, “Select”). |
| src/hooks/use-mobile.ts | Allows callers to specify the mobile breakpoint and re-runs the media-query effect when it changes. |
| src/components/ui/sidebar.tsx | Aligns sidebar docking with Tailwind lg and uses the new breakpoint-aware useIsMobile. |
| src/components/ui/popover.tsx | Adds collision padding and clamps popover size to Radix “available height” to prevent off-screen overflow. |
| src/components/server-catalog/CatalogToolbar.tsx | Implements the new catalog filters popover UI (single expanded section, counts, clear-all placement). |
| src/components/plugins/PluginToolbar.tsx | Aligns plugin filters popover layout with the new containment/scroll behavior and shared toolbar row layout. |
| e2e/server-catalog.spec.ts | Updates Playwright coverage to validate popover-based filter interactions and immediate commits. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 12 out of 12 changed files in this pull request and generated no new comments.
Suppressed comments (2)
src/components/plugins/PluginToolbar.tsx:170
- These container breakpoints do not match the panel's width ladder. At
lg, the 24rem border-box panel is narrower than@sm(24rem) after padding, so it stays one column; even the widest 32rem panel can never reach@xl(36rem), making three columns unreachable.@xs/@mdalign the grid with the intended 24rem/32rem panel steps.
<div className="scrollbar-thin grid max-h-120 min-h-0 flex-1 grid-cols-1 gap-x-4 overflow-y-auto rounded-md border p-2 @sm:grid-cols-2 @xl:grid-cols-3">
src/components/server-catalog/CatalogToolbar.tsx:153
- The three-column variant is unreachable. This container is at most 34rem border-box, and its padding/border leave the queryable content width below Tailwind's 32rem
@lgcontainer breakpoint, so the catalog remains at two columns even atlg. Use the@mdcontainer breakpoint for the third column so it activates only after the panel grows to 34rem.
<div className="scrollbar-thin grid max-h-52 grid-cols-1 gap-x-4 overflow-y-auto rounded-md border p-2 @sm:grid-cols-2 @lg:grid-cols-3">
Spacing adjustments added; good suggestion! The container query thresholds never matched the panel they measure. Size queries resolve against the content box, which p-4 and the border make 2rem+2px narrower than the width utilities, so 18/24/32rem of panel leave 15.875/21.875/29.875rem to query against. @sm therefore never fired at lg and @XL never fired at all, capping the list at one column on lg and two on xl. @xs and @md sit inside those widths with room to spare. SidebarGroupAction and SidebarMenuAction kept their md: variants after the collapse breakpoint moved to lg, so between 768 and 1023px they dropped the enlarged touch hitbox and hid hover-only actions while the sidebar itself was rendering as a touch sheet. Neither has call sites outside the tests today, so this was latent rather than a live regression. PopoverContent gained overflow-y-auto without scrollbar-thin, so a panel tall enough to scroll paints an OS scrollbar over its rounded corners, which is the defect the utility was added to avoid. useIsMobile's new breakpoint parameter had no coverage; both added tests fail against a hook that ignores it. Signed-off-by: Anna Effort <anna.effort@ibm.com>
Signed-off-by: Anna Effort <anna.effort@ibm.com>
…lumn Spacing adjustments added; good suggestion! The third column was unreachable by 2px. Container queries resolve against the content box, so lg:w-[34rem] left 31.875rem once p-4 and the border came off, just under the 32rem @lg asks for. Widening the panel to 36rem clears the threshold with room, rather than dropping it to @md, where md:w-[30rem] would then sit 2px under it and take the third column one step too early. Signed-off-by: Anna Effort <anna.effort@ibm.com>
ea154bb to
d80a233
Compare
marekdano
left a comment
There was a problem hiding this comment.
Just one medium finding
1. Hidden selection count on collapsed filter sections
- File:
src/components/server-catalog/CatalogToolbar.tsx:142 - Importance: Medium — genuine accessibility regression introduced by this PR's new collapse behavior; affects screen reader users on every visit to the popover, but doesn't block any workflow since options remain reachable by expanding.
- Summary: The selected-item count badge next to a collapsed section's "Select" radio is
aria-hidden, so screen reader users get no indication of how many options are chosen in a section they haven't expanded. - Failure scenario: A screen reader user opens the Filters popover; Categories is collapsed to its Select row because Providers is expanded by default, but 2 categories are already selected. The count badge showing "2" is
aria-hidden, so the user hears only "Select, radio button, checked" with no indication that anything is filtered — they have to expand every section to discover what's already selected. This collapse-with-hidden-count pattern is new in this PR (sections used to always render expanded).
|
@marekdano Good catch! Fixed in 3bc1ade (note: that SHA is stale after a rebase... the commit is The Select radio now carries I went with a description rather than folding the count into the radio's accessible name for two reasons: "Select" is what the control does regardless of how many options are ticked, so a name that changes with filter state reads as a different control on each visit; and it keeps the existing unit/e2e New i18n key |
The count badge beside a collapsed section's Select radio was aria-hidden, so a screen reader user had no way to tell that a section they had not expanded was already filtering results. Describe the count onto the radio instead, leaving the badge visual-only. A description rather than part of the radio's name: "Select" is what the control does either way, and a name that changes with the filter state reads as a different control on each visit. Signed-off-by: Anna Effort <anna.effort@ibm.com>
3bc1ade to
4dcdddc
Compare
marekdano
left a comment
There was a problem hiding this comment.
Nested double-scroll in Plugins filters popover
Root cause, src/components/ui/popover.tsx:14-38: the shared PopoverContent primitive (used by both toolbars) now clamps itself:
className={cn(
"... max-h-(--radix-popover-content-available-height) ... overflow-y-auto ...",
className,
)}--radix-popover-content-available-height is a Radix-computed CSS var — the actual space between the trigger and the nearest viewport edge. So the outer popover panel is now scrollable whenever content doesn't fit that space.
The bug, src/components/plugins/PluginToolbar.tsx:176: the tags grid inside PluginFiltersPopover still has its own independent scroll region, unchanged from before this shared-component update:
<div className="scrollbar-thin grid max-h-120 min-h-0 flex-1 grid-cols-1 gap-x-4 gap-y-1 overflow-y-auto rounded-md border p-2 pl-3 @xs:grid-cols-2 @md:grid-cols-3">max-h-120 is 30rem — fine on its own when there's room. The problem is PluginFiltersPopover stacks a search/hook <Select> and this tags grid and other filter controls all inside one PopoverContent at once (unlike CatalogFiltersPopover, which only ever shows one section's grid at a time — that's why CatalogToolbar doesn't have this problem).
Repro: shrink the viewport height (devtools open, small window, or a laptop screen) so --radix-popover-content-available-height drops below the combined height of the hook selector + tags label + tags grid. Now:
- the outer
PopoverContentclamps and scrolls (new shared behavior), and - the inner tags grid still independently clamps and scrolls at its own
max-h-120.
Two nested scrollable regions are active simultaneously — mouse wheel/trackpad scroll gets captured by whichever region the cursor happens to be over, and you can end up with a visible inner scrollbar inside an outer scrollbar.
Fix: drop the inner max-h-120 overflow-y-auto and let the outer PopoverContent be the single scroll region (simplest — doesn't touch the shared primitive's usage elsewhere).
Reopening the popover reseeded modes.provider to Select unconditionally, and that value is bound straight to the radio group. A user who had put Providers on All came back to Select checked with nothing ticked. Category and Tags derive their mode from the committed filter, but Providers cannot: All and an empty Select both commit provider=[]. Carry the previous mode over instead. First open still comes from DEFAULT_MODES, so the panel opens expanded on Select as designed. Signed-off-by: Anna Effort <anna.effort@ibm.com>
Below roughly 700px of viewport the panel scrolled at the same time as the options grid inside it, so two scrollbars were live at once and the wheel went to whichever the cursor was over. The sections were plain blocks, so none of them could give: a block's automatic minimum size is its content, which left the panel no way to fit its clamp except to scroll. Let the open section shrink and hold the others at their natural height, so the grid absorbs the squeeze and stays the only scroll region. Signed-off-by: Anna Effort <anna.effort@ibm.com>
|
@marekdano, thanks for digging into the containment interaction. Playwright was run against the popover with a 60-tag list, reading
The panel's The same collision does occur in
So There is an e2e regression test at 700px asserting that the grid scrolls and the panel does not. It fails on the pre-fix code and passes now. |
marekdano
left a comment
There was a problem hiding this comment.
Tnank you for working on this! The PR looks good!
LGTM 🚀
MCP server catalog filters reworked for better alignment with Plugin filters, iterating on the previous dialog version (ty @marekdano! 🙌 nice work on that)
Details:
b721613) Iterating back to the popover approach with filters taking effect immediately. Sections expand one at a time, with the rest showing just their Select row and count.dbda5f0): panels are clamped to--radix-popover-content-available-heightso they can't run off screen at any width. The clamp lives inui/popover.tsxand applies to every consumer. Option lists move fromfieldsettorole=groupand from CSS columns to a grid, both of which were breaking the height cap.5219d9f,57b34b5): the sidebar docks atlgrather thanmd, where it left toolbars too littleroom;
useIsMobiletakes a breakpoint so the 768 default is unchanged for other callers.color-schemeis declared so native scrollbars and form controls follow the dark palette. Both toolbars put search and the Filters trigger on one row, and the Plugins popover is end-aligned with a narrower width ladder.