mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-26 12:26:38 -06:00
fix(ui): stop sidebar session sections from painting over each other in short windows (#104447)
This commit is contained in:
committed by
GitHub
parent
6c415c44c9
commit
9523a52871
@@ -1084,6 +1084,80 @@ describeControlUiE2e("Control UI session management mocked Gateway E2E", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("scrolls long session lists in short windows instead of squeezing sections", async () => {
|
||||
const baseTime = Date.parse("2026-07-01T16:00:00.000Z");
|
||||
const rows = [
|
||||
...Array.from({ length: 8 }, (_, index) =>
|
||||
sessionRow(`agent:main:work-${index}`, `Work session ${index}`, baseTime - index * 60_000, {
|
||||
worktree: { branch: `openclaw/wt-${index}`, repoRoot: "/Users/dev/Projects/clawdbot" },
|
||||
}),
|
||||
),
|
||||
...Array.from({ length: 30 }, (_, index) =>
|
||||
sessionRow(`agent:main:chat-${index}`, `Chat ${index}`, baseTime - (index + 10) * 60_000),
|
||||
),
|
||||
];
|
||||
const context = await browser.newContext({
|
||||
locale: "en-US",
|
||||
serviceWorkers: "block",
|
||||
viewport: { height: 620, width: 1280 },
|
||||
});
|
||||
const page = await context.newPage();
|
||||
await installMockGateway(page, {
|
||||
methodResponses: {
|
||||
"sessions.list": sessionsListResponse(rows),
|
||||
},
|
||||
sessionKey: "agent:main:main",
|
||||
});
|
||||
try {
|
||||
await page.goto(`${server.baseUrl}chat`);
|
||||
await expect
|
||||
.poll(() => page.locator(".sidebar-recent-session").count(), { timeout: 15_000 })
|
||||
.toBeGreaterThanOrEqual(rows.length);
|
||||
await captureUiProof(page, "short-window-session-sections.png");
|
||||
|
||||
// Sections must stack below each other, not paint over the rows above.
|
||||
const overlaps = await page.evaluate(() => {
|
||||
const rects = [
|
||||
...document.querySelectorAll(".sidebar-recent-session, .sidebar-recent-sessions__head"),
|
||||
]
|
||||
.map((element) => {
|
||||
const rect = element.getBoundingClientRect();
|
||||
return { top: rect.top, bottom: rect.bottom };
|
||||
})
|
||||
.filter((rect) => rect.bottom > rect.top)
|
||||
.toSorted((a, b) => a.top - b.top);
|
||||
let bad = 0;
|
||||
for (let index = 1; index < rects.length; index += 1) {
|
||||
if (rects[index].top < rects[index - 1].bottom - 2) {
|
||||
bad += 1;
|
||||
}
|
||||
}
|
||||
return bad;
|
||||
});
|
||||
expect(overlaps).toBe(0);
|
||||
|
||||
// The squeeze regression compressed sections into the viewport with no
|
||||
// overflow; a healthy list is taller than its container and scrolls.
|
||||
const scroll = await page.evaluate(() => {
|
||||
const list = document.querySelector(".sidebar-recent-sessions");
|
||||
if (!list) {
|
||||
return null;
|
||||
}
|
||||
list.scrollTop = list.scrollHeight;
|
||||
return {
|
||||
clientHeight: list.clientHeight,
|
||||
scrollHeight: list.scrollHeight,
|
||||
scrollTop: list.scrollTop,
|
||||
};
|
||||
});
|
||||
expect(scroll).not.toBeNull();
|
||||
expect(scroll?.scrollHeight ?? 0).toBeGreaterThan(scroll?.clientHeight ?? 0);
|
||||
expect(scroll?.scrollTop ?? 0).toBeGreaterThan(0);
|
||||
} finally {
|
||||
await context.close();
|
||||
}
|
||||
});
|
||||
|
||||
it("keeps sidebar session controls reachable on touch pointers", async () => {
|
||||
const context = await browser.newContext({
|
||||
hasTouch: true,
|
||||
|
||||
@@ -695,6 +695,9 @@ html.openclaw-native-nav .shell-nav-expand:focus-visible {
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
/* Sticky headers inside the sidebar reuse --sidebar-bg so scrolled rows
|
||||
never shine through with a mismatched tone. */
|
||||
--sidebar-bg: color-mix(in srgb, var(--bg) 96%, var(--bg-elevated) 4%);
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -702,11 +705,11 @@ html.openclaw-native-nav .shell-nav-expand:focus-visible {
|
||||
min-height: 0;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
background: color-mix(in srgb, var(--bg) 96%, var(--bg-elevated) 4%);
|
||||
background: var(--sidebar-bg);
|
||||
}
|
||||
|
||||
:root[data-theme-mode="light"] .sidebar {
|
||||
background: color-mix(in srgb, var(--panel) 98%, white 2%);
|
||||
--sidebar-bg: color-mix(in srgb, var(--panel) 98%, white 2%);
|
||||
}
|
||||
|
||||
.sidebar-shell {
|
||||
@@ -990,6 +993,13 @@ html.openclaw-native-nav .shell-nav-expand:focus-visible {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
/* The scroller's children must not flex-shrink: shrink squeezes each section
|
||||
to its min-height while the fixed-height rows inside overflow and paint
|
||||
over the following section. Fixed sizing makes the list scroll instead. */
|
||||
.sidebar-recent-sessions > * {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.sidebar-recent-sessions__group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -1039,7 +1049,7 @@ html.openclaw-native-nav .shell-nav-expand:focus-visible {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 2;
|
||||
background: var(--bg);
|
||||
background: var(--sidebar-bg);
|
||||
}
|
||||
|
||||
.sidebar-recent-sessions__head .sidebar-recent-sessions__label-text {
|
||||
|
||||
Reference in New Issue
Block a user