Research
Security News
Quasar RAT Disguised as an npm Package for Detecting Vulnerabilities in Ethereum Smart Contracts
Socket researchers uncover a malicious npm package posing as a tool for detecting vulnerabilities in Etherium smart contracts.
@fluentui/react-table
Advanced tools
React components for building web experiences
This package contains two high level components and their subcomponents.
This component is considered low-level and should be used when there is a need for more customization and support for non-standard features. Please check out the DataGrid component if you don't need lots of customization and rely on common features. There is less work involved and you will benefit from first class Microsoft design and accessibility support.
A Table displays sets of two-dimensional data. The primitive components can be fully customized to support different
feature sets. The library provides a default useTableFeatures
hook that handles the business logic and state management of common
features. There is no obligation to use our hook with these components, we've created it for convenience.
The useTableFeatures
hook was designed with feature composition in mind. This means that they are composed using
plugins hooks such as useTableSort
and useTableSelection
as a part of useTableFeatures
. This means
that as the feature set expands, users will not need to pay the bundle size price for features that they do not intend
to use. Please consult the usage examples below for more details.
import * as React from 'react';
import {
FolderRegular,
EditRegular,
OpenRegular,
DocumentRegular,
PeopleRegular,
DocumentPdfRegular,
VideoRegular,
} from '@fluentui/react-icons';
import {
TableBody,
TableCell,
TableRow,
Table,
TableHeader,
TableHeaderCell,
TableCellLayout,
PresenceBadgeStatus,
Avatar,
} from '@fluentui/react-components';
const items = [
{
file: { label: 'Meeting notes', icon: <DocumentRegular /> },
author: { label: 'Max Mustermann', status: 'available' },
lastUpdated: { label: '7h ago', timestamp: 1 },
lastUpdate: {
label: 'You edited this',
icon: <EditRegular />,
},
},
{
file: { label: 'Thursday presentation', icon: <FolderRegular /> },
author: { label: 'Erika Mustermann', status: 'busy' },
lastUpdated: { label: 'Yesterday at 1:45 PM', timestamp: 2 },
lastUpdate: {
label: 'You recently opened this',
icon: <OpenRegular />,
},
},
{
file: { label: 'Training recording', icon: <VideoRegular /> },
author: { label: 'John Doe', status: 'away' },
lastUpdated: { label: 'Yesterday at 1:45 PM', timestamp: 2 },
lastUpdate: {
label: 'You recently opened this',
icon: <OpenRegular />,
},
},
{
file: { label: 'Purchase order', icon: <DocumentPdfRegular /> },
author: { label: 'Jane Doe', status: 'offline' },
lastUpdated: { label: 'Tue at 9:30 AM', timestamp: 3 },
lastUpdate: {
label: 'You shared this in a Teams chat',
icon: <PeopleRegular />,
},
},
];
const columns = [
{ columnKey: 'file', label: 'File' },
{ columnKey: 'author', label: 'Author' },
{ columnKey: 'lastUpdated', label: 'Last updated' },
{ columnKey: 'lastUpdate', label: 'Last update' },
];
export const Default = () => {
return (
<Table arial-label="Default table">
<TableHeader>
<TableRow>
{columns.map(column => (
<TableHeaderCell key={column.columnKey}>{column.label}</TableHeaderCell>
))}
</TableRow>
</TableHeader>
<TableBody>
{items.map(item => (
<TableRow key={item.file.label}>
<TableCell>
<TableCellLayout media={item.file.icon}>{item.file.label}</TableCellLayout>
</TableCell>
<TableCell>
<TableCellLayout
media={
<Avatar
aria-label={item.author.label}
name={item.author.label}
badge={{ status: item.author.status as PresenceBadgeStatus }}
/>
}
>
{item.author.label}
</TableCellLayout>
</TableCell>
<TableCell>{item.lastUpdated.label}</TableCell>
<TableCell>
<TableCellLayout media={item.lastUpdate.icon}>{item.lastUpdate.label}</TableCellLayout>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
);
};
import * as React from 'react';
import {
FolderRegular,
EditRegular,
OpenRegular,
DocumentRegular,
PeopleRegular,
DocumentPdfRegular,
VideoRegular,
} from '@fluentui/react-icons';
import {
TableBody,
TableCell,
TableRow,
Table,
TableHeader,
TableHeaderCell,
TableSelectionCell,
TableCellLayout,
useTableFeatures,
TableColumnDefinition,
useTableSelection,
useTableSort,
createTableColumn,
TableColumnId,
PresenceBadgeStatus,
Avatar,
useArrowNavigationGroup,
} from '@fluentui/react-components';
type FileCell = {
label: string;
icon: JSX.Element;
};
type LastUpdatedCell = {
label: string;
timestamp: number;
};
type LastUpdateCell = {
label: string;
icon: JSX.Element;
};
type AuthorCell = {
label: string;
status: PresenceBadgeStatus;
};
type Item = {
file: FileCell;
author: AuthorCell;
lastUpdated: LastUpdatedCell;
lastUpdate: LastUpdateCell;
};
const items: Item[] = [
{
file: { label: 'Meeting notes', icon: <DocumentRegular /> },
author: { label: 'Max Mustermann', status: 'available' },
lastUpdated: { label: '7h ago', timestamp: 3 },
lastUpdate: {
label: 'You edited this',
icon: <EditRegular />,
},
},
{
file: { label: 'Thursday presentation', icon: <FolderRegular /> },
author: { label: 'Erika Mustermann', status: 'busy' },
lastUpdated: { label: 'Yesterday at 1:45 PM', timestamp: 2 },
lastUpdate: {
label: 'You recently opened this',
icon: <OpenRegular />,
},
},
{
file: { label: 'Training recording', icon: <VideoRegular /> },
author: { label: 'John Doe', status: 'away' },
lastUpdated: { label: 'Yesterday at 1:45 PM', timestamp: 2 },
lastUpdate: {
label: 'You recently opened this',
icon: <OpenRegular />,
},
},
{
file: { label: 'Purchase order', icon: <DocumentPdfRegular /> },
author: { label: 'Jane Doe', status: 'offline' },
lastUpdated: { label: 'Tue at 9:30 AM', timestamp: 1 },
lastUpdate: {
label: 'You shared this in a Teams chat',
icon: <PeopleRegular />,
},
},
];
const columns: TableColumnDefinition<Item>[] = [
createTableColumn<Item>({
columnId: 'file',
compare: (a, b) => {
return a.file.label.localeCompare(b.file.label);
},
}),
createTableColumn<Item>({
columnId: 'author',
compare: (a, b) => {
return a.author.label.localeCompare(b.author.label);
},
}),
createTableColumn<Item>({
columnId: 'lastUpdated',
compare: (a, b) => {
return a.lastUpdated.timestamp - b.lastUpdated.timestamp;
},
}),
createTableColumn<Item>({
columnId: 'lastUpdate',
compare: (a, b) => {
return a.lastUpdate.label.localeCompare(b.lastUpdate.label);
},
}),
];
export const DataGrid = () => {
const {
getRows,
selection: { allRowsSelected, someRowsSelected, toggleAllRows, toggleRow, isRowSelected },
sort: { getSortDirection, toggleColumnSort, sort },
} = useTableFeatures(
{
columns,
items,
},
[
useTableSelection({
selectionMode: 'multiselect',
defaultSelectedItems: new Set([0, 1]),
}),
useTableSort({ defaultSortState: { sortColumn: 'file', sortDirection: 'ascending' } }),
],
);
const rows = sort(
getRows(row => {
const selected = isRowSelected(row.rowId);
return {
...row,
onClick: (e: React.MouseEvent) => toggleRow(e, row.rowId),
onKeyDown: (e: React.KeyboardEvent) => {
if (e.key === ' ') {
e.preventDefault();
toggleRow(e, row.rowId);
}
},
selected,
appearance: selected ? ('brand' as const) : ('none' as const),
};
}),
);
const headerSortProps = (columnId: TableColumnId) => ({
onClick: (e: React.MouseEvent) => {
toggleColumnSort(e, columnId);
},
sortDirection: getSortDirection(columnId),
});
const keyboardNavAttr = useArrowNavigationGroup({ axis: 'grid' });
return (
<Table {...keyboardNavAttr} role="grid" sortable aria-label="DataGrid implementation with Table primitives">
<TableHeader>
<TableRow>
<TableSelectionCell
checked={allRowsSelected ? true : someRowsSelected ? 'mixed' : false}
aria-checked={allRowsSelected ? true : someRowsSelected ? 'mixed' : false}
role="checkbox"
onClick={toggleAllRows}
checkboxIndicator={{ 'aria-label': 'Select all rows ' }}
/>
<TableHeaderCell {...headerSortProps('file')}>File</TableHeaderCell>
<TableHeaderCell {...headerSortProps('author')}>Author</TableHeaderCell>
<TableHeaderCell {...headerSortProps('lastUpdated')}>Last updated</TableHeaderCell>
<TableHeaderCell {...headerSortProps('lastUpdate')}>Last update</TableHeaderCell>
</TableRow>
</TableHeader>
<TableBody>
{rows.map(({ item, selected, onClick, onKeyDown, appearance }) => (
<TableRow
key={item.file.label}
onClick={onClick}
onKeyDown={onKeyDown}
aria-selected={selected}
appearance={appearance}
>
<TableSelectionCell
role="gridcell"
aria-selected={selected}
checked={selected}
checkboxIndicator={{ 'aria-label': 'Select row' }}
/>
<TableCell tabIndex={0} role="gridcell" aria-selected={selected}>
<TableCellLayout media={item.file.icon}>{item.file.label}</TableCellLayout>
</TableCell>
<TableCell tabIndex={0} role="gridcell">
<TableCellLayout
media={
<Avatar
aria-label={item.author.label}
name={item.author.label}
badge={{ status: item.author.status }}
/>
}
>
{item.author.label}
</TableCellLayout>
</TableCell>
<TableCell tabIndex={0} role="gridcell">
{item.lastUpdated.label}
</TableCell>
<TableCell tabIndex={0} role="gridcell">
<TableCellLayout media={item.lastUpdate.icon}>{item.lastUpdate.label}</TableCellLayout>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
);
};
This component is a higher level extension of the Table
primitive components and the useTableFeatures
hook.
DataGrid
is a feature-rich component that uses useTableFeatures
internally,
so there should always be full feature parity with what can be
achieved with primitives. This component is opinionated and this is intentional. If the desired scenario can
be achieved easily and does not vary too much from documented examples, it can be very convenient. If the desired
scenario varies a lot from the documented examples please use the Table
components with useTableFeatures
(or
another state management solution of choice).
import * as React from 'react';
import {
FolderRegular,
EditRegular,
OpenRegular,
DocumentRegular,
PeopleRegular,
DocumentPdfRegular,
VideoRegular,
} from '@fluentui/react-icons';
import {
PresenceBadgeStatus,
Avatar,
DataGridBody,
DataGridRow,
DataGrid,
DataGridHeader,
DataGridHeaderCell,
DataGridCell,
TableCellLayout,
TableColumnDefinition,
createTableColumn,
} from '@fluentui/react-components';
type FileCell = {
label: string;
icon: JSX.Element;
};
type LastUpdatedCell = {
label: string;
timestamp: number;
};
type LastUpdateCell = {
label: string;
icon: JSX.Element;
};
type AuthorCell = {
label: string;
status: PresenceBadgeStatus;
};
type Item = {
file: FileCell;
author: AuthorCell;
lastUpdated: LastUpdatedCell;
lastUpdate: LastUpdateCell;
};
const items: Item[] = [
{
file: { label: 'Meeting notes', icon: <DocumentRegular /> },
author: { label: 'Max Mustermann', status: 'available' },
lastUpdated: { label: '7h ago', timestamp: 1 },
lastUpdate: {
label: 'You edited this',
icon: <EditRegular />,
},
},
{
file: { label: 'Thursday presentation', icon: <FolderRegular /> },
author: { label: 'Erika Mustermann', status: 'busy' },
lastUpdated: { label: 'Yesterday at 1:45 PM', timestamp: 2 },
lastUpdate: {
label: 'You recently opened this',
icon: <OpenRegular />,
},
},
{
file: { label: 'Training recording', icon: <VideoRegular /> },
author: { label: 'John Doe', status: 'away' },
lastUpdated: { label: 'Yesterday at 1:45 PM', timestamp: 2 },
lastUpdate: {
label: 'You recently opened this',
icon: <OpenRegular />,
},
},
{
file: { label: 'Purchase order', icon: <DocumentPdfRegular /> },
author: { label: 'Jane Doe', status: 'offline' },
lastUpdated: { label: 'Tue at 9:30 AM', timestamp: 3 },
lastUpdate: {
label: 'You shared this in a Teams chat',
icon: <PeopleRegular />,
},
},
];
const columns: TableColumnDefinition<Item>[] = [
createTableColumn<Item>({
columnId: 'file',
compare: (a, b) => {
return a.file.label.localeCompare(b.file.label);
},
renderHeaderCell: () => {
return 'File';
},
renderCell: item => {
return <TableCellLayout media={item.file.icon}>{item.file.label}</TableCellLayout>;
},
}),
createTableColumn<Item>({
columnId: 'author',
compare: (a, b) => {
return a.author.label.localeCompare(b.author.label);
},
renderHeaderCell: () => {
return 'Author';
},
renderCell: item => {
return (
<TableCellLayout
media={
<Avatar aria-label={item.author.label} name={item.author.label} badge={{ status: item.author.status }} />
}
>
{item.author.label}
</TableCellLayout>
);
},
}),
createTableColumn<Item>({
columnId: 'lastUpdated',
compare: (a, b) => {
return a.lastUpdated.timestamp - b.lastUpdated.timestamp;
},
renderHeaderCell: () => {
return 'Last updated';
},
renderCell: item => {
return item.lastUpdated.label;
},
}),
createTableColumn<Item>({
columnId: 'lastUpdate',
compare: (a, b) => {
return a.lastUpdate.label.localeCompare(b.lastUpdate.label);
},
renderHeaderCell: () => {
return 'Last update';
},
renderCell: item => {
return <TableCellLayout media={item.lastUpdate.icon}>{item.lastUpdate.label}</TableCellLayout>;
},
}),
];
export const Default = () => {
return (
<DataGrid
items={items}
columns={columns}
sortable
selectionMode="multiselect"
getRowId={item => item.file.label}
onSelectionChange={(e, data) => console.log(data)}
>
<DataGridHeader>
<DataGridRow selectionCell={{ 'aria-label': 'Select all rows' }}>
{({ renderHeaderCell }) => <DataGridHeaderCell>{renderHeaderCell()}</DataGridHeaderCell>}
</DataGridRow>
</DataGridHeader>
<DataGridBody<Item>>
{({ item, rowId }) => (
<DataGridRow<Item> key={rowId} selectionCell={{ 'aria-label': 'Select row' }}>
{({ renderCell }) => <DataGridCell>{renderCell(item)}</DataGridCell>}
</DataGridRow>
)}
</DataGridBody>
</DataGrid>
);
};
FAQs
React components for building web experiences
We found that @fluentui/react-table demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 12 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
Socket researchers uncover a malicious npm package posing as a tool for detecting vulnerabilities in Etherium smart contracts.
Security News
Research
A supply chain attack on Rspack's npm packages injected cryptomining malware, potentially impacting thousands of developers.
Research
Security News
Socket researchers discovered a malware campaign on npm delivering the Skuld infostealer via typosquatted packages, exposing sensitive data.