A
Accueil > Components > DataTable

DataTable

Tableau de données inspiré du DataTable de Primer. Comme chez Primer, l'API est déclarative : on décrit les columns (id, header, renderCell, align, sortable, width) et on fournit les data, le composant se charge de la mise en page (bordures, alternance des cellules, alignement).
Tri
: cliquer sur l'en-tête d'une colonne sortable fait cycler asc → desc → aucun tri. Le tri peut être piloté par le parent via sort/onSortChange (comme Primer, le tri des données reste à la charge du parent), ou laissé en gestion interne pour un simple affichage.
Sélection
: selectable ajoute une colonne de case à cocher avec sélection multiple et case "tout sélectionner" (à 3 états). Contrôlable via selectedKeys/onSelectionChange, sinon gérée en interne.
Pagination
: contrairement à Primer, le Figma Alveole intègre un pied de tableau avec compteur + pagination. On le fournit via le slot footer, avec les composants DataTableFooter et DataTablePagination exportés séparément — le tableau reste agnostique de la pagination (page côté serveur ou client).
Densité
: size (sm par défaut, md, lg) fait varier les paddings des cellules (en-tête, corps, colonne de sélection) sans changer la mise en page.
Beaucoup de colonnes
: l'en-tête et les lignes défilent horizontalement ensemble (via un ScrollView commun) dès que la somme des largeurs de colonnes dépasse l'espace disponible. Les colonnes sans width restent flexibles (elles se partagent l'espace restant) : pour garantir le défilement horizontal, donner une width fixe à chaque colonne, comme dans l'exemple ManyColumns.
TagsuiInformationsFigmaExemples10StylesDisponibles
Default
RepositoryVisibilitéMis à jourcodeql-dca-workerInternal2 heuresaegirPublic3 heuresstrapiPublic8 heurescodeql-ci-nightliesPublichierdependabot-updatesPublicla semaine dernière
export const Default = () => <DataTable data={repositories} columns={baseColumns} keyExtractor={repo => repo.id} />;
WithSelection
RepositoryVisibilitéMis à jourcodeql-dca-workerInternal2 heuresaegirPublic3 heuresstrapiPublic8 heurescodeql-ci-nightliesPublichierdependabot-updatesPublicla semaine dernière
export const WithSelection = () => (
<DataTable data={repositories} columns={baseColumns} keyExtractor={repo => repo.id} selectable />
);
Sizes
sm (défaut)
RepositoryVisibilitéMis à jourcodeql-dca-workerInternal2 heuresaegirPublic3 heuresstrapiPublic8 heurescodeql-ci-nightliesPublichierdependabot-updatesPublicla semaine dernière
md
RepositoryVisibilitéMis à jourcodeql-dca-workerInternal2 heuresaegirPublic3 heuresstrapiPublic8 heurescodeql-ci-nightliesPublichierdependabot-updatesPublicla semaine dernière
lg
RepositoryVisibilitéMis à jourcodeql-dca-workerInternal2 heuresaegirPublic3 heuresstrapiPublic8 heurescodeql-ci-nightliesPublichierdependabot-updatesPublicla semaine dernière
export const Sizes = () => (
<Box display="flex" flexDirection="column" gap={'4W'}>
<Box display="flex" flexDirection="column" gap={'1V'}>
<Typography style={{ fontWeight: 600 }}>sm (défaut)</Typography>
<DataTable data={repositories} columns={baseColumns} keyExtractor={repo => repo.id} selectable size="sm" />
</Box>
<Box display="flex" flexDirection="column" gap={'1V'}>
<Typography style={{ fontWeight: 600 }}>md</Typography>
<DataTable data={repositories} columns={baseColumns} keyExtractor={repo => repo.id} selectable size="md" />
</Box>
<Box display="flex" flexDirection="column" gap={'1V'}>
<Typography style={{ fontWeight: 600 }}>lg</Typography>
<DataTable data={repositories} columns={baseColumns} keyExtractor={repo => repo.id} selectable size="lg" />
</Box>
</Box>
);
ManyColumns
RepositoryPropriétaireVisibilitéLangageÉtoilesForksIssues ouvertesPull requestsCICouvertureDernier commitMis à jourLicenceTaillecodeql-dca-workerAna MartinsInternalTypeScript1281242Succès92%a1b2c3d2 heuresMIT4.2 MoaegirJoe DaltonPublicJavaScript1 04287216Succès78%e4f5g6h3 heuresApache-2.012.8 MostrapiPeter ParkerPublicJavaScript58 2107 42031248Échec64%i7j8k9l8 heuresMIT86.4 Mocodeql-ci-nightliesJohn SnowPublicYAML34310En coursm1n2o3phierMIT0.8 Modependabot-updatesAna MartinsPublicRuby2101893Succès85%q4r5s6tla semaine dernièreBSD-3-Clause2.1 Mo
export const ManyColumns = () => (
<DataTable data={repositoriesDetailed} columns={manyColumns} keyExtractor={repo => repo.id} selectable />
);
WithSorting
RepositoryVisibilitéMis à jourdependabot-updatesPublicla semaine dernièrecodeql-ci-nightliesPublichierstrapiPublic8 heuresaegirPublic3 heurescodeql-dca-workerInternal2 heures
export const WithSorting = () => {
const [sort, setSort] = React.useState<DataTableSort | null>({ columnId: 'updatedAt', direction: 'desc' });
const sortedData = React.useMemo(() => {
if (!sort) return repositories;
const sorted = [...repositories].sort((a, b) => {
const left = a[sort.columnId as keyof Repository];
const right = b[sort.columnId as keyof Repository];
return String(left).localeCompare(String(right));
});
return sort.direction === 'asc' ? sorted : sorted.reverse();
}, [sort]);
return (
<DataTable
data={sortedData}
columns={baseColumns}
keyExtractor={repo => repo.id}
sort={sort}
onSortChange={setSort}
/>
);
};
WithCustomCells
RepositoryPropriétaireDescriptionVisibilité
AM
codeql-dca-worker
Ana MartinsInternalInternal
JD
aegir
Joe DaltonOutils de buildPublic
PP
strapi
Peter ParkerCMS headlessPublic
JS
codeql-ci-nightlies
John SnowAnalyse de sécuritéPublic
AM
dependabot-updates
Ana MartinsMises à jour autoPublic
export const WithCustomCells = () => {
const columns: DataTableColumn<Repository>[] = [
{
id: 'name',
header: 'Repository',
renderCell: repo => (
<>
<Avatar size="xs" fallbackText={repo.owner} />
<Typography style={{ marginLeft: 8 }}>{repo.name}</Typography>
</>
),
},
{
id: 'owner',
header: 'Propriétaire',
renderCell: repo => <Typography>{repo.owner}</Typography>,
},
{
id: 'description',
header: 'Description',
renderCell: repo => <Typography>{repo.description}</Typography>,
},
{
id: 'visibility',
header: 'Visibilité',
align: 'end',
renderCell: repo => (
<Badge variant={repo.visibility === 'Public' ? 'info' : 'default'} size="sm">
{repo.visibility}
</Badge>
),
},
];
return <DataTable data={repositories} columns={columns} keyExtractor={repo => repo.id} />;
};
WithoutHeader
codeql-dca-workerInternal2 heuresaegirPublic3 heuresstrapiPublic8 heurescodeql-ci-nightliesPublichierdependabot-updatesPublicla semaine dernière
export const WithoutHeader = () => (
<DataTable data={repositories} columns={baseColumns} keyExtractor={repo => repo.id} hideHeader />
);
WithPagination
RepositoryVisibilitéMis à jourcodeql-dca-workerInternal2 heuresaegirPublic3 heuresstrapiPublic8 heurescodeql-ci-nightliesPublichierdependabot-updatesPublicla semaine dernière
1-5 sur 48Précédent12345678910Suivant
export const WithPagination = () => {
const [page, setPage] = React.useState(1);
return (
<DataTable
data={repositories}
columns={baseColumns}
keyExtractor={repo => repo.id}
footer={
<DataTableFooter counter="1-5 sur 48">
<DataTablePagination page={page} pageCount={10} onPageChange={setPage} />
</DataTableFooter>
}
/>
);
};
AsDeploymentList
Reproduction de la vue "Deployments" de Vercel avec nos composants : Tag pour les pastilles d'environnement, Badge pour le statut, Avatar pour l'auteur, ActionMenu pour le menu contextuel "..." de chaque ligne, et Popover sur la date pour afficher le détail (temps relatif + horodatage UTC et fuseau local), comme chez Vercel. Le pied de page est ici un simple slot footer (pas DataTableFooter, qui sert au duo compteur+pagination) : un bouton pleine largeur, puis une note de rétention accompagnée d'une action.
Deployments
Bump follow-redirects from 1.15.9 to 1.16.0Error1m 51sPreviewf3c020fdependabot/npm_and_yarn/follow-redirects…
D
Bump axios from 1.8.4 to 1.15.0Error1m 51sPreviewf0e960bdependabot/npm_and_yarn/axios-1.15.0
D
Bump serialize-javascript et terser-webpack-pluginError1m 43sPreview638473adependabot/npm_and_yarn/multi-0d13b2d87f
D
Bump yamlError1m 55sPreviewd59cb81dependabot/npm_and_yarn/multi-c136cad177
D
✨ Merge pull request #9 — mdast-util-to-hast 13.2.1Ready2m 1sProduction9d71b6cmain
C
Bump mdast-util-to-hast from 13.2.0 à 13.2.1Ready1m 58sPreview9bdc055dependabot/npm_and_yarn/mdast-util-to-ha…
D
🎨 Ajoute un style sur le blogReady51sProduction7c70351main
C
✨ feat(posts): add metaDescription fieldReady49sProductionff3f424main
C
La rétention des déploiements est activée — certains seront supprimés après un délai.
export const AsDeploymentList = () => {
const { text, color } = useTheme();
const deploymentColumns: DataTableColumn<Deployment>[] = [
{
id: 'message',
header: 'Déploiement',
width: 400,
renderCell: deployment => <Typography style={text['Corps de texte'].SM.Regular}>{deployment.message}</Typography>,
},
{
id: 'status',
header: 'Statut',
width: 140,
renderCell: deployment => (
<Box display="flex" flexDirection="row" gap={'1W'} style={{ alignItems: 'center' }}>
<Badge variant={deployment.status === 'error' ? 'error' : 'success'} size="sm">
{deployment.status === 'error' ? 'Error' : 'Ready'}
</Badge>
<Typography style={{ ...text['Corps de texte'].SM.Regular, color: color.light.text['mention-grey'] }}>
{deployment.duration}
</Typography>
</Box>
),
},
{
id: 'environment',
header: 'Environnement',
width: 100,
renderCell: deployment => (
<Tag color={deployment.promoted ? 'action' : 'default'} size="sm">
{deployment.environment === 'production' ? 'Production' : 'Preview'}
</Tag>
),
},
{
id: 'commit',
header: 'Commit',
width: 110,
renderCell: deployment => (
<Box display="flex" flexDirection="row" gap={'1V'} style={{ alignItems: 'center' }}>
<LucideIcon name="GitCommitHorizontal" size="sm" />
<Typography style={{ ...text['Corps de texte'].SM.Regular, fontFamily: 'monospace' }}>
{deployment.commit}
</Typography>
</Box>
),
},
{
id: 'source',
header: 'Source',
width: 353,
renderCell: deployment => (
<Box display="flex" flexDirection="row" gap={'1V'} style={{ alignItems: 'center', minWidth: 0 }}>
<LucideIcon name="GitBranch" size="sm" />
<Typography
style={{
...text['Corps de texte'].SM.Regular,
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis',
}}
>
{deployment.source}
</Typography>
</Box>
),
},
{
id: 'date',
header: 'Date',
width: 70,
renderCell: deployment => <DeploymentDateCell deployment={deployment} />,
},
{
id: 'author',
header: '',
width: 56,
renderCell: deployment => <Avatar size="sm" fallbackText={deployment.author} />,
},
{
id: 'actions',
header: '',
width: 40,
align: 'end',
renderCell: () => <DeploymentActionsCell />,
},
];
return (
<Box display="flex" flexDirection="column" gap={'3V'}>
<Typography style={{ fontSize: 20, fontWeight: 700 }}>Deployments</Typography>
<DataTable
size="md"
data={deployments}
columns={deploymentColumns}
keyExtractor={deployment => deployment.id}
hideHeader
/>
<Box display="flex" flexDirection="column" gap="1W">
<Box>
<Button title="Charger plus" variant="secondary" size="md" fullWidth />
</Box>
<Box display="flex" flexDirection="row" style={{ alignItems: 'center', justifyContent: 'space-between' }}>
<Typography style={{ ...text['Corps de texte'].XS.Regular, color: color.light.text['mention-grey'] }}>
La rétention des déploiements est activée — certains seront supprimés après un délai.
</Typography>
<Button title="Voir les supprimés" variant="secondary" size="sm" />
</Box>
</Box>
</Box>
);
};
Empty
RepositoryVisibilitéMis à jour
Aucun repositoryCréez votre premier repository pour le voir apparaître ici.
export const Empty = () => (
<DataTable
data={[]}
columns={baseColumns}
keyExtractor={repo => repo.id}
renderNoContent={() => (
<EmptyState
title="Aucun repository"
description="Créez votre premier repository pour le voir apparaître ici."
iconName="FolderGit2"
/>
)}
/>
);
Alveole UI Kit — v1.9.0