fix(issues): anchor full-width table rows

Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
Lambda
2026-07-16 03:45:49 +08:00
parent f414a2de5c
commit 5dff6aae0b
2 changed files with 76 additions and 27 deletions

View File

@@ -3,7 +3,7 @@ import { beforeEach, describe, expect, it, vi } from "vitest";
import { screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { renderWithI18n } from "../../test/i18n";
import { QuickCreateFooter } from "./table-view";
import { IssueTableGroupRow, QuickCreateFooter } from "./table-view";
const { createIssue } = vi.hoisted(() => ({
createIssue: vi.fn(),
@@ -38,6 +38,7 @@ describe("QuickCreateFooter", () => {
const input = screen.getByPlaceholderText("Add an issue…");
const submit = screen.getByRole("button", { name: "Add" });
expect(input.closest("form")).toHaveClass("sticky", "left-1.5");
expect(submit).toBeDisabled();
await user.type(input, " Clickable quick create ");
@@ -56,4 +57,32 @@ describe("QuickCreateFooter", () => {
}),
);
});
it("keeps full-width row controls anchored during horizontal scrolling", async () => {
const user = userEvent.setup();
const onToggle = vi.fn();
renderWithI18n(
<table>
<tbody>
<IssueTableGroupRow
group={{
kind: "group",
key: "status:backlog",
label: "Backlog",
count: 13,
collapsed: false,
}}
colSpan={3}
onToggle={onToggle}
/>
</tbody>
</table>,
);
const group = screen.getByRole("button", { name: /Backlog\s*13/ });
expect(group).toHaveClass("sticky", "left-4", "w-fit");
await user.click(group);
expect(onToggle).toHaveBeenCalledOnce();
});
});

View File

@@ -572,7 +572,7 @@ export function QuickCreateFooter({
<TableRow className="hover:bg-muted/30">
<TableCell colSpan={colSpan} className="p-1.5">
<form
className="flex max-w-2xl items-center gap-2"
className="sticky left-1.5 flex max-w-2xl items-center gap-2"
onSubmit={(event) => {
event.preventDefault();
submit();
@@ -607,6 +607,42 @@ export function QuickCreateFooter({
);
}
type IssueTableGroupRowProps = {
group: Extract<IssueTableDisplayRow, { kind: "group" }>;
colSpan: number;
onToggle: () => void;
};
export function IssueTableGroupRow({
group,
colSpan,
onToggle,
}: IssueTableGroupRowProps) {
return (
<TableRow
className="bg-muted/40 hover:bg-muted/60"
onClick={onToggle}
>
<TableCell colSpan={colSpan} className="h-9 px-4 py-1.5">
<button
type="button"
className="sticky left-4 flex w-fit items-center gap-2 text-xs font-medium"
>
{group.collapsed ? (
<ChevronRight className="size-3.5" />
) : (
<ChevronDown className="size-3.5" />
)}
{group.label}
<span className="font-normal tabular-nums text-muted-foreground">
{group.count}
</span>
</button>
</TableCell>
</TableRow>
);
}
function propertyDisplayValue(
property: IssueProperty,
value: IssuePropertyValue | undefined,
@@ -1115,7 +1151,10 @@ export function TableView({
return (
<TableCell
key={column.id}
className="overflow-hidden px-4 py-1.5 text-xs text-muted-foreground"
className={cn(
"overflow-hidden px-4 py-1.5 text-xs text-muted-foreground",
column.getIsPinned() && "bg-background",
)}
style={getCellStyle(column, { withBorder: true, hasExplicitSize: true })}
>
{key === "title"
@@ -1286,30 +1325,11 @@ export function TableView({
renderRow={(row) => {
if (row.original.kind !== "group") return null;
return (
<TableRow
className="bg-muted/40 hover:bg-muted/60"
onClick={() => toggleTableGroupCollapsed(row.original.key)}
>
<TableCell
colSpan={table.getVisibleLeafColumns().length}
className="h-9 px-4 py-1.5"
>
<button
type="button"
className="flex items-center gap-2 text-xs font-medium"
>
{row.original.collapsed ? (
<ChevronRight className="size-3.5" />
) : (
<ChevronDown className="size-3.5" />
)}
{row.original.label}
<span className="font-normal tabular-nums text-muted-foreground">
{row.original.count}
</span>
</button>
</TableCell>
</TableRow>
<IssueTableGroupRow
group={row.original}
colSpan={table.getVisibleLeafColumns().length}
onToggle={() => toggleTableGroupCollapsed(row.original.key)}
/>
);
}}
className="min-h-0 flex-1"