fix: truncate long error text in fleet prune estimate per-node rows (#1802)

Unreachable nodes rendered their error string in a shrink-0 span with no
width bound, so a verbose daemon or proxy error pushed the row past the
card edge. Bound the error span to 55% and ellipsize, matching the
existing pattern in the label-and-stop card; the full message stays
available via the row title tooltip.
This commit is contained in:
Anso
2026-08-08 17:39:20 -04:00
committed by GitHub
parent 5014ad20a6
commit 4f30db3abc
2 changed files with 26 additions and 1 deletions
@@ -126,6 +126,31 @@ it('shows 0 reclaimable · partial when successful targets report zero bytes', a
expect(screen.getByText('PART')).toBeInTheDocument();
});
it('truncates a long unreachable error without clipping reachable byte counts', async () => {
const longError = `Pilot tunnel is disconnected: ${'x'.repeat(400)}`;
const twoNodes = [...nodes, { id: 2, name: 'edge', status: 'offline' }] as unknown as FleetNode[];
mockedFetch.mockImplementation((url: string) => {
if (url === '/fleet/prune/estimate') {
return Promise.resolve(jsonResponse(200, {
totalBytes: 4096,
perNode: [
{ nodeId: 1, nodeName: 'central', reclaimableBytes: 4096, reachable: true },
{ nodeId: 2, nodeName: 'edge', reclaimableBytes: 0, reachable: false, error: longError },
],
}));
}
return Promise.resolve(jsonResponse(404, {}));
});
render(<FleetPruneCard nodes={twoNodes} />);
const errorSpan = await screen.findByText(longError);
expect(errorSpan.className).toContain('truncate');
expect(errorSpan.className).toContain('max-w-[55%]');
expect(screen.getByTitle(longError)).toBeInTheDocument();
const byteSpan = screen.getByText('4 KB');
expect(byteSpan.className).not.toContain('truncate');
});
it('drops the unlock footer once dry run review is valid', async () => {
const user = userEvent.setup();
mockedFetch.mockImplementation((url: string) => {
@@ -416,7 +416,7 @@ function EstimateSection({ estimate }: { estimate: EstimateState }) {
{node.reachable ? (node.partial ? 'PART' : 'OK') : '--'}
</span>
<span className="min-w-0 flex-1 truncate font-mono text-[11px] text-stat-value">{node.nodeName}</span>
<span className={cn(KICKER, 'shrink-0 tabular-nums', node.reachable ? 'text-stat-subtitle' : 'text-stat-icon')}>
<span className={cn(KICKER, 'shrink-0 tabular-nums', node.reachable ? 'text-stat-subtitle' : 'max-w-[55%] truncate text-stat-icon')}>
{node.reachable ? formatBytes(node.reclaimableBytes) : (node.error ?? 'unreachable')}
</span>
</li>