fix: truncate long sidebar stack names before trailing indicators clip (#1562)

Clamp the stack list width with ScrollArea block and min-w-0 on the row
flex chain so long names ellipsize instead of pushing update dots past the
sidebar edge. Add E2E layout coverage for trailing-indicator edge cases.
This commit is contained in:
Anso
2026-07-05 04:57:59 -04:00
committed by GitHub
parent f2b5c68d84
commit 33231089c3
7 changed files with 363 additions and 11 deletions
@@ -205,7 +205,7 @@ export function StackList(props: StackListProps & StackListBulkProps) {
<CommandItem
value={file}
onSelect={() => onSelectFile(file)}
className="p-0 data-[selected=true]:bg-transparent"
className="min-w-0 w-full p-0 data-[selected=true]:bg-transparent"
>
<StackRow
file={file}
+16 -4
View File
@@ -105,11 +105,11 @@ export function StackRow(props: StackRowProps) {
<span className="flex-1 truncate font-mono text-sm min-w-0">{displayName}</span>
{/* Fixed trailing icon slot: update dot > check-failed > git pending */}
<span className="w-3.5 h-3.5 flex items-center justify-center shrink-0">
<span className="w-3.5 h-3.5 flex items-center justify-center shrink-0" data-testid="stack-row-trailing">
{hasUpdate ? (
<RowTooltip
trigger={(
<span className="relative inline-flex w-2 h-2">
<span className="relative inline-flex w-2 h-2" data-testid="stack-trailing-update">
<span className="absolute inset-0 rounded-full bg-update opacity-75 animate-ping" />
<span className="relative w-2 h-2 rounded-full bg-update" />
</span>
@@ -118,12 +118,24 @@ export function StackRow(props: StackRowProps) {
/>
) : checkStatus === 'failed' ? (
<RowTooltip
trigger={<AlertCircle className="w-3 h-3 text-muted-foreground/70" strokeWidth={1.5} />}
trigger={(
<AlertCircle
className="w-3 h-3 text-muted-foreground/70"
strokeWidth={1.5}
data-testid="stack-trailing-check-failed"
/>
)}
label={lastError ? `Update check failed: ${lastError}` : 'Update check failed'}
/>
) : hasGitPending ? (
<RowTooltip
trigger={<GitBranch className="w-3 h-3 text-brand" strokeWidth={1.5} />}
trigger={(
<GitBranch
className="w-3 h-3 text-brand"
strokeWidth={1.5}
data-testid="stack-trailing-git-pending"
/>
)}
label="Git source update pending"
/>
) : null}
@@ -95,7 +95,7 @@ export function StackSidebar(props: StackSidebarProps) {
onClear={onClearSelection}
/>
)}
<ScrollArea className="flex-1 px-2 pb-2">
<ScrollArea block className="flex-1 px-2 pb-2">
<div data-stacks-loaded={list.isLoading ? 'false' : 'true'}>
<StackList {...list} bulkMode={bulkMode} selectedFiles={selectedFiles} onToggleSelect={onToggleSelect} />
</div>
@@ -123,4 +123,11 @@ describe('StackRow', () => {
expect(container.querySelector('[data-slot="cursor-container"]')).toBeNull();
expect(container.querySelector('.bg-update')).toBeNull();
});
it('constrains long stack names so trailing indicators stay in the row', () => {
const longName = 'tick-grafana-docker-observability-stack';
render(<StackRow {...base({ displayName: longName })} />);
expect(screen.getByTestId('stack-row')).toHaveClass('min-w-0');
expect(screen.getByText(longName)).toHaveClass('truncate');
});
});
@@ -1,7 +1,7 @@
import { cn } from '@/lib/utils';
export const sidebarRowBase = cn(
'relative flex items-center gap-2 w-full px-2 py-1.5 rounded-md mb-0.5',
'relative flex items-center gap-2 w-full min-w-0 px-2 py-1.5 rounded-md mb-0.5',
// 44px tap target on touch viewports without changing desktop density.
'max-md:min-h-11 max-md:py-2.5',
'font-mono text-[13px] text-muted-foreground',