diff --git a/src/shared/design-system/styles/components.css b/src/shared/design-system/styles/components.css index debea6000f..c249b72b26 100644 --- a/src/shared/design-system/styles/components.css +++ b/src/shared/design-system/styles/components.css @@ -2094,7 +2094,9 @@ .buzz-menu-popup { min-width: 11.5rem; max-width: min(20rem, var(--available-width), calc(100vw - var(--space-4))); - max-height: min(24rem, var(--available-height)); + /* No fixed cap: macOS hides overlay scrollbars until scrolled, so rows past one look + absent. Only a viewport too short for the menu still scrolls. */ + max-height: var(--available-height); overflow-y: auto; @apply floating-surface; color: var(--text-standard); diff --git a/tests/browser/channel-lifecycle.spec.mjs b/tests/browser/channel-lifecycle.spec.mjs index c4b0c2beeb..0153d656e9 100644 --- a/tests/browser/channel-lifecycle.spec.mjs +++ b/tests/browser/channel-lifecycle.spec.mjs @@ -831,3 +831,41 @@ test.describe("remote Solo unarchive", () => { expect(app.report.unexpected).toEqual([]); }); }); + +// Browser-only: Playwright clicks scroll items into view, so only layout shows +// whether the shared menu clips a permitted action behind a hidden scrollbar. +test.describe("Solo chat owned through its agent", () => { + test.use({ + soloChannels: ["11111111-1111-4111-8111-111111111111"], + lifecycleRole: "member", + lifecycleOwnerAgent: true, + readState: true, + }); + test("shows every lifecycle action without scrolling the menu", async ({ + page, + app, + }) => { + await page.goto(app.origin); + await openPage(page, "Messages"); + const row = page + .getByRole("navigation", { name: "Subscribed channels" }) + .getByRole("button", { name: "Lifecycle channel", exact: true }); + await row.focus(); + await page.keyboard.press("Shift+F10"); + const menu = page.getByRole("menu", { + name: "Actions for Lifecycle channel", + }); + // The owner-agent grant offers all three to a plain member. + for (const name of ["Leave chat", "Archive chat", "Delete chat"]) + await expect( + menu.getByRole("menuitem", { name, exact: true }), + ).toBeInViewport({ ratio: 1 }); + // At the default text scale, the old 24rem cap clipped this menu by its + // fixed-pixel parts; larger text grows rows and a rem cap alike. + expect( + await menu.evaluate((popup) => popup.scrollHeight - popup.clientHeight), + ).toBe(0); + expect(app.report.lifecyclePublications ?? []).toHaveLength(0); + expect(app.report.unexpected).toEqual([]); + }); +});