DataTable

En tabell med mer komplisert funksjonalitet, som sortering og paginering. Implementert med TanStack-table.

Komponenten er klar til testing, men ikke ferdig

Komponenten har blitt utarbeidet nok til at det ikke kommer store endringer, men trenger å bli testet for å avdekke behov. Gi tilbakemelding om noe lugger eller mangler.

Breaking changes varsles i endringsloggen som patches, ikke som major oppdateringer, for å unngå versjonsinflasjon.

Send innspill

Eksempler

Det er ikke lagd en egen DataTabell-komponenter, untatt DataTableTh. Under ser du eksempler på implementasjoner med TanStack-tabellen. TanStack-table gir en hook useReactTable for logikken til tabellen, som sortering og paginering. For synlig versjon av komponentene, se Figma.

Skisse av datatabell med flere rader, sortering, handlinger, filter.
Datatabeller har funksjonalitet som filtrering, sortering, søk og paginering. Se Figma for nyeste skisse.

Enkel datatabell

En datatabell implementert med TanStack-table består av 4 deler: data, tabell, columns og ytterligere funksjonalitet.

Her ser du kode for data og tabellen:

"use client";

import {
  Table,
  TableThead,
  TableTr,
  TableTh,
  TableTbody,
  TableTd
} from "@aneo-org/design-react";
import {
  useReactTable,
  getCoreRowModel,
  flexRender
} from "@tanstack/react-table";
import type { Payment } from "./columns";
import { columns } from "./columns";

const data: Payment[] = [
  {
    id: "728ed52f",
    amount: 100,
    status: "pending",
    email: "m@example.com"
  },
  {
    id: "489e1d42",
    amount: 125,
    status: "processing",
    email: "example@gmail.com"
  }
];

function DataTable(): JSX.Element {
  const table = useReactTable({
    data,
    columns,
    getCoreRowModel: getCoreRowModel(),
  });

  return (
    <Table>
      <TableThead>
        {table.getHeaderGroups().map((headerGroup) => (
          <TableTr key={headerGroup.id}>
            {headerGroup.headers.map((header) => {
              return (
                <TableTh key={header.id}>
                  {header.isPlaceholder
                    ? null
                    : flexRender(
                        header.column.columnDef.header,
                        header.getContext()
                      )}
                </TableTh>
              );
            })}
          </TableTr>
        ))}
      </TableThead>
      <TableTbody>
        {table.getRowModel().rows.length ? (
          table.getRowModel().rows.map((row) => (
            <TableTr
              data-state={row.getIsSelected() && "selected"}
              key={row.id}
            >
              {row.getVisibleCells().map((cell) => (
                <TableTd key={cell.id}>
                  {flexRender(cell.column.columnDef.cell, cell.getContext())}
                </TableTd>
              ))}
            </TableTr>
          ))
        ) : (
          <TableTr>
            <TableTd colSpan={columns.length}>
              Ingen resultater.
            </TableTd>
          </TableTr>
        )}
      </TableTbody>
    </Table>
  );
}

Columns styrer hvordan tabellen skal se ut, og er knyttet til dataene:

"use client"

import { ColumnDef } from "@tanstack/react-table"

export type Payment = {
  id: string
  amount: number
  status: "pending" | "processing" | "success" | "failed"
  email: string
}

export const columns: ColumnDef<Payment>[] = [
  {
    accessorKey: "status",
    header: "Status",
  },
  {
    accessorKey: "email",
    header: "Email",
  },
  {
    accessorKey: "amount",
    header: "Amount",
  },
]

Datatabell med sortering

For å legge til ytterligere funksjonalitet, må useReactTable utvides. For sortering må state legges til:

"use client";

import {
  Table,
  TableThead,
  TableTr,
  TableTh,
  TableTbody,
  TableTd
} from "@aneo-org/design-react";
import {
  useReactTable,
  getCoreRowModel,
  flexRender,
  getSortedRowModel
} from "@tanstack/react-table";
import type { Payment } from "./columns";
import { columns } from "./columns";

function DataTable(): JSX.Element {
  const [sorting, setSorting] = React.useState<SortingState>([]);

  const table = useReactTable({
    data,
    columns,
    getCoreRowModel: getCoreRowModel(),
    onSortingChange: setSorting,
    getSortedRowModel: getSortedRowModel(),
    state: {
      sorting
    }
  });

  return (
    <Table>
      <TableThead>
        {table.getHeaderGroups().map((headerGroup) => (
          <TableTr key={headerGroup.id}>
            {headerGroup.headers.map((header) => {
              return (
                <TableTh key={header.id}>
                  {header.isPlaceholder
                    ? null
                    : flexRender(
                        header.column.columnDef.header,
                        header.getContext()
                      )}
                </TableTh>
              );
            })}
          </TableTr>
        ))}
      </TableThead>
      <TableTbody>
        {table.getRowModel().rows.length ? (
          table.getRowModel().rows.map((row) => (
            <TableTr
              data-state={row.getIsSelected() && "selected"}
              key={row.id}
            >
              {row.getVisibleCells().map((cell) => (
                <TableTd key={cell.id}>
                  {flexRender(cell.column.columnDef.cell, cell.getContext())}
                </TableTd>
              ))}
            </TableTr>
          ))
        ) : (
          <TableTr>
            <TableTd colSpan={columns.length}>
              Ingen resultater.
            </TableTd>
          </TableTr>
        )}
      </TableTbody>
    </Table>
  );
}

Vi må også innom columns for å legge til funksjonalitet som sortering. Nå ønsker vi at epost-kolonna ikke bare viser en tekst, men en knapp:

"use client"
 
import { ColumnDef } from "@tanstack/react-table"
 
export const columns: ColumnDef<Payment>[] = [
  {
    accessorKey: "email",
    header: ({ column }) => {
      return (
        <Button
          variant="ghost"
          onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
        >
          Email
          <ArrowUpDown />
        </Button>
      )
    },
  },
]

Datatabell med paginering

For å legge til annen funksjonalitet utenfor tabellen, som paginering, kan du wrappe tabellen i en container, og bruke Pagination-komponenten (se egen side). Du må også oppdatere useReactTable til å inkludere paginering:

function DataTable<TData, TValue>({
  columns,
  data,
}: DataTableProps<TData, TValue>) {
  const table = useReactTable({
    data,
    columns,
    getCoreRowModel: getCoreRowModel(),
    getPaginationRowModel: getPaginationRowModel(),
  })

  const currentPage = table.getState().pagination.pageIndex + 1;
  const totalPages = table.getPageCount();

  const handlePageChange = (newPage) => {
    table.setPageIndex(newPage + 1);
  };

  const siblingCount = useBreakpointValue({
    base: 0,
    medium: 1
  })
 
  return (
    <div>
      <Table>
      // ...
      </Table>
      <Pagination
        currentPage={currentPage}
        nextLabel="neste"
        onPageChange={handlePageChange}
        pageLabel="side"
        prevLabel="forrige"
        totalPages={15}
        siblingCount={siblingCount}
      />
    </div>
  )
}

Retningslinjer

Hvorfor så få datatabell-komponenter i biblioteket?

Ofte har hver datatabell unike behov, så det er vanskelig å lage en komponent som er en forenklende abstraksjon, og som samtidig gir fleksibiliteten som trengs ved datatabeller.

Dermed har vi prøvd å unngå å lage egne datatabell-komponenenter, men anbefaler heller å bruke TanStack Table. Det er et populært valg, og har funksjonalitet som sortering, redigerbare celler, paginering, styre synlighet av celler og paginering.

shadcn har en super guide på hvordan du går fra en enkel datatabell til å legge til funksjonaliteten du trenger her.

Kode

<Table />

import { Table } from "@aneo-org/design-react/serverComponents";

Rot-tabell, se egen side.