From 12cc46e0f2f99f48ff4f5e19090fcf88030675b9 Mon Sep 17 00:00:00 2001 From: sneak Date: Fri, 2 Oct 2026 04:17:16 +0000 Subject: [PATCH] Wrap the top bar's buttons onto a second row when narrow (closes #272) The top bar's two sides were held apart only by justify-between, with no gap and no wrapping, so at 390 px "by @sneak" ended where the New App button began. The bar's row now wraps and has a gap: in a window too narrow for one row, New App and Logout move to a second row; on wider windows the bar looks as before. Model: opus-5-5 --- TODO.md | 4 ++++ internal/handlers/dashboard_layout_test.go | 23 ++++++++++++++++++++++ templates/base.html | 2 +- 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/TODO.md b/TODO.md index 26506dd..196aba3 100644 --- a/TODO.md +++ b/TODO.md @@ -20,6 +20,10 @@ regress. # Completed Steps +- 2026-10-02: In a window too narrow for the top bar, such as 390 px, the New + App and Logout buttons move to a second row instead of running into "by + @sneak"; the bar keeps a gap between its two sides at every width (#272). + - 2026-10-02: App names may contain dots, such as `sneak.berlin`: lowercase letters and numbers joined by single dots or by hyphens, 2 to 63 characters. Docker accepts every such name in the image name `upaas-`; a dot needs a diff --git a/internal/handlers/dashboard_layout_test.go b/internal/handlers/dashboard_layout_test.go index 2ab09da..1d572b8 100644 --- a/internal/handlers/dashboard_layout_test.go +++ b/internal/handlers/dashboard_layout_test.go @@ -52,3 +52,26 @@ func TestDashboardTableFitsCard(t *testing.T) { assert.Contains(t, cellTag, "whitespace-normal") assert.Contains(t, cellTag, "break-all") } + +// TestTopBarWrapsWhenNarrow checks that the top bar keeps a gap between the +// µPaaS title and the New App and Logout buttons, and puts the buttons on a +// second row in a window too narrow for one, instead of letting them meet. +func TestTopBarWrapsWhenNarrow(t *testing.T) { + t.Parallel() + + testCtx := setupTestHandlers(t) + + request := httptest.NewRequestWithContext(t.Context(), http.MethodGet, "/", nil) + recorder := httptest.NewRecorder() + + testCtx.handlers.HandleDashboard().ServeHTTP(recorder, request) + + require.Equal(t, http.StatusOK, recorder.Code) + + _, afterNav, found := strings.Cut(recorder.Body.String(), `