fix(FN-2714): restore task timer chip to footer metadata row
- Move TaskCard time indicator rendering from the header back into the footer metadata row - Render timer and files-changed metadata together in the shared footer row layout - Update timer chip CSS to stay right-aligned with margin-left:auto and prevent shrinking - Update TaskCard and mobile board tests to assert footer placement and alignment behavior
This commit is contained in:
@@ -482,6 +482,8 @@
|
|||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: var(--space-xs);
|
gap: var(--space-xs);
|
||||||
|
margin-left: auto;
|
||||||
|
flex-shrink: 0;
|
||||||
padding: var(--space-xs) var(--space-sm);
|
padding: var(--space-xs) var(--space-sm);
|
||||||
border: 1px solid color-mix(in srgb, var(--text-muted) 30%, transparent);
|
border: 1px solid color-mix(in srgb, var(--text-muted) 30%, transparent);
|
||||||
border-radius: var(--radius-pill);
|
border-radius: var(--radius-pill);
|
||||||
|
|||||||
@@ -1208,16 +1208,6 @@ function TaskCardComponent({
|
|||||||
{abbreviateMissionTitle(missionTitle ?? task.missionId)}
|
{abbreviateMissionTitle(missionTitle ?? task.missionId)}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
{timeIndicator && (
|
|
||||||
<span
|
|
||||||
className="card-time-indicator"
|
|
||||||
title={timeIndicator.title}
|
|
||||||
aria-label={timeIndicator.ariaLabel}
|
|
||||||
>
|
|
||||||
<Clock size={12} />
|
|
||||||
<span>{timeIndicator.label}</span>
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
<div className="card-header-actions">
|
<div className="card-header-actions">
|
||||||
{canEdit && (
|
{canEdit && (
|
||||||
<button
|
<button
|
||||||
@@ -1394,9 +1384,19 @@ function TaskCardComponent({
|
|||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
})()}
|
})()}
|
||||||
{filesChangedButton && (
|
{(filesChangedButton || timeIndicator) && (
|
||||||
<div className="card-footer-row">
|
<div className="card-footer-row">
|
||||||
{filesChangedButton}
|
{filesChangedButton}
|
||||||
|
{timeIndicator && (
|
||||||
|
<span
|
||||||
|
className="card-time-indicator"
|
||||||
|
title={timeIndicator.title}
|
||||||
|
aria-label={timeIndicator.ariaLabel}
|
||||||
|
>
|
||||||
|
<Clock size={12} />
|
||||||
|
<span>{timeIndicator.label}</span>
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{((task.dependencies && task.dependencies.length > 0) || queued || task.status === "queued" || task.blockedBy) && (
|
{((task.dependencies && task.dependencies.length > 0) || queued || task.status === "queued" || task.blockedBy) && (
|
||||||
|
|||||||
@@ -503,7 +503,7 @@ describe("TaskCard", () => {
|
|||||||
expect(timer?.getAttribute("aria-label")).toContain("Completed processing duration 2h");
|
expect(timer?.getAttribute("aria-label")).toContain("Completed processing duration 2h");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("renders files-changed metadata in footer row and timer chip in header", () => {
|
it("renders files-changed metadata and timer chip in footer row", () => {
|
||||||
vi.useFakeTimers();
|
vi.useFakeTimers();
|
||||||
vi.setSystemTime(new Date("2026-04-25T18:00:00.000Z"));
|
vi.setSystemTime(new Date("2026-04-25T18:00:00.000Z"));
|
||||||
|
|
||||||
@@ -539,9 +539,9 @@ describe("TaskCard", () => {
|
|||||||
expect(filesChanged).not.toBeNull();
|
expect(filesChanged).not.toBeNull();
|
||||||
expect(timer).not.toBeNull();
|
expect(timer).not.toBeNull();
|
||||||
expect(footerRow?.contains(filesChanged)).toBe(true);
|
expect(footerRow?.contains(filesChanged)).toBe(true);
|
||||||
expect(footerRow?.contains(timer)).toBe(false);
|
expect(footerRow?.contains(timer)).toBe(true);
|
||||||
expect(header?.contains(timer)).toBe(true);
|
expect(header?.contains(timer)).toBe(false);
|
||||||
expect(Array.from(footerRow?.children ?? [])).toEqual([filesChanged]);
|
expect(Array.from(footerRow?.children ?? [])).toEqual([filesChanged, timer]);
|
||||||
});
|
});
|
||||||
it.each(["triage", "todo", "in-review", "archived"] as const)(
|
it.each(["triage", "todo", "in-review", "archived"] as const)(
|
||||||
"does not render timer chip for %s cards",
|
"does not render timer chip for %s cards",
|
||||||
|
|||||||
@@ -274,9 +274,9 @@ describe("TaskCard mobile", () => {
|
|||||||
expectRuleToContain(css, ".card-footer-row", "flex-wrap: nowrap;");
|
expectRuleToContain(css, ".card-footer-row", "flex-wrap: nowrap;");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("keeps TaskCard timer chip in-flow and reserves right alignment for header actions", () => {
|
it("keeps TaskCard timer chip in-flow and right-aligned in footer metadata row", () => {
|
||||||
const css = loadAllAppCss();
|
const css = loadAllAppCss();
|
||||||
expectRuleNotToContain(css, ".card-time-indicator", "margin-left: auto;");
|
expectRuleToContain(css, ".card-time-indicator", "margin-left: auto;");
|
||||||
expectRuleToContain(css, ".card-header-actions", "margin-left: auto;");
|
expectRuleToContain(css, ".card-header-actions", "margin-left: auto;");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user