fix(ui): make resizable DataTable columns fill available width

When a resizable DataTable pinned its width to table.getTotalSize(), the
table became exactly the summed pixel width of its visible columns. Hiding
columns via the view options menu shrank that sum below the container, so
the table stopped filling its box and left an empty strip on the right

Set the table to width 100% with min-width floored at the summed columns
instead. The columns now stretch to fill the container and redistribute
when columns are hidden, while still scrolling rather than squishing once
the summed widths exceed the container. This mirrors the min-width pattern
the view_logs table already uses, and fixes every resizable table built on
the shared component at once
This commit is contained in:
Yuneng Jiang 2026-07-11 17:37:16 -07:00
parent 4baeb283f3
commit 89508405e1
No known key found for this signature in database
2 changed files with 52 additions and 1 deletions

View file

@ -507,6 +507,55 @@ describe("DataTable layout", () => {
});
});
describe("DataTable resizing width", () => {
const sizedColumns: ColumnDef<Person, unknown>[] = [
{
accessorKey: "name",
header: "Name",
size: 100,
cell: ({ row }) => <span data-testid="name-cell">{row.original.name}</span>,
},
{
accessorKey: "email",
header: "Email",
size: 200,
cell: ({ row }) => <span>{row.original.email}</span>,
},
];
const tableEl = (container: HTMLElement): HTMLTableElement | null =>
container.querySelector<HTMLTableElement>('[data-slot="table"]');
it("fills the container and only floors the width at the summed column pixels", () => {
const { container } = render(<DataTable data={CHARLIE_ALICE_BOB} columns={sizedColumns} enableColumnResizing />);
const table = tableEl(container);
// width tracks the container, not a fixed pixel sum, so leftover space is absorbed
expect(table?.style.width).toBe("100%");
// min-width preserves the summed columns so it scrolls rather than squishing when cramped
expect(table?.style.minWidth).toBe("300px");
});
it("keeps filling the container after a column is hidden", async () => {
const user = userEvent.setup();
const { container } = render(
<DataTable
data={CHARLIE_ALICE_BOB}
columns={sizedColumns}
enableColumnResizing
toolbar={(table) => <DataTableViewOptions table={table} />}
/>,
);
await user.click(screen.getByTestId("view-options-trigger"));
await user.click(await screen.findByTestId("view-option-email"));
await waitFor(() => expect(container.querySelector('th[data-header-id="email"]')).toBeNull());
const table = tableEl(container);
expect(table?.style.width).toBe("100%");
expect(table?.style.minWidth).toBe("100px");
});
});
describe("DataTable misconfiguration guards", () => {
it("throws when server sorting is missing required props", () => {
const spy = vi.spyOn(console, "error").mockImplementation(() => {});

View file

@ -508,7 +508,9 @@ export function DataTable<TData extends RowData, TValue>(props: DataTableProps<T
const rows = table.getRowModel().rows;
const visibleColumnCount = table.getVisibleLeafColumns().length;
const stickyHeader = maxBodyHeight !== undefined;
const tableStyle = enableColumnResizing ? { width: table.getTotalSize() } : undefined;
const tableStyle: React.CSSProperties | undefined = enableColumnResizing
? { width: "100%", minWidth: table.getTotalSize() }
: undefined;
const renderPagination = (): React.ReactNode => {
if (paginationSlot !== undefined) {