fix(web): filter toggle hover as border, not background (#1460) (#1461)

* fix(web): filter toggle hover as border, not background (#1460)

The Logging/History/Network filter toggles used the same
--mantine-primary-color-light background for both their hover and their
active (toggled-on) state, so clicking a button to turn it off while the
cursor was still over it looked like a no-op — the hover background
masked the removal of the active background.

- Add a shared FilterToggleButton element used by LogControls,
  NetworkControls, and MessageDirectionFilter, replacing the duplicated
  UnstyledButton + inline `bg={active ? … : undefined}` in each.
- Add a `filterToggle` UnstyledButton theme variant + `.filter-toggle`
  rules: hover is now a thin border (reserved via a transparent 1px
  border so it doesn't shift layout); the active state is the filled
  background, keyed off `aria-pressed="true"`.
- This leaves the `.list-item` selectable rows (Prompts/Apps/Tools/
  Resources lists, section headers) untouched — they keep their
  hover-fill since they aren't toggles.
- Add `aria-pressed` to the Logging level toggles (previously missing).

Tests + stories cover the active/hover distinction via aria-pressed for
FilterToggleButton and all three controls.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(web): move filter-toggle border to CSS so :hover actually shows (#1460)

The previous attempt set the reserved `1px solid transparent` border via
the theme's `styles` callback, which Mantine emits as an *inline* style.
Inline styles outrank stylesheet `:hover` rules, so
`.filter-toggle:hover { border-color }` never applied — hovering showed
no border at all (verified in a real browser via Playwright).

Move the border (and border-radius) out of the inline theme styles into
the `.filter-toggle` class in App.css, so `:hover` can override the
transparent border-color. The active fill (`[aria-pressed="true"]`) was
already correct.

Verified with real Playwright page.hover: inactive+hover now shows a
1px --inspector-brand-primary border, active shows the fill, active+hover
shows both, all distinct. Story play functions assert the active fill and
the reserved transparent border (the hover border can't be asserted in the
synthetic-event test runner, so it's documented as Playwright-verified).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Cliff Hall 2026-06-13 19:52:27 -04:00 • committed by GitHub
commit d90cc4c34b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 256 additions and 68 deletions

View file

@ -32,6 +32,20 @@ describe("NetworkControls", () => {
expect(onFilterChange).toHaveBeenLastCalledWith("x");
});
it("clears the filter text when the clear button is clicked", async () => {
const user = userEvent.setup();
const onFilterChange = vi.fn();
renderWithMantine(
<NetworkControls
{...baseProps}
filterText="auth"
onFilterChange={onFilterChange}
/>,
);
await user.click(screen.getByRole("button", { name: "Clear" }));
expect(onFilterChange).toHaveBeenCalledWith("");
});
it("reflects category visibility via aria-pressed", () => {
renderWithMantine(
<NetworkControls

View file

@ -3,12 +3,11 @@ import {
CloseButton,
Group,
Stack,
Text,
TextInput,
Title,
UnstyledButton,
} from "@mantine/core";
import type { FetchRequestCategory } from "@inspector/core/mcp/types.js";
import { FilterToggleButton } from "../../elements/FilterToggleButton/FilterToggleButton";
const NETWORK_CATEGORIES: FetchRequestCategory[] = ["auth", "transport"];
@ -64,24 +63,15 @@ export function NetworkControls({
</SubtleButton>
</Group>
<Stack gap="xs">
{NETWORK_CATEGORIES.map((category) => {
const active = visibleCategories[category];
return (
<UnstyledButton
key={category}
w="100%"
p="sm"
variant="listItem"
aria-pressed={active}
bg={active ? "var(--mantine-primary-color-light)" : undefined}
onClick={() => onToggleCategory(category, !active)}
>
<Text c={CATEGORY_COLORS[category]} ta="center" fw={500}>
{category}
</Text>
</UnstyledButton>
);
})}
{NETWORK_CATEGORIES.map((category) => (
<FilterToggleButton
key={category}
label={category}
color={CATEGORY_COLORS[category]}
active={visibleCategories[category]}
onToggle={(visible) => onToggleCategory(category, visible)}
/>
))}
</Stack>
</Stack>
);