From ab214514e0f7f6a4069da439136bb92d163822f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= <1005065+DeepDiver1975@users.noreply.github.com> Date: Sat, 29 Aug 2026 23:28:26 +0200 Subject: [PATCH 1/2] test(workspace): pin nav rail to a single scrolling column on short viewports (#146) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Thomas Müller <1005065+DeepDiver1975@users.noreply.github.com> --- .../ModuleTabsScrollingTests.cs | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 tests/LageBuch.Acceptance.Tests/ModuleTabsScrollingTests.cs diff --git a/tests/LageBuch.Acceptance.Tests/ModuleTabsScrollingTests.cs b/tests/LageBuch.Acceptance.Tests/ModuleTabsScrollingTests.cs new file mode 100644 index 0000000..41405b5 --- /dev/null +++ b/tests/LageBuch.Acceptance.Tests/ModuleTabsScrollingTests.cs @@ -0,0 +1,69 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Controls.Presenters; +using Avalonia.Headless; +using Avalonia.Headless.XUnit; +using Avalonia.Threading; +using Avalonia.VisualTree; +using LageBuch.App.Shared.Views; + +namespace LageBuch.Acceptance.Tests; + +// The left nav rail is a TabControl with TabStripPlacement="Left". On a viewport too short to fit +// all rail tabs vertically, Avalonia's WrapPanel (the default ItemsPanel, v-flipped by the theme for +// Left placement) used to wrap into a second side-by-side column -- confusing on a phone-height +// window, and the second column can run off the right edge entirely. These tests pin that the rail +// stays in ONE column and overflows into a scrollbar instead of wrapping (#146). +public class ModuleTabsScrollingTests +{ + // Short enough to force wrap regardless of which header banners are visible: 10 tabs at + // MinHeight=50 need 500px, and the header/footer stacks eat most of the rest. + private const double ShortHeight = 400.0; + + [AvaloniaFact] + public void Nav_rail_stays_one_column_and_scrolls_on_a_short_viewport() + { + var vm = WorkspaceRenderHelper.BuildEditableWorkspaceWithAllBars(); + var window = new Window + { + Content = new IncidentWorkspaceView { DataContext = vm }, + Width = 1920, + Height = ShortHeight, + }; + window.Show(); + Dispatcher.UIThread.RunJobs(); + + // Capture BEFORE the assertions: on the buggy build the column assertion fails, and the + // RENDER_OUT frame of the wrapped two-column rail is exactly the "before" screenshot. + var dir = Environment.GetEnvironmentVariable("RENDER_OUT"); + if (!string.IsNullOrWhiteSpace(dir)) + { + Directory.CreateDirectory(dir); + using var frame = window.CaptureRenderedFrame()!; + frame.SavePng(Path.Combine(dir, "module-tabs-scrolling.png")); + } + + var tabs = ((IncidentWorkspaceView)window.Content!).GetControl("ModuleTabs"); + var tabItems = tabs.GetVisualDescendants().OfType().ToArray(); + + // Nothing lost: every declared rail tab is still realized. + Assert.Equal(10, tabItems.Length); + + // One column, not side-by-side columns: every tab shares the same horizontal origin. + var columns = tabItems + .Select(t => t.TranslatePoint(new Point(0, 0), tabs)!.Value.X) + .Distinct() + .ToArray(); + Assert.True(columns.Length == 1, + $"nav rail wrapped into {columns.Length} columns at x=" + + string.Join(", ", columns.Select(c => c.ToString("F0"))) + + " -- it must overflow into a scrollbar instead."); + + // The overflow lands in a ScrollViewer, not silent clipping. + var strip = tabs.GetVisualDescendants().OfType() + .First(p => p.Name == "PART_ItemsPresenter"); + var scroll = strip.GetVisualAncestors().OfType().First(); + Assert.True(scroll.Extent.Height > scroll.Viewport.Height, + $"rail is {scroll.Extent.Height:F0}px tall in a {scroll.Viewport.Height:F0}px viewport -- no overflow to scroll."); + } +} \ No newline at end of file From 41010a7f73e757ab8f0a782c0d57042d0506d644 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= <1005065+DeepDiver1975@users.noreply.github.com> Date: Sat, 29 Aug 2026 23:28:29 +0200 Subject: [PATCH 2/2] fix(workspace): scroll sidebar instead of wrapping into columns on short viewports (#146) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Thomas Müller <1005065+DeepDiver1975@users.noreply.github.com> --- src/LageBuch.App.Shared/Theme/Styles.axaml | 39 ++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/LageBuch.App.Shared/Theme/Styles.axaml b/src/LageBuch.App.Shared/Theme/Styles.axaml index 56cfeb6..3daf781 100644 --- a/src/LageBuch.App.Shared/Theme/Styles.axaml +++ b/src/LageBuch.App.Shared/Theme/Styles.axaml @@ -283,6 +283,45 @@