fix(health): the user hover card sits on the name row only
Over the whole tile it opened together with the CPU bar's own help tip and the two overlapped.
This commit is contained in:
@@ -721,8 +721,10 @@ export function HostSections({ host, heap }: { host: HostState; heap?: HeapInfo
|
||||
const name = groupName(g);
|
||||
const isOpen = open === g.key;
|
||||
return (
|
||||
<Tip key={g.key} raw={userTip(g)} block>
|
||||
<div className="space-y-2" data-testid="server-user">
|
||||
<div key={g.key} className="space-y-2" data-testid="server-user">
|
||||
{/* The hover card sits on the name row only: over the whole
|
||||
tile it stacked on the CPU bar's own help tip. */}
|
||||
<Tip raw={userTip(g)} block>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setOpen(isOpen ? null : g.key)}
|
||||
@@ -743,27 +745,27 @@ export function HostSections({ host, heap }: { host: HostState; heap?: HeapInfo
|
||||
{t("host.processCount", { n: g.processes })}
|
||||
</span>
|
||||
</button>
|
||||
<HealthBar label="CPU" value={g.cpu} tip={t("host.cpuHelp")} />
|
||||
<HealthBar label={`RAM · ${formatBytes(g.memory)}`} value={g.memoryPercent} />
|
||||
{isOpen && (
|
||||
<ul className="space-y-1 rounded-md bg-surface-2/60 px-2 py-1.5">
|
||||
{g.top.map((p) => (
|
||||
<li
|
||||
key={p.pid}
|
||||
className="flex justify-between gap-2 text-[10px] font-mono"
|
||||
>
|
||||
<span className="truncate text-fg-secondary">
|
||||
{p.name} <span className="text-fg-muted">#{p.pid}</span>
|
||||
</span>
|
||||
<span className="flex-shrink-0 text-fg-muted">
|
||||
{fmtPct(p.cpu)} · {formatBytes(p.memory)}
|
||||
</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</div>
|
||||
</Tip>
|
||||
</Tip>
|
||||
<HealthBar label="CPU" value={g.cpu} tip={t("host.cpuHelp")} />
|
||||
<HealthBar label={`RAM · ${formatBytes(g.memory)}`} value={g.memoryPercent} />
|
||||
{isOpen && (
|
||||
<ul className="space-y-1 rounded-md bg-surface-2/60 px-2 py-1.5">
|
||||
{g.top.map((p) => (
|
||||
<li
|
||||
key={p.pid}
|
||||
className="flex justify-between gap-2 text-[10px] font-mono"
|
||||
>
|
||||
<span className="truncate text-fg-secondary">
|
||||
{p.name} <span className="text-fg-muted">#{p.pid}</span>
|
||||
</span>
|
||||
<span className="flex-shrink-0 text-fg-muted">
|
||||
{fmtPct(p.cpu)} · {formatBytes(p.memory)}
|
||||
</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
@@ -207,18 +207,22 @@ describe("HostHealth", () => {
|
||||
render(<Tab />);
|
||||
const tile = await screen.findByTestId("server-user");
|
||||
await vi.waitFor(() => expect(api.users.list).toHaveBeenCalled());
|
||||
fireEvent.mouseEnter(tile.parentElement!);
|
||||
fireEvent.mouseEnter(within(tile).getByRole("button").parentElement!);
|
||||
const tip = await screen.findByText(/Login: mia/);
|
||||
expect(tip.textContent).toContain("Role: Developer");
|
||||
expect(tip.textContent).toMatch(/Today: \$1\.5/);
|
||||
expect(tip.textContent).toContain("2 workspaces · 5 sessions");
|
||||
// Only the name row carries it: the CPU bar keeps its own help tip alone.
|
||||
fireEvent.mouseLeave(within(tile).getByRole("button").parentElement!);
|
||||
fireEvent.mouseEnter(within(tile).getByRole("meter", { name: "CPU" }));
|
||||
expect(screen.queryByText(/Login: mia/)).toBeNull();
|
||||
});
|
||||
|
||||
it("shows a developer only the login name, without reading the users list", async () => {
|
||||
Object.assign(perms, { seesAll: false, local: false, isAdmin: false });
|
||||
render(<Tab />);
|
||||
const tile = await screen.findByTestId("server-user");
|
||||
fireEvent.mouseEnter(tile.parentElement!);
|
||||
fireEvent.mouseEnter(within(tile).getByRole("button").parentElement!);
|
||||
const tip = await screen.findByText(/Login: mia/);
|
||||
expect(tip.textContent).not.toContain("Today");
|
||||
expect(api.users.list).not.toHaveBeenCalled();
|
||||
|
||||
Reference in New Issue
Block a user