feat(KB-193): auto-expand steps for in-progress task cards

- Auto-expand steps disclosure when task is in-progress
- Keep steps collapsed for todo/done/archived states
- Add comprehensive tests for auto-expand behavior in TaskCard
- Update TaskCard component with expanded state logic
This commit is contained in:
gsxdsm
2026-03-30 12:49:25 -07:00
parent b0f1de4eb1
commit fc0ce7cf95
2 changed files with 226 additions and 1 deletions

View File

@@ -127,7 +127,7 @@ function TaskCardComponent({
const [editTitle, setEditTitle] = useState(task.title || "");
const [editDescription, setEditDescription] = useState(task.description || "");
const [isSaving, setIsSaving] = useState(false);
const [showSteps, setShowSteps] = useState(false);
const [showSteps, setShowSteps] = useState(task.column === "in-progress");
const titleInputRef = useRef<HTMLInputElement>(null);
const descTextareaRef = useRef<HTMLTextAreaElement>(null);

View File

@@ -1276,7 +1276,9 @@ describe("TaskCard steps toggle", () => {
});
it("shows steps toggle with count when task has steps", () => {
// Use 'todo' column to test default collapsed behavior
const task = makeTask({
column: "todo",
steps: [
{ name: "Step 1", status: "done" },
{ name: "Step 2", status: "pending" },
@@ -1297,7 +1299,9 @@ describe("TaskCard steps toggle", () => {
});
it("clicking toggle expands and shows step list", () => {
// Use 'todo' column to test default collapsed behavior
const task = makeTask({
column: "todo",
steps: [
{ name: "First step", status: "done" },
{ name: "Second step", status: "in-progress" },
@@ -1321,7 +1325,9 @@ describe("TaskCard steps toggle", () => {
});
it("clicking toggle again collapses step list", () => {
// Use 'todo' column to test default collapsed behavior
const task = makeTask({
column: "todo",
steps: [{ name: "Single step", status: "pending" }],
});
@@ -1345,7 +1351,9 @@ describe("TaskCard steps toggle", () => {
});
it("step list renders correct number of steps", () => {
// Use 'todo' column to test default collapsed behavior
const task = makeTask({
column: "todo",
steps: [
{ name: "Step 1", status: "done" },
{ name: "Step 2", status: "in-progress" },
@@ -1371,7 +1379,9 @@ describe("TaskCard steps toggle", () => {
});
it("completed steps have strikethrough style", () => {
// Use 'todo' column to test default collapsed behavior
const task = makeTask({
column: "todo",
steps: [
{ name: "Done step", status: "done" },
{ name: "Pending step", status: "pending" },
@@ -1397,7 +1407,9 @@ describe("TaskCard steps toggle", () => {
});
it("toggle does not trigger card click when clicked", () => {
// Use 'todo' column to test default collapsed behavior
const task = makeTask({
column: "todo",
steps: [{ name: "Test step", status: "pending" }],
});
const onOpenDetail = vi.fn();
@@ -1418,7 +1430,9 @@ describe("TaskCard steps toggle", () => {
});
it("aria-expanded reflects toggle state", () => {
// Use 'todo' column to test default collapsed behavior
const task = makeTask({
column: "todo",
steps: [{ name: "Test step", status: "pending" }],
});
@@ -1438,7 +1452,9 @@ describe("TaskCard steps toggle", () => {
});
it("chevron icon rotates when expanded", () => {
// Use 'todo' column to test default collapsed behavior
const task = makeTask({
column: "todo",
steps: [{ name: "Test step", status: "pending" }],
});
@@ -1484,7 +1500,9 @@ describe("TaskCard steps toggle", () => {
});
it("step list renders skipped status with correct CSS class", () => {
// Use 'todo' column to test default collapsed behavior
const task = makeTask({
column: "todo",
steps: [
{ name: "Done step", status: "done" },
{ name: "Skipped step", status: "skipped" },
@@ -1539,6 +1557,213 @@ describe("TaskCard steps toggle", () => {
});
});
/**
* Tests for auto-expanded steps disclosure in in-progress column (KB-193).
*/
describe("TaskCard steps auto-expand", () => {
const noopToast = vi.fn();
beforeEach(() => {
vi.clearAllMocks();
});
it("steps are expanded by default for 'in-progress' column tasks", () => {
const task = makeTask({
column: "in-progress",
steps: [
{ name: "Step 1", status: "done" },
{ name: "Step 2", status: "pending" },
],
});
render(
<TaskCard
task={task}
onOpenDetail={vi.fn()}
addToast={noopToast}
/>
);
// Steps should be visible without clicking
expect(screen.getByText("Step 1")).toBeDefined();
expect(screen.getByText("Step 2")).toBeDefined();
// Toggle should show "Hide steps"
const toggle = screen.getByRole("button", { name: /Hide steps/i });
expect(toggle.getAttribute("aria-expanded")).toBe("true");
});
it("steps are collapsed by default for 'triage' column tasks", () => {
const task = makeTask({
column: "triage",
steps: [
{ name: "Step 1", status: "done" },
{ name: "Step 2", status: "pending" },
],
});
render(
<TaskCard
task={task}
onOpenDetail={vi.fn()}
addToast={noopToast}
/>
);
// Steps should not be visible
expect(screen.queryByText("Step 1")).toBeNull();
expect(screen.queryByText("Step 2")).toBeNull();
// Toggle should show "Show steps"
const toggle = screen.getByRole("button", { name: /Show steps/i });
expect(toggle.getAttribute("aria-expanded")).toBe("false");
});
it("steps are collapsed by default for 'todo' column tasks", () => {
const task = makeTask({
column: "todo",
steps: [
{ name: "Step 1", status: "done" },
{ name: "Step 2", status: "pending" },
],
});
render(
<TaskCard
task={task}
onOpenDetail={vi.fn()}
addToast={noopToast}
/>
);
// Steps should not be visible
expect(screen.queryByText("Step 1")).toBeNull();
expect(screen.queryByText("Step 2")).toBeNull();
// Toggle should show "Show steps"
const toggle = screen.getByRole("button", { name: /Show steps/i });
expect(toggle.getAttribute("aria-expanded")).toBe("false");
});
it("steps are collapsed by default for 'in-review' column tasks", () => {
const task = makeTask({
column: "in-review",
steps: [
{ name: "Step 1", status: "done" },
{ name: "Step 2", status: "pending" },
],
});
render(
<TaskCard
task={task}
onOpenDetail={vi.fn()}
addToast={noopToast}
/>
);
// Steps should not be visible
expect(screen.queryByText("Step 1")).toBeNull();
expect(screen.queryByText("Step 2")).toBeNull();
// Toggle should show "Show steps"
const toggle = screen.getByRole("button", { name: /Show steps/i });
expect(toggle.getAttribute("aria-expanded")).toBe("false");
});
it("steps are collapsed by default for 'done' column tasks", () => {
const task = makeTask({
column: "done",
steps: [
{ name: "Step 1", status: "done" },
{ name: "Step 2", status: "done" },
],
});
render(
<TaskCard
task={task}
onOpenDetail={vi.fn()}
addToast={noopToast}
/>
);
// Steps should not be visible
expect(screen.queryByText("Step 1")).toBeNull();
expect(screen.queryByText("Step 2")).toBeNull();
// Toggle should show "Show steps"
const toggle = screen.getByRole("button", { name: /Show steps/i });
expect(toggle.getAttribute("aria-expanded")).toBe("false");
});
it("toggle button works to collapse steps on in-progress cards", () => {
const task = makeTask({
column: "in-progress",
steps: [
{ name: "Step 1", status: "done" },
{ name: "Step 2", status: "pending" },
],
});
render(
<TaskCard
task={task}
onOpenDetail={vi.fn()}
addToast={noopToast}
/>
);
// Steps should be visible initially
expect(screen.getByText("Step 1")).toBeDefined();
// Click toggle to collapse
const toggle = screen.getByRole("button", { name: /Hide steps/i });
fireEvent.click(toggle);
// Steps should now be hidden
expect(screen.queryByText("Step 1")).toBeNull();
expect(screen.queryByText("Step 2")).toBeNull();
// Toggle should now show "Show steps"
expect(toggle.getAttribute("aria-label")).toBe("Show steps");
expect(toggle.getAttribute("aria-expanded")).toBe("false");
});
it("toggle button works to expand steps on non-in-progress cards", () => {
const task = makeTask({
column: "todo",
steps: [
{ name: "Step 1", status: "done" },
{ name: "Step 2", status: "pending" },
],
});
render(
<TaskCard
task={task}
onOpenDetail={vi.fn()}
addToast={noopToast}
/>
);
// Steps should be hidden initially
expect(screen.queryByText("Step 1")).toBeNull();
// Click toggle to expand
const toggle = screen.getByRole("button", { name: /Show steps/i });
fireEvent.click(toggle);
// Steps should now be visible
expect(screen.getByText("Step 1")).toBeDefined();
expect(screen.getByText("Step 2")).toBeDefined();
// Toggle should now show "Hide steps"
expect(toggle.getAttribute("aria-label")).toBe("Hide steps");
expect(toggle.getAttribute("aria-expanded")).toBe("true");
});
});
/**
* Tests for GitHub badges rendering in TaskCard.
*/