Skip to content

Commit

Permalink
docs: Improves Table documentation (#25787)
Browse files Browse the repository at this point in the history
* docs: Improves `Table` documentation

- Cleanup docstrings
- Adds main component description
- Adds descriptions to relevant examples
- Adds subcomponent props
- Reorders examples

* changefile

* suggestions

* suggestions

* Add story for DataGrid implementation
  • Loading branch information
ling1726 authored Nov 25, 2022
1 parent 56a61fc commit f7d7f82
Show file tree
Hide file tree
Showing 20 changed files with 446 additions and 22 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "prerelease",
"comment": "docs: Update docstrings for props",
"packageName": "@fluentui/react-table",
"email": "[email protected]",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,20 @@ export type TableSlots = {
export type TableContextValue = {
/**
* Affects the sizes of all table subcomponents
* @default
* @default medium
*/
size: 'extra-small' | 'small' | 'medium';

/**
* Render all table elements as divs intead of semantic table elements
* Using divs no longer uses `display: table` layout but `display: flex`
* @default false
*/
noNativeElements: boolean;

/**
* Whether the table is sortable
* @default false
*/
sortable: boolean;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ export type TableCellLayoutSlots = {
* TableCellLayout Props
*/
export type TableCellLayoutProps = ComponentProps<Partial<TableCellLayoutSlots>> & {
/**
* Renders design variants of the table cell
* @default undefined
*/
appearance?: 'primary';
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ export type TableHeaderCellSlots = {
* TableHeaderCell Props
*/
export type TableHeaderCellProps = ComponentProps<Partial<TableHeaderCellSlots>> & {
/**
* @default undefined
*/
sortDirection?: SortDirection;
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ export type TableRowSlots = {
*/
export type TableRowProps = ComponentProps<TableRowSlots> & {
/**
* A table row can have different variants
* A table row can have different variants. These appearances are
* intended to be used with selection.
* @default none
*/
appearance?: 'brand' | 'neutral' | 'none';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,25 @@ export type TableSelectionCellSlots = {
*/
export type TableSelectionCellProps = ComponentProps<Partial<Omit<TableSelectionCellSlots, 'media'>>> & {
/**
* A table can have two kinds of selection modes
* A table can have two kinds of selection modes.
* @default checkbox
*/
type?: 'checkbox' | 'radio';

/**
* @default false
*/
checked?: CheckboxProps['checked'];

/**
* Only visible when checked or the parent row is hovered
* Only visible when checked or the parent row is hovered/focused
* @default false
*/
subtle?: boolean;

/**
* Completely hides the selection cell visually but takes up the same space
* @default false
*/
hidden?: boolean;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,3 +112,16 @@ export const CellActions = () => {
</Table>
);
};

CellActions.parameters = {
docs: {
description: {
story: [
'`TableCellActions` is a container that is visible when the `TableRow`is hovered',
'or when the current focused element is within the row. It is commonly used to contain interactive actions',
'like buttons, but can be used for any generic content. Please ensure that the contents of cell actions',
'are accessible.',
].join('\n'),
},
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -108,3 +108,14 @@ export const CellNavigation = () => {
</Table>
);
};

CellNavigation.parameters = {
docs: {
description: {
story: [
'The `Table` primitive components do not support keyboard navigation. This should be added by users.',
'Cell navigation can be achieved simply using the `useArrowNavigationGroup` utility provided by the Library.',
].join('\n'),
},
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
import * as React from 'react';
import {
FolderRegular,
EditRegular,
OpenRegular,
DocumentRegular,
PeopleRegular,
DocumentPdfRegular,
VideoRegular,
} from '@fluentui/react-icons';
import { PresenceBadgeStatus, Avatar, useArrowNavigationGroup } from '@fluentui/react-components';
import {
TableBody,
TableCell,
TableRow,
Table,
TableHeader,
TableHeaderCell,
TableSelectionCell,
TableCellLayout,
useTable,
ColumnDefinition,
useSelection,
useSort,
createColumn,
ColumnId,
} from '@fluentui/react-components/unstable';

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 />,
},
},
];

export const DataGrid = () => {
const columns: ColumnDefinition<Item>[] = React.useMemo(
() => [
createColumn<Item>({
columnId: 'file',
compare: (a, b) => {
return a.file.label.localeCompare(b.file.label);
},
}),
createColumn<Item>({
columnId: 'author',
compare: (a, b) => {
return a.author.label.localeCompare(b.author.label);
},
}),
createColumn<Item>({
columnId: 'lastUpdated',
compare: (a, b) => {
return a.lastUpdated.timestamp - b.lastUpdated.timestamp;
},
}),
createColumn<Item>({
columnId: 'lastUpdate',
compare: (a, b) => {
return a.lastUpdate.label.localeCompare(b.lastUpdate.label);
},
}),
],
[],
);

const {
getRows,
selection: { allRowsSelected, someRowsSelected, toggleAllRows, toggleRow, isRowSelected },
sort: { getSortDirection, toggleColumnSort, sort },
} = useTable(
{
columns,
items,
},
[
useSelection({
selectionMode: 'multiselect',
defaultSelectedItems: new Set([0, 1]),
}),
useSort({ 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: ColumnId) => ({
onClick: (e: React.MouseEvent) => {
toggleColumnSort(e, columnId);
},
sortDirection: getSortDirection(columnId),
});

const keyboardNavAttr = useArrowNavigationGroup({ axis: 'grid' });

return (
<Table {...keyboardNavAttr} sortable>
<TableHeader>
<TableRow>
<TableSelectionCell
tabIndex={0}
checkboxIndicator={{ tabIndex: -1 }}
checked={allRowsSelected ? true : someRowsSelected ? 'mixed' : false}
onClick={toggleAllRows}
/>
<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 tabIndex={0} checkboxIndicator={{ tabIndex: -1 }} checked={selected} />
<TableCell tabIndex={0}>
<TableCellLayout media={item.file.icon}>{item.file.label}</TableCellLayout>
</TableCell>
<TableCell tabIndex={0}>
<TableCellLayout media={<Avatar badge={{ status: item.author.status }} />}>
{item.author.label}
</TableCellLayout>
</TableCell>
<TableCell tabIndex={0}>{item.lastUpdated.label}</TableCell>
<TableCell tabIndex={0}>
<TableCellLayout media={item.lastUpdate.icon}>{item.lastUpdate.label}</TableCellLayout>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
);
};

DataGrid.parameters = {
docs: {
description: {
story: [
'The `DataGrid` component is simply a composition of hook and primitive `Table` components',
'along with some convenience features such as accessible markup and event handlers.',
'Any feature of the `DataGrid` is achievable with the primitive components and hook',
].join('\n'),
},
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,8 @@ export const MultipleSelect = () => {
...row,
onClick: (e: React.MouseEvent) => toggleRow(e, row.rowId),
onKeyDown: (e: React.KeyboardEvent) => {
if (e.key === ' ' || e.key === 'Enter') {
if (e.key === ' ') {
e.preventDefault();
toggleRow(e, row.rowId);
}
},
Expand All @@ -143,10 +144,12 @@ export const MultipleSelect = () => {
const keyboardNavAttr = useArrowNavigationGroup({ axis: 'grid' });

return (
<Table>
<Table {...keyboardNavAttr}>
<TableHeader>
<TableRow>
<TableSelectionCell
tabIndex={0}
checkboxIndicator={{ tabIndex: -1 }}
checked={allRowsSelected ? true : someRowsSelected ? 'mixed' : false}
onClick={toggleAllRows}
/>
Expand All @@ -156,7 +159,7 @@ export const MultipleSelect = () => {
<TableHeaderCell>Last update</TableHeaderCell>
</TableRow>
</TableHeader>
<TableBody {...keyboardNavAttr}>
<TableBody>
{rows.map(({ item, selected, onClick, onKeyDown, appearance }) => (
<TableRow
key={item.file.label}
Expand Down Expand Up @@ -184,3 +187,15 @@ export const MultipleSelect = () => {
</Table>
);
};

MultipleSelect.parameters = {
docs: {
description: {
story: [
'Selection can be achieved easily by combining the `TableSelectionCell` component along with',
'other primitive components. The hook can handle state management for selection, although its use is not',
'necessary if users already have their own state management.',
].join('\n'),
},
},
};
Loading

0 comments on commit f7d7f82

Please sign in to comment.