FN-7867: make task-card priority badges icon-only to stop meta-badge wrapping
Board TaskCard priority badges no longer render visible priority text, so the .card-meta-badges row cannot wrap onto a new line when a priority label widens it. - TaskCard.tsx: drop the visible priority-label <span>, add title/aria-label with the full priority label, and keep the label reachable via a visually-hidden span for assistive tech - TaskCard.css: update the FNXC comment on .card-priority-badge to reflect the icon-only rationale (spacing/geometry rules unchanged, still shared with the Task Detail chip/select) - TaskCard.test.tsx: assert icon-only rendering (no visible text node/span), title/aria-label correctness, visually-hidden label content, and that the badge is absent for tasks without a priority; loosen the mocked lucide icons to forward arbitrary props - Add a patch changeset documenting the icon-only priority badge fix Files changed: .changeset/icon-only-priority-badges.md | 7 +++++ packages/dashboard/app/components/TaskCard.css | 2 +- packages/dashboard/app/components/TaskCard.tsx | 12 ++++++--- .../app/components/__tests__/TaskCard.test.tsx | 30 +++++++++++++++++----- 4 files changed, 39 insertions(+), 12 deletions(-) Fusion-Task-Id: FN-7867 Fusion-Task-Lineage: 5c845c23-0a9f-47b4-9a75-6c410b507ef4 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
This commit is contained in:
7
.changeset/icon-only-priority-badges.md
Normal file
7
.changeset/icon-only-priority-badges.md
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
summary: Task card priority badges now show icon-only so they no longer wrap to a new line.
|
||||
category: fix
|
||||
dev: Updates the board TaskCard priority badge to keep labels in title, aria-label, and visually-hidden text.
|
||||
@@ -516,7 +516,7 @@ FN-6774 removes the saturated stuck-task edge stripe from board cards. Keep the
|
||||
}
|
||||
|
||||
.card-priority-badge {
|
||||
/* FNXC:PriorityColorCoding 2026-07-11-00:00: Priority badges add a glyph beside the label; keep inline-flex geometry and token spacing so badge height/wrap invariants do not change. */
|
||||
/* 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. */
|
||||
gap: var(--space-xs);
|
||||
letter-spacing: 0.4px;
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ import { WorkspaceWorktreesSummary, isWorkspaceTask } from "./WorkspaceWorktrees
|
||||
import { WorkflowIcon } from "./WorkflowIcon";
|
||||
import { TaskContextMenu, buildTaskActionMenuModel, getTaskPrAutomationLabel, type TaskContextMenuColumnFlags, type TaskContextMenuColumnMetadata, type TaskMenuActionDescriptor } from "./TaskContextMenu";
|
||||
import { formatCost, hasTaskCost, taskTotalCost } from "../utils/taskTokenCost";
|
||||
import { getPriorityColorVar, getPriorityIcon } from "../utils/priorityIndicator";
|
||||
import { getPriorityColorVar, getPriorityIcon, getPriorityLabel } from "../utils/priorityIndicator";
|
||||
|
||||
/** Per-branch progress snapshot (U13). Surfaced as an optional additive field
|
||||
* on the task payload for the parallel-window badge (U9). */
|
||||
@@ -3086,10 +3086,14 @@ function TaskCardComponent({
|
||||
{hasCardMetaBadges && (
|
||||
<div className="card-meta-badges" data-testid="card-meta-badges">
|
||||
{showPriorityBadge && (
|
||||
<span className={`card-priority-badge card-priority-badge--${normalizedPriority}`}>
|
||||
{/* FNXC:PriorityColorCoding 2026-07-11-00:00: Cards render the shared priority glyph with the priorityIndicator urgency color while preserving the existing badge text and non-normal visibility gate. */}
|
||||
<span
|
||||
className={`card-priority-badge card-priority-badge--${normalizedPriority}`}
|
||||
title={getPriorityLabel(normalizedPriority)}
|
||||
aria-label={getPriorityLabel(normalizedPriority)}
|
||||
>
|
||||
{/* FNXC:PriorityIconOnlyBadge 2026-07-12-00:00: FN-7867 makes task-card priority badges icon-only so priority text cannot widen .card-meta-badges and force wrapping; preserve the label through title, aria-label, and visually-hidden text while keeping the shared urgency color. */}
|
||||
<PriorityBadgeIcon size={10} aria-hidden="true" style={{ color: getPriorityColorVar(normalizedPriority) }} />
|
||||
<span>{normalizedPriority}</span>
|
||||
<span className="visually-hidden">{getPriorityLabel(normalizedPriority)}</span>
|
||||
</span>
|
||||
)}
|
||||
{task.executionMode === "fast" && (
|
||||
|
||||
@@ -21,7 +21,7 @@ import { NavigationHistoryProvider, useNavigationHistory } from "../../hooks/use
|
||||
import { useOverlayDismiss } from "../../hooks/useOverlayDismiss";
|
||||
import type { ConfirmOptions } from "../../hooks/useConfirm";
|
||||
import { TASK_PRIORITIES, type Task, type TaskPriority } from "@fusion/core";
|
||||
import { getPriorityColorVar } from "../../utils/priorityIndicator";
|
||||
import { getPriorityColorVar, getPriorityLabel } from "../../utils/priorityIndicator";
|
||||
|
||||
// Mock lucide-react to avoid SVG rendering issues in test env
|
||||
vi.mock("lucide-react", () => ({
|
||||
@@ -42,10 +42,10 @@ vi.mock("lucide-react", () => ({
|
||||
RotateCw: () => null,
|
||||
Zap: () => <svg data-testid="icon-zap" />,
|
||||
AlertTriangle: () => null,
|
||||
ArrowDown: ({ style }: { style?: React.CSSProperties }) => <svg data-testid="priority-icon-low" className="lucide-arrow-down" style={style} />,
|
||||
Flag: ({ style }: { style?: React.CSSProperties }) => <svg data-testid="priority-icon-normal" className="lucide-flag" style={style} />,
|
||||
ArrowUp: ({ style }: { style?: React.CSSProperties }) => <svg data-testid="priority-icon-high" className="lucide-arrow-up" style={style} />,
|
||||
TriangleAlert: ({ style }: { style?: React.CSSProperties }) => <svg data-testid="priority-icon-urgent" className="lucide-triangle-alert" style={style} />,
|
||||
ArrowDown: ({ style, ...props }: React.SVGProps<SVGSVGElement>) => <svg data-testid="priority-icon-low" className="lucide-arrow-down" style={style} {...props} />,
|
||||
Flag: ({ style, ...props }: React.SVGProps<SVGSVGElement>) => <svg data-testid="priority-icon-normal" className="lucide-flag" style={style} {...props} />,
|
||||
ArrowUp: ({ style, ...props }: React.SVGProps<SVGSVGElement>) => <svg data-testid="priority-icon-high" className="lucide-arrow-up" style={style} {...props} />,
|
||||
TriangleAlert: ({ style, ...props }: React.SVGProps<SVGSVGElement>) => <svg data-testid="priority-icon-urgent" className="lucide-triangle-alert" style={style} {...props} />,
|
||||
ArrowUpRight: () => null,
|
||||
// FN-7592: the overseer badge now renders an icon child instead of a text label,
|
||||
// so tests must see a real SVG (like Zap) rather than a no-op render.
|
||||
@@ -2790,7 +2790,7 @@ describe("TaskCard", () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it("renders shared urgency-colored glyphs for visible priority badges while normal stays hidden", () => {
|
||||
it("renders icon-only urgency-colored priority badges with accessible labels while normal stays hidden", () => {
|
||||
for (const priority of TASK_PRIORITIES) {
|
||||
const { container, unmount } = render(
|
||||
<TaskCard
|
||||
@@ -2807,13 +2807,29 @@ describe("TaskCard", () => {
|
||||
continue;
|
||||
}
|
||||
|
||||
const label = getPriorityLabel(priority);
|
||||
expect(badge).not.toBeNull();
|
||||
expect(badge).toHaveTextContent(priority);
|
||||
expect(badge).toHaveAttribute("aria-label", label);
|
||||
expect(badge).toHaveAttribute("title", label);
|
||||
expect(Array.from(badge?.childNodes ?? []).filter((node) => node.nodeType === Node.TEXT_NODE && node.textContent?.trim())).toEqual([]);
|
||||
const visibleLabelSpans = Array.from(badge?.querySelectorAll("span") ?? []).filter((span) => !span.classList.contains("visually-hidden"));
|
||||
expect(visibleLabelSpans).toEqual([]);
|
||||
expect(badge?.querySelector(".visually-hidden")).toHaveTextContent(label);
|
||||
const icon = badge?.querySelector("svg");
|
||||
expect(icon).not.toBeNull();
|
||||
expect(icon).toHaveAttribute("aria-hidden", "true");
|
||||
expect(icon?.getAttribute("style")).toContain(`color: ${getPriorityColorVar(priority)}`);
|
||||
unmount();
|
||||
}
|
||||
|
||||
const { container } = render(
|
||||
<TaskCard
|
||||
task={makeTask({ priority: undefined })}
|
||||
onOpenDetail={noop}
|
||||
addToast={noop}
|
||||
/>,
|
||||
);
|
||||
expect(container.querySelector(".card-priority-badge")).toBeNull();
|
||||
});
|
||||
|
||||
it("renders partial card meta groups without empty wrappers when time is absent", () => {
|
||||
|
||||
Reference in New Issue
Block a user