FN-7618: stretch Oversight dropdown trigger to match Priority/Execution-mode height

Fixes the Task Detail Oversight dropdown trigger rendering shorter than the Priority and Execution-mode controls on non-mobile viewports.

- Made `.detail-oversight-menu-dropdown` an `inline-flex` with `align-items: stretch` so the popover-positioning wrapper participates in `.detail-meta-inline-controls`'s stretch behavior instead of only sizing to its own content.
- Added `align-self: stretch` to `.detail-oversight-menu-trigger` so it fills the now-stretched wrapper, matching Priority/Execution-mode's direct-child stretch.
- Added a regression test asserting the wrapper/trigger stretch declarations exist, apply at every viewport, and don't leak into the absolutely-positioned popover.
- Added a patch changeset documenting the fix.

Files changed:
 .changeset/fn-7618-oversight-trigger-height.md     |  7 ++++
 .../dashboard/app/components/TaskDetailModal.css   | 26 ++++++++++++
 ...etailModal.responsive-and-dependencies.test.tsx | 48 ++++++++++++++++++++++
 3 files changed, 81 insertions(+)

Fusion-Task-Id: FN-7618
Fusion-Task-Lineage: f918c2aa-5677-446c-a7dc-17c72adb9c27
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
This commit is contained in:
gsxdsm
2026-07-05 21:22:55 -07:00
parent 1b1e1f147f
commit 1ea3c86769
3 changed files with 81 additions and 0 deletions

View File

@@ -0,0 +1,7 @@
---
"@runfusion/fusion": patch
---
summary: Task-detail Oversight button now matches Priority/Execution-mode height on desktop.
category: fix
dev: `.detail-oversight-menu-dropdown` (the popover-positioning wrapper) is now `inline-flex; align-items: stretch` so it participates in `.detail-meta-inline-controls`'s stretch, and `.detail-oversight-menu-trigger` gets `align-self: stretch` to fill it — matching Priority/Execution-mode's direct-child stretch behavior without any new hardcoded height.

View File

@@ -686,8 +686,25 @@ FNXC:PlannerOversight 2026-07-05-00:00:
FN-7604 — this overflow menu is now the single universal surface at every
viewport (desktop and mobile); it is no longer mobile-only.
*/
/*
FNXC:TaskDetail 2026-07-05-14:00:
FN-7618 — Priority (`<label>`) and Execution-mode (`<button>`) are direct
flex children of `.detail-meta-inline-controls { align-items: stretch }`, so
they stretch to the shared `--detail-priority-control-min-height` row height.
FN-7604 wrapped the Oversight trigger in this `position: relative` div
(required for the popover's absolute positioning), which became the flex
child instead of the trigger itself — so the trigger only got its own
intrinsic `min-height` and never stretched to match its taller siblings.
Make the wrapper an `inline-flex` with `align-items: stretch` so it
participates in the cluster's stretch on every viewport (desktop and
mobile); the trigger below fills the wrapper's full height. The popover
(`.detail-oversight-menu`) stays `position: absolute`, so this does not
affect its size or anchoring — only the trigger's box height changes.
*/
.detail-oversight-menu-dropdown {
position: relative;
display: inline-flex;
align-items: stretch;
}
.detail-oversight-menu-trigger {
@@ -706,7 +723,16 @@ viewport (desktop and mobile); it is no longer mobile-only.
FN-7604 — this trigger is now the single universal surface at every
viewport (the desktop-only `.detail-oversight-chip` it used to swap in for
was removed).
FNXC:TaskDetail 2026-07-05-14:00:
FN-7618 — `align-self: stretch` makes the trigger fill the height of its
`.detail-oversight-menu-dropdown` wrapper (now `inline-flex`/`align-items:
stretch`), which itself stretches to the shared cluster row height. The
`min-height` token above remains as the floor for any context where the
wrapper isn't stretched (e.g. isolated rendering), so the trigger never
drops below Priority/Execution-mode's height either way.
*/
align-self: stretch;
border-width: var(--btn-border-width);
border-color: var(--border);
border-radius: var(--detail-control-border-radius);

View File

@@ -256,6 +256,54 @@ describe("TaskDetailModal", () => {
expect(oversightTriggerBlock).not.toMatch(/border-radius:\s*var\(--radius-pill\)/);
});
it("stretches the Oversight dropdown wrapper so the trigger matches the Priority/Execution-mode row height on every surface (FN-7618)", () => {
const css = readDashboardStylesSource();
const mobileBlock = getCssAtRuleBlockContaining(css, "@media (max-width: 768px)", ".detail-meta-inline-controls");
const priorityChipBlock = getExactCssRuleBlock(css, ".detail-priority-chip");
const executionToggleBlock = getExactCssRuleBlock(css, ".detail-execution-mode-toggle");
const oversightTriggerBlock = getExactCssRuleBlock(css, ".detail-oversight-menu-trigger");
const oversightDropdownBlock = getExactCssRuleBlock(css, ".detail-oversight-menu-dropdown");
const oversightMenuBlock = getExactCssRuleBlock(css, ".detail-oversight-menu");
// Priority and Execution-mode are direct flex children of
// `.detail-meta-inline-controls { align-items: stretch }`, so they
// stretch to the shared row height via the same min-height token.
for (const block of [priorityChipBlock, executionToggleBlock, oversightTriggerBlock]) {
expect(block).toContain("min-height: var(--detail-priority-control-min-height);");
}
// The Oversight trigger instead lives inside a `position: relative`
// `.detail-oversight-menu-dropdown` wrapper (required for the popover's
// absolute positioning). Without the wrapper itself participating in
// the cluster's stretch, the trigger only gets its own intrinsic
// min-height and renders shorter than its siblings on non-mobile
// widths. Assert the wrapper stretches and the trigger fills it, so a
// future change that drops either declaration fails this test.
expect(oversightDropdownBlock).toContain("position: relative;");
expect(oversightDropdownBlock).toContain("display: inline-flex;");
expect(oversightDropdownBlock).toContain("align-items: stretch;");
expect(oversightTriggerBlock).toContain("align-self: stretch;");
// This invariant is not scoped to a mobile-only media block: the
// cluster's base rule (which the dropdown/trigger rules above read
// from) applies at every width, and there must be no non-mobile
// override that removes the stretch behavior.
expect(css).not.toMatch(/@media[^{]*\(min-width:[^{]*\{[\s\S]*?\.detail-oversight-menu-dropdown\s*\{[^}]*align-items:\s*(?:center|flex-start|flex-end);/);
// The wrapper stretch must not affect the popover: it stays absolutely
// positioned (independent of the flex layout) and unstretched.
expect(oversightMenuBlock).toContain("position: absolute;");
expect(oversightMenuBlock).not.toContain("align-self: stretch;");
expect(oversightMenuBlock).not.toMatch(/height:\s*100%/);
// No `@media (max-width: 768px)` override removes the wrapper's stretch
// declarations, so the mobile `flex-wrap: wrap` fallback keeps the same
// fix in effect.
expect(mobileBlock).not.toMatch(/\.detail-oversight-menu-dropdown\s*\{[^}]*align-items:\s*(?:center|flex-start|flex-end|normal);/);
expect(mobileBlock).not.toMatch(/\.detail-oversight-menu-trigger\s*\{[^}]*align-self:\s*(?:auto|center|flex-start|flex-end);/);
});
it("renders the Priority dropdown chip like the Oversight dropdown chip, on every surface (FN-7597)", () => {
const css = readDashboardStylesSource();