Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/app/ui/react/src/web-app.js
Original file line number Diff line number Diff line change
Expand Up @@ -6105,7 +6105,7 @@ function SessionPane({ controller, actions = null, panelClassName = "", structur
const panelActions = React.createElement(React.Fragment, null,
!actionsOnly && isBulkSelection
? React.createElement("span", {
className: "ps-mini-button-label",
className: "ps-mini-button-label ps-session-selection-label",
style: { padding: "0 6px", fontSize: "12px", opacity: 0.85 },
title: "Multiple sessions selected. Move to group or cancel selected sessions; click Clear to exit.",
}, `${selectedCount} selected`)
Expand Down
23 changes: 23 additions & 0 deletions packages/app/web/src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -2359,6 +2359,29 @@ body.is-resizing-pane-y iframe {
}
}

/* Bulk selection adds a count and Clear control to the session actions. Give
that wider toolbar its own row instead of rendering it below the clipped
fixed header. The ordinary single-selection header remains one row. */
.ps-panel-header:has(.ps-session-selection-label) {
height: auto;
min-height: var(--ps-panel-header-h);
max-height: none;
flex: 0 0 auto;
flex-wrap: wrap;
overflow: visible;
padding-block: 4px;
}

.ps-panel-header:has(.ps-session-selection-label) > .ps-panel-title {
flex: 1 0 100%;
overflow: hidden;
}

.ps-panel-header:has(.ps-session-selection-label) > .ps-panel-header-right {
width: 100%;
justify-content: flex-end;
}

.ps-work-index-tab {
border: 0;
border-bottom: 2px solid transparent;
Expand Down
38 changes: 38 additions & 0 deletions packages/app/web/test/e2e/fleet-work-index.spec.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,44 @@ test("admin personal view keeps Session actions inside a narrow work-index heade
}
});

test("bulk selection keeps the terminate and delete action visible", async ({ page }) => {
await page.setViewportSize({ width: 696, height: 452 });
await page.goto(`http://127.0.0.1:${stub.port}`);
const rows = page.locator(".ps-session-list-button");
await rows.first().waitFor();

await rows.nth(0).click({ modifiers: ["Control"] });
await rows.nth(1).click({ modifiers: ["Control"] });

const header = page.locator("#ps-work-index-panel .ps-panel-header").first();
const terminate = header.getByRole("button", {
name: /Terminate 2 selected sessions/,
});
await expect(terminate).toBeVisible();

const geometry = await header.evaluate((element) => {
const bounds = element.getBoundingClientRect();
const button = element.querySelector(
'[aria-label^="Terminate 2 selected sessions"]',
)?.getBoundingClientRect();
return {
header: { left: bounds.left, top: bounds.top, right: bounds.right, bottom: bounds.bottom },
button: button
? { left: button.left, top: button.top, right: button.right, bottom: button.bottom }
: null,
};
});

expect(geometry.button).not.toBeNull();
expect(geometry.button.left).toBeGreaterThanOrEqual(geometry.header.left);
expect(geometry.button.top).toBeGreaterThanOrEqual(geometry.header.top);
expect(geometry.button.right).toBeLessThanOrEqual(geometry.header.right);
expect(geometry.button.bottom).toBeLessThanOrEqual(geometry.header.bottom);

await terminate.click();
await expect(page.getByRole("button", { name: "Hard Delete 2 Sessions" })).toBeVisible();
});

test("admin Fleet view keeps Session transcripts stable and read-only", async ({ page }) => {
const requests = [];
page.on("request", (request) => {
Expand Down
Loading