From 91e277e9ba23530d5dc8cf3c34a7838757ec5791 Mon Sep 17 00:00:00 2001 From: 4gray <4gray@users.noreply.github.com> Date: Sat, 10 Oct 2026 21:49:57 +0200 Subject: [PATCH] fix(settings): fit long versions in the sidebar and keep side panels off the window edge (#1875) * fix(settings): fit long versions in the sidebar and keep side panels off the window edge The About item's version was flex-shrink: 0 beside a zero-basis label, so a nightly build string squeezed "About" to nothing and overflowed the panel. The label now has an auto basis and the version yields first, truncating with an ellipsis and keeping the full string in its tooltip. The context panel used height: 100% plus padding without a border-box reset, so it ran 24px past its host and the footer item sat on the window edge. The host is now a flex column and the panel fills it as a flex item, which restores the bottom padding for every side panel variant. Co-Authored-By: Claude Opus 5.5 * test(settings): answer the GitHub releases check in the version layout E2E The sidebar renders the version only after the releases request answers. CI runners are rate-limited on api.github.com, so the tag never appeared and the test failed on its first locator. Fulfil the request locally with an older release so the version shows without an update badge. Co-Authored-By: Claude Opus 5.5 --------- Co-authored-by: Claude Opus 5.5 --- .changes/settings-sidebar-version-fit.md | 8 +++ apps/web-e2e/src/settings.e2e.ts | 50 +++++++++++++++++++ ...pace-settings-context-panel.component.scss | 14 +++++- ...e-settings-context-panel.component.spec.ts | 6 +++ ...kspace-settings-context-panel.component.ts | 1 + ...space-shell-context-sidebar.component.scss | 8 ++- 6 files changed, 83 insertions(+), 4 deletions(-) create mode 100644 .changes/settings-sidebar-version-fit.md diff --git a/.changes/settings-sidebar-version-fit.md b/.changes/settings-sidebar-version-fit.md new file mode 100644 index 000000000..56125a024 --- /dev/null +++ b/.changes/settings-sidebar-version-fit.md @@ -0,0 +1,8 @@ +--- +type: fix +area: settings +--- + +A long nightly version no longer pushes "About" out of the settings sidebar: +the version shortens with an ellipsis and shows in full on hover. Side panels +also keep their bottom spacing instead of running into the window edge. diff --git a/apps/web-e2e/src/settings.e2e.ts b/apps/web-e2e/src/settings.e2e.ts index 19f1ec828..f0b01388a 100644 --- a/apps/web-e2e/src/settings.e2e.ts +++ b/apps/web-e2e/src/settings.e2e.ts @@ -229,6 +229,56 @@ test.describe('Settings', () => { ).toHaveAttribute('aria-checked', 'true'); }); + test('@settings @web About keeps its label and clears the window bottom', async ({ + page, + }) => { + // The sidebar shows the version only once the GitHub releases check + // answers; CI runners are often rate-limited there, so answer locally + // with an older release (no update badge replaces the version). + await page.route('**/repos/4gray/iptvnator/releases*', (route) => + route.fulfill({ + json: [{ created_at: '2020-01-01T00:00:00Z', name: '0.0.1' }], + }) + ); + await openSettings(page); + const about = page.locator( + '.settings-nav__footer [data-test-id="settings-section-about"]' + ); + const version = about.locator('[data-test-id="settings-nav-version"]'); + await expect(version).toBeVisible(); + // A nightly build string is far wider than the default panel. + await version.evaluate((node) => { + node.textContent = '0.25.0-nightly.20261010.3937'; + }); + + const layout = await about.evaluate((link) => { + const label = link.querySelector('.nav-item-label') as HTMLElement; + const tag = link.querySelector( + '[data-test-id="settings-nav-version"]' + ) as HTMLElement; + const panel = link.closest('.context-panel') as HTMLElement; + const linkBox = link.getBoundingClientRect(); + return { + labelClipped: label.scrollWidth > label.clientWidth, + versionClipped: tag.scrollWidth > tag.clientWidth, + linkOverflows: link.scrollWidth > link.clientWidth, + linkBottom: linkBox.bottom, + panelBottom: panel.getBoundingClientRect().bottom, + viewportHeight: window.innerHeight, + }; + }); + + expect(layout.labelClipped).toBe(false); + expect(layout.versionClipped).toBe(true); + expect(layout.linkOverflows).toBe(false); + // The panel ends at the window edge, and its bottom padding keeps the + // footer item off it. + expect(layout.panelBottom).toBeLessThanOrEqual(layout.viewportHeight); + expect( + layout.viewportHeight - layout.linkBottom + ).toBeGreaterThanOrEqual(12); + }); + test('@settings @web Esc leaves settings like the header Back', async ({ page, }) => { diff --git a/libs/workspace/shell/feature/src/lib/workspace-context-panel/workspace-settings-context-panel.component.scss b/libs/workspace/shell/feature/src/lib/workspace-context-panel/workspace-settings-context-panel.component.scss index f90beb0be..4e166f5db 100644 --- a/libs/workspace/shell/feature/src/lib/workspace-context-panel/workspace-settings-context-panel.component.scss +++ b/libs/workspace/shell/feature/src/lib/workspace-context-panel/workspace-settings-context-panel.component.scss @@ -112,8 +112,10 @@ } } +// Auto basis, not `flex: 1`: a zero basis gives the label no claim on the +// row, so any wide trailing element squeezed it to nothing. .nav-item-label { - flex: 1; + flex: 1 1 auto; min-width: 0; line-height: 1.2; white-space: nowrap; @@ -138,8 +140,16 @@ background: var(--app-selection-color); } +// The version yields first when the panel narrows: a nightly build string +// (0.25.0-nightly.YYYYMMDD.N) is wider than the default panel. The large +// shrink factor makes it give way before the label starts to; the full string +// stays in the tooltip. .nav-item-version { - flex-shrink: 0; + flex: 0 1000 auto; + min-width: 0; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; font-family: ui-monospace, 'SF Mono', Menlo, monospace; font-size: 11px; color: var(--app-muted-color, var(--mat-sys-on-surface-variant)); diff --git a/libs/workspace/shell/feature/src/lib/workspace-context-panel/workspace-settings-context-panel.component.spec.ts b/libs/workspace/shell/feature/src/lib/workspace-context-panel/workspace-settings-context-panel.component.spec.ts index b6cb12cfb..1ae1fd23e 100644 --- a/libs/workspace/shell/feature/src/lib/workspace-context-panel/workspace-settings-context-panel.component.spec.ts +++ b/libs/workspace/shell/feature/src/lib/workspace-context-panel/workspace-settings-context-panel.component.spec.ts @@ -163,6 +163,12 @@ describe('WorkspaceSettingsContextPanelComponent', () => { link('about').querySelector('[data-test-id="settings-nav-version"]') ?.textContent ).toBe('0.25.0'); + // A long nightly string is truncated; the tooltip keeps all of it. + expect( + link('about') + .querySelector('[data-test-id="settings-nav-version"]') + ?.getAttribute('title') + ).toBe('0.25.0'); expect(link('general').querySelector('.nav-item-version')).toBeNull(); ctx.setUpdateAvailable(true); diff --git a/libs/workspace/shell/feature/src/lib/workspace-context-panel/workspace-settings-context-panel.component.ts b/libs/workspace/shell/feature/src/lib/workspace-context-panel/workspace-settings-context-panel.component.ts index 05b70ad06..d83fbb970 100644 --- a/libs/workspace/shell/feature/src/lib/workspace-context-panel/workspace-settings-context-panel.component.ts +++ b/libs/workspace/shell/feature/src/lib/workspace-context-panel/workspace-settings-context-panel.component.ts @@ -133,6 +133,7 @@ interface SettingsNavGroupView { {{ version }} } diff --git a/libs/workspace/shell/feature/src/lib/workspace-shell/components/workspace-shell-context-sidebar/workspace-shell-context-sidebar.component.scss b/libs/workspace/shell/feature/src/lib/workspace-shell/components/workspace-shell-context-sidebar/workspace-shell-context-sidebar.component.scss index f046a107e..7b79fbc22 100644 --- a/libs/workspace/shell/feature/src/lib/workspace-shell/components/workspace-shell-context-sidebar/workspace-shell-context-sidebar.component.scss +++ b/libs/workspace/shell/feature/src/lib/workspace-shell/components/workspace-shell-context-sidebar/workspace-shell-context-sidebar.component.scss @@ -1,5 +1,6 @@ :host { - display: block; + display: flex; + flex-direction: column; min-width: 0; min-height: 0; } @@ -16,7 +17,10 @@ display: flex; flex-direction: column; gap: 6px; - height: 100%; + // Fills the host as a flex item rather than with `height: 100%`: without + // a border-box reset that height excludes the padding, so the panel ran + // its bottom padding past the window edge and items sat on it. + flex: 1 1 auto; padding: 12px; width: 300px; min-width: 0;