From 6fd49f78c8c32bc4217f05cac64f7935af5ea864 Mon Sep 17 00:00:00 2001 From: gsxdsm Date: Thu, 6 Aug 2026 20:36:31 -0700 Subject: [PATCH] FN-8820: contain mobile message structure picker Keep long structure selections within the shared Messages composer on narrow layouts. - Make structure picker controls shrinkable and full-width in mobile stacks. - Cover long-label attachment behavior and shared full-page/modal callers. - Add a patch changeset for the mobile containment fix. Files changed: .../fn-8820-message-composer-mobile-width.md | 7 +++++ .../dashboard/app/components/MessageComposer.css | 17 +++++++++++ .../__tests__/MessageComposer.mobile-width.test.ts | 34 ++++++++++++++++++++++ .../components/__tests__/MessageComposer.test.tsx | 14 +++++++++ 4 files changed, 72 insertions(+) Fusion-Task-Id: FN-8820 Fusion-Task-Lineage: a66d51a6-6d92-4402-938c-698c09177583 Co-authored-by: Fusion (runfusion.ai) --- .../fn-8820-message-composer-mobile-width.md | 7 ++++ .../app/components/MessageComposer.css | 17 ++++++++++ .../MessageComposer.mobile-width.test.ts | 34 +++++++++++++++++++ .../__tests__/MessageComposer.test.tsx | 14 ++++++++ 4 files changed, 72 insertions(+) create mode 100644 .changeset/fn-8820-message-composer-mobile-width.md create mode 100644 packages/dashboard/app/components/__tests__/MessageComposer.mobile-width.test.ts diff --git a/.changeset/fn-8820-message-composer-mobile-width.md b/.changeset/fn-8820-message-composer-mobile-width.md new file mode 100644 index 0000000000..b5b6d338d7 --- /dev/null +++ b/.changeset/fn-8820-message-composer-mobile-width.md @@ -0,0 +1,7 @@ +--- +"@runfusion/fusion": patch +--- + +summary: Keep Messages structure selection within narrow mobile composers. +category: fix +dev: Shared MessageComposer sizing now contains long structure labels in full-page and modal Messages. diff --git a/packages/dashboard/app/components/MessageComposer.css b/packages/dashboard/app/components/MessageComposer.css index 9f173e81d0..6d03e2208f 100644 --- a/packages/dashboard/app/components/MessageComposer.css +++ b/packages/dashboard/app/components/MessageComposer.css @@ -15,7 +15,24 @@ flex: 1 1 auto; } +/* +FNXC:MessageComposerMobileWidth 2026-08-07-03:22: +Long native-structure kind and label values must not give the shared Messages picker an intrinsic width that escapes a narrow composer. The shared select remains shrinkable at every breakpoint; mobile explicitly fills its stacked field so both full-page and modal callers inherit containment. +*/ +.message-composer-structure-controls .message-composer-select { + min-width: 0; + width: 100%; + max-width: 100%; +} + @media (max-width: 768px) { + .message-composer-field--structures, + .message-composer-structure-controls { + width: 100%; + max-width: 100%; + min-width: 0; + } + .message-composer-structure-list > li { align-items: stretch; } diff --git a/packages/dashboard/app/components/__tests__/MessageComposer.mobile-width.test.ts b/packages/dashboard/app/components/__tests__/MessageComposer.mobile-width.test.ts new file mode 100644 index 0000000000..f6bf247b92 --- /dev/null +++ b/packages/dashboard/app/components/__tests__/MessageComposer.mobile-width.test.ts @@ -0,0 +1,34 @@ +import { describe, expect, it } from "vitest"; +import { loadComponentCss, readAppFile } from "../../test/cssFixture"; + +function getRuleBlock(css: string, selector: string) { + const escapedSelector = selector.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); + return css.match(new RegExp(`${escapedSelector}\\s*\\{([^}]*)\\}`))?.[1] ?? ""; +} + +describe("MessageComposer mobile structure picker width", () => { + const css = loadComponentCss("MessageComposer.css"); + + it("keeps the long-label select shrinkable in the shared desktop base rule", () => { + const selectRule = getRuleBlock(css, ".message-composer-structure-controls .message-composer-select"); + + expect(selectRule).toMatch(/min-width\s*:\s*0/); + expect(selectRule).toMatch(/width\s*:\s*100%/); + expect(selectRule).toMatch(/max-width\s*:\s*100%/); + }); + + it("fills the stacked mobile field without a caller-specific Messages patch", () => { + const mobileCss = css.slice(css.indexOf("@media (max-width: 768px)")); + const mobileFieldRule = getRuleBlock(mobileCss, ".message-composer-field--structures,\n .message-composer-structure-controls"); + + expect(mobileFieldRule).toMatch(/min-width\s*:\s*0/); + expect(mobileFieldRule).toMatch(/width\s*:\s*100%/); + expect(mobileFieldRule).toMatch(/max-width\s*:\s*100%/); + + for (const caller of ["MailboxView.tsx", "MailboxModal.tsx"]) { + const source = readAppFile(`components/${caller}`); + expect(source).toContain('from "./MessageComposer"'); + expect(source).toContain("nativeStructureCandidates={nativeStructureCandidates}"); + } + }); +}); diff --git a/packages/dashboard/app/components/__tests__/MessageComposer.test.tsx b/packages/dashboard/app/components/__tests__/MessageComposer.test.tsx index f61dd0d76a..03a29a0098 100644 --- a/packages/dashboard/app/components/__tests__/MessageComposer.test.tsx +++ b/packages/dashboard/app/components/__tests__/MessageComposer.test.tsx @@ -147,6 +147,20 @@ describe("MessageComposer", () => { expect(screen.getByText("No structures available")).toBeInTheDocument(); }); + it("preserves a long structure label when attaching from the shared picker", () => { + const longLabel = "A deliberately long structure label that must remain available without changing attachment metadata"; + render(); + + const picker = screen.getByTestId("message-composer-attach-structure"); + expect(picker).not.toBeDisabled(); + expect(picker).toHaveTextContent(`mission-with-a-deliberately-long-kind: ${longLabel}`); + fireEvent.change(picker, { target: { value: "0" } }); + + expect(screen.getByTestId("message-composer-attached-structures")).toHaveTextContent(longLabel); + }); + it("disables send button when content is empty", () => { render(); const sendBtn = screen.getByTestId("message-composer-send");