Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 98 additions & 2 deletions frontend/__tests__/components/ui/table/DataTable.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,14 @@ import { render, screen, fireEvent } from "@solidjs/testing-library";
import { createSignal } from "solid-js";
import { describe, it, expect, vi, beforeEach } from "vitest";

import { DataTable } from "../../../../src/ts/components/ui/table/DataTable";
import {
DataTable,
DataTableColumnDef,
} from "../../../../src/ts/components/ui/table/DataTable";

const [localStorage, setLocalStorage] = createSignal([]);
const [localStorage, setLocalStorage] = createSignal<
{ id: string; desc: boolean }[]
>([]);
vi.mock("../../../../src/ts/hooks/useLocalStorage", () => {
return {
useLocalStorage: () => {
Expand Down Expand Up @@ -59,6 +64,7 @@ describe("DataTable", () => {
sm: true,
md: true,
});
setLocalStorage([]);
});

it("renders table headers and rows", () => {
Expand Down Expand Up @@ -157,4 +163,94 @@ describe("DataTable", () => {
expect(nameHeader).toHaveClass("sm:hidden");
expect(ageHeader).toHaveClass("hidden sm:table-cell");
});

it("updates rows when data changes", async () => {
const [rows, setRows] = createSignal<Person[]>(data);
render(() => (
<DataTable id="reactiveData" columns={columns} data={rows()} />
));

expect(screen.getAllByRole("row")).toHaveLength(3);
setRows([{ name: "Carol", age: 40 }]);

expect(await screen.findByText("Carol")).toBeInTheDocument();
expect(screen.queryByText("Alice")).toBeNull();
expect(screen.getAllByRole("row")).toHaveLength(2);
});

it("updates headers when columns change", async () => {
const [cols, setCols] = createSignal<DataTableColumnDef<Person>[]>(columns);
render(() => <DataTable id="reactiveCols" columns={cols()} data={data} />);

expect(screen.getByRole("button", { name: "Age" })).toBeInTheDocument();

setCols([
{
id: "age",
accessorKey: "age",
header: "Years",
// oxlint-disable-next-line typescript/no-unsafe-return typescript/no-unsafe-call
cell: (info: any) => info.getValue(),
},
]);

expect(
await screen.findByRole("button", { name: "Years" }),
).toBeInTheDocument();
expect(screen.queryByRole("button", { name: "Age" })).toBeNull();
});

it("applies persisted sorting from the initial render", () => {
setLocalStorage([{ id: "age", desc: false }]);

render(() => (
<DataTable id="persistedSort" columns={columns} data={data} />
));

const rows = screen.getAllByRole("row");
expect(rows[1]).toHaveTextContent("Bob"); // asc: age 20 first
expect(rows[2]).toHaveTextContent("Alice");
});

it("reports sorting without reordering rows in manual mode", async () => {
const onSortingChange = vi.fn();
render(() => (
<DataTable
id="manualSort"
columns={columns}
data={data}
onSortingChange={onSortingChange}
/>
));

fireEvent.click(screen.getByRole("button", { name: "Age" }));

expect(onSortingChange).toHaveBeenCalledWith([{ id: "age", desc: true }]);
const rows = screen.getAllByRole("row");
expect(rows[1]).toHaveTextContent("Alice"); // data order untouched
expect(rows[2]).toHaveTextContent("Bob");
});

it("marks the active row for rowSelection", () => {
const activeRow = (): string | null => "Bob";
render(() => (
<DataTable
id="selection"
columns={columns}
data={data}
rowSelection={{
getRowId: (row) => row.name,
activeRow,
class: "row-selected",
}}
/>
));

const bob = screen.getByText("Bob").closest("tr");
expect(bob).toHaveAttribute("data-state", "selected");
expect(bob).toHaveClass("row-selected");
expect(screen.getByText("Alice").closest("tr")).not.toHaveAttribute(
"data-state",
);
});
});
2 changes: 1 addition & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
"@tanstack/solid-hotkeys": "0.10.0",
"@tanstack/solid-query": "5.104.5",
"@tanstack/solid-router": "1.170.38",
"@tanstack/solid-table": "8.21.3",
"@tanstack/solid-table": "9.3.1",
"@ts-rest/core": "3.52.1",
"animejs": "4.2.2",
"balloon-css": "1.2.0",
Expand Down
8 changes: 6 additions & 2 deletions frontend/src/ts/components/modals/PbTablesModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@ import { Formatting } from "../../utils/format";
import { getLanguageDisplayString } from "../../utils/strings";
import { AnimatedModal } from "../common/AnimatedModal";
import { Fa } from "../common/Fa";
import { DataTable, DataTableColumnDef } from "../ui/table/DataTable";
import {
DataTable,
DataTableColumnDef,
DataTableFeatures,
} from "../ui/table/DataTable";

type PBWithMode2 = PersonalBest & {
mode2: Mode2<Mode>;
Expand Down Expand Up @@ -52,7 +56,7 @@ function getColumns(options: {
format: Formatting;
mode: Mode;
}): DataTableColumnDef<PBRow>[] {
const defineColumn = createColumnHelper<PBRow>().accessor;
const defineColumn = createColumnHelper<DataTableFeatures, PBRow>().accessor;
const { format: f, mode: m } = options;

const columns = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@ import { replaceSpacesWithUnderscores } from "../../../utils/strings";
import AsyncContent from "../../common/AsyncContent";
import { Button } from "../../common/Button";
import { Fa } from "../../common/Fa";
import { DataTable, DataTableColumnDef } from "../../ui/table/DataTable";
import {
DataTable,
DataTableColumnDef,
DataTableFeatures,
} from "../../ui/table/DataTable";
import { Section } from "./utils";

export function ApeKeysTab() {
Expand Down Expand Up @@ -71,7 +75,8 @@ export function ApeKeysTab() {
}

function getColumns(): DataTableColumnDef<ApeKeyEntry>[] {
const defineColumn = createColumnHelper<ApeKeyEntry>().accessor;
const defineColumn = createColumnHelper<DataTableFeatures, ApeKeyEntry>()
.accessor;

const columns = [
defineColumn("_id", {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@ import { showSimpleModal } from "../../../states/simple-modal";
import AsyncContent from "../../common/AsyncContent";
import { Button } from "../../common/Button";
import { User } from "../../common/User";
import { DataTable, DataTableColumnDef } from "../../ui/table/DataTable";
import {
DataTable,
DataTableColumnDef,
DataTableFeatures,
} from "../../ui/table/DataTable";
import { Section } from "./utils";

export function BlockedUsersTab() {
Expand Down Expand Up @@ -44,7 +48,8 @@ export function BlockedUsersTab() {
}

function getColumns(): DataTableColumnDef<Connection>[] {
const defineColumn = createColumnHelper<Connection>().accessor;
const defineColumn = createColumnHelper<DataTableFeatures, Connection>()
.accessor;
const cols = [
defineColumn("initiatorName", {
header: "name",
Expand Down
11 changes: 9 additions & 2 deletions frontend/src/ts/components/pages/account/Table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ import { Formatting } from "../../../utils/format";
import { replaceUnderscoresWithSpaces } from "../../../utils/strings";
import { Button } from "../../common/Button";
import { Fa, FaProps } from "../../common/Fa";
import { DataTable, DataTableColumnDef } from "../../ui/table/DataTable";
import {
DataTable,
DataTableColumnDef,
DataTableFeatures,
} from "../../ui/table/DataTable";
import { MiniResultChart } from "./MiniResultChart";

type Sorting = {
Expand Down Expand Up @@ -93,7 +97,10 @@ function getColumns<M extends Mode>({
tags: TagItem[];
onMiniResultChartSelected(val: string): void;
}): DataTableColumnDef<SnapshotResult<M>>[] {
const defineColumn = createColumnHelper<SnapshotResult<M>>().accessor;
const defineColumn = createColumnHelper<
DataTableFeatures,
SnapshotResult<M>
>().accessor;
const columns = [
defineColumn("isPb", {
header: "",
Expand Down
8 changes: 6 additions & 2 deletions frontend/src/ts/components/pages/connections/FriendsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,11 @@ import { Button } from "../../common/Button";
import { H2 } from "../../common/Headers";
import { LoadingCircle } from "../../common/LoadingCircle";
import { User } from "../../common/User";
import { DataTable, DataTableColumnDef } from "../../ui/table/DataTable";
import {
DataTable,
DataTableColumnDef,
DataTableFeatures,
} from "../../ui/table/DataTable";

export function FriendsList() {
const query = useQuery(() => getFriendsListQuery());
Expand Down Expand Up @@ -125,7 +129,7 @@ function getColumns({
}: {
format: Formatting;
}): DataTableColumnDef<Friend>[] {
const defineColumn = createColumnHelper<Friend>().accessor;
const defineColumn = createColumnHelper<DataTableFeatures, Friend>().accessor;
const cols = [
defineColumn("name", {
enableSorting: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@ import AsyncContent from "../../common/AsyncContent";
import { Button } from "../../common/Button";
import { H2 } from "../../common/Headers";
import { User } from "../../common/User";
import { DataTable, DataTableColumnDef } from "../../ui/table/DataTable";
import {
DataTable,
DataTableColumnDef,
DataTableFeatures,
} from "../../ui/table/DataTable";

export function PendingRequests() {
const columns = createMemo(() => getColumns());
Expand All @@ -41,7 +45,8 @@ export function PendingRequests() {
}

function getColumns(): DataTableColumnDef<Connection>[] {
const defineColumn = createColumnHelper<Connection>().accessor;
const defineColumn = createColumnHelper<DataTableFeatures, Connection>()
.accessor;
const cols = [
defineColumn("initiatorName", {
enableSorting: true,
Expand Down
Loading
Loading