From f414a2de5c0e45d427c43906bbf6bdc8d75cd179 Mon Sep 17 00:00:00 2001 From: Lambda Date: Thu, 16 Jul 2026 03:17:33 +0800 Subject: [PATCH] fix(issues): keep pinned table cells opaque Co-authored-by: multica-agent --- packages/ui/components/ui/data-table.tsx | 13 +++-- .../components/data-table-sticky.test.tsx | 51 +++++++++++++++++++ 2 files changed, 59 insertions(+), 5 deletions(-) create mode 100644 packages/views/issues/components/data-table-sticky.test.tsx diff --git a/packages/ui/components/ui/data-table.tsx b/packages/ui/components/ui/data-table.tsx index 5606971f16..b81b3e5707 100644 --- a/packages/ui/components/ui/data-table.tsx +++ b/packages/ui/components/ui/data-table.tsx @@ -223,7 +223,8 @@ export function DataTable({ // content scrolling beneath them and follow row hover state. className={cn( "overflow-hidden px-4 py-2", - isPinned && "bg-background group-hover:bg-muted/50", + isPinned && + "bg-background group-hover:bg-[color-mix(in_oklab,var(--muted)_50%,var(--background))]", )} style={getCellStyle(cell.column, { withBorder: true, @@ -293,12 +294,14 @@ export function DataTable({ // exceeds column.size. Tooltip / dropdown / // hover-card bodies are portaled, so they are // unaffected. - // Pinned header cell uses muted/30 so it blends - // into the header strip rather than appearing as - // a white block under sticky scroll. + // Pinned cells must be opaque: translucent backgrounds + // reveal horizontally scrolled columns underneath. Mix + // muted with background to preserve the same visual tone + // as muted/30 without introducing alpha. className={cn( "relative h-8 overflow-hidden px-4 py-2 text-xs uppercase tracking-wider text-muted-foreground", - isPinned && "bg-muted/30 backdrop-blur", + isPinned && + "bg-[color-mix(in_oklab,var(--muted)_30%,var(--background))]", )} style={getCellStyle(header.column, { withBorder: true, diff --git a/packages/views/issues/components/data-table-sticky.test.tsx b/packages/views/issues/components/data-table-sticky.test.tsx new file mode 100644 index 0000000000..7a98951e70 --- /dev/null +++ b/packages/views/issues/components/data-table-sticky.test.tsx @@ -0,0 +1,51 @@ +import { render, screen } from "@testing-library/react"; +import { + getCoreRowModel, + useReactTable, + type ColumnDef, +} from "@tanstack/react-table"; +import { describe, expect, it } from "vitest"; +import { DataTable } from "@multica/ui/components/ui/data-table"; + +type Row = { + title: string; + status: string; +}; + +const columns: ColumnDef[] = [ + { accessorKey: "title", header: "Issue" }, + { accessorKey: "status", header: "Status" }, +]; + +function PinnedTable() { + const table = useReactTable({ + data: [{ title: "Pinned title", status: "In progress" }], + columns, + getCoreRowModel: getCoreRowModel(), + state: { columnPinning: { left: ["title"], right: [] } }, + }); + + return ; +} + +describe("DataTable pinned columns", () => { + it("uses opaque backgrounds so scrolled columns cannot show through", () => { + render(); + + const titleHeader = screen.getByRole("columnheader", { name: /^Issue/ }); + const titleCell = screen.getByRole("cell", { name: "Pinned title" }); + const statusCell = screen.getByRole("cell", { name: "In progress" }); + + expect(titleHeader).toHaveClass( + "bg-[color-mix(in_oklab,var(--muted)_30%,var(--background))]", + ); + expect(titleHeader).not.toHaveClass("bg-muted/30", "backdrop-blur"); + expect(titleCell).toHaveClass( + "bg-background", + "group-hover:bg-[color-mix(in_oklab,var(--muted)_50%,var(--background))]", + ); + expect(titleCell).not.toHaveClass("group-hover:bg-muted/50"); + expect(titleCell).toHaveStyle({ position: "sticky", zIndex: 1 }); + expect(statusCell).not.toHaveStyle({ position: "sticky" }); + }); +});