Data Table

1.5.1

Organiza informações em linhas e colunas para explorar, comparar e operar sobre grandes volumes de dados, com seleção de linhas, ações em lote e paginação.

  • Listar objetos com atributos comparáveis: exibir listagens extensas em que cada item compartilha as mesmas colunas e vale a pena compará-los entre si. Ex.: "Pedidos", "Produtos", "Clientes".
  • Operar sobre várias linhas ao mesmo tempo: quando é preciso selecionar mais de um item para aplicar uma ação em comum. Ex.: "Arquivar pedidos selecionados", "Exportar produtos".
  • Listar poucos itens ou sem atributos comparáveis: com menos de 3 elementos, ou quando eles não compartilham colunas entre si, a grade não agrega valor. Nesse caso, usar Data List.
  • Destacar cada item com imagens ou visualizações enriquecidas: quando a miniatura ou o detalhe visual de cada produto importa mais do que comparar seus atributos em colunas. Nesse caso, usar Product Data List.
  • Definir a ordem dos itens arrastando-os manualmente: quando a prioridade não depende de comparar um atributo, mas de uma ordem manual (ex.: destaques de uma vitrine). Nesse caso, usar Sortable.
Nº do pedido
ClienteTotalStatus
#1042Dr. Johnnie BinsR$ 45.900,00
Concluído
#1041Earnest BergeR$ 18.500,00
Pendente
#1040Irene PurdyR$ 62.300,00
Concluído

Mostrando 1-3 de 34 pedidos

Header, linhas, células e footer

  1. Header: linha superior com os cabeçalhos de coluna; uma célula pode incluir um controle próprio de ordenação (não é uma prop do componente, é implementado com um IconButton, como no exemplo Default).
  2. Row: cada linha representa um item da listagem, com suas próprias células e seu checkbox de seleção.
  3. Cell: célula de uma linha ou do header; contém o valor de uma coluna, texto, um controle ou uma ação.
  4. Checkbox: controle de seleção presente no Header (seleciona ou desmarca todas as linhas) e em cada Row (seleciona essa linha); é obrigatório em ambos e habilita a seleção em lote.
  5. Footer (opcional): faixa inferior com a contagem de itens (itemCount) e, opcionalmente, a paginação.
ProdutoCategoriaEstoquePreçoAções
Mouse sem fioEletrônicos24R$ 45.900,00
Luminária de mesaCasa8R$ 62.300,00
Garrafa térmicaCasa0R$ 18.500,00

Seleção em lote e menu de ações por linha

  1. BulkActions (opcional): barra sticky renderizada sobre a tabela quando bulkActions recebe conteúdo; agrupa o checkbox "selecionar tudo", um label com a contagem de linhas selecionadas e as ações em lote.
  2. Menu de ações (opcional): menu de ações por linha disparado por um Icon Button (ícone de 3 pontos), para executar uma ação pontual sobre essa linha sem sair da listagem.
Nº do pedidoClienteTotalQtd. de produtos
#1042Dr. Johnnie BinsR$ 45.900,004
#1041Earnest BergeR$ 18.500,001
#1040Irene PurdyR$ 62.300,002

A variante bulkActions (seleção em lote) permite operar sobre um grande número de itens de forma massiva. A seleção pode ser feita pelo checkbox localizado no header da tabela, para selecionar todas as linhas, ou individualmente, pelos checkboxes de cada linha.

Ao utilizar o DataTable.BulkActions, a barra sticky com a contagem de linhas selecionadas e as ações em lote aparece quando há pelo menos uma linha selecionada.

Essa funcionalidade permite escolher a ação desejada por meio de um dropdown, que pode conter um número indefinido de ações.

Nº do pedidoClienteTotalStatus
#1042Dr. Johnnie BinsR$ 45.900,00
Concluído
#1041Earnest BergeR$ 18.500,00
Pendente

Mostrando 1-10 de 48 pedidos

Com footer: ao passar DataTable.Footer, a contagem de itens é exibida à esquerda e os controles de Pagination à direita; a prop footer é omitida quando a listagem completa cabe em uma única tela e não é preciso paginar. Ex.: "Mostrando 1-10 de 48 pedidos".

ProdutoEstoque
Mouse sem fio24
Luminária de mesa8

Ação direta: quando cada linha tem uma única ação frequente, posicioná-la como um Icon Button na última célula. Ex.: um ícone de edição.

ProdutoEstoqueAções
Mouse sem fio24
Luminária de mesa8

Ações múltiplas: quando cada linha tem 2 ou 3 ações frequentes, posicioná-las como Icon Buttons separados lado a lado em vez de escondê-las atrás de um menu. Ex.: compartilhar, duplicar, excluir.

ProdutoEstoqueAções
Mouse sem fio24
Luminária de mesa8

Menu de ações: quando cada linha oferece 4 ou mais ações, ou alguma delas é secundária ou pouco frequente, agrupá-las em DataTable.Dropdown com DataTable.DropdownAction em vez de sobrecarregar a linha com ícones soltos. Ex.: "Editar", "Duplicar", "Excluir".

O gatilho do DataTable.Dropdown tem largura fixa (240px no desktop, 180px no mobile) que não pode ser configurada via props; dimensione a coluna de Ações de acordo para que o gatilho não fique cortado.

O DataTable funciona melhor em telas em que o espaço disponível permite exibir várias colunas comparáveis sem scroll horizontal. Costuma viver dentro do body de Page, com controles de busca e filtros próprios do consumidor acima e o footer com paginação fixo abaixo.

No desktop, há espaço suficiente para exibir todas as colunas relevantes de forma simultânea, incluindo as ações por linha e a barra de ações em lote, sem cortar o conteúdo. Geralmente, a tabela aproveita toda a largura disponível da tela para garantir a correta visualização das informações.

Por definição, a tabela é acompanhada por elementos de configuração que facilitam sua operação, como um Search Input e botões para ativar opções de filtro e ordenação.

Nos casos de maior complexidade, podem ser usadas configurações de filtro rápido por meio de um Segmented Control, facilitando o acesso a filtros frequentes de forma direta.

34 pedidos

PedidoClienteTotalStatus
#1042Dr. Johnnie BinsR$ 45.900,00
Concluído
#1041Earnest BergeR$ 18.500,00
Pendente

Mostrando 1-2 de 34 pedidos

Em telas estreitas, exibir todas as colunas força scroll horizontal dentro da tabela. Convém priorizar as colunas-chave à esquerda (com width em DataTable.Cell) e reduzir a quantidade de colunas visíveis; se o item tiver poucos atributos realmente comparáveis, avaliar Data List em vez disso.

PedidoStatus
#1042
Concluído

1 de 34

ProdutoEstoque
Mouse sem fio24

Exibir a barra de ações em lote apenas quando há pelo menos uma linha selecionada.

ProdutoEstoque
Mouse sem fio24

Não manter a barra de ações em lote visível com zero linhas selecionadas: ela ocupa espaço sem oferecer nenhuma ação disponível.

ProdutoEstoqueAções
Mouse sem fio24

Use o DataTable.Dropdown quando a linha tiver 4 ou mais ações, ou ações secundárias/pouco frequentes.

ProdutoEstoqueAções
Mouse sem fio24

Use Icon Buttons soltos quando a linha tiver 2 ou 3 ações frequentes; para 4 ou mais, ou ações secundárias, agrupe-as em um Dropdown (como no exemplo anterior).

  • Estrutura de tabela nativa: DataTable, DataTable.Row e DataTable.Cell estendem os props de Table, portanto são renderizados como elementos table/tr/td semânticos: os roles de tabela, linha e célula são expostos pelo próprio HTML, sem necessidade de atributos ARIA manuais.
  • Seleção com Checkbox real: o checkbox do Header e do Row usa o componente Checkbox, que associa seu label ao input nativo; manter esse label (mesmo que o design mostre apenas o estado visual) para que um leitor de tela anuncie qual linha ou qual ação de seleção global ele representa.
  • A ordenação por coluna não é automática: o DataTable não inclui uma prop de ordem; se um controle de ordenação for adicionado em uma célula de header (como no exemplo Default), adicionar manualmente aria-sort ("ascending", "descending" ou "none") nessa célula e um aria-label descritivo no controle que dispara a mudança de ordem, já que costuma ser exibido apenas com um ícone.
  • Rotular as ações por linha: um IconButton de ação direta, cada IconButton de um grupo de ações soltas, e o gatilho de DataTable.Dropdown/menu kebab precisam de um aria-label ou texto descritivo, pois costumam ser exibidos apenas com ícone ou com um placeholder genérico ("Ações").

Instale o componente via terminal.

npm install @nimbus-ds/data-table

Tabela de pedidos com ordenação por coluna, seleção de linhas, ações em lote e paginação.

import React, { useEffect, useState } from "react";
import { DataTable } from "@nimbus-ds/patterns";
import { Tag, Box, IconButton, Chip } from "@nimbus-ds/components";
import {
  ChevronDownIcon,
  CheckCircleIcon,
  ChevronUpIcon,
  ExclamationTriangleIcon,
} from "@nimbus-ds/icons";


const pageSize = 5;

const orders = [
  {
    id: 10,
    clientName: "Dr. Johnnie Bins",
    total: "R$16.788,20",
    qty: "9",
    status: false,
  },
  {
    id: 9,
    clientName: "Earnest Berge",
    total: "R$62.657,83",
    qty: "3",
    status: false,
  },
  {
    id: 8,
    clientName: "Irene Purdy",
    total: "R$17.692,10",
    qty: "4",
    status: false,
  },
  {
    id: 7,
    clientName: "Owen Swift DVM",
    total: "R$60.269,67",
    qty: "5",
    status: false,
  },
  {
    id: 6,
    clientName: "Felipe Ferry",
    total: "R$75.058,94",
    qty: "3",
    status: false,
  },
  {
    id: 5,
    clientName: "Derek Kub",
    total: "R$29.068,91",
    qty: "1",
    status: false,
  },
  {
    id: 4,
    clientName: "Elisa Vandervort",
    total: "R$22.636,41",
    qty: "5",
    status: false,
  },
  {
    id: 3,
    clientName: "Rochelle Spencer",
    total: "R$76.244,05",
    qty: "9",
    status: false,
  },
  {
    id: 2,
    clientName: "Angelina Koelpin",
    total: "R$65.306,79",
    qty: "4",
    status: false,
  },
  {
    id: 1,
    clientName: "Edna Jacobi",
    total: "R$97.025,32",
    qty: "6",
    status: false,
  },
];

const Example: React.FC = () => {
  interface RowProps {
    id: number;
    clientName: string;
    total: string;
    qty: string;
    status: boolean;
  }

  const [rows, setRows] = useState<RowProps[]>(orders);
  const [checkedRows, setCheckedRows] = useState<number[]>([]);
  const [headerCheckboxStatus, setHeaderCheckboxStatus] = useState(false);
  const [headerIndeterminateStatus, setHeaderIndeterminateStatus] =
    useState(false);
  const [currentPage, setCurrentPage] = useState<number>(1);
  const [sortDirection, setSortDirection] = useState<
    "ascending" | "descending"
  >("descending");
  const [sortColumn, setSortColumn] = useState<"id" | "clientName">("id");

  useEffect(() => {
    if (checkedRows.length === rows.length) {
      setHeaderCheckboxStatus(true);
      setHeaderIndeterminateStatus(false);
    } else if (checkedRows.length > 0) {
      setHeaderCheckboxStatus(false);
      setHeaderIndeterminateStatus(true);
    } else {
      setHeaderCheckboxStatus(false);
      setHeaderIndeterminateStatus(false);
    }
  }, [checkedRows.length, rows.length]);

  const handleRowClick = (id: number) => {
    if (checkedRows.includes(id)) {
      setCheckedRows(checkedRows.filter((rowId) => rowId !== id));
    } else {
      setCheckedRows([...checkedRows, id]);
    }
  };

  const handleHeaderCheckboxClick = () => {
    if (headerCheckboxStatus) {
      setCheckedRows([]);
    } else {
      const rowIds = rows.map((row) => row.id);
      setCheckedRows(rowIds);
    }
  };

  const handleBulkUpdateStatusClick = (status: boolean) => {
    const updatedRows = rows.map((row) => {
      const checked = checkedRows.includes(row.id);
      return { ...row, status: checked ? status : row.status };
    });
    setRows(updatedRows);
  };

  const handlePageChange = (page: number): void => {
    setCurrentPage(page);
  };

  const handleSort = (column: "id" | "clientName") => {
    if (column === sortColumn) {
      setSortDirection(
        sortDirection === "ascending" ? "descending" : "ascending"
      );
    } else {
      setSortColumn(column);
      setSortDirection("ascending");
    }
  };

  const sortCompareFunction = (rowA: RowProps, rowB: RowProps) => {
    if (sortColumn === "id") {
      return sortDirection === "ascending"
        ? rowA.id - rowB.id
        : rowB.id - rowA.id;
    }
    if (sortColumn === "clientName") {
      return sortDirection === "ascending"
        ? rowA.clientName.localeCompare(rowB.clientName)
        : rowB.clientName.localeCompare(rowA.clientName);
    }
    return 0;
  };

  const getDisplayedRows = (): RowProps[] => {
    const sortedRows = rows.slice().sort(sortCompareFunction);
    const startIndex = (currentPage - 1) * pageSize;
    const endIndex = startIndex + pageSize;
    return sortedRows.slice(startIndex, endIndex);
  };

  const displayedRows = getDisplayedRows();
  const totalRows = rows.length;
  const firstRow = (currentPage - 1) * pageSize + 1;
  const lastRow = Math.min(currentPage * pageSize, totalRows);

  const tableHeader = (
    <DataTable.Header
      checkbox={{
        name: "check-all-rows",
        checked: headerCheckboxStatus,
        onChange: handleHeaderCheckboxClick,
        indeterminate: headerIndeterminateStatus,
      }}
    >
      <DataTable.Cell width="120px">
        <Box display="flex" gap="2" alignItems="center">
          Order no.
          <IconButton
            source={
              sortDirection === "ascending" ? (
                <ChevronUpIcon size={10} />
              ) : (
                <ChevronDownIcon size={10} />
              )
            }
            size="1rem"
            onClick={() => handleSort("id")}
          />
        </Box>
      </DataTable.Cell>
      <DataTable.Cell width="auto">Client name</DataTable.Cell>
      <DataTable.Cell width="120px">Total</DataTable.Cell>
      <DataTable.Cell width="120px">Qty. of products</DataTable.Cell>
      <DataTable.Cell width="120px">Order status</DataTable.Cell>
    </DataTable.Header>
  );

  const tableFooter = (
    <DataTable.Footer
      itemCount={`Showing ${firstRow}-${lastRow} orders of ${totalRows}`}
      pagination={{
        pageCount: Math.ceil(totalRows / pageSize),
        activePage: currentPage,
        onPageChange: handlePageChange,
      }}
    />
  );

  const hasBulkActions = checkedRows.length > 0 && (
    <DataTable.BulkActions
      checkbox={{
        name: "check-all",
        checked: headerCheckboxStatus,
        onChange: handleHeaderCheckboxClick,
        indeterminate: headerIndeterminateStatus,
      }}
      label={`${checkedRows.length} selected`}
      action={
        <Box display="flex" gap="1">
          <Chip
            onClick={() => handleBulkUpdateStatusClick(true)}
            text="Fulfill orders"
          />
          <Chip
            onClick={() => handleBulkUpdateStatusClick(false)}
            text="Unfulfill orders"
          />
        </Box>
      }
    />
  );

  return (
    <DataTable
      header={tableHeader}
      footer={tableFooter}
      bulkActions={hasBulkActions}
    >
      {displayedRows.map((row) => {
        const { id, status } = row;
        const statusIcon = status ? (
          <CheckCircleIcon />
        ) : (
          <ExclamationTriangleIcon />
        );
        const statusAppearance = status ? "success" : "warning";
        const statusMsg = status ? "Fulfilled" : "Pending";

        return (
          <DataTable.Row
            key={id}
            backgroundColor={
              checkedRows.includes(id)
                ? {
                    rest: "primary-surface",
                    hover: "primary-surfaceHighlight",
                  }
                : {
                    rest: "neutral-background",
                    hover: "neutral-surface",
                  }
            }
            checkbox={{
              name: `check-${id}`,
              checked: checkedRows.includes(id),
              onChange: () => handleRowClick(id),
            }}
          >
            <DataTable.Cell>#{row.id}</DataTable.Cell>
            <DataTable.Cell>{row.clientName}</DataTable.Cell>
            <DataTable.Cell>{row.total}</DataTable.Cell>
            <DataTable.Cell>{row.qty}</DataTable.Cell>
            <DataTable.Cell>
              <Tag appearance={statusAppearance}>
                {statusIcon}
                {statusMsg}
              </Tag>
            </DataTable.Cell>
          </DataTable.Row>
        );
      })}
    </DataTable>
  );
};

export default Example;

As propriedades adicionais são repassadas ao elemento table que o DataTable renderiza. Consulte a documentação do elemento table para ver a lista de atributos aceitos.

  • Table — componente base de tabela sobre o qual o DataTable é construído.
  • Pagination — controla a navegação entre páginas recebida por DataTable.Footer.
  • Checkbox — controle de seleção usado no Header e no Row.
  • Data List — alternativa quando os itens não compartilham atributos comparáveis.
  • Product Data List — alternativa quando cada item precisa de uma visualização enriquecida.
  • Sortable — alternativa quando a ordem é definida arrastando itens em vez de comparar atributos.

DataTable

NameTypeDefaultDescription

bulkActions

React.ReactNode

Bulk actions component rendered with a sticky position over the top of the table element.

header*

React.ReactNode

Table header content.

footer

React.ReactNode

Optional table footer content.

children*

React.ReactNode

Table body content.

containerProps

BoxProps

Props passed to the container box element.

DataTable.BulkActions

NameTypeDefaultDescription

checkbox*

object

Properties of the checkbox element rendered in the Bulk Actions component.

link

<Link />

Optional link element rendered next to the Bulk Actions controller.

action*

React.ReactNode

Action component that controls the Bulk Actions.

label*

string

Lable for the checkbox element.

DataTable.Cell

NameTypeDefaultDescription

children*

React.ReactNode

Content of the List component.

DataTable.Dropdown

NameTypeDefaultDescription

placeholder*

string

Placeholder text displayed in the dropdown trigger button.

children*

React.ReactNode

Content to be rendered inside the dropdown popover. Typically DataTable.DropdownAction and DataTable.DropdownDivider components.

DataTable.DropdownAction

NameTypeDefaultDescription

icon

React.ReactNode

Icon element to be displayed before the label.

label*

string

Text label for the action item.

onClick

object

Click handler for the action.

disabled

boolean

Whether the action is disabled.

DataTable.DropdownDivider

NameTypeDefaultDescription

DataTable.DropdownSection

NameTypeDefaultDescription

children*

React.ReactNode

Content of the section body.

DataTable.Footer

NameTypeDefaultDescription

itemCount*

string

Left-hand side text intended for displaying an item count.

pagination

object

Pagination element rendered on the right-side of the footer.

DataTable.Header

NameTypeDefaultDescription

checkbox*

object

Checkbox element rendered on the table header that controls all rows.

children*

React.ReactNode

Row content.

DataTable.Row

NameTypeDefaultDescription

checkbox*

object

Checkbox element rendered on the row that controls whether the row is selected.

children*

React.ReactNode

Content of the row.

Ajude-nos a melhorar a documentação

Encontrou um problema ou tem uma sugestão? Conte para a gente.