From 295226ea646516258e370877fb8477cdd297232f Mon Sep 17 00:00:00 2001 From: gsxdsm Date: Wed, 22 Jul 2026 17:56:27 -0700 Subject: [PATCH] FN-8501: align mobile task-detail footer actions Align the mobile task-detail Move action with the footer edge. - Let the footer spacer absorb surplus mobile width instead of the Move dropdown. - Cover canonical footer order and triage actions-absent behavior. - Add a patch changeset for the mobile footer fix. Files changed: .changeset/fn-8501-mobile-footer-alignment.md | 7 ++++ .../dashboard/app/components/TaskDetailModal.css | 8 ++++- ...etailModal.responsive-and-dependencies.test.tsx | 40 ++++++++++++++++++++++ 3 files changed, 54 insertions(+), 1 deletion(-) Fusion-Task-Id: FN-8501 Fusion-Task-Lineage: a87bb5af-d16e-4ca8-9111-3168c8440f14 Co-authored-by: Fusion (runfusion.ai) --- .changeset/fn-8501-mobile-footer-alignment.md | 7 ++++ .../app/components/TaskDetailModal.css | 8 +++- ...Modal.responsive-and-dependencies.test.tsx | 40 +++++++++++++++++++ 3 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 .changeset/fn-8501-mobile-footer-alignment.md diff --git a/.changeset/fn-8501-mobile-footer-alignment.md b/.changeset/fn-8501-mobile-footer-alignment.md new file mode 100644 index 0000000000..e0b8599d5a --- /dev/null +++ b/.changeset/fn-8501-mobile-footer-alignment.md @@ -0,0 +1,7 @@ +--- +"@runfusion/fusion": patch +--- + +summary: Align mobile task-detail Move actions with the footer edge. +category: fix +dev: The Task Detail spacer now owns mobile footer surplus width. diff --git a/packages/dashboard/app/components/TaskDetailModal.css b/packages/dashboard/app/components/TaskDetailModal.css index 37cda58ae5..84be4878fe 100644 --- a/packages/dashboard/app/components/TaskDetailModal.css +++ b/packages/dashboard/app/components/TaskDetailModal.css @@ -3402,6 +3402,12 @@ Live and Feed Activity expansion overlays the content instead of reserving a too for overflow, so scoped tokenized packing, flex shrink guards, and label ellipsis let every text-bearing footer control yield while upward menus stay unclipped. + + FNXC:TaskDetailModalResponsive 2026-07-22-17:12: + FN-8501 requires the flexible spacer—not the Move dropdown wrapper—to absorb + available mobile row space. Keeping the wrapper content-sized puts the primary + transition at the trailing edge while its controls retain the FN-8492 shrink + and ellipsis safeguards for dense review variants. */ .task-detail-content .modal-actions { flex-wrap: nowrap; @@ -3422,7 +3428,7 @@ Live and Feed Activity expansion overlays the content instead of reserving a too } .task-detail-content .detail-move-dropdown { - flex: 1 1 auto; + flex: 0 1 auto; } .task-detail-content .detail-move-actions-in-review { diff --git a/packages/dashboard/app/components/__tests__/TaskDetailModal.responsive-and-dependencies.test.tsx b/packages/dashboard/app/components/__tests__/TaskDetailModal.responsive-and-dependencies.test.tsx index 4ec011bec8..e43b480ab0 100644 --- a/packages/dashboard/app/components/__tests__/TaskDetailModal.responsive-and-dependencies.test.tsx +++ b/packages/dashboard/app/components/__tests__/TaskDetailModal.responsive-and-dependencies.test.tsx @@ -901,6 +901,7 @@ describe("TaskDetailModal", () => { mobileBlock, ".task-detail-content .detail-actions-dropdown,\n .task-detail-content .detail-move-dropdown", ); + const desktopMoveDropdownBlock = getExactCssRuleBlock(css, ".detail-move-dropdown"); const expandedChatBlock = getExactCssRuleBlock(css, ".task-detail-content--chat-expanded .modal-actions"); /* @@ -917,6 +918,13 @@ describe("TaskDetailModal", () => { expect(spacerBlock).toContain("min-width: 0;"); expect(dropdownBlock).toContain("min-width: 0;"); expect(dropdownBlock).toContain("flex-shrink: 1;"); + // FNXC:TaskDetailModalResponsive 2026-07-22-17:12: The Actions + Move + // symptom regressed when Move grew into the spacer's middle region. This + // mobile-only contract leaves all surplus width to the spacer, making the + // move control trailing-aligned without changing desktop dropdown layout. + expect(mobileBlock).toMatch(/\.task-detail-content \.detail-move-dropdown\s*\{\s*flex:\s*0 1 auto;/); + expect(mobileBlock).not.toMatch(/\.task-detail-content \.detail-move-dropdown\s*\{\s*flex:\s*1 1 auto;/); + expect(desktopMoveDropdownBlock).not.toContain("flex:"); expect(inReviewBlock).toContain("display: flex;"); expect(inReviewBlock).toContain("flex-wrap: nowrap;"); expect(inReviewBlock).toContain("min-width: 0;"); @@ -974,6 +982,38 @@ describe("TaskDetailModal", () => { expect(standardFooter).toBeTruthy(); expect(standardFooter?.contains(screen.getByRole("button", { name: "Actions" }))).toBe(true); expect(standardFooter?.querySelector(".detail-move-btn")).toBeTruthy(); + + const footerChildren = Array.from(standardFooter?.children ?? []); + const actionsIndex = footerChildren.findIndex((child) => child.classList.contains("detail-actions-dropdown")); + const spacerIndex = footerChildren.findIndex((child) => child.classList.contains("modal-actions-spacer")); + const moveIndex = footerChildren.findIndex((child) => child.classList.contains("detail-move-dropdown")); + + // The shared modal, embedded panel, dock, and mobile sheet all mount this + // canonical footer order: leading Actions, flexible spacer, trailing Move. + expect(actionsIndex).toBeGreaterThanOrEqual(0); + expect(spacerIndex).toBeGreaterThan(actionsIndex); + expect(moveIndex).toBeGreaterThan(spacerIndex); + }); + + it("keeps the triage footer usable when Actions is absent", () => { + const { container } = render( + , + ); + const footer = container.querySelector(".modal-actions"); + + expect(footer?.querySelector(".detail-actions-dropdown")).toBeNull(); + expect(footer?.querySelector(".modal-actions-spacer")).toBeTruthy(); + expect(footer?.querySelector(".detail-move-dropdown .detail-move-btn")).toBeTruthy(); + expect(screen.getByRole("button", { name: "Delete task" })).toBeTruthy(); }); it("modal-actions contains Delete and Pause buttons for non-done tasks (via Actions dropdown)", () => {