FN-8667: align fast-mode badges with task status
Keep fast-mode metadata badges in the task-card header's shared wrapping context. - Make the meta badge wrapper layout-transparent so fast, priority, and oversight badges wrap independently with status. - Prevent fast and priority chips from shrinking and update desktop and mobile layout coverage. - Add a patch changeset for aligned task-card badges. Files changed: .changeset/task-card-fast-mode-badge-wrap.md | 7 ++++ packages/dashboard/app/components/TaskCard.css | 16 ++++---- .../__tests__/TaskCard.badge-wrap.test.tsx | 46 ++++++++++++++++++++++ .../app/components/__tests__/TaskCard.test.tsx | 6 +-- 4 files changed, 64 insertions(+), 11 deletions(-) Fusion-Task-Id: FN-8667 Fusion-Task-Lineage: 34bdb410-8d8a-4b1f-8bc4-105ae324874e Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
This commit is contained in:
7
.changeset/task-card-fast-mode-badge-wrap.md
Normal file
7
.changeset/task-card-fast-mode-badge-wrap.md
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
summary: Keep fast-mode task-card badges aligned with status badges when space allows.
|
||||
category: fix
|
||||
dev: The task-card meta badge wrapper is layout-transparent so nested chips share the header wrap context.
|
||||
@@ -125,13 +125,12 @@ The size chip is a direct header child immediately after the task id, before thi
|
||||
min-height: var(--card-chip-height);
|
||||
}
|
||||
|
||||
/*
|
||||
FNXC:TaskCardLayout 2026-08-01-05:38:
|
||||
The fast-mode badge must share the header line with status, priority, and oversight badges whenever space remains. Keep this nested semantic wrapper layout-transparent so its meta badges join `.card-header-badges` directly instead of wrapping as one atomic group.
|
||||
*/
|
||||
.card-meta-badges {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: var(--space-xs);
|
||||
row-gap: var(--space-xs);
|
||||
min-width: 0;
|
||||
display: contents;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -577,6 +576,7 @@ The size chip must derive its box from the shared header-chip padding, line-heig
|
||||
|
||||
.card-priority-badge {
|
||||
/* FNXC:PriorityIconOnlyBadge 2026-07-12-00:00: FN-7867 removes visible priority text from board cards, so these shared spacing declarations no longer add card text width; keep them to preserve the Task Detail priority chip/select geometry that reuses the same badge class. */
|
||||
flex-shrink: 0;
|
||||
gap: var(--space-xs);
|
||||
letter-spacing: 0.4px;
|
||||
}
|
||||
@@ -604,6 +604,7 @@ Zap at the badge text size so it remains centered without making the header row
|
||||
.card-execution-mode-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
justify-content: center;
|
||||
box-sizing: border-box;
|
||||
font-size: 0.625rem;
|
||||
@@ -2026,8 +2027,7 @@ The three-dot menu is the sole card move/action entry point. Keep this shared bu
|
||||
}
|
||||
|
||||
|
||||
.card-header-badges,
|
||||
.card-meta-badges {
|
||||
.card-header-badges {
|
||||
gap: calc(var(--space-xs) / 2);
|
||||
row-gap: var(--space-xs);
|
||||
}
|
||||
|
||||
@@ -282,6 +282,52 @@ describe("TaskCard badge wrapping (FN-5162)", () => {
|
||||
expect(container.querySelector(".card-header-actions")).toBeNull();
|
||||
});
|
||||
|
||||
it.each([
|
||||
["fast alone", { priority: "normal", plannerOversightLevel: "off" }],
|
||||
["fast with priority", { priority: "urgent", plannerOversightLevel: "off" }],
|
||||
["fast with oversight", { priority: "normal", plannerOversightLevel: "steer" }],
|
||||
["fast with priority and oversight", { priority: "urgent", plannerOversightLevel: "steer" }],
|
||||
] as const)("keeps %s beside a queued-to-plan status chip in the shared wrap context", (_name, meta) => {
|
||||
const { container: queuedContainer } = render(
|
||||
<TaskCard
|
||||
task={makeTask({
|
||||
column: "todo",
|
||||
status: undefined,
|
||||
steps: [],
|
||||
executionMode: "fast",
|
||||
...meta,
|
||||
})}
|
||||
onOpenDetail={noop}
|
||||
addToast={noop}
|
||||
/>,
|
||||
);
|
||||
|
||||
const headerBadges = queuedContainer.querySelector(".card-header-badges") as HTMLElement;
|
||||
const statusBadge = queuedContainer.querySelector(".card-status-badge") as HTMLElement;
|
||||
const metaBadges = queuedContainer.querySelector('[data-testid="card-meta-badges"]') as HTMLElement;
|
||||
const fastBadge = queuedContainer.querySelector(".card-execution-mode-badge") as HTMLElement;
|
||||
|
||||
expect(headerBadges).toBeTruthy();
|
||||
expect(statusBadge).toHaveTextContent("Queued to plan");
|
||||
expect(metaBadges).toBeTruthy();
|
||||
expect(fastBadge).toBeTruthy();
|
||||
expect(metaBadges.parentElement).toBe(headerBadges);
|
||||
expect(headerBadges.contains(statusBadge)).toBe(true);
|
||||
expect(headerBadges.contains(fastBadge)).toBe(true);
|
||||
expect(getComputedStyle(metaBadges).display).toBe("contents");
|
||||
expect(getComputedStyle(fastBadge).flexShrink).toBe("0");
|
||||
const priorityBadge = queuedContainer.querySelector(".card-priority-badge") as HTMLElement | null;
|
||||
if (priorityBadge) expect(getComputedStyle(priorityBadge).flexShrink).toBe("0");
|
||||
});
|
||||
|
||||
it("keeps the layout-transparent meta wrapper contract in the mobile badge context", () => {
|
||||
const mobileSection = getCssBlocks(loadedCss, "max-width: 768px").join("\n");
|
||||
|
||||
expectCssRuleToContain(loadedCss, ".card-meta-badges", "display: contents;");
|
||||
expectCssRuleToContain(mobileSection, ".card-header-badges", "gap: calc(var(--space-xs) / 2);");
|
||||
expect(mobileSection).not.toMatch(/\.card-meta-badges\s*\{/);
|
||||
});
|
||||
|
||||
it("places a fast-mode size badge after the task id before wrapping header badges", () => {
|
||||
const { container: sizedContainer } = render(
|
||||
<TaskCard
|
||||
|
||||
@@ -3873,11 +3873,11 @@ describe("TaskCard", () => {
|
||||
expect(container.querySelector(".card-footer-row-right")).toBeNull();
|
||||
});
|
||||
|
||||
it("defines responsive flex-wrap styling for grouped card meta badges", () => {
|
||||
it("keeps grouped card meta badges layout-transparent in the shared header wrap context", () => {
|
||||
const fullCss = loadAllAppCss();
|
||||
|
||||
expect(fullCss).toMatch(/\.card-meta-badges\s*\{[^}]*display:\s*flex;[^}]*flex-wrap:\s*wrap;[^}]*gap:\s*var\(--space-xs\);[^}]*\}/);
|
||||
expect(fullCss).toMatch(/@media[^{]*\(max-width:\s*768px\)[^{]*\{[\s\S]*?\.card-meta-badges\s*\{[^}]*gap:\s*calc\(var\(--space-xs\) \/ 2\);[^}]*\}/);
|
||||
expect(fullCss).toMatch(/\.card-meta-badges\s*\{[^}]*display:\s*contents;[^}]*\}/);
|
||||
expect(fullCss).toMatch(/@media[^{]*\(max-width:\s*768px\)[^{]*\{[\s\S]*?\.card-header-badges\s*\{[^}]*gap:\s*calc\(var\(--space-xs\) \/ 2\);[^}]*\}/);
|
||||
});
|
||||
|
||||
describe("retry button on failed tasks", () => {
|
||||
|
||||
Reference in New Issue
Block a user