diff --git a/.changeset/fn-7597-priority-dropdown-matches-oversight.md b/.changeset/fn-7597-priority-dropdown-matches-oversight.md new file mode 100644 index 0000000000..4d89e6363c --- /dev/null +++ b/.changeset/fn-7597-priority-dropdown-matches-oversight.md @@ -0,0 +1,7 @@ +--- +"@runfusion/fusion": patch +--- + +summary: Task-detail Priority dropdown now matches the Oversight dropdown's size, border, and typography. +category: fix +dev: Removed the Priority-only forced select/option uppercase, added a neutral chip background scoped to `.detail-priority-chip.card-priority-badge--normal` for the untinted `normal` level, and reused the FN-7585 shared `--btn-border-width`/`--border`/`--detail-control-border-radius`/`--detail-priority-control-min-height` tokens so both dropdowns render as one control style across desktop and the mobile oversight-overflow surface. diff --git a/packages/dashboard/app/components/TaskDetailModal.css b/packages/dashboard/app/components/TaskDetailModal.css index c4418e0345..93f0b22277 100644 --- a/packages/dashboard/app/components/TaskDetailModal.css +++ b/packages/dashboard/app/components/TaskDetailModal.css @@ -415,13 +415,27 @@ The task-detail modal metadata must keep priority, execution mode, provenance, P opacity: 0.75; } +/* +FNXC:TaskDetail 2026-07-05-12:00: +FN-7597 — the untinted `normal` priority level has no `--low/--high/--urgent` +tint, so without this rule it renders as a bordered-but-empty shell while the +Oversight chip's `--off` level always gets a neutral, token-based background +(`.card-oversight-badge--off` in TaskCard.css). Give `.detail-priority-chip`'s +`normal` state the SAME neutral `color-mix(... var(--text-muted) ...)` +treatment so the two dropdowns read as one consistent control style. Scoped +to the detail chip (not a global `.card-priority-badge--normal` rule) so the +read-only TaskCard priority badge (out of scope) is untouched. +*/ +.detail-priority-chip.card-priority-badge--normal { + background: color-mix(in srgb, var(--text-muted) 12%, transparent); + color: var(--text-muted); +} + .detail-priority-select { border: 0; background: transparent; color: inherit; font: inherit; - text-transform: uppercase; - letter-spacing: inherit; cursor: pointer; padding: 0; min-height: inherit; @@ -437,10 +451,17 @@ The task-detail modal metadata must keep priority, execution mode, provenance, P cursor: wait; } +/* +FNXC:TaskDetail 2026-07-05-12:00: +FN-7597 — drop the Priority-only forced uppercase on the select/option so its +typography matches `.detail-oversight-select` exactly (which relies on the +ancestor `.card-*-badge` label's own `text-transform: uppercase` instead of +redeclaring it here). Visual case is unchanged since the label already +uppercases its content; this only removes a duplicated, drift-prone override. +*/ .detail-priority-select option { color: var(--text); background: var(--surface); - text-transform: uppercase; } .detail-execution-mode-toggle { 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 9e348aadaa..f988e2b5ce 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 @@ -253,6 +253,57 @@ describe("TaskDetailModal", () => { expect(oversightChipBlock).not.toMatch(/border-radius:\s*var\(--radius-pill\)/); }); + it("renders the Priority dropdown chip like the Oversight dropdown chip, on every surface (FN-7597)", () => { + const css = readDashboardStylesSource(); + + const priorityChipBlock = getExactCssRuleBlock(css, ".detail-priority-chip"); + const oversightChipBlock = getExactCssRuleBlock(css, ".detail-oversight-chip"); + const oversightTriggerBlock = getExactCssRuleBlock(css, ".detail-oversight-menu-trigger"); + const prioritySelectBlock = getExactCssRuleBlock(css, ".detail-priority-select"); + const oversightSelectBlock = getExactCssRuleBlock(css, ".detail-oversight-select"); + const prioritySelectOptionBlock = getExactCssRuleBlock(css, ".detail-priority-select option"); + const oversightSelectOptionBlock = getExactCssRuleBlock(css, ".detail-oversight-select option"); + + // Same box size AND same border source for the desktop Priority chip vs. + // BOTH oversight surfaces (desktop chip and the mobile overflow trigger). + for (const block of [priorityChipBlock, oversightChipBlock, oversightTriggerBlock]) { + expect(block).toContain("min-height: var(--detail-priority-control-min-height);"); + expect(block).toContain("border-width: var(--btn-border-width);"); + expect(block).toContain("border-color: var(--border);"); + expect(block).toContain("border-radius: var(--detail-control-border-radius);"); + expect(block).toContain("box-sizing: border-box;"); + } + + // Same select typography: neither select force-uppercases its own text + // or options; both rely on the ancestor chip label's uppercase transform, + // so a regression re-adding a Priority-only override fails this. + expect(prioritySelectBlock).not.toMatch(/text-transform\s*:/); + expect(oversightSelectBlock).not.toMatch(/text-transform\s*:/); + expect(prioritySelectOptionBlock).not.toMatch(/text-transform\s*:/); + expect(oversightSelectOptionBlock).not.toMatch(/text-transform\s*:/); + expect(prioritySelectBlock).toContain("font: inherit;"); + expect(oversightSelectBlock).toContain("font: inherit;"); + + // The untinted `normal` priority level must resolve a real, non-transparent + // neutral chip background (not a borderless/background-less shell), just + // like the Oversight chip's neutral `--off` background. + const priorityNormalBlock = getExactCssRuleBlock(css, ".detail-priority-chip.card-priority-badge--normal"); + const oversightOffBlock = getExactCssRuleBlock(css, ".card-oversight-badge--off"); + expect(priorityNormalBlock).toMatch(/background:\s*color-mix\(in srgb, var\(--text-muted\)/); + expect(oversightOffBlock).toMatch(/background:\s*color-mix\(in srgb, var\(--text-muted\)/); + + // The semantic priority tints (info/warning/error family) must survive — + // this task must not flatten low/high/urgent to the same neutral tone. + expect(css).toMatch(/\.card-priority-badge--low\s*\{[^}]*background:\s*color-mix\(in srgb, var\(--color-info\)/); + expect(css).toMatch(/\.card-priority-badge--high\s*\{[^}]*background:\s*color-mix\(in srgb, var\(--color-warning\)/); + expect(css).toMatch(/\.card-priority-badge--urgent\s*\{[^}]*background:\s*color-mix\(in srgb, var\(--color-error\)/); + + // `--saving` only dims opacity; it must never change box size/border. + const prioritySavingBlock = getExactCssRuleBlock(css, ".detail-priority-chip--saving"); + expect(prioritySavingBlock.replace(/\s+/g, "")).toBe("opacity:0.75;"); + expect(prioritySavingBlock).not.toMatch(/border|min-height|padding/); + }); + it("keeps grouped timestamp metadata inline on desktop and mobile", () => { const css = readDashboardStylesSource();