fix(web): keep wide network-entry URLs from overflowing the column (#1623)

Long request URLs stretched every Network card to their own max-content
width, bleeding the cards (and their status badges) past the pinned
monitoring column. Mantine's ScrollArea `content` slot defaults to
`min-width: max-content`, so a non-wrapping URL forced the whole list wider
than its viewport.

Add an opt-in `constrainContentWidth` prop to EmbeddableScrollArea that
relaxes that slot to `min-width: 0`, so the list can't grow past its
viewport and each row manages its own overflow — the URL already lives in a
horizontal ScrollArea, which now actually scrolls. Scoped to the Network
stream panel (Logs/Protocol rows wrap or truncate, so they keep the default
where a long line scrolls the list).

Verified in Storybook (embedded 380px column): cards clamp to the viewport,
no list/page horizontal overflow, and the URL scrolls within its own area.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BrihGWcrM9JGRyu41nzZYw
This commit is contained in:
cliffhall 2026-07-09 20:11:43 -04:00
commit 20e4ce7b43
2 changed files with 83 additions and 1 deletions

View file

@ -31,4 +31,27 @@ describe("EmbeddableScrollArea", () => {
);
expect(ref.current).toBeInstanceOf(HTMLElement);
});
it.each([false, true])(
"constrains the content width when asked (embedded=%s)",
(embedded) => {
const ref = createRef<HTMLDivElement>();
renderWithMantine(
<EmbeddableScrollArea
embedded={embedded}
viewportRef={ref}
constrainContentWidth
>
<div>constrained content</div>
</EmbeddableScrollArea>,
);
expect(screen.getByText("constrained content")).toBeInTheDocument();
// The Mantine ScrollArea `content` slot has its default
// `min-width: max-content` relaxed to 0 so a long non-wrapping row
// (e.g. a network URL) can't stretch the list past its viewport (#1623).
const content = document.querySelector(".mantine-ScrollArea-content");
expect(content).not.toBeNull();
expect((content as HTMLElement).style.minWidth).toBe("0");
},
);
});

View file

@ -17,8 +17,23 @@ export interface EmbeddableScrollAreaProps {
embedded: boolean;
viewportRef: Ref<HTMLDivElement>;
children: ReactNode;
/**
* Constrain the scrolled content to the viewport width instead of letting it
* grow to its own `max-content`. Mantine's ScrollArea `content` slot defaults
* to `min-width: max-content`, so a row with non-wrapping content (e.g. a long
* network URL) stretches every card past the column and it bleeds out (#1623).
* When true, the content can shrink to the viewport and each row must manage
* its own overflow (the Network URL scrolls inside its own inner ScrollArea).
* Left off for panels whose rows already wrap/truncate (Logs, Protocol), where
* the default lets a long line scroll the list horizontally instead.
*/
constrainContentWidth?: boolean;
}
// Relax the `content` slot's default `min-width: max-content` so the list can't
// grow wider than its viewport; see `constrainContentWidth`.
const CONSTRAIN_CONTENT_STYLES = { content: { minWidth: 0 } } as const;
/**
* The scroll region shared by the Logs / Protocol / Network stream panels, which
* render both full-size (their own tab) and embedded (the monitoring column).
@ -28,7 +43,9 @@ export function EmbeddableScrollArea({
embedded,
viewportRef,
children,
constrainContentWidth = false,
}: EmbeddableScrollAreaProps) {
const styles = constrainContentWidth ? CONSTRAIN_CONTENT_STYLES : undefined;
if (embedded) {
return (
<Stack flex={1} mih={0} gap={0}>
@ -37,6 +54,7 @@ export function EmbeddableScrollArea({
mah="100%"
type="scroll"
offsetScrollbars
styles={styles}
>
{children}
</ScrollArea.Autosize>
@ -49,6 +67,7 @@ export function EmbeddableScrollArea({
mah={FULLSIZE_MAH}
type="scroll"
offsetScrollbars
styles={styles}
>
{children}
</ScrollArea.Autosize>