/**
* Showcase · table-expandable-rows — 展開行 (V9)
*
* Expandable detail row pattern for a 勤怠 (attendance) admin list. Clicking a row
* reveals an inline detail panel (border-left 3px primary) directly beneath it;
* the toggle is EXCLUSIVE — opening one row collapses any other.
*
* Why compose from
instead of :
* DataTable.Content renders each row from the column defs and offers no slot to
* inject an extra
(the detail panel) between data rows, nor an expanded /
* exclusive-open state. So the master list is built from the real low-level
* Table family (Table / TableHeader / TableRow / TableHead / TableCell) — the
* same primitives DataTable itself uses — and the panel is a spanning
);
}
// ── Master list with exclusive expandable rows ─────────────────────────────────
const COLSPAN = 6; // toggle + 従業員 + 部署 + 直近 + 出勤日数 + 残業
function ExpandableList() {
// Exclusive open: a single id (or null) — opening one collapses any other.
const [openId, setOpenId] = React.useState("E-1043");
const toggle = (id: string) => {
setOpenId((cur) => (cur === id ? null : id));
};
return (
従業員部署直近の状態出勤日数残業 累計
{EMPLOYEES.map((emp) => {
const isOpen = openId === emp.id;
const panelId = `row-detail-${emp.id}`;
return (
{/* THE DISCLOSURE IS THE BUTTON, NOT THE ROW (gh#643, found by check:frame-axe).
This row used to carry `tabIndex={0}` + `aria-expanded` + `aria-controls` and be
the control itself. `aria-expanded` is valid on a TREEGRID row and not on a table
row, so axe scored it `aria-conditional-attr` — and the row was a focus stop with
no role and no name, which is the worse half of the same mistake. The chevron is
now a real button: it owns the state, the name and the tab stop. Clicking the row
still works, as a pointer convenience that claims no semantics of its own. */}
{
toggle(emp.id);
}}
className="hover:bg-muted/50 cursor-pointer"
>
{emp.name}
{emp.id} · No.{emp.code}
{emp.dept}{emp.workDays}{emp.overtime}
{isOpen && (
{/* `flush` — the detail panel owns its own inset, so it spans the full cell
width instead of being indented by the cell padding. */}
)}
);
})}