feat(web): monitoring column search, close-to-fullscreen, and open/close animation (#1616)

- Add a search box to MonitoringControls (right of the tabs). It's a single
  shared search kept across column tab switches, filtering whichever screen is
  shown. Embedded panels apply this text but still ignore the sidebar-only
  filters (levels/categories/directions/method), which have no control there.
- Close button now uses the "collapse sidebar" icon (TbLayoutSidebarRightCollapse),
  the mirror of the pin button's expand icon.
- Closing the column carries its selection to the primary view: the screen the
  user was watching becomes the active header tab (rather than snapping back to
  the pre-pin selection).
- Animate the column open/close: the handle + column slide in from the right and
  fade (reversing on close) via a Mantine Transition.
- HistoryListPanel: drop the redundant "History (N)" header when there are no
  pinned requests (lone unpinned section renders headerless).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
cliffhall 2026-07-08 20:16:15 -04:00
commit c5e064ff71
3 changed files with 448 additions and 169 deletions

View file

@ -127,8 +127,25 @@ describe("HistoryScreen", () => {
expect(screen.queryByPlaceholderText("Search...")).toBeNull();
});
it("shows the full stream when embedded, ignoring the live filter", () => {
// A search that matches nothing would empty the full-size screen...
it("applies the search text but ignores the method filter when embedded", () => {
renderWithMantine(
<HistoryScreen
{...baseProps}
ui={{
...EMPTY_HISTORY_UI,
// Method filter would exclude the tools/list entries on the full-size
// screen...
methodFilter: "resources/list",
// ...but the column search matches them.
search: "tools",
}}
embedded
/>,
);
expect(screen.queryByText("No request history")).toBeNull();
});
it("hides entries not matching the column search when embedded", () => {
renderWithMantine(
<HistoryScreen
{...baseProps}
@ -136,7 +153,6 @@ describe("HistoryScreen", () => {
embedded
/>,
);
// ...but the embedded column shows the full stream (not the empty state).
expect(screen.queryByText("No request history")).toBeNull();
expect(screen.getByText("No request history")).toBeInTheDocument();
});
});

View file

@ -147,14 +147,38 @@ describe("LoggingScreen", () => {
expect(screen.queryByRole("button", { name: "Set" })).toBeNull();
});
it("shows the full stream when embedded, ignoring the live filter", () => {
it("applies the search text but ignores the level filter when embedded", () => {
const entries = [
{
receivedAt: new Date(),
params: { level: "info" as const, data: "hello" },
},
];
renderWithMantine(
<LoggingScreen
{...baseProps}
entries={entries}
ui={{
...EMPTY_LOGS_UI,
// The level filter would hide this entry on the full-size screen...
visibleLevels: { ...EMPTY_LOGS_UI.visibleLevels, info: false },
// ...but the column search matches it.
filterText: "hello",
}}
embedded
/>,
);
// Search matches + level filter is ignored → the entry shows.
expect(screen.getByText("hello")).toBeInTheDocument();
});
it("hides entries not matching the column search when embedded", () => {
const entries = [
{
receivedAt: new Date(),
params: { level: "info" as const, data: "hello" },
},
];
// A filter that would hide the entry on the full-size screen...
renderWithMantine(
<LoggingScreen
{...baseProps}
@ -163,7 +187,6 @@ describe("LoggingScreen", () => {
embedded
/>,
);
// ...is ignored in the embedded, sidebar-less column.
expect(screen.getByText("hello")).toBeInTheDocument();
expect(screen.queryByText("hello")).toBeNull();
});
});

View file

@ -180,8 +180,25 @@ describe("NetworkScreen", () => {
expect(screen.getByRole("button", { name: "Export" })).toBeInTheDocument();
});
it("shows the full stream when embedded, ignoring the live filter", () => {
// A filter that matches nothing would empty the full-size screen...
it("applies the search text but ignores the category filter when embedded", () => {
renderWithMantine(
<NetworkScreen
{...baseProps}
ui={{
...EMPTY_NETWORK_UI,
// Category filter would hide the transport entry on the full-size
// screen...
visibleCategories: { auth: true, transport: false },
// ...but the column search matches it.
filterText: "example.com",
}}
embedded
/>,
);
expect(screen.queryByText("No network requests")).toBeNull();
});
it("hides entries not matching the column search when embedded", () => {
renderWithMantine(
<NetworkScreen
{...baseProps}
@ -189,7 +206,6 @@ describe("NetworkScreen", () => {
embedded
/>,
);
// ...but the embedded column shows the full stream (not the empty state).
expect(screen.queryByText("No network requests")).toBeNull();
expect(screen.getByText("No network requests")).toBeInTheDocument();
});
});