diff options
| author | Paul Buetow <paul@buetow.org> | 2026-02-28 09:56:33 +0200 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-02-28 12:50:12 +0200 |
| commit | d2cc2def17b58da762d88090c0c298dc2e87dd1d (patch) | |
| tree | 354259359f7e9d21166a61cdafd22d0d0a4a83ee | |
| parent | 2dc3e8caf088c01169080ca381454c653d4af4ad (diff) | |
fix(ui): remove double-render of expandedCellView in View()
Bugfix task (UUID d04dccbd): expandedCellView() was called once
unconditionally in the base JoinVertical and again inside the
cellExpanded guard, producing a duplicate expanded-cell line
whenever the user toggled cell expansion.
Fix: remove the unconditional call; expand only when cellExpanded is true.
Also corrected updateTableHeight() which still reserved an extra row
for the now-removed base expanded line (windowHeight-3 → windowHeight-2).
Added regression test TestExpandedCellViewNoDoubleRender that verifies
the expanded content is absent when cellExpanded is false and appears
exactly once when true.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
| -rw-r--r-- | internal/ui/table.go | 6 | ||||
| -rw-r--r-- | internal/ui/table_test.go | 44 |
2 files changed, 48 insertions, 2 deletions
diff --git a/internal/ui/table.go b/internal/ui/table.go index 7348dab..2cc2b3d 100644 --- a/internal/ui/table.go +++ b/internal/ui/table.go @@ -625,10 +625,12 @@ func (m Model) View() string { if m.showTaskDetail { return m.renderTaskDetail() } + // expandedCellView is only appended when the user has toggled the + // expanded-cell panel open; including it unconditionally caused a + // double-render whenever cellExpanded was true. view := lipgloss.JoinVertical(lipgloss.Left, m.topStatusLine(), m.tbl.View(), - m.expandedCellView(), m.statusLine(), ) if m.cellExpanded { @@ -1208,7 +1210,7 @@ func (m *Model) updateTableHeight() { if m.windowHeight == 0 { return } - h := m.windowHeight - 3 // space for two status bars and base expanded line + h := m.windowHeight - 2 // space for top and bottom status bars if m.cellExpanded { h-- } diff --git a/internal/ui/table_test.go b/internal/ui/table_test.go index 4e52842..63e1cf7 100644 --- a/internal/ui/table_test.go +++ b/internal/ui/table_test.go @@ -644,6 +644,50 @@ func TestEscClosesHelp(t *testing.T) { } } +// TestExpandedCellViewNoDoubleRender is a regression test for a bug where +// expandedCellView() was appended to the layout unconditionally AND again +// inside the cellExpanded guard, producing a duplicate line when expanded. +// It verifies that: +// - when cellExpanded is false, expandedCellView content is absent from View() +// - when cellExpanded is true, expandedCellView content appears exactly once +// - the expanded content does not appear when expandedCellView returns "" +func TestExpandedCellViewNoDoubleRender(t *testing.T) { + tmp := t.TempDir() + taskPath := setupBasicTask(t, tmp) + setupEnv(t, taskPath) + + m, err := New(nil, "firefox") + if err != nil { + t.Fatalf("New: %v", err) + } + // Give the model a non-zero window size so View() renders real content. + m.windowHeight = 24 + mv, _ := (&m).Update(tea.WindowSizeMsg{Width: 120, Height: 24}) + m = *mv.(*Model) + + // Determine what expandedCellView() currently returns so we know what to + // search for in the full View() output. + expanded := m.expandedCellView() + if expanded == "" { + t.Skip("expandedCellView returned empty string; cannot test placement") + } + + // With cellExpanded false (the default), the expanded content must be absent. + m.cellExpanded = false + viewCollapsed := m.View() + if strings.Contains(viewCollapsed, expanded) { + t.Fatalf("cellExpanded=false: expandedCellView content unexpectedly present in View()") + } + + // With cellExpanded true, the expanded content must appear exactly once. + m.cellExpanded = true + viewExpanded := m.View() + count := strings.Count(viewExpanded, expanded) + if count != 1 { + t.Fatalf("cellExpanded=true: expandedCellView content appears %d times in View(), want exactly 1", count) + } +} + func TestSearchExitHotkeys(t *testing.T) { tmp := t.TempDir() taskPath := setupBasicTask(t, tmp) |
