From 9f4477d568ff6cc7119c38e68e37451163afd272 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 15 Jun 2026 15:04:17 +0900 Subject: [PATCH 1/2] red(app): cover mounted collapsible table bodies --- apps/app/tests/e2e-web/tier1.spec.ts | 31 ++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/apps/app/tests/e2e-web/tier1.spec.ts b/apps/app/tests/e2e-web/tier1.spec.ts index 1aa71726..86927a30 100644 --- a/apps/app/tests/e2e-web/tier1.spec.ts +++ b/apps/app/tests/e2e-web/tier1.spec.ts @@ -52,6 +52,37 @@ test("actor list page groups actors by role with per-actor detail", async ({ await expect(page.getByText("결제 게이트웨이")).toBeVisible(); }); +test("collapsible table groups keep controlled row groups mounted", async ({ + page +}) => { + await page.goto("/projects/CHECKOUT"); + + const usecaseToggle = page.getByRole("button", { name: "사용자 목표" }); + await expect(usecaseToggle).toHaveAttribute("aria-expanded", "true"); + await expect(usecaseToggle).toHaveAttribute( + "aria-controls", + "usecase-group-USER_GOAL" + ); + + const usecaseRows = page.locator("tbody#usecase-group-USER_GOAL"); + await expect(usecaseRows).toHaveCount(1); + await usecaseToggle.click(); + await expect(usecaseToggle).toHaveAttribute("aria-expanded", "false"); + await expect(usecaseRows).toHaveClass(/hidden/); + + await page.goto("/projects/CHECKOUT/actors"); + + const actorToggle = page.getByRole("button", { name: /지원 액터/ }); + await expect(actorToggle).toHaveAttribute("aria-expanded", "true"); + await expect(actorToggle).toHaveAttribute("aria-controls", "actor-group-SUPPORTING"); + + const actorRows = page.locator("tbody#actor-group-SUPPORTING"); + await expect(actorRows).toHaveCount(1); + await actorToggle.click(); + await expect(actorToggle).toHaveAttribute("aria-expanded", "false"); + await expect(actorRows).toHaveClass(/hidden/); +}); + test("use case detail renders Cockburn fields", async ({ page }) => { await page.goto("/projects/CHECKOUT/usecases/CHECKOUT-001"); await expect( From 6b8663587580a80febb098197b31b0d05d2948fa Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 15 Jun 2026 15:05:45 +0900 Subject: [PATCH 2/2] green(app): keep collapsible table bodies mounted --- apps/app/app/components/ActorTable.tsx | 63 +++++++++++++----------- apps/app/app/components/UsecaseTable.tsx | 59 ++++++++++++---------- 2 files changed, 66 insertions(+), 56 deletions(-) diff --git a/apps/app/app/components/ActorTable.tsx b/apps/app/app/components/ActorTable.tsx index 1cc5a5d0..ea3d5753 100644 --- a/apps/app/app/components/ActorTable.tsx +++ b/apps/app/app/components/ActorTable.tsx @@ -1,6 +1,6 @@ "use client"; -import { useState } from "react"; +import { Fragment, useState } from "react"; import { ChevronRight, CircleDot, ListOrdered, User } from "lucide-react"; import { Avatar, AvatarFallback } from "@/components/ui/avatar"; import { Badge } from "@/components/ui/badge"; @@ -148,41 +148,46 @@ export function ActorTable({ ) : ( groups.map(([type, items]) => { const isOpen = !collapsed.has(type); + const groupId = `actor-group-${type}`; return ( - - - - - - - {isOpen && - items.map((actor) => ( + + + + + + + + + + {items.map((actor) => ( ))} - + + ); }) )} diff --git a/apps/app/app/components/UsecaseTable.tsx b/apps/app/app/components/UsecaseTable.tsx index d8cf56a4..d54589e7 100644 --- a/apps/app/app/components/UsecaseTable.tsx +++ b/apps/app/app/components/UsecaseTable.tsx @@ -1,6 +1,6 @@ "use client"; -import { useState } from "react"; +import { Fragment, useState } from "react"; import Link from "next/link"; import { ChevronRight, @@ -193,39 +193,44 @@ export function UsecaseTable({ ) : ( groups.map(([level, items]) => { const isOpen = !collapsed.has(level); + const groupId = `usecase-group-${level}`; return ( - - - - - - - {isOpen && - items.map((usecase) => ( + + + + + + + + + + {items.map((usecase) => ( ))} - + + ); }) )}