feat(FN-4540): relocate in-review move control to TaskCard
Relocated the in-review move control within TaskCard, refactoring both the component layout and styling with accompanying tests — the CSS and TSX changes indicate a significant repositioning of controls while the new test file validates the updated behavior. Fusion-Task-Id: FN-4540
This commit is contained in:
@@ -552,11 +552,31 @@
|
||||
margin-top: var(--space-xs);
|
||||
}
|
||||
|
||||
.card-bottom-row {
|
||||
margin-top: var(--space-sm);
|
||||
}
|
||||
|
||||
.card-bottom-row--split {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: var(--space-sm);
|
||||
}
|
||||
|
||||
.card-bottom-left-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.card-bottom-left-row:focus-within .card-send-back-btn {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.card-bottom-right-row {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
margin-top: var(--space-sm);
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
@@ -1137,6 +1157,13 @@
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.card-bottom-left-row .card-send-back-menu {
|
||||
top: auto;
|
||||
right: auto;
|
||||
bottom: calc(100% + var(--space-xs));
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.card-send-back-menu-item {
|
||||
display: block;
|
||||
width: 100%;
|
||||
@@ -1259,10 +1286,14 @@
|
||||
font-size: 0.625rem;
|
||||
}
|
||||
|
||||
.card-bottom-right-row {
|
||||
.card-bottom-row {
|
||||
margin-top: var(--space-xs);
|
||||
}
|
||||
|
||||
.card-bottom-row--split {
|
||||
gap: var(--space-xs);
|
||||
}
|
||||
|
||||
/* Card: smaller status badges for 280px width */
|
||||
.card-status-badge,
|
||||
.card-priority-badge,
|
||||
|
||||
@@ -801,6 +801,7 @@ function TaskCardComponent({
|
||||
const showTrackingIndicator = hasGithubTrackingLink
|
||||
&& !hasMatchingIssueInfoBadge
|
||||
&& !hasMatchingSourceIssue;
|
||||
const showInReviewMoveControl = task.column === "in-review" && Boolean(onMoveTask);
|
||||
const branchMetadata = useMemo(() => getVisibleTaskCardBranches(task), [task.id, task.branch, task.baseBranch]);
|
||||
const hasBranchMetadata = Boolean(branchMetadata.branch || branchMetadata.baseBranch);
|
||||
const isAgentCreated = isAgentCreatedTask(task);
|
||||
@@ -1610,35 +1611,6 @@ function TaskCardComponent({
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{task.column === "in-review" && onMoveTask && (
|
||||
<div className="card-send-back" ref={sendBackRef}>
|
||||
<button
|
||||
className="card-send-back-btn"
|
||||
onClick={handleSendBackClick}
|
||||
title="Move task"
|
||||
aria-label="Move task"
|
||||
aria-haspopup="menu"
|
||||
aria-expanded={showSendBackMenu}
|
||||
>
|
||||
Move
|
||||
<ChevronDown size={10} />
|
||||
</button>
|
||||
{showSendBackMenu && (
|
||||
<div className="card-send-back-menu" role="menu">
|
||||
{VALID_TRANSITIONS["in-review"].map((col) => (
|
||||
<button
|
||||
key={col}
|
||||
className="card-send-back-menu-item"
|
||||
role="menuitem"
|
||||
onClick={(e) => handleSendBackOptionClick(e, col)}
|
||||
>
|
||||
{col === "done" ? "Done (no merge)" : COLUMN_LABELS[col]}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{task.size && (
|
||||
<span className={`card-size-badge size-${task.size.toLowerCase()}`}>
|
||||
{task.size}
|
||||
@@ -1850,20 +1822,55 @@ function TaskCardComponent({
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{showTrackingIndicator && githubTrackedIssue && (
|
||||
<div className="card-bottom-right-row">
|
||||
<a
|
||||
className="card-github-tracking-chip card-github-tracking-link"
|
||||
href={githubTrackedIssue.url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
title={`Linked GitHub issue: ${githubTrackedIssue.owner}/${githubTrackedIssue.repo}#${githubTrackedIssue.number}`}
|
||||
aria-label={`Linked GitHub issue #${githubTrackedIssue.number}`}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<ProviderIcon provider="github" size="sm" />
|
||||
<span>{`#${githubTrackedIssue.number}`}</span>
|
||||
</a>
|
||||
{(showInReviewMoveControl || (showTrackingIndicator && githubTrackedIssue)) && (
|
||||
<div className={`card-bottom-row${showInReviewMoveControl && showTrackingIndicator ? " card-bottom-row--split" : ""}`}>
|
||||
{showInReviewMoveControl && (
|
||||
<div className="card-bottom-left-row">
|
||||
<div className="card-send-back" ref={sendBackRef}>
|
||||
<button
|
||||
className="card-send-back-btn"
|
||||
onClick={handleSendBackClick}
|
||||
title="Move task"
|
||||
aria-label="Move task"
|
||||
aria-haspopup="menu"
|
||||
aria-expanded={showSendBackMenu}
|
||||
>
|
||||
Move
|
||||
<ChevronDown size={10} />
|
||||
</button>
|
||||
{showSendBackMenu && (
|
||||
<div className="card-send-back-menu" role="menu">
|
||||
{VALID_TRANSITIONS["in-review"].map((col) => (
|
||||
<button
|
||||
key={col}
|
||||
className="card-send-back-menu-item"
|
||||
role="menuitem"
|
||||
onClick={(e) => handleSendBackOptionClick(e, col)}
|
||||
>
|
||||
{col === "done" ? "Done (no merge)" : COLUMN_LABELS[col]}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{showTrackingIndicator && githubTrackedIssue && (
|
||||
<div className="card-bottom-right-row">
|
||||
<a
|
||||
className="card-github-tracking-chip card-github-tracking-link"
|
||||
href={githubTrackedIssue.url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
title={`Linked GitHub issue: ${githubTrackedIssue.owner}/${githubTrackedIssue.repo}#${githubTrackedIssue.number}`}
|
||||
aria-label={`Linked GitHub issue #${githubTrackedIssue.number}`}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<ProviderIcon provider="github" size="sm" />
|
||||
<span>{`#${githubTrackedIssue.number}`}</span>
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<PluginSlot slotId="task-card-badge" projectId={projectId} />
|
||||
|
||||
@@ -1281,6 +1281,48 @@ describe("TaskCard", () => {
|
||||
expect(actionsContainer?.contains(archiveBtn)).toBe(true);
|
||||
});
|
||||
|
||||
it("FN-4540 renders in-review Move control in card-bottom-left-row and keeps menu behavior", () => {
|
||||
const onMoveTask = vi.fn();
|
||||
const { container } = render(
|
||||
<TaskCard
|
||||
task={makeTask({ column: "in-review" })}
|
||||
onOpenDetail={noop}
|
||||
addToast={noop}
|
||||
onMoveTask={onMoveTask}
|
||||
/>,
|
||||
);
|
||||
|
||||
const moveButton = screen.getByRole("button", { name: "Move task" });
|
||||
const actionsContainer = container.querySelector(".card-header-actions");
|
||||
const bottomLeft = moveButton.closest(".card-bottom-left-row");
|
||||
|
||||
expect(actionsContainer).not.toBeNull();
|
||||
expect(actionsContainer?.contains(moveButton)).toBe(false);
|
||||
expect(bottomLeft).not.toBeNull();
|
||||
|
||||
fireEvent.click(moveButton);
|
||||
fireEvent.click(screen.getByRole("menuitem", { name: "Done (no merge)" }));
|
||||
|
||||
expect(onMoveTask).toHaveBeenCalledWith("FN-001", "done", undefined);
|
||||
});
|
||||
|
||||
it("FN-4540 keeps in-progress Send back control in card-header-actions", () => {
|
||||
const { container } = render(
|
||||
<TaskCard
|
||||
task={makeTask({ column: "in-progress" })}
|
||||
onOpenDetail={noop}
|
||||
addToast={noop}
|
||||
onMoveTask={vi.fn()}
|
||||
/>,
|
||||
);
|
||||
|
||||
const sendBackButton = screen.getByRole("button", { name: "Send back" });
|
||||
const actionsContainer = container.querySelector(".card-header-actions");
|
||||
|
||||
expect(actionsContainer).not.toBeNull();
|
||||
expect(actionsContainer?.contains(sendBackButton)).toBe(true);
|
||||
});
|
||||
|
||||
it("shows timer chip for in-progress cards summing workflow runtime + timed events", () => {
|
||||
const { container } = render(
|
||||
<TaskCard
|
||||
|
||||
Reference in New Issue
Block a user