Skip to content

Mcpcatalog filters adjustments - #40

Merged
vishu-bh merged 9 commits into
mainfrom
mcpcatalog-filters-adjustments
Aug 19, 2026
Merged

Mcpcatalog filters adjustments#40
vishu-bh merged 9 commits into
mainfrom
mcpcatalog-filters-adjustments

Conversation

@a-effort

@a-effort a-effort commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

MCP server catalog filters reworked for better alignment with Plugin filters, iterating on the previous dialog version (ty @marekdano! 🙌 nice work on that)

Details:

  • Catalog filters: (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.
  • Popover containment (dbda5f0): panels are clamped to --radix-popover-content-available-height so they can't run off screen at any width. The clamp lives in ui/popover.tsx and applies to every consumer. Option lists move from fieldset to role=group and from CSS columns to a grid, both of which were breaking the height cap.
  • Layout (5219d9f, 57b34b5): the sidebar docks at lg rather than md, where it left toolbars too little
    room; useIsMobile takes a breakpoint so the 768 default is unchanged for other callers. color-scheme is 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.
  • Fixes the popover and responsive issues in the shared primitives.
CleanShot 2026-08-18 at 00 01 32

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>
@a-effort
a-effort requested review from gcgoncalves, marekdano and vishu-bh and a lite review from Copilot August 18, 2026 07:20
@a-effort
a-effort marked this pull request as ready for review August 18, 2026 07:20
Signed-off-by: Anna Effort <anna.effort@ibm.com>
@a-effort
a-effort force-pushed the mcpcatalog-filters-adjustments branch from b721613 to ea154bb Compare August 18, 2026 07:24

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 useIsMobile breakpoint).
  • 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.

Comment thread src/components/plugins/PluginToolbar.tsx Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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/@md align 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 @lg container breakpoint, so the catalog remains at two columns even at lg. Use the @md container 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>
@a-effort
a-effort force-pushed the mcpcatalog-filters-adjustments branch from ea154bb to d80a233 Compare August 18, 2026 08:05

@marekdano marekdano left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

@a-effort

a-effort commented Aug 18, 2026

Copy link
Copy Markdown
Contributor Author

@marekdano Good catch! Fixed in 3bc1ade (note: that SHA is stale after a rebase... the commit is 4dcdddc on the branch now)

The Select radio now carries aria-describedby pointing at an sr-only count string, rendered only when the section has selections. The visual badge stays aria-hidden since on its own it would announce a bare "2". A screen reader user tabbing to a collapsed section now hears "Select, radio button, checked, 2 selected".

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 getByRole("radio", { name: "Select" }) queries meaningful rather than making them track selection state.

New i18n key mcpServer.catalog.selectedCount, pluralized across en-US/es-ES/pt-BR. Covered by a test that selects two providers, collapses the section, and asserts the accessible description.

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>
@a-effort
a-effort force-pushed the mcpcatalog-filters-adjustments branch from 3bc1ade to 4dcdddc Compare August 18, 2026 14:19
@a-effort
a-effort requested a review from marekdano August 18, 2026 14:25

@marekdano marekdano left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 PopoverContent clamps 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).

Comment thread src/components/server-catalog/CatalogToolbar.tsx
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>
@a-effort

a-effort commented Aug 18, 2026

Copy link
Copy Markdown
Contributor Author

@marekdano, thanks for digging into the containment interaction. Playwright was run against the popover with a 60-tag list, reading scrollHeight > clientHeight on both the panel and the grid.

Viewport height Panel scrolls? Grid scrolls?
1000px no yes
800px no yes
600px no yes
500px no yes
400px no yes

The panel's scrollHeight matches its clientHeight all the way down. The reason is the flex sizing already present in that component: the header and hook select are shrink-0, and the tags column is min-h-0 flex-1. When the content exceeds --radix-popover-content-available-height, flex fits the column into the clamp rather than overflowing it, and the grid's own overflow-y-auto takes it from there. The panel never becomes scrollable, and only one region is ever live.

The same collision does occur in CatalogFiltersPopover, the one the note exempts. There the sections are plain blocks with no flex sizing, and a block's automatic minimum size is its content, so nothing can yield and the panel has to scroll alongside the already-scrolling options grid. It reproduces from roughly 700px of viewport downward, which is an ordinary window size:

Viewport height Before After
800px single single
700px double single
600px double single
500px double single

So PluginToolbar.tsx is untouched, and the catalog panel is fixed instead: the open section becomes flex min-h-0 flex-col so it can shrink, the collapsed ones are held at shrink-0, and the grid absorbs the squeeze as the single scroll region. The shared PopoverContent primitive is unchanged, so the containment fix still applies everywhere else.

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.

@a-effort
a-effort requested a review from Yosiefeyob August 19, 2026 04:29

@marekdano marekdano left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tnank you for working on this! The PR looks good!

LGTM 🚀

@vishu-bh vishu-bh left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 🚀

@vishu-bh
vishu-bh merged commit b7330ac into main Aug 19, 2026
5 checks passed
@a-effort a-effort self-assigned this Aug 19, 2026
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.

4 participants