From 3a4c20ed505b3765a2df7e94492792902eaec0a9 Mon Sep 17 00:00:00 2001 From: Daniel Lu Date: Mon, 10 May 2021 10:40:44 -0700 Subject: [PATCH 01/62] renaming table component to TableView --- .../@react-spectrum/icon/docs/IconTable.tsx | 6 +- .../table/src/{Table.tsx => TableView.tsx} | 6 +- packages/@react-spectrum/table/src/index.ts | 2 +- .../table/stories/CRUDExample.tsx | 6 +- .../table/stories/HidingColumns.tsx | 6 +- .../table/stories/Table.stories.tsx | 118 ++++++------- .../@react-spectrum/table/test/Table.test.js | 156 +++++++++--------- 7 files changed, 150 insertions(+), 150 deletions(-) rename packages/@react-spectrum/table/src/{Table.tsx => TableView.tsx} (99%) diff --git a/packages/@react-spectrum/icon/docs/IconTable.tsx b/packages/@react-spectrum/icon/docs/IconTable.tsx index 5040245e12b..c6b5ad22365 100644 --- a/packages/@react-spectrum/icon/docs/IconTable.tsx +++ b/packages/@react-spectrum/icon/docs/IconTable.tsx @@ -11,7 +11,7 @@ */ import {ActionButton} from '@react-spectrum/button'; -import {Cell, Column, Row, Table, TableBody, TableHeader} from '@react-spectrum/table'; +import {Cell, Column, Row, TableBody, TableHeader, TableView} from '@react-spectrum/table'; import Paste from '@spectrum-icons/workflow/Paste'; import React, {useEffect, useState} from 'react'; import {SearchField} from '@react-spectrum/searchfield'; @@ -77,7 +77,7 @@ export default function IconTable(props: {iconPackage: string}) { onClear={onSearchClear} onChange={onSearch} marginBottom="20px" /> - + Icon Name @@ -97,7 +97,7 @@ export default function IconTable(props: {iconPackage: string}) { ) } -
+ ); } diff --git a/packages/@react-spectrum/table/src/Table.tsx b/packages/@react-spectrum/table/src/TableView.tsx similarity index 99% rename from packages/@react-spectrum/table/src/Table.tsx rename to packages/@react-spectrum/table/src/TableView.tsx index ab3203ce249..e5d6b521323 100644 --- a/packages/@react-spectrum/table/src/Table.tsx +++ b/packages/@react-spectrum/table/src/TableView.tsx @@ -68,7 +68,7 @@ function useTableContext() { return useContext(TableContext); } -function Table(props: SpectrumTableProps, ref: DOMRef) { +function TableView(props: SpectrumTableProps, ref: DOMRef) { props = useProviderProps(props); let {isQuiet} = props; let {styleProps} = useStyleProps(props); @@ -695,5 +695,5 @@ function CenteredWrapper({children}) { ); } -const _Table = React.forwardRef(Table); -export {_Table as Table}; +const _TableView = React.forwardRef(TableView); +export {_TableView as TableView}; diff --git a/packages/@react-spectrum/table/src/index.ts b/packages/@react-spectrum/table/src/index.ts index 6b418762553..5c069763b11 100644 --- a/packages/@react-spectrum/table/src/index.ts +++ b/packages/@react-spectrum/table/src/index.ts @@ -12,7 +12,7 @@ /// -export * from './Table'; +export * from './TableView'; import {Column} from '@react-stately/table'; import {SpectrumColumnProps} from '@react-types/table'; diff --git a/packages/@react-spectrum/table/stories/CRUDExample.tsx b/packages/@react-spectrum/table/stories/CRUDExample.tsx index d07c4d7c085..55d99815dc5 100644 --- a/packages/@react-spectrum/table/stories/CRUDExample.tsx +++ b/packages/@react-spectrum/table/stories/CRUDExample.tsx @@ -15,7 +15,7 @@ import {ActionGroup} from '@react-spectrum/actiongroup'; import Add from '@spectrum-icons/workflow/Add'; import {AlertDialog, Dialog, DialogContainer, useDialogContainer} from '@react-spectrum/dialog'; import {ButtonGroup} from '@react-spectrum/buttongroup'; -import {Cell, Column, Row, Table, TableBody, TableHeader} from '../'; +import {Cell, Column, Row, TableBody, TableHeader, TableView} from '../'; import {Content} from '@react-spectrum/view'; import Delete from '@spectrum-icons/workflow/Delete'; import {Divider} from '@react-spectrum/divider'; @@ -51,7 +51,7 @@ export function CRUDExample() { } - + First Name Last Name @@ -78,7 +78,7 @@ export function CRUDExample() { ) } -
+ setDialog(null)}> {dialog?.action === 'add' && toggleColumn(c.key)}>{c.title} )} - + visibleColumns.has(c.key))}> {column => {column.title}} @@ -64,7 +64,7 @@ export function HidingColumns() { )} -
+ ); } diff --git a/packages/@react-spectrum/table/stories/Table.stories.tsx b/packages/@react-spectrum/table/stories/Table.stories.tsx index 01ad421bdf9..279890531af 100644 --- a/packages/@react-spectrum/table/stories/Table.stories.tsx +++ b/packages/@react-spectrum/table/stories/Table.stories.tsx @@ -13,7 +13,7 @@ import {action} from '@storybook/addon-actions'; import {ActionButton} from '@react-spectrum/button'; import Add from '@spectrum-icons/workflow/Add'; -import {Cell, Column, Row, Table, TableBody, TableHeader} from '../'; +import {Cell, Column, Row, TableBody, TableHeader, TableView} from '../'; import {Content} from '@react-spectrum/view'; import {CRUDExample} from './CRUDExample'; import Delete from '@spectrum-icons/workflow/Delete'; @@ -89,11 +89,11 @@ function renderEmptyState() { } let onSelectionChange = action('onSelectionChange'); -storiesOf('Table', module) +storiesOf('TableView', module) .add( 'static', () => ( - + Foo Bar @@ -111,13 +111,13 @@ storiesOf('Table', module) Three -
+ ) ) .add( 'static with selection', () => ( - onSelectionChange([...s])}> + onSelectionChange([...s])}> Foo Bar @@ -135,13 +135,13 @@ storiesOf('Table', module) Three -
+ ) ) .add( 'dynamic', () => ( - + {column => {column.name}} @@ -152,13 +152,13 @@ storiesOf('Table', module) ) } -
+ ) ) .add( 'dynamic with selection', () => ( - onSelectionChange([...s])}> + onSelectionChange([...s])}> {column => {column.name}} @@ -169,13 +169,13 @@ storiesOf('Table', module) ) } -
+ ) ) .add( 'dynamic with single selection', () => ( - onSelectionChange([...s])}> + onSelectionChange([...s])}> {column => {column.name}} @@ -186,13 +186,13 @@ storiesOf('Table', module) ) } -
+ ) ) .add( 'dynamic with disabled, single selection', () => ( - onSelectionChange([...s])}> + onSelectionChange([...s])}> {column => {column.name}} @@ -203,13 +203,13 @@ storiesOf('Table', module) ) } -
+ ) ) .add( 'dynamic with disabled, multiple selection', () => ( - onSelectionChange([...s])}> + onSelectionChange([...s])}> {column => {column.name}} @@ -220,13 +220,13 @@ storiesOf('Table', module) ) } -
+ ) ) .add( 'dynamic with disabled, multiple selection, quiet', () => ( - onSelectionChange([...s])}> + onSelectionChange([...s])}> {column => {column.name}} @@ -237,13 +237,13 @@ storiesOf('Table', module) ) } -
+ ) ) .add( 'static with nested columns', () => ( - onSelectionChange([...s])}> + onSelectionChange([...s])}> Test @@ -268,13 +268,13 @@ storiesOf('Table', module) Three -
+ ) ) .add( 'dynamic with nested columns', () => ( - onSelectionChange([...s])}> + onSelectionChange([...s])}> {column => {column.name} @@ -287,7 +287,7 @@ storiesOf('Table', module) ) } -
+ ) ) .add( @@ -295,7 +295,7 @@ storiesOf('Table', module) () => ( - onSelectionChange([...s])}> + onSelectionChange([...s])}> Foo Bar @@ -318,7 +318,7 @@ storiesOf('Table', module) Three -
+
) @@ -326,7 +326,7 @@ storiesOf('Table', module) .add( 'many columns and rows', () => ( - onSelectionChange([...s])}> + onSelectionChange([...s])}> {column => {column.name} @@ -339,14 +339,14 @@ storiesOf('Table', module) ) } -
+ ), {chromatic: {disable: true}} ) .add( 'isQuiet, many columns and rows', () => ( - onSelectionChange([...s])}> + onSelectionChange([...s])}> {column => {column.name} @@ -359,14 +359,14 @@ storiesOf('Table', module) ) } -
+ ), {chromatic: {disable: true}} ) .add( 'column widths and dividers', () => ( - onSelectionChange([...s])}> + onSelectionChange([...s])}> File Name Type @@ -384,13 +384,13 @@ storiesOf('Table', module) 120 KB -
+ ) ) .add( 'isQuiet, column widths and dividers', () => ( - onSelectionChange([...s])}> + onSelectionChange([...s])}> File Name Type @@ -408,13 +408,13 @@ storiesOf('Table', module) 120 KB -
+ ) ) .add( 'density="compact"', () => ( - onSelectionChange([...s])}> + onSelectionChange([...s])}> File Name Type @@ -432,13 +432,13 @@ storiesOf('Table', module) 120 KB -
+ ) ) .add( 'density="spacious"', () => ( - onSelectionChange([...s])}> + onSelectionChange([...s])}> File Name Type @@ -456,13 +456,13 @@ storiesOf('Table', module) 120 KB -
+ ) ) .add( 'overflowMode="wrap"', () => ( - onSelectionChange([...s])}> + onSelectionChange([...s])}> File Name Type @@ -480,13 +480,13 @@ storiesOf('Table', module) 120 KB -
+ ) ) .add( 'overflowMode="wrap", density="compact"', () => ( - onSelectionChange([...s])}> + onSelectionChange([...s])}> File Name Type @@ -504,13 +504,13 @@ storiesOf('Table', module) 120 KB -
+ ) ) .add( 'overflowMode="wrap", density="spacious"', () => ( - onSelectionChange([...s])}> + onSelectionChange([...s])}> File Name Type @@ -528,13 +528,13 @@ storiesOf('Table', module) 120 KB -
+ ) ) .add( 'custom isRowHeader labeling', () => ( - onSelectionChange([...s])}> + onSelectionChange([...s])}> First Name Last Name @@ -552,7 +552,7 @@ storiesOf('Table', module) February 10 -
+ ) ) .add( @@ -570,7 +570,7 @@ storiesOf('Table', module) .add( 'isLoading', () => ( - + {column => {column.name} @@ -583,13 +583,13 @@ storiesOf('Table', module) ) } -
+ ) ) .add( 'isLoading more', () => ( - + {column => {column.name} @@ -602,13 +602,13 @@ storiesOf('Table', module) ) } -
+ ) ) .add( 'renderEmptyState', () => ( - + {column => {column.name} @@ -617,7 +617,7 @@ storiesOf('Table', module) {[]} -
+ ) ) .add( @@ -628,8 +628,8 @@ storiesOf('Table', module) .add( 'hideHeader', () => ( - @@ -737,7 +737,7 @@ storiesOf('Table', module) Three -
+ ) ) .add( @@ -793,7 +793,7 @@ function AsyncLoadingExample() { return (
list.remove(list.items[0].data.id)}>Remove first item - + Score Title @@ -811,7 +811,7 @@ function AsyncLoadingExample() { ) } -
+
); } @@ -900,7 +900,7 @@ function ProjectListTable() { value={filterText} onChange={(onChange)} /> - {(key) => {item[key]}} )} -
+
); @@ -980,7 +980,7 @@ function AsyncServerFilterTable(props) { placeholder={'Search by name'} defaultValue={list.filterText} onChange={(onChange)} /> - {(key) => {item[key]}} )} -
+ ); } diff --git a/packages/@react-spectrum/table/test/Table.test.js b/packages/@react-spectrum/table/test/Table.test.js index 5a49ae68393..9c2ea1f8173 100644 --- a/packages/@react-spectrum/table/test/Table.test.js +++ b/packages/@react-spectrum/table/test/Table.test.js @@ -13,7 +13,7 @@ import {act, fireEvent, render as renderComponent, within} from '@testing-library/react'; import {ActionButton} from '@react-spectrum/button'; import Add from '@spectrum-icons/workflow/Add'; -import {Cell, Column, Row, Table, TableBody, TableHeader} from '../'; +import {Cell, Column, Row, TableBody, TableHeader, TableView} from '../'; import {CRUDExample} from '../stories/CRUDExample'; import {getFocusableTreeWalker} from '@react-aria/focus'; import {HidingColumns} from '../stories/HidingColumns'; @@ -56,7 +56,7 @@ for (let i = 1; i <= 100; i++) { manyItems.push({id: i, foo: 'Foo ' + i, bar: 'Bar ' + i, baz: 'Baz ' + i}); } -describe('Table', function () { +describe('TableView', function () { let offsetWidth, offsetHeight; beforeAll(function () { @@ -100,7 +100,7 @@ describe('Table', function () { it('renders a static table', function () { let {getByRole} = render( - + Foo Bar @@ -118,7 +118,7 @@ describe('Table', function () { Baz 2 -
+ ); let grid = getByRole('grid'); @@ -180,7 +180,7 @@ describe('Table', function () { it('renders a static table with selection', function () { let {getByRole} = render( - + Foo Bar @@ -198,7 +198,7 @@ describe('Table', function () { Baz 2 -
+ ); let grid = getByRole('grid'); @@ -275,7 +275,7 @@ describe('Table', function () { it('renders a dynamic table', function () { let {getByRole} = render( - + {column => {column.name}} @@ -286,7 +286,7 @@ describe('Table', function () { ) } -
+ ); let grid = getByRole('grid'); @@ -338,7 +338,7 @@ describe('Table', function () { it('renders a dynamic table with selection', function () { let {getByRole} = render( - + {column => {column.name}} @@ -349,7 +349,7 @@ describe('Table', function () { ) } -
+ ); let grid = getByRole('grid'); @@ -418,7 +418,7 @@ describe('Table', function () { it('renders a static table with nested columns', function () { let {getByRole} = render( - + Test @@ -443,7 +443,7 @@ describe('Table', function () { Baz 2 -
+ ); let grid = getByRole('grid'); @@ -523,7 +523,7 @@ describe('Table', function () { it('renders a dynamic table with nested columns', function () { let {getByRole} = render( - + {column => {column.name} @@ -536,7 +536,7 @@ describe('Table', function () { ) } -
+ ); let grid = getByRole('grid'); @@ -626,7 +626,7 @@ describe('Table', function () { it('renders a table with multiple row headers', function () { let {getByRole} = render( - + First Name Last Name @@ -644,7 +644,7 @@ describe('Table', function () { February 10 -
+ ); let grid = getByRole('grid'); @@ -678,7 +678,7 @@ describe('Table', function () { // locale is being set here, since we can't nest them, use original render function let renderTable = (locale = 'en-US', props = {}) => renderComponent( - + {column => {column.name}} @@ -689,14 +689,14 @@ describe('Table', function () { ) } -
+
); // locale is being set here, since we can't nest them, use original render function let renderNested = (locale = 'en-US') => renderComponent( - + {column => {column.name} @@ -709,12 +709,12 @@ describe('Table', function () { ) } -
+
); let renderMany = () => render( - + {column => {column.name} @@ -727,7 +727,7 @@ describe('Table', function () { ) } -
+ ); let focusCell = (tree, text) => act(() => getCell(tree, text).focus()); @@ -1109,7 +1109,7 @@ describe('Table', function () { describe('type to select', function () { let renderTypeSelect = () => render( - + First Name Last Name @@ -1132,7 +1132,7 @@ describe('Table', function () { December 12 -
+ ); it('focuses cell by typing letters in rapid succession', function () { @@ -1240,7 +1240,7 @@ describe('Table', function () { let renderFocusable = () => render( <> - + Foo Bar @@ -1258,7 +1258,7 @@ describe('Table', function () { Baz 2 -
+ ); @@ -1501,7 +1501,7 @@ describe('Table', function () { describe('selection', function () { let renderJSX = (props, items = manyItems) => ( - + {column => {column.name}} @@ -1512,7 +1512,7 @@ describe('Table', function () { ) } -
+ ); let renderTable = (props, items = manyItems) => render(renderJSX(props, items)); @@ -2155,7 +2155,7 @@ describe('Table', function () { describe('single selection', function () { let renderJSX = (props, items = manyItems) => ( - + {column => {column.name}} @@ -2166,7 +2166,7 @@ describe('Table', function () { ) } -
+ ); let renderTable = (props, items = manyItems) => render(renderJSX(props, items)); @@ -2376,7 +2376,7 @@ describe('Table', function () { describe('press/hover interactions and selection mode', function () { let TableExample = (props) => ( - + {column => {column.name}} @@ -2387,7 +2387,7 @@ describe('Table', function () { ) } -
+ ); it('displays pressed/hover styles when row is pressed/hovered and selection mode is not "none"', function () { @@ -2704,7 +2704,7 @@ describe('Table', function () { describe('async loading', function () { let defaultTable = ( - + Foo Bar @@ -2719,12 +2719,12 @@ describe('Table', function () { Bar 2 -
+ ); it('should display a spinner when loading', function () { let tree = render( - + Foo Bar @@ -2732,7 +2732,7 @@ describe('Table', function () { {[]} -
+ ); let table = tree.getByRole('grid'); @@ -2758,7 +2758,7 @@ describe('Table', function () { it('should display a spinner at the bottom when loading more', function () { let tree = render( - + Foo Bar @@ -2773,7 +2773,7 @@ describe('Table', function () { Bar 2 -
+ ); let table = tree.getByRole('grid'); @@ -2805,7 +2805,7 @@ describe('Table', function () { let onLoadMore = jest.fn(); let tree = render( - + Foo Bar @@ -2817,7 +2817,7 @@ describe('Table', function () { )} -
+ ); let body = tree.getAllByRole('rowgroup')[1]; @@ -2843,7 +2843,7 @@ describe('Table', function () { let onLoadMoreSpy = jest.fn(); let TableMock = (props) => ( - + Foo Bar @@ -2855,7 +2855,7 @@ describe('Table', function () { )} -
+ ); render(); @@ -2866,7 +2866,7 @@ describe('Table', function () { it('should display an empty state when there are no items', function () { let tree = render( -

No results

}> +

No results

}> Foo Bar @@ -2874,7 +2874,7 @@ describe('Table', function () { {[]} -
+ ); let table = tree.getByRole('grid'); @@ -2901,7 +2901,7 @@ describe('Table', function () { describe('sorting', function () { it('should set aria-sort="none" on sortable column headers', function () { let tree = render( - + Foo Bar @@ -2914,7 +2914,7 @@ describe('Table', function () { Baz 1 -
+ ); let table = tree.getByRole('grid'); @@ -2927,7 +2927,7 @@ describe('Table', function () { it('should set aria-sort="ascending" on sorted column header', function () { let tree = render( - + Foo Bar @@ -2940,7 +2940,7 @@ describe('Table', function () { Baz 1 -
+ ); let table = tree.getByRole('grid'); @@ -2953,7 +2953,7 @@ describe('Table', function () { it('should set aria-sort="descending" on sorted column header', function () { let tree = render( - + Foo Bar @@ -2966,7 +2966,7 @@ describe('Table', function () { Baz 1 -
+ ); let table = tree.getByRole('grid'); @@ -2980,7 +2980,7 @@ describe('Table', function () { it('should fire onSortChange when there is no existing sortDescriptor', function () { let onSortChange = jest.fn(); let tree = render( - + Foo Bar @@ -2993,7 +2993,7 @@ describe('Table', function () { Baz 1 -
+ ); let table = tree.getByRole('grid'); @@ -3012,7 +3012,7 @@ describe('Table', function () { it('should toggle the sort direction from ascending to descending', function () { let onSortChange = jest.fn(); let tree = render( - + Foo Bar @@ -3025,7 +3025,7 @@ describe('Table', function () { Baz 1 -
+ ); let table = tree.getByRole('grid'); @@ -3044,7 +3044,7 @@ describe('Table', function () { it('should toggle the sort direction from descending to ascending', function () { let onSortChange = jest.fn(); let tree = render( - + Foo Bar @@ -3057,7 +3057,7 @@ describe('Table', function () { Baz 1 -
+ ); let table = tree.getByRole('grid'); @@ -3076,7 +3076,7 @@ describe('Table', function () { it('should trigger sorting on a different column', function () { let onSortChange = jest.fn(); let tree = render( - + Foo Bar @@ -3089,7 +3089,7 @@ describe('Table', function () { Baz 1 -
+ ); let table = tree.getByRole('grid'); @@ -3109,7 +3109,7 @@ describe('Table', function () { describe('layout', function () { describe('row heights', function () { let renderTable = (props, scale) => render( - + {column => {column.name}} @@ -3120,7 +3120,7 @@ describe('Table', function () { ) } -
+ , scale); it('should layout rows with default height', function () { @@ -3266,7 +3266,7 @@ describe('Table', function () { }); let tree = render( - + {column => {column.name}} @@ -3277,7 +3277,7 @@ describe('Table', function () { ) } -
+ ); let rows = tree.getAllByRole('row'); expect(rows).toHaveLength(5); @@ -3311,7 +3311,7 @@ describe('Table', function () { describe('column widths', function () { it('should divide the available width by default', function () { let tree = render( - + {column => {column.name}} @@ -3322,7 +3322,7 @@ describe('Table', function () { ) } -
+ ); let rows = tree.getAllByRole('row'); @@ -3337,7 +3337,7 @@ describe('Table', function () { it('should support explicitly sized columns', function () { let tree = render( - + Foo Bar @@ -3350,7 +3350,7 @@ describe('Table', function () { ) } -
+ ); let rows = tree.getAllByRole('row'); @@ -3364,7 +3364,7 @@ describe('Table', function () { it('should divide remaining width amoung remaining columns', function () { let tree = render( - + Foo Bar @@ -3377,7 +3377,7 @@ describe('Table', function () { ) } -
+ ); let rows = tree.getAllByRole('row'); @@ -3392,7 +3392,7 @@ describe('Table', function () { it('should support percentage widths', function () { let tree = render( - + Foo Bar @@ -3405,7 +3405,7 @@ describe('Table', function () { ) } -
+ ); let rows = tree.getAllByRole('row'); @@ -3419,7 +3419,7 @@ describe('Table', function () { it('should support minWidth', function () { let tree = render( - + Foo Bar @@ -3432,7 +3432,7 @@ describe('Table', function () { ) } -
+ ); let rows = tree.getAllByRole('row'); @@ -3447,7 +3447,7 @@ describe('Table', function () { it('should support maxWidth', function () { let tree = render( - + Foo Bar @@ -3460,7 +3460,7 @@ describe('Table', function () { ) } -
+ ); let rows = tree.getAllByRole('row'); @@ -3475,7 +3475,7 @@ describe('Table', function () { it('should compute the correct widths for tiered headings with selection', function () { let tree = render( - + {column => {column.name}} @@ -3486,7 +3486,7 @@ describe('Table', function () { ) } -
+ ); let rows = tree.getAllByRole('row'); @@ -3588,7 +3588,7 @@ describe('Table', function () { describe('headerless columns', function () { let renderTable = (props, scale, showDivider = false) => render( - + Foo @@ -3605,7 +3605,7 @@ describe('Table', function () { -
+ , scale); it('renders table with headerless column with default scale', function () { From 270158dcb24243554c6e975f6ce5216661cca014 Mon Sep 17 00:00:00 2001 From: Daniel Lu Date: Mon, 10 May 2021 11:01:17 -0700 Subject: [PATCH 02/62] fixing lint --- .../actionbar/stories/ActionBar.stories.tsx | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/@react-spectrum/actionbar/stories/ActionBar.stories.tsx b/packages/@react-spectrum/actionbar/stories/ActionBar.stories.tsx index f5352b8e8f0..17e5b99dd90 100644 --- a/packages/@react-spectrum/actionbar/stories/ActionBar.stories.tsx +++ b/packages/@react-spectrum/actionbar/stories/ActionBar.stories.tsx @@ -11,7 +11,7 @@ */ import {ActionBar, ActionBarContainer, Item} from '../'; -import {Cell, Column, Row, Table, TableBody, TableHeader} from '@react-spectrum/table'; +import {Cell, Column, Row, TableBody, TableHeader, TableView} from '@react-spectrum/table'; import React, {useState} from 'react'; import {Selection} from '@react-types/shared'; import {storiesOf} from '@storybook/react'; @@ -40,7 +40,7 @@ storiesOf('ActionBar', module) const [selectedKeys, setSelectedKeys] = useState(new Set()); return ( - setSelectedKeys(keys)}> @@ -54,7 +54,7 @@ storiesOf('ActionBar', module) ) } -
+ { @@ -68,4 +68,3 @@ storiesOf('ActionBar', module) ); } ); - From 26abdda63e8b0943a3d80364404f6c72974df809 Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 8 Apr 2021 16:01:19 -0700 Subject: [PATCH 03/62] Table spectrum docs wip --- packages/@react-spectrum/table/docs/Table.mdx | 619 ++++++++++++++++++ .../@react-spectrum/table/src/TableView.tsx | 3 + packages/@react-types/table/src/index.d.ts | 55 +- 3 files changed, 674 insertions(+), 3 deletions(-) create mode 100644 packages/@react-spectrum/table/docs/Table.mdx diff --git a/packages/@react-spectrum/table/docs/Table.mdx b/packages/@react-spectrum/table/docs/Table.mdx new file mode 100644 index 00000000000..4f85287c55a --- /dev/null +++ b/packages/@react-spectrum/table/docs/Table.mdx @@ -0,0 +1,619 @@ + + +import {Layout} from '@react-spectrum/docs'; +export default Layout; + +import docs from 'docs:@react-spectrum/table'; +import tableTypes from 'docs:@react-types/table/src/index.d.ts'; +import {HeaderInfo, PropTable} from '@react-spectrum/docs'; +import packageData from '@react-spectrum/table/package.json'; + +```jsx import +import {ActionButton} from '@react-spectrum/button'; +import Add from '@spectrum-icons/workflow/Add'; +import {Cell, Column, Row, Table, TableBody, TableHeader} from '@react-spectrum/table'; +import {Flex} from '@react-spectrum/layout'; +``` + +--- +category: Table +keywords: [table, grid] +--- + +# Table + +

{docs.exports.Table.description}

+ + + +## Example +```tsx example + + + + Name + Date Modified + Type + + + + Games + 6/7/2020 + File folder + + + Program Files + 4/7/2021 + File folder + + + bootmgr + 11/20/2010 + System file + + + log.txt + 1/18/2016 + Text Document + + +
+
+``` + +## Content +Table expects a TableHeader and a TableBody as children. In turn, TableHeader and TableBody follow the [Collection Components](../react-stately/collections.html) API, accepting both static and dynamic collections. +The TableHeader accepts either static Columns or a `columns` prop with a renderer function for dynamic rendering. Similarly, TableBody accepts either static Rows or a `items` prop with a renderer function. +Row follows the same pattern, accepting Cells as children instead. + +Basic usage of Table, seen in the example above, shows multiple Columns and Rows populated with strings and Cells respectively. Static collections, as in this example, can be used when the contents of the Table is known ahead of time. + +Dynamic collections, as shown below, can be used when the options come from an external data source such as an API call, or update over time. Providing the data in this way allows Table to automatically cache the rendering of each item, +which dramatically improves performance. Be sure to match each Column's key with an identical property in the Row object. + +```tsx example +function Example() { + let columns = [ + {name: 'Name', uid: 'name'}, + {name: 'Date Modified', uid: 'date'}, + {name: 'Type', uid: 'type'} + ]; + + let rows = [ + {id: '1', name: 'Games', date: '6/7/2020', type: 'File folder'}, + {id: '2', name: 'Program Files', date: '4/7/2021', type: 'File folder'}, + {id: '3', name: 'bootmgr', date: '11/20/2010', type: 'System file'}, + {id: '4', name: 'log.txt', date: '1/18/2016', type: 'Text Document'} + ]; + + return ( + + + + {column => ( + + {column.name} + + )} + + + {item => ( + + {/* Note this key is equal to the key of the the column, + not the key set on the Row prior */} + {key => {item[key]}} + + )} + +
+
+ ); +} +``` + +### Internationalization +To internationalize a Table, all text content within the Table should be replaced with localized strings. This includes all `aria-labels` provided to the Table, Columns, Rows, and Cells. +For languages that are read right-to-left (e.g. Hebrew and Arabic), the layout of Table is automatically flipped. + +## Labeling +### Accessibility +An `aria-label` must be provided to the Table for accessibility. If the Table is labeled by a separate element, an `aria-labelledby` prop must be provided using the id of the labeling element instead. + +## Asynchronous loading +Table supports loading data asynchronously, and will display a progress circle reflecting the current load state, +set by the `isLoading` prop. It also supports infinite scrolling to load more data on demand as the user scrolls, via the `onLoadMore` prop. + +This example uses the [useAsyncList](../react-stately/useAsyncList.html) hook to handle loading the data. +See the docs for more information. + +```tsx example +import {useAsyncList} from '@react-stately/data'; + +function AsyncTable() { + let columns = [ + {name: 'Name', key: 'name'}, + {name: 'Height', key: 'height'}, + {name: 'Mass', key: 'mass'} + ]; + + let list = useAsyncList({ + getKey: (item) => item.name, + async load({signal, cursor}) { + if (cursor) { + cursor = cursor.replace(/^http:\/\//i, 'https://'); + } + + let res = await fetch(cursor || `https://swapi.dev/api/people/?search=`, {signal}); + let json = await res.json(); + + return { + items: json.results, + cursor: json.next + }; + } + }); + + return ( + + + + {(column) => ( + {column.name} + )} + + + {(item) => ( + {(key) => {item[key]}} + )} + +
+
+ ); +} +``` + +## Events + +### Selection +Table supports multiple selection modes. By default, selection is disabled, however this can be changed using the `selectionMode` prop. +Use `defaultSelectedKeys` to provide a default set of selected rows. Note that the value of the selected keys must match the `key` prop of the Row. + +The example below demonstrates how one would use `defaultSelectedKeys`. + +```tsx example + + + + Name + Type + Level + + + + Charizard + Fire, Flying + 67 + + + Blastoise + Water + 56 + + + Venusaur + Grass, Poison + 83 + + + Pikachu + Electric + 100 + + +
+
+``` + +To programmatically control row selection, use the `selectedKeys` prop paired with the `onSelectionChange` callback. The `key` prop from the selected row will +be passed into the callback when the row is pressed, allowing you to update `selectedKeys` accordingly. + +Here is how you would control selection for the above example. + +```tsx example export=true +function PokemonTable(props) { + let columns = [ + {name: 'Name', uid: 'name'}, + {name: 'Type', uid: 'type'}, + {name: 'Level', uid: 'level'} + ]; + + let rows = [ + {id: '1', name: 'Charizard', type: 'Fire, Flying', level: '67'}, + {id: '2', name: 'Blastoise', type: 'Water', level: '56'}, + {id: '3', name: 'Venusaur', type: 'Grass, Poison', level: '83'}, + {id: '4', name: 'Pikachu', type: 'Electric', level: '100'} + ]; + + let [selected, setSelected] = React.useState(new Set(['2'])); + + return ( + + + + {column => ( + + {column.name} + + )} + + + {item => ( + + {key => {item[key]}} + + )} + +
+
+ ); +} +``` + +Multiple selection can be enabled by setting `selectionMode` to `multiple`. + +```tsx example +// Using the same table as above + +``` + +Table also supports a `disallowEmptySelection` prop which forces the user to have at least one row in the Table selected at all times. + +```tsx example +// Using the same table as above + +``` + +### Sorting +Table supports sorting its data when a column header is pressed. To designate that a Column should support sorting, provide it with +the `allowsSorting` prop. The Table accepts a `SortDescriptor` prop that defines the current column key to sort by and the sort direction (ascending/descending). +When the user presses a column's sort icon, the column's key and sort direction is passed into the `onSortChange` callback, allowing you to update +the `SortDescriptor` appropriately. + +This example performs client side sorting by passing a `sort` function to the [useAsyncList](../react-stately/useAsyncList.html) hook. +See the docs for more information. + +```tsx example +function AsyncSortTable() { + let columns = [ + {name: 'Name', key: 'name'}, + {name: 'Height', key: 'height'}, + {name: 'Mass', key: 'mass'} + ]; + + let list = useAsyncList({ + getKey: (item) => item.name, + async load({signal, cursor}) { + if (cursor) { + cursor = cursor.replace(/^http:\/\//i, 'https://'); + } + + let res = await fetch(cursor || `https://swapi.dev/api/people/?search`, {signal}); + let json = await res.json(); + + return { + items: json.results, + cursor: json.next + }; + }, + async sort({items, sortDescriptor}) { + return { + items: items.slice().sort((a, b) => { + let cmp = a[sortDescriptor.column] < b[sortDescriptor.column] ? -1 : 1; + if (sortDescriptor.direction === 'descending') { + cmp *= -1; + } + return cmp; + }) + }; + } + }); + + return ( + + + + {(column) => ( + {column.name} + )} + + + {(item) => ( + {(key) => {item[key]}} + )} + +
+
+ ); +} +``` + +## Props + +### Table props + + + +### Table header props + + + +// TODO maybe don't need this, explain through examples? + +### Column props + + + +// TODO maybe don't need this, explain through examples? Also might need to comment out some of the column types if they aren't supported yet + +### Table body props + + + +// TODO maybe don't need this, explain through examples? + +### Row props + + + +// TODO maybe don't need this, explain through examples? + +### Cell props + + + +// TODO maybe don't need this, explain through examples? + + +## Visual options + +### Hide header +Individual column headers can be hidden by providing the `hideHeader` prop to the Column. A tooltip is rendered when the column header is focused +to compensate for the lack of a visual title. + +```tsx example export=true +function TableExample(props) { + let columns = [ + {name: 'First Name', key: 'firstName'}, + {name: 'Last Name', key: 'lastName'}, + {name: 'Add Info', key: 'addInfo'}, + {name: 'Age', key: 'age'} + ]; + + let rows = [ + {id: '1', firstName: 'John', lastName: 'Doe', age: '45'}, + {id: '2', firstName: 'Jane', lastName: 'Doe', age: '37'}, + {id: '3', firstName: 'Joe', lastName: 'Schmoe', age: '67'}, + {id: '4', firstName: 'Joe', lastName: 'Bloggs', age: '12'}, + {id: '5', firstName: 'Longgggg Wrapping', lastName: 'Name', age: '56'} + ]; + + return ( + + + + {column => ( + + {column.name} + + )} + + + {item => + ( + {key => + key === 'addInfo' + ? + : {item[key]} + } + ) + } + +
+
+ ); +} +``` + +### Quiet +[View guidelines](https://spectrum.adobe.com/page/table/#Standard-or-quiet) + +```tsx example +// Using same setup as hide header example + +``` + +### Disabled +Use the `disabledKeys` prop to specify which rows to disable in the Table. + +```tsx example +// Using same setup as hide header example + +``` + +### Density +The amount of vertical padding that each row contains can be modified by providing the `density` prop. + +```tsx example +// Using same setup as hide header example + + + + +``` + +### Overflow mode +By default, text content that overflows its table cell will be truncated. You can have it wrap instead by passing `overflowMode="wrap"` +to the Table. + +```tsx example +// Using same setup as hide header example + +``` + +### Column alignment +[View guidelines](https://spectrum.adobe.com/page/table/#Usage-guidelines) + +```tsx example + + + + Name + Type + Size + + + + 2021406_Proposal + PDF + 86 KB + + + Budget Template + XLS + 120 KB + + + Onboarding + PPT + 472 KB + + +
+
+``` + +### Empty state +Use the `renderEmptyState` prop to customize what the Table will display if there are no rows provided. + +```tsx example +import {Content} from '@react-spectrum/view'; +import {IllustratedMessage} from '@react-spectrum/illustratedmessage'; +import {Heading} from '@react-spectrum/text'; + +function renderEmptyState() { + return ( + + + + + No results + No results found + + ); +} + + + + + Name + Type + Size + + + {[]} + +
+
+``` + +### Nested columns +// TODO add description here + +```tsx example + + + + + Name + Type + Size + + + + + 2021406_Proposal + PDF + 86 KB + + + Budget Template + XLS + 120 KB + + + Onboarding + PPT + 472 KB + + +
+
+``` + +### Focusable cells +// TODO add description here + +### Column widths +// TODO add description here + +### Column dividers +[View guidelines](https://spectrum.adobe.com/page/table/#Column-dividers) + + +TODO: // Other column specific props like isRowHeader labeling +TODO: // Modify all numeric columns to align end properly diff --git a/packages/@react-spectrum/table/src/TableView.tsx b/packages/@react-spectrum/table/src/TableView.tsx index e5d6b521323..399981af0cd 100644 --- a/packages/@react-spectrum/table/src/TableView.tsx +++ b/packages/@react-spectrum/table/src/TableView.tsx @@ -695,5 +695,8 @@ function CenteredWrapper({children}) { ); } +/** + * Tables are containers for displaying information. They allow users to quickly scan, sort, compare, and take action on large amounts of data. + */ const _TableView = React.forwardRef(TableView); export {_TableView as TableView}; diff --git a/packages/@react-types/table/src/index.d.ts b/packages/@react-types/table/src/index.d.ts index 636a6f2767d..b1a7e0a30a9 100644 --- a/packages/@react-types/table/src/index.d.ts +++ b/packages/@react-types/table/src/index.d.ts @@ -15,64 +15,113 @@ import {GridCollection, GridNode} from '@react-types/grid'; import {Key, ReactElement, ReactNode} from 'react'; export interface TableProps extends MultipleSelection, Sortable { - children: ReactElement | TableBodyProps | SectionProps | RowProps>[], + /** The elements that make up the Table. Includes the TableHeader, TableBody, Columns, and Rows. TODO: I think this shouldn't have Section and row? Also need to fix prop table rendering for this */ + children: ReactElement | TableBodyProps>[], + /** A list of row keys to disable. */ disabledKeys?: Iterable } export interface SpectrumTableProps extends TableProps, DOMProps, AriaLabelingProps, StyleProps { + /** + * Sets the amount of vertical padding within each Table cell. + * @default 'regular' + */ density?: 'compact' | 'regular' | 'spacious', + /** + * Sets the overflow behavior for the Table cell contents. + * @default 'truncate' + */ overflowMode?: 'wrap' | 'truncate', + /** Whether the Table should be displayed with a quiet style. */ isQuiet?: boolean, + /** Sets what the Table should render when there is no content to display. */ renderEmptyState?: () => JSX.Element } export interface TableHeaderProps { + /** A list of Table columns. */ columns?: T[], + /** A list of `Column(s)` or a function. If the latter, a list of columns must be provided using the `columns` prop.` */ children: ColumnElement | ColumnElement[] | ColumnRenderer } type ColumnElement = ReactElement>; type ColumnRenderer = (item: T) => ColumnElement; export interface ColumnProps { + /** Rendered contents of the column if `children` contains child columns. */ title?: ReactNode, + /** Static child columns or content to render as the column header. */ children: ReactNode | ColumnElement | ColumnElement[], + /** A list of child columns used when dynamically rendering nested child columns. */ childColumns?: T[], + // TODO Not sure if this is a thing, doesn't seem to get passed through? + /** An accessibility label for the column. */ 'aria-label'?: string, + /** The width of the column. */ width?: number | string, + /** The minimum width of the column. */ minWidth?: number | string, + /** The maximum width of the column. */ maxWidth?: number | string, + /** The default width of the column. TODO: is this even a thing? If so, why? */ defaultWidth?: number | string } // TODO: how to support these in CollectionBuilder... export interface SpectrumColumnProps extends ColumnProps { + /** + * The alignment of the column's contents relative to its allotted width. + * @default 'start' + */ align?: 'start' | 'center' | 'end', - allowsResizing?: boolean, - allowsReordering?: boolean, + // /** TODO: Don't think this is implemented yet */ + // allowsResizing?: boolean, + // /** TODO: Don't think this is implemented yet */ + // allowsReordering?: boolean, + /** Whether the column allows sorting. */ allowsSorting?: boolean, + /** Whether the column should stick to the viewport when scrolling. TODO check this */ isSticky?: boolean, // shouldStick?? + /** Whether the column is a row header and should be announced. TODO check this */ isRowHeader?: boolean, + /** Whether the column should render a divider between it and the next column. */ showDivider?: boolean, + /** + * Whether the column should hide its header text. A tooltip with the column's header text + * will be displayed when the column header is focused instead. + */ hideHeader?: boolean } export interface TableBodyProps extends AsyncLoadable { + /** The contents of the table body. Supports static items or a function for dynamic rendering. */ children: CollectionChildren, + /** A list of row objects in the table body used when dynamically rendering rows. */ items?: Iterable } export interface RowProps { // treeble case? + /** A list of child item objects used when dynamically rendering row children. */ childItems?: Iterable, + /** Whether this row has children, even if not loaded yet. */ hasChildItems?: boolean, + /** Rendered contents of the row or row child items. */ children: CellElement | CellElement[] | CellRenderer, + /** A string representation of the row's contents, used for features like typeahead. */ textValue?: string, // ??? + // TODO Not sure if this is a thing, doesn't seem to get passed through? + /** An accessibility label for the row. */ 'aria-label'?: string // ??? } export interface CellProps { + /** The contents of the cell. */ children: ReactNode, + /** A string representation of the cell's contents, used for features like typeahead. */ textValue?: string, + // TODO Not sure if this is a thing, doesn't seem to get passed through? + /** An accessibility label for the cell. */ 'aria-label'?: string } From 6e8c34d61beee2b9d622aad318bbd22ebbc07577 Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 8 Apr 2021 17:12:10 -0700 Subject: [PATCH 04/62] fixing alignment and ordering --- packages/@react-spectrum/table/docs/Table.mdx | 321 +++++++++++++----- 1 file changed, 231 insertions(+), 90 deletions(-) diff --git a/packages/@react-spectrum/table/docs/Table.mdx b/packages/@react-spectrum/table/docs/Table.mdx index 4f85287c55a..f4ecff7078e 100644 --- a/packages/@react-spectrum/table/docs/Table.mdx +++ b/packages/@react-spectrum/table/docs/Table.mdx @@ -44,29 +44,29 @@ keywords: [table, grid] Name - Date Modified Type + Date Modified Games - 6/7/2020 File folder + 6/7/2020 Program Files - 4/7/2021 File folder + 4/7/2021 bootmgr - 11/20/2010 System file + 11/20/2010 log.txt - 1/18/2016 Text Document + 1/18/2016
@@ -87,8 +87,8 @@ which dramatically improves performance. Be sure to match each Column's key with function Example() { let columns = [ {name: 'Name', uid: 'name'}, - {name: 'Date Modified', uid: 'date'}, - {name: 'Type', uid: 'type'} + {name: 'Type', uid: 'type'}, + {name: 'Date Modified', uid: 'date'} ]; let rows = [ @@ -103,7 +103,7 @@ function Example() { {column => ( - + {column.name} )} @@ -170,7 +170,9 @@ function AsyncTable() {
{(column) => ( - {column.name} + + {column.name} + )} Name Type - Level + Level @@ -256,7 +258,7 @@ function PokemonTable(props) {
{column => ( - + {column.name} )} @@ -338,7 +340,9 @@ function AsyncSortTable() {
{(column) => ( - {column.name} + + {column.name} + )} +
+ + Name + Add + Delete + + + + Red Panda + + + + + + + + + + + + + Harbor Seal + + + + + + + + + + + + + Groundhog + + + + + + + + + + + + + Otter + + + + + + + + + + + + +
+ +``` + +### Column widths +Columns support three different width props: `minWidth`, `width`, and `maxWidth`. + +```tsx example + + + + Name + Type + Size + + + + 2021406_Proposal + PDF + 86 KB + + + Budget Template + XLS + 120 KB + + + Onboarding + PPT + 472 KB + + + Welcome + TXT + 24 KB + + +
+
+``` + +### Column dividers +[View guidelines](https://spectrum.adobe.com/page/table/#Column-dividers) + +```tsx examples + + + + Name + Type + Size + + + + 2021406_Proposal + PDF + 86 KB + + + Budget Template + XLS + 120 KB + + + Onboarding + PPT + 472 KB + + + Welcome + TXT + 24 KB + + +
+
+``` + +### Column alignment +[View guidelines](https://spectrum.adobe.com/page/table/#Usage-guidelines) + +```tsx example + + + + Name + Type + Size + + + + 2021406_Proposal + PDF + 86 KB + + + Budget Template + XLS + 120 KB + + + Onboarding + PPT + 472 KB + + + Welcome + TXT + 24 KB + + +
+
+``` + +### Nested columns +Table supports nesting columns, allowing you to render "tiered" column headers. + +```tsx example + + + + + Name + Type + Size + + + + + 2021406_Proposal + PDF + 86 KB + + + Budget Template + XLS + 120 KB + + + Onboarding + PPT + 472 KB + + +
+
+``` ### Hide header Individual column headers can be hidden by providing the `hideHeader` prop to the Column. A tooltip is rendered when the column header is focused @@ -505,38 +723,6 @@ to the Table. ``` -### Column alignment -[View guidelines](https://spectrum.adobe.com/page/table/#Usage-guidelines) - -```tsx example - - - - Name - Type - Size - - - - 2021406_Proposal - PDF - 86 KB - - - Budget Template - XLS - 120 KB - - - Onboarding - PPT - 472 KB - - -
-
-``` - ### Empty state Use the `renderEmptyState` prop to customize what the Table will display if there are no rows provided. @@ -571,49 +757,4 @@ function renderEmptyState() { ``` -### Nested columns -// TODO add description here - -```tsx example - - - - - Name - Type - Size - - - - - 2021406_Proposal - PDF - 86 KB - - - Budget Template - XLS - 120 KB - - - Onboarding - PPT - 472 KB - - -
-
-``` - -### Focusable cells -// TODO add description here - -### Column widths -// TODO add description here - -### Column dividers -[View guidelines](https://spectrum.adobe.com/page/table/#Column-dividers) - - -TODO: // Other column specific props like isRowHeader labeling -TODO: // Modify all numeric columns to align end properly +TODO: // Other column specific props like isRowHeader labeling, perhaps accessibility From 54d7c6ceb124e1679b2faf090b343c8fc4c15e35 Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 9 Apr 2021 11:27:27 -0700 Subject: [PATCH 05/62] adding isRowHeader example, removing props that are't implemented --- packages/@react-spectrum/table/docs/Table.mdx | 64 ++++++++++++++----- packages/@react-types/table/src/index.d.ts | 23 +++---- 2 files changed, 60 insertions(+), 27 deletions(-) diff --git a/packages/@react-spectrum/table/docs/Table.mdx b/packages/@react-spectrum/table/docs/Table.mdx index f4ecff7078e..fdb0631133e 100644 --- a/packages/@react-spectrum/table/docs/Table.mdx +++ b/packages/@react-spectrum/table/docs/Table.mdx @@ -40,7 +40,7 @@ keywords: [table, grid] ## Example ```tsx example - + Name @@ -99,7 +99,7 @@ function Example() { ]; return ( - +
{column => ( @@ -124,13 +124,47 @@ function Example() { ``` ### Internationalization -To internationalize a Table, all text content within the Table should be replaced with localized strings. This includes all `aria-labels` provided to the Table, Columns, Rows, and Cells. +To internationalize a Table, all text content within the Table should be replaced with localized strings. This includes the `aria-label` provided to the Table if any. For languages that are read right-to-left (e.g. Hebrew and Arabic), the layout of Table is automatically flipped. ## Labeling ### Accessibility An `aria-label` must be provided to the Table for accessibility. If the Table is labeled by a separate element, an `aria-labelledby` prop must be provided using the id of the labeling element instead. +By default, the first column of the Table is used as the row header and is announced by assistive technology when navigating through the rows. You can override this behavior by providing the `isRowHeader` prop +to one or more Columns, allowing you to customize which columns should label the rows of the Table. + +The example below applies `isRowHeader` to the "First Name" and "Last Name" columns so that each row is announced with the person's full name (e.g. "John Doe"). + +```tsx example + +
+ + First Name + Last Name + Age + + + + John + Doe + 45 + + + Jane + Doe + 37 + + + Joe + Schmoe + 67 + + +
+
+``` + ## Asynchronous loading Table supports loading data asynchronously, and will display a progress circle reflecting the current load state, set by the `isLoading` prop. It also supports infinite scrolling to load more data on demand as the user scrolls, via the `onLoadMore` prop. @@ -166,7 +200,7 @@ function AsyncTable() { }); return ( - + {(column) => ( @@ -198,7 +232,7 @@ Use `defaultSelectedKeys` to provide a default set of selected rows. Note that t The example below demonstrates how one would use `defaultSelectedKeys`. ```tsx example - +
Name @@ -254,7 +288,7 @@ function PokemonTable(props) { let [selected, setSelected] = React.useState(new Set(['2'])); return ( - +
{column => ( @@ -336,7 +370,7 @@ function AsyncSortTable() { }); return ( - +
{(column) => ( @@ -426,7 +460,7 @@ function AsyncSortTable() { import Edit from '@spectrum-icons/workflow/Edit'; import Delete from '@spectrum-icons/workflow/Delete'; - +
Name @@ -495,7 +529,7 @@ import Delete from '@spectrum-icons/workflow/Delete'; Columns support three different width props: `minWidth`, `width`, and `maxWidth`. ```tsx example - +
Name @@ -532,7 +566,7 @@ Columns support three different width props: `minWidth`, `width`, and `maxWidth` [View guidelines](https://spectrum.adobe.com/page/table/#Column-dividers) ```tsx examples - +
Name @@ -569,7 +603,7 @@ Columns support three different width props: `minWidth`, `width`, and `maxWidth` [View guidelines](https://spectrum.adobe.com/page/table/#Usage-guidelines) ```tsx example - +
Name @@ -606,7 +640,7 @@ Columns support three different width props: `minWidth`, `width`, and `maxWidth` Table supports nesting columns, allowing you to render "tiered" column headers. ```tsx example - +
@@ -658,7 +692,7 @@ function TableExample(props) { ]; return ( - +
{column => ( @@ -743,7 +777,7 @@ function renderEmptyState() { ); } - +
Name @@ -756,5 +790,3 @@ function renderEmptyState() {
``` - -TODO: // Other column specific props like isRowHeader labeling, perhaps accessibility diff --git a/packages/@react-types/table/src/index.d.ts b/packages/@react-types/table/src/index.d.ts index b1a7e0a30a9..09d38c6f783 100644 --- a/packages/@react-types/table/src/index.d.ts +++ b/packages/@react-types/table/src/index.d.ts @@ -10,12 +10,13 @@ * governing permissions and limitations under the License. */ -import {AriaLabelingProps, AsyncLoadable, CollectionChildren, DOMProps, MultipleSelection, SectionProps, Sortable, StyleProps} from '@react-types/shared'; +import {AriaLabelingProps, AsyncLoadable, CollectionChildren, DOMProps, MultipleSelection, Sortable, StyleProps} from '@react-types/shared'; import {GridCollection, GridNode} from '@react-types/grid'; import {Key, ReactElement, ReactNode} from 'react'; export interface TableProps extends MultipleSelection, Sortable { - /** The elements that make up the Table. Includes the TableHeader, TableBody, Columns, and Rows. TODO: I think this shouldn't have Section and row? Also need to fix prop table rendering for this */ + // TODO: I think this shouldn't have Section and row? Also need to fix prop table rendering for this + /** The elements that make up the Table. Includes the TableHeader, TableBody, Columns, and Rows. */ children: ReactElement | TableBodyProps>[], /** A list of row keys to disable. */ disabledKeys?: Iterable @@ -41,7 +42,7 @@ export interface SpectrumTableProps extends TableProps, DOMProps, AriaLabe export interface TableHeaderProps { /** A list of Table columns. */ columns?: T[], - /** A list of `Column(s)` or a function. If the latter, a list of columns must be provided using the `columns` prop.` */ + /** A list of `Column(s)` or a function. If the latter, a list of columns must be provided using the `columns` prop. */ children: ColumnElement | ColumnElement[] | ColumnRenderer } @@ -56,7 +57,7 @@ export interface ColumnProps { childColumns?: T[], // TODO Not sure if this is a thing, doesn't seem to get passed through? /** An accessibility label for the column. */ - 'aria-label'?: string, + // 'aria-label'?: string, /** The width of the column. */ width?: number | string, /** The minimum width of the column. */ @@ -80,9 +81,9 @@ export interface SpectrumColumnProps extends ColumnProps { // allowsReordering?: boolean, /** Whether the column allows sorting. */ allowsSorting?: boolean, - /** Whether the column should stick to the viewport when scrolling. TODO check this */ - isSticky?: boolean, // shouldStick?? - /** Whether the column is a row header and should be announced. TODO check this */ + // /** Whether the column should stick to the viewport when scrolling. */ + // isSticky?: boolean, // shouldStick?? Not implemented yet? + /** Whether a column is a row header and should be announced by assistive technology during row navigation. */ isRowHeader?: boolean, /** Whether the column should render a divider between it and the next column. */ showDivider?: boolean, @@ -109,20 +110,20 @@ export interface RowProps { /** Rendered contents of the row or row child items. */ children: CellElement | CellElement[] | CellRenderer, /** A string representation of the row's contents, used for features like typeahead. */ - textValue?: string, // ??? + textValue?: string // ??? // TODO Not sure if this is a thing, doesn't seem to get passed through? /** An accessibility label for the row. */ - 'aria-label'?: string // ??? + // 'aria-label'?: string // ??? } export interface CellProps { /** The contents of the cell. */ children: ReactNode, /** A string representation of the cell's contents, used for features like typeahead. */ - textValue?: string, + textValue?: string // TODO Not sure if this is a thing, doesn't seem to get passed through? /** An accessibility label for the cell. */ - 'aria-label'?: string + // 'aria-label'?: string } export type CellElement = ReactElement; From a7c9d5ce3c12826ca214a8188f80a7014ee16db5 Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 9 Apr 2021 11:32:09 -0700 Subject: [PATCH 06/62] forgot to remove extra comments --- packages/@react-spectrum/table/docs/Table.mdx | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/packages/@react-spectrum/table/docs/Table.mdx b/packages/@react-spectrum/table/docs/Table.mdx index fdb0631133e..a79c072a12e 100644 --- a/packages/@react-spectrum/table/docs/Table.mdx +++ b/packages/@react-spectrum/table/docs/Table.mdx @@ -411,8 +411,6 @@ function AsyncSortTable() { } }} links={tableTypes.links} /> -// TODO maybe don't need this, explain through examples? - ### Column props -// TODO maybe don't need this, explain through examples? Also might need to comment out some of the column types if they aren't supported yet - ### Table body props -// TODO maybe don't need this, explain through examples? - ### Row props -// TODO maybe don't need this, explain through examples? - ### Cell props -// TODO maybe don't need this, explain through examples? - - ## Visual options ### Focusable cells ```tsx example From 1d6b42c0660d84779d9ad83fcbc98fff42cab704 Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 9 Apr 2021 12:19:43 -0700 Subject: [PATCH 07/62] more polish --- packages/@react-spectrum/table/docs/Table.mdx | 40 ++++++++++--------- packages/@react-types/table/src/index.d.ts | 2 +- 2 files changed, 22 insertions(+), 20 deletions(-) diff --git a/packages/@react-spectrum/table/docs/Table.mdx b/packages/@react-spectrum/table/docs/Table.mdx index a79c072a12e..8def238fb9b 100644 --- a/packages/@react-spectrum/table/docs/Table.mdx +++ b/packages/@react-spectrum/table/docs/Table.mdx @@ -40,7 +40,7 @@ keywords: [table, grid] ## Example ```tsx example - + Name @@ -99,7 +99,7 @@ function Example() { ]; return ( - +
{column => ( @@ -137,7 +137,7 @@ to one or more Columns, allowing you to customize which columns should label the The example below applies `isRowHeader` to the "First Name" and "Last Name" columns so that each row is announced with the person's full name (e.g. "John Doe"). ```tsx example - +
First Name @@ -179,7 +179,8 @@ function AsyncTable() { let columns = [ {name: 'Name', key: 'name'}, {name: 'Height', key: 'height'}, - {name: 'Mass', key: 'mass'} + {name: 'Mass', key: 'mass'}, + {name: 'Birth Year', key: 'birth_year'} ]; let list = useAsyncList({ @@ -200,7 +201,7 @@ function AsyncTable() { }); return ( - +
{(column) => ( @@ -232,7 +233,7 @@ Use `defaultSelectedKeys` to provide a default set of selected rows. Note that t The example below demonstrates how one would use `defaultSelectedKeys`. ```tsx example - +
Name @@ -288,7 +289,7 @@ function PokemonTable(props) { let [selected, setSelected] = React.useState(new Set(['2'])); return ( - +
{column => ( @@ -338,7 +339,8 @@ function AsyncSortTable() { let columns = [ {name: 'Name', key: 'name'}, {name: 'Height', key: 'height'}, - {name: 'Mass', key: 'mass'} + {name: 'Mass', key: 'mass'}, + {name: 'Birth Year', key: 'birth_year'} ]; let list = useAsyncList({ @@ -370,7 +372,7 @@ function AsyncSortTable() { }); return ( - +
{(column) => ( @@ -449,7 +451,7 @@ function AsyncSortTable() { import Edit from '@spectrum-icons/workflow/Edit'; import Delete from '@spectrum-icons/workflow/Delete'; - +
Name @@ -518,7 +520,7 @@ import Delete from '@spectrum-icons/workflow/Delete'; Columns support three different width props: `minWidth`, `width`, and `maxWidth`. ```tsx example - +
Name @@ -554,13 +556,13 @@ Columns support three different width props: `minWidth`, `width`, and `maxWidth` ### Column dividers [View guidelines](https://spectrum.adobe.com/page/table/#Column-dividers) -```tsx examples - +```tsx example +
Name - Type - Size + Type + Size @@ -592,7 +594,7 @@ Columns support three different width props: `minWidth`, `width`, and `maxWidth` [View guidelines](https://spectrum.adobe.com/page/table/#Usage-guidelines) ```tsx example - +
Name @@ -629,7 +631,7 @@ Columns support three different width props: `minWidth`, `width`, and `maxWidth` Table supports nesting columns, allowing you to render "tiered" column headers. ```tsx example - +
@@ -681,7 +683,7 @@ function TableExample(props) { ]; return ( - +
{column => ( @@ -766,7 +768,7 @@ function renderEmptyState() { ); } - +
Name diff --git a/packages/@react-types/table/src/index.d.ts b/packages/@react-types/table/src/index.d.ts index 09d38c6f783..e136adcf0eb 100644 --- a/packages/@react-types/table/src/index.d.ts +++ b/packages/@react-types/table/src/index.d.ts @@ -64,7 +64,7 @@ export interface ColumnProps { minWidth?: number | string, /** The maximum width of the column. */ maxWidth?: number | string, - /** The default width of the column. TODO: is this even a thing? If so, why? */ + /** The default width of the column. */ defaultWidth?: number | string } From 6fbf0825a29733ce61cc0041fb457cf8fe827289 Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 9 Apr 2021 12:23:47 -0700 Subject: [PATCH 08/62] tweaking sizes --- packages/@react-spectrum/table/docs/Table.mdx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/@react-spectrum/table/docs/Table.mdx b/packages/@react-spectrum/table/docs/Table.mdx index 8def238fb9b..516c561b8cf 100644 --- a/packages/@react-spectrum/table/docs/Table.mdx +++ b/packages/@react-spectrum/table/docs/Table.mdx @@ -201,7 +201,7 @@ function AsyncTable() { }); return ( - +
{(column) => ( @@ -372,7 +372,7 @@ function AsyncSortTable() { }); return ( - +
{(column) => ( @@ -520,7 +520,7 @@ import Delete from '@spectrum-icons/workflow/Delete'; Columns support three different width props: `minWidth`, `width`, and `maxWidth`. ```tsx example - +
Name @@ -557,7 +557,7 @@ Columns support three different width props: `minWidth`, `width`, and `maxWidth` [View guidelines](https://spectrum.adobe.com/page/table/#Column-dividers) ```tsx example - +
Name @@ -594,7 +594,7 @@ Columns support three different width props: `minWidth`, `width`, and `maxWidth` [View guidelines](https://spectrum.adobe.com/page/table/#Usage-guidelines) ```tsx example - +
Name @@ -631,7 +631,7 @@ Columns support three different width props: `minWidth`, `width`, and `maxWidth` Table supports nesting columns, allowing you to render "tiered" column headers. ```tsx example - +
From 97e6f11b294e351f18c4513a1069f680ce954691 Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 13 Apr 2021 10:48:03 -0700 Subject: [PATCH 09/62] proof reading and fixing some examples --- packages/@react-spectrum/table/docs/Table.mdx | 185 +++++++++--------- 1 file changed, 94 insertions(+), 91 deletions(-) diff --git a/packages/@react-spectrum/table/docs/Table.mdx b/packages/@react-spectrum/table/docs/Table.mdx index 516c561b8cf..723974c6688 100644 --- a/packages/@react-spectrum/table/docs/Table.mdx +++ b/packages/@react-spectrum/table/docs/Table.mdx @@ -74,14 +74,15 @@ keywords: [table, grid] ``` ## Content -Table expects a TableHeader and a TableBody as children. In turn, TableHeader and TableBody follow the [Collection Components](../react-stately/collections.html) API, accepting both static and dynamic collections. +Table expects a TableHeader and a TableBody as children. In turn, TableHeader and TableBody follow the [Collection Components](../react-stately/collections.html) API. The TableHeader accepts either static Columns or a `columns` prop with a renderer function for dynamic rendering. Similarly, TableBody accepts either static Rows or a `items` prop with a renderer function. Row follows the same pattern, accepting Cells as children instead. Basic usage of Table, seen in the example above, shows multiple Columns and Rows populated with strings and Cells respectively. Static collections, as in this example, can be used when the contents of the Table is known ahead of time. Dynamic collections, as shown below, can be used when the options come from an external data source such as an API call, or update over time. Providing the data in this way allows Table to automatically cache the rendering of each item, -which dramatically improves performance. Be sure to match each Column's key with an identical property in the Row object. +which dramatically improves performance. Make sure that each rendered property in the Row object matches with a Column's key. In the example below, the `uid` of each Column is set as its key and matches with a +property within each Row object. ```tsx example function Example() { @@ -227,10 +228,10 @@ function AsyncTable() { ## Events ### Selection -Table supports multiple selection modes. By default, selection is disabled, however this can be changed using the `selectionMode` prop. -Use `defaultSelectedKeys` to provide a default set of selected rows. Note that the value of the selected keys must match the `key` prop of the Row. +Table supports multiple selection modes. By default, selection is disabled, but this can be modified using the `selectionMode` prop. +Use `defaultSelectedKeys` to provide a default set of selected rows. Note that the value of the selected keys must match the `key` prop of the Row. -The example below demonstrates how one would use `defaultSelectedKeys`. +The example below uses `defaultSelectedKeys` to select the row with key equal to "2". ```tsx example @@ -332,7 +333,7 @@ When the user presses a column's sort icon, the column's key and sort direction the `SortDescriptor` appropriately. This example performs client side sorting by passing a `sort` function to the [useAsyncList](../react-stately/useAsyncList.html) hook. -See the docs for more information. +See the docs for more information on how to perform server side sorting. ```tsx example function AsyncSortTable() { @@ -446,70 +447,37 @@ function AsyncSortTable() { }} links={tableTypes.links} /> ## Visual options -### Focusable cells -```tsx example -import Edit from '@spectrum-icons/workflow/Edit'; -import Delete from '@spectrum-icons/workflow/Delete'; +### Column alignment +[View guidelines](https://spectrum.adobe.com/page/table/#Usage-guidelines) - -
+```tsx example + +
- Name - Add - Delete + Name + Type + Size - Red Panda - - - - - - - - - - + 2021406_Proposal + PDF + 86 KB - Harbor Seal - - - - - - - - - - + Budget Template + XLS + 120 KB - Groundhog - - - - - - - - - - + Onboarding + PPT + 472 KB - Otter - - - - - - - - - - + Welcome + TXT + 24 KB
@@ -590,16 +558,18 @@ Columns support three different width props: `minWidth`, `width`, and `maxWidth`
``` -### Column alignment -[View guidelines](https://spectrum.adobe.com/page/table/#Usage-guidelines) +### Nested columns +Table supports nesting columns, allowing you to render "tiered" column headers. ```tsx example - +
- Name - Type - Size + + Name + Type + Size + @@ -617,44 +587,77 @@ Columns support three different width props: `minWidth`, `width`, and `maxWidth` PPT 472 KB - - Welcome - TXT - 24 KB -
``` -### Nested columns -Table supports nesting columns, allowing you to render "tiered" column headers. +### Focusable cells +Cells accept any renderable node, allowing you to have focusable children within the table. ```tsx example - - +import Edit from '@spectrum-icons/workflow/Edit'; +import Delete from '@spectrum-icons/workflow/Delete'; + + +
- - Name - Type - Size - + Name + Add + Delete - 2021406_Proposal - PDF - 86 KB + Red Panda + + + + + + + + + + - Budget Template - XLS - 120 KB + Harbor Seal + + + + + + + + + + - Onboarding - PPT - 472 KB + Groundhog + + + + + + + + + + + + + Otter + + + + + + + + + +
@@ -679,7 +682,7 @@ function TableExample(props) { {id: '2', firstName: 'Jane', lastName: 'Doe', age: '37'}, {id: '3', firstName: 'Joe', lastName: 'Schmoe', age: '67'}, {id: '4', firstName: 'Joe', lastName: 'Bloggs', age: '12'}, - {id: '5', firstName: 'Longgggg Wrapping', lastName: 'Name', age: '56'} + {id: '5', firstName: 'Longggggggggggg Wrapping', lastName: 'Name', age: '56'} ]; return ( @@ -721,11 +724,11 @@ function TableExample(props) { ``` ### Disabled -Use the `disabledKeys` prop to specify which rows to disable in the Table. +Use the `disabledKeys` prop to specify which rows to disable selection for in the Table. ```tsx example // Using same setup as hide header example - + ``` ### Density From 010c5e10afc332ed89fdda494a39c3592d001a93 Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 13 Apr 2021 11:54:17 -0700 Subject: [PATCH 10/62] fixing sort example --- packages/@react-spectrum/table/docs/Table.mdx | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/packages/@react-spectrum/table/docs/Table.mdx b/packages/@react-spectrum/table/docs/Table.mdx index 723974c6688..a41c7b4786a 100644 --- a/packages/@react-spectrum/table/docs/Table.mdx +++ b/packages/@react-spectrum/table/docs/Table.mdx @@ -185,7 +185,6 @@ function AsyncTable() { ]; let list = useAsyncList({ - getKey: (item) => item.name, async load({signal, cursor}) { if (cursor) { cursor = cursor.replace(/^http:\/\//i, 'https://'); @@ -345,7 +344,6 @@ function AsyncSortTable() { ]; let list = useAsyncList({ - getKey: (item) => item.name, async load({signal, cursor}) { if (cursor) { cursor = cursor.replace(/^http:\/\//i, 'https://'); @@ -360,14 +358,25 @@ function AsyncSortTable() { }; }, async sort({items, sortDescriptor}) { + let sorted = items.sort((a, b) => { + let cmp; + let first = a[sortDescriptor.column].replace("BBY", ""); + let second = b[sortDescriptor.column].replace("BBY", ""); + + if (+first || +second) { + cmp = +first < +second ? -1 : 1; + } else { + cmp = first <= second ? -1 : 1; + } + + if (sortDescriptor.direction === 'descending') { + cmp *= -1; + } + return cmp; + }); + return { - items: items.slice().sort((a, b) => { - let cmp = a[sortDescriptor.column] < b[sortDescriptor.column] ? -1 : 1; - if (sortDescriptor.direction === 'descending') { - cmp *= -1; - } - return cmp; - }) + items: sorted }; } }); From 99b6b69fd6d8f2e30642328296867073bf06853b Mon Sep 17 00:00:00 2001 From: Daniel Lu Date: Thu, 29 Apr 2021 11:28:29 -0700 Subject: [PATCH 11/62] updating table export --- packages/@react-spectrum/table/docs/Table.mdx | 6 +----- packages/@react-spectrum/table/src/TableView.tsx | 2 +- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/packages/@react-spectrum/table/docs/Table.mdx b/packages/@react-spectrum/table/docs/Table.mdx index a41c7b4786a..0ce13cb69dd 100644 --- a/packages/@react-spectrum/table/docs/Table.mdx +++ b/packages/@react-spectrum/table/docs/Table.mdx @@ -409,11 +409,7 @@ function AsyncSortTable() { ### Table props - + ### Table header props diff --git a/packages/@react-spectrum/table/src/TableView.tsx b/packages/@react-spectrum/table/src/TableView.tsx index 399981af0cd..7926befadd3 100644 --- a/packages/@react-spectrum/table/src/TableView.tsx +++ b/packages/@react-spectrum/table/src/TableView.tsx @@ -698,5 +698,5 @@ function CenteredWrapper({children}) { /** * Tables are containers for displaying information. They allow users to quickly scan, sort, compare, and take action on large amounts of data. */ -const _TableView = React.forwardRef(TableView); +const _TableView = React.forwardRef(TableView) as (props: SpectrumTableProps & {ref?: DOMRef}) => ReactElement; export {_TableView as TableView}; From a6bdfa056fb2b5d341a808bbdd1301634e21803d Mon Sep 17 00:00:00 2001 From: Daniel Lu Date: Thu, 29 Apr 2021 16:31:04 -0700 Subject: [PATCH 12/62] modifiying sort example to remove loadMore --- packages/@react-spectrum/table/docs/Table.mdx | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/packages/@react-spectrum/table/docs/Table.mdx b/packages/@react-spectrum/table/docs/Table.mdx index 0ce13cb69dd..b1f9868944b 100644 --- a/packages/@react-spectrum/table/docs/Table.mdx +++ b/packages/@react-spectrum/table/docs/Table.mdx @@ -344,17 +344,12 @@ function AsyncSortTable() { ]; let list = useAsyncList({ - async load({signal, cursor}) { - if (cursor) { - cursor = cursor.replace(/^http:\/\//i, 'https://'); - } - - let res = await fetch(cursor || `https://swapi.dev/api/people/?search`, {signal}); + async load({signal}) { + let res = await fetch(`https://swapi.dev/api/people/?search`, {signal}); let json = await res.json(); return { - items: json.results, - cursor: json.next + items: json.results }; }, async sort({items, sortDescriptor}) { @@ -393,8 +388,7 @@ function AsyncSortTable() { + isLoading={list.isLoading}> {(item) => ( {(key) => {item[key]}} )} From bc873f3361dad25c431f1fadf77cdf92210c9863 Mon Sep 17 00:00:00 2001 From: Daniel Lu Date: Thu, 13 May 2021 10:24:01 -0700 Subject: [PATCH 13/62] updating docs for TableView rename --- packages/@react-spectrum/table/docs/Table.mdx | 110 +++++++++--------- 1 file changed, 55 insertions(+), 55 deletions(-) diff --git a/packages/@react-spectrum/table/docs/Table.mdx b/packages/@react-spectrum/table/docs/Table.mdx index b1f9868944b..a96bff6a476 100644 --- a/packages/@react-spectrum/table/docs/Table.mdx +++ b/packages/@react-spectrum/table/docs/Table.mdx @@ -18,22 +18,22 @@ import packageData from '@react-spectrum/table/package.json'; ```jsx import import {ActionButton} from '@react-spectrum/button'; import Add from '@spectrum-icons/workflow/Add'; -import {Cell, Column, Row, Table, TableBody, TableHeader} from '@react-spectrum/table'; +import {Cell, Column, Row, TableView, TableBody, TableHeader} from '@react-spectrum/table'; import {Flex} from '@react-spectrum/layout'; ``` --- -category: Table +category: Collections keywords: [table, grid] --- -# Table +# TableView -

{docs.exports.Table.description}

+

{docs.exports.TableView.description}

@@ -41,7 +41,7 @@ keywords: [table, grid] ## Example ```tsx example - + Name Type @@ -69,18 +69,18 @@ keywords: [table, grid] 1/18/2016 -
+
``` ## Content -Table expects a TableHeader and a TableBody as children. In turn, TableHeader and TableBody follow the [Collection Components](../react-stately/collections.html) API. +TableView expects a TableHeader and a TableBody as children. In turn, TableHeader and TableBody follow the [Collection Components](../react-stately/collections.html) API. The TableHeader accepts either static Columns or a `columns` prop with a renderer function for dynamic rendering. Similarly, TableBody accepts either static Rows or a `items` prop with a renderer function. Row follows the same pattern, accepting Cells as children instead. -Basic usage of Table, seen in the example above, shows multiple Columns and Rows populated with strings and Cells respectively. Static collections, as in this example, can be used when the contents of the Table is known ahead of time. +Basic usage of TableView, seen in the example above, shows multiple Columns and Rows populated with strings and Cells respectively. Static collections, as in this example, can be used when the contents of the TableView is known ahead of time. -Dynamic collections, as shown below, can be used when the options come from an external data source such as an API call, or update over time. Providing the data in this way allows Table to automatically cache the rendering of each item, +Dynamic collections, as shown below, can be used when the options come from an external data source such as an API call, or update over time. Providing the data in this way allows TableView to automatically cache the rendering of each item, which dramatically improves performance. Make sure that each rendered property in the Row object matches with a Column's key. In the example below, the `uid` of each Column is set as its key and matches with a property within each Row object. @@ -101,7 +101,7 @@ function Example() { return ( - + {column => ( @@ -118,28 +118,28 @@ function Example() { )} -
+
); } ``` ### Internationalization -To internationalize a Table, all text content within the Table should be replaced with localized strings. This includes the `aria-label` provided to the Table if any. -For languages that are read right-to-left (e.g. Hebrew and Arabic), the layout of Table is automatically flipped. +To internationalize a TableView, all text content within the TableView should be replaced with localized strings. This includes the `aria-label` provided to the TableView if any. +For languages that are read right-to-left (e.g. Hebrew and Arabic), the layout of TableView is automatically flipped. ## Labeling ### Accessibility -An `aria-label` must be provided to the Table for accessibility. If the Table is labeled by a separate element, an `aria-labelledby` prop must be provided using the id of the labeling element instead. +An `aria-label` must be provided to the TableView for accessibility. If the TableView is labeled by a separate element, an `aria-labelledby` prop must be provided using the id of the labeling element instead. -By default, the first column of the Table is used as the row header and is announced by assistive technology when navigating through the rows. You can override this behavior by providing the `isRowHeader` prop -to one or more Columns, allowing you to customize which columns should label the rows of the Table. +By default, the first column of the TableView is used as the row header and is announced by assistive technology when navigating through the rows. You can override this behavior by providing the `isRowHeader` prop +to one or more Columns, allowing you to customize which columns should label the rows of the TableView. The example below applies `isRowHeader` to the "First Name" and "Last Name" columns so that each row is announced with the person's full name (e.g. "John Doe"). ```tsx example - + First Name Last Name @@ -162,12 +162,12 @@ The example below applies `isRowHeader` to the "First Name" and "Last Name" colu 67 -
+
``` ## Asynchronous loading -Table supports loading data asynchronously, and will display a progress circle reflecting the current load state, +TableView supports loading data asynchronously, and will display a progress circle reflecting the current load state, set by the `isLoading` prop. It also supports infinite scrolling to load more data on demand as the user scrolls, via the `onLoadMore` prop. This example uses the [useAsyncList](../react-stately/useAsyncList.html) hook to handle loading the data. @@ -202,7 +202,7 @@ function AsyncTable() { return ( - + {(column) => ( @@ -218,7 +218,7 @@ function AsyncTable() { {(key) => {item[key]}} )} -
+
); } @@ -227,14 +227,14 @@ function AsyncTable() { ## Events ### Selection -Table supports multiple selection modes. By default, selection is disabled, but this can be modified using the `selectionMode` prop. +TableView supports multiple selection modes. By default, selection is disabled, but this can be modified using the `selectionMode` prop. Use `defaultSelectedKeys` to provide a default set of selected rows. Note that the value of the selected keys must match the `key` prop of the Row. The example below uses `defaultSelectedKeys` to select the row with key equal to "2". ```tsx example - + Name Type @@ -262,7 +262,7 @@ The example below uses `defaultSelectedKeys` to select the row with key equal to 100 -
+
``` @@ -290,7 +290,7 @@ function PokemonTable(props) { return ( - + {column => ( @@ -305,7 +305,7 @@ function PokemonTable(props) { )} -
+
); } @@ -318,7 +318,7 @@ Multiple selection can be enabled by setting `selectionMode` to `multiple`. ``` -Table also supports a `disallowEmptySelection` prop which forces the user to have at least one row in the Table selected at all times. +TableView also supports a `disallowEmptySelection` prop which forces the user to have at least one row in the TableView selected at all times. ```tsx example // Using the same table as above @@ -326,8 +326,8 @@ Table also supports a `disallowEmptySelection` prop which forces the user to hav ``` ### Sorting -Table supports sorting its data when a column header is pressed. To designate that a Column should support sorting, provide it with -the `allowsSorting` prop. The Table accepts a `SortDescriptor` prop that defines the current column key to sort by and the sort direction (ascending/descending). +TableView supports sorting its data when a column header is pressed. To designate that a Column should support sorting, provide it with +the `allowsSorting` prop. The TableView accepts a `SortDescriptor` prop that defines the current column key to sort by and the sort direction (ascending/descending). When the user presses a column's sort icon, the column's key and sort direction is passed into the `onSortChange` callback, allowing you to update the `SortDescriptor` appropriately. @@ -378,7 +378,7 @@ function AsyncSortTable() { return ( - + {(column) => ( @@ -393,7 +393,7 @@ function AsyncSortTable() { {(key) => {item[key]}} )} -
+
); } @@ -401,11 +401,11 @@ function AsyncSortTable() { ## Props -### Table props +### TableView props - + -### Table header props +### TableHeader props -### Table body props +### TableBody props - + Name Type @@ -479,7 +479,7 @@ function AsyncSortTable() { 24 KB -
+
``` @@ -488,7 +488,7 @@ Columns support three different width props: `minWidth`, `width`, and `maxWidth` ```tsx example - + Name Type @@ -516,7 +516,7 @@ Columns support three different width props: `minWidth`, `width`, and `maxWidth` 24 KB -
+
``` @@ -525,7 +525,7 @@ Columns support three different width props: `minWidth`, `width`, and `maxWidth` ```tsx example - + Name Type @@ -553,16 +553,16 @@ Columns support three different width props: `minWidth`, `width`, and `maxWidth` 24 KB -
+
``` ### Nested columns -Table supports nesting columns, allowing you to render "tiered" column headers. +TableView supports nesting columns, allowing you to render "tiered" column headers. ```tsx example - + Name @@ -587,19 +587,19 @@ Table supports nesting columns, allowing you to render "tiered" column headers. 472 KB -
+
``` ### Focusable cells -Cells accept any renderable node, allowing you to have focusable children within the table. +Cells accept any renderable node, allowing you to have focusable children within the TableView. ```tsx example import Edit from '@spectrum-icons/workflow/Edit'; import Delete from '@spectrum-icons/workflow/Delete'; - + Name Add @@ -659,7 +659,7 @@ import Delete from '@spectrum-icons/workflow/Delete'; -
+
``` @@ -686,7 +686,7 @@ function TableExample(props) { return ( - + {column => ( ) } -
+
); } @@ -723,7 +723,7 @@ function TableExample(props) { ``` ### Disabled -Use the `disabledKeys` prop to specify which rows to disable selection for in the Table. +Use the `disabledKeys` prop to specify which rows to disable selection for in the TableView. ```tsx example // Using same setup as hide header example @@ -743,7 +743,7 @@ The amount of vertical padding that each row contains can be modified by providi ### Overflow mode By default, text content that overflows its table cell will be truncated. You can have it wrap instead by passing `overflowMode="wrap"` -to the Table. +to the TableView. ```tsx example // Using same setup as hide header example @@ -751,7 +751,7 @@ to the Table. ``` ### Empty state -Use the `renderEmptyState` prop to customize what the Table will display if there are no rows provided. +Use the `renderEmptyState` prop to customize what the TableView will display if there are no rows provided. ```tsx example import {Content} from '@react-spectrum/view'; @@ -771,7 +771,7 @@ function renderEmptyState() { } - + Name Type @@ -780,6 +780,6 @@ function renderEmptyState() { {[]} -
+
``` From 91aa2ea771f1f0c61f5aff53870a67e48b47b8c3 Mon Sep 17 00:00:00 2001 From: Daniel Lu Date: Thu, 13 May 2021 10:26:53 -0700 Subject: [PATCH 14/62] renaming table mdx to tableview --- packages/@react-spectrum/table/docs/{Table.mdx => TableView.mdx} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename packages/@react-spectrum/table/docs/{Table.mdx => TableView.mdx} (100%) diff --git a/packages/@react-spectrum/table/docs/Table.mdx b/packages/@react-spectrum/table/docs/TableView.mdx similarity index 100% rename from packages/@react-spectrum/table/docs/Table.mdx rename to packages/@react-spectrum/table/docs/TableView.mdx From 56c996592f44b7b7bdaef3bf0151cd3a724c5a87 Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 13 Apr 2021 14:55:55 -0700 Subject: [PATCH 15/62] useTableState docs and useTable template --- packages/@react-aria/table/docs/useTable.mdx | 72 +++++++++++++++++++ .../@react-stately/grid/src/useGridState.ts | 2 + .../table/docs/useTableState.mdx | 40 +++++++++++ .../@react-stately/table/src/useTableState.ts | 11 +++ packages/@react-types/grid/src/index.d.ts | 2 + packages/@react-types/table/src/index.d.ts | 7 ++ 6 files changed, 134 insertions(+) create mode 100644 packages/@react-aria/table/docs/useTable.mdx create mode 100644 packages/@react-stately/table/docs/useTableState.mdx diff --git a/packages/@react-aria/table/docs/useTable.mdx b/packages/@react-aria/table/docs/useTable.mdx new file mode 100644 index 00000000000..755eefdf2f5 --- /dev/null +++ b/packages/@react-aria/table/docs/useTable.mdx @@ -0,0 +1,72 @@ + + +import {Layout} from '@react-spectrum/docs'; +export default Layout; + +import docs from 'docs:@react-aria/table'; +import {HeaderInfo, FunctionAPI, TypeContext, InterfaceType, TypeLink} from '@react-spectrum/docs'; +import packageData from '@react-aria/table/package.json'; + +```jsx import +import {useTable, useTableColumnHeader, useTableRow, useTableRowHeader, useTableSelectionCheckbox} from '@react-aria/table'; +``` + +--- +category: Table +keywords: [table, aria] +--- + +# useTable + +

{docs.exports.useTable.description}

+ + + +## API + + + + + + + +## Features + +// Talk about implementing a table via table tr etc and the challenges + +// add list of things the hooks offer + +## Anatomy + +// talk about anatomy of a table, column, rows, row headers, selection checkboxes +// do we have an anatomy image? +// mention state object + +## State management +// mention useTableState + +## Example + +// Perhaps a checkbox vs non checkbox version? + +## Usage +// Add this section? +// show dynamic vs static +// see other Table examples in docs + +## Internationalization + + +### RTL diff --git a/packages/@react-stately/grid/src/useGridState.ts b/packages/@react-stately/grid/src/useGridState.ts index 38a70ba724f..172f9769c3c 100644 --- a/packages/@react-stately/grid/src/useGridState.ts +++ b/packages/@react-stately/grid/src/useGridState.ts @@ -5,7 +5,9 @@ import {SelectionManager, useMultipleSelectionState} from '@react-stately/select export interface GridState> { collection: C, + /** A set of keys for rows that are disabled. */ disabledKeys: Set, + /** A selection manager to read and update row selection state. */ selectionManager: SelectionManager } diff --git a/packages/@react-stately/table/docs/useTableState.mdx b/packages/@react-stately/table/docs/useTableState.mdx new file mode 100644 index 00000000000..5523a080c1c --- /dev/null +++ b/packages/@react-stately/table/docs/useTableState.mdx @@ -0,0 +1,40 @@ + + +import {Layout} from '@react-spectrum/docs'; +export default Layout; + +import docs from 'docs:@react-stately/table'; +import {ClassAPI, HeaderInfo, FunctionAPI} from '@react-spectrum/docs'; +import packageData from '@react-stately/table/package.json'; + +--- +category: Table +keywords: [table, state] +--- + +# useTableState + +

{docs.exports.useTableState.description}

+ + + +## API + + + +## Interface + + + +## Example + +See the docs for [useTable](/react-aria/useTable.html) in react-aria for an example of `useTableState`. diff --git a/packages/@react-stately/table/src/useTableState.ts b/packages/@react-stately/table/src/useTableState.ts index da2718318ca..a2d422465cc 100644 --- a/packages/@react-stately/table/src/useTableState.ts +++ b/packages/@react-stately/table/src/useTableState.ts @@ -18,9 +18,13 @@ import {TableCollection} from './TableCollection'; import {useCollection} from '@react-stately/collections'; export interface TableState extends GridState> { + /** A collection of rows and columns in the table. */ collection: ITableCollection, + /** Whether the row selection checkboxes should be displayed. */ showSelectionCheckboxes: boolean, + /** The current sorted column and direction. */ sortDescriptor: SortDescriptor, + /** Calls the provided onSortChange handler with the provided column key and sort direction. */ sort(columnKey: Key): void } @@ -30,7 +34,10 @@ export interface CollectionBuilderContext { columns: Node[] } +// TODO: perhaps define things like `items`, `selectedKeys` etc here instead of relying on extend +// since the table type descriptions for these props should be more specific to table? export interface TableStateProps extends CollectionBase, MultipleSelection, Sortable { + /** Whether the row selection checkboxes should be displayed. */ showSelectionCheckboxes?: boolean } @@ -39,6 +46,10 @@ const OPPOSITE_SORT_DIRECTION = { descending: 'ascending' as SortDirection }; +/** + * Provides state management for a table component. Handles building a collection + * of columns and rows from props. In addition, it tracks row selection and manages sort order changes. + */ export function useTableState(props: TableStateProps): TableState { let {selectionMode = 'none'} = props; diff --git a/packages/@react-types/grid/src/index.d.ts b/packages/@react-types/grid/src/index.d.ts index 7417ff95aaf..bd4ee79f29d 100644 --- a/packages/@react-types/grid/src/index.d.ts +++ b/packages/@react-types/grid/src/index.d.ts @@ -14,7 +14,9 @@ import {Collection, Node} from '@react-types/shared'; import {Key} from 'react'; export interface GridCollection extends Collection> { + /** The number of columns in the grid. */ columnCount: number, + /** A list of rows in the grid. */ rows: GridNode[] } diff --git a/packages/@react-types/table/src/index.d.ts b/packages/@react-types/table/src/index.d.ts index e136adcf0eb..b9cf155289b 100644 --- a/packages/@react-types/table/src/index.d.ts +++ b/packages/@react-types/table/src/index.d.ts @@ -129,9 +129,16 @@ export interface CellProps { export type CellElement = ReactElement; export type CellRenderer = (columnKey: Key) => CellElement; +// TODO: perhaps defined `rows`, etc here so that we can table specific definitions export interface TableCollection extends GridCollection { + // TODO perhaps elaborate on this? maybe not clear enought, essentially returns the table header rows (e.g. in a tiered headers table, will return the nodes containing the top tier column, next tier, etc) + /** A list of header row nodes in the table. */ headerRows: GridNode[], + /** A list of column nodes in the table. */ columns: GridNode[], + // TODO perhaps elaborate here, link to rowheader docs? https://www.digitala11y.com/rowheader-role/? + /** A set of column keys that serve as the row header. */ rowHeaderColumnKeys: Set, + /** The node that makes up the body of the table. */ body: GridNode } From 229f69c1b71c84f2b5a67da668a87cb8c8865c43 Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 16 Apr 2021 16:52:57 -0700 Subject: [PATCH 16/62] progress on usetable aria docs --- packages/@react-aria/grid/src/useGrid.ts | 11 + packages/@react-aria/grid/src/useGridRow.ts | 7 + .../@react-aria/table/docs/TableAnatomy.svg | 1727 +++++++++++++++++ packages/@react-aria/table/docs/useTable.mdx | 116 +- packages/@react-aria/table/src/useTable.ts | 1 + .../table/src/useTableColumnHeader.ts | 6 + .../table/src/useTableRowHeader.ts | 5 + .../table/src/useTableSelectionCheckbox.ts | 3 + 8 files changed, 1867 insertions(+), 9 deletions(-) create mode 100644 packages/@react-aria/table/docs/TableAnatomy.svg diff --git a/packages/@react-aria/grid/src/useGrid.ts b/packages/@react-aria/grid/src/useGrid.ts index ef39d2ef288..e1afc22c98b 100644 --- a/packages/@react-aria/grid/src/useGrid.ts +++ b/packages/@react-aria/grid/src/useGrid.ts @@ -21,13 +21,24 @@ import {useCollator, useLocale} from '@react-aria/i18n'; import {useSelectableCollection} from '@react-aria/selection'; export interface GridProps extends DOMProps, AriaLabelingProps { + /** The ref attached to the grid element. */ ref: RefObject, + /** Whether the grid uses virtual scrolling. */ isVirtualized?: boolean, + /** + * An optional keyboard delegate implementation for type to select, + * to override the default. + */ keyboardDelegate?: KeyboardDelegate, + /** + * Whether initial grid focus should be placed on the grid row or grid cell. + * @default 'row' + */ focusMode?: 'row' | 'cell' } export interface GridAria { + /** Props for the grid element. */ gridProps: HTMLAttributes } diff --git a/packages/@react-aria/grid/src/useGridRow.ts b/packages/@react-aria/grid/src/useGridRow.ts index 49143840652..fad2f8cfd61 100644 --- a/packages/@react-aria/grid/src/useGridRow.ts +++ b/packages/@react-aria/grid/src/useGridRow.ts @@ -18,15 +18,22 @@ import {usePress} from '@react-aria/interactions'; import {useSelectableItem} from '@react-aria/selection'; export interface GridRowProps { + /** An object representing the grid row. Contains all the relevant information that makes up the grid row. */ node: Node, + /** The ref attached to the grid row. */ ref?: RefObject, + /** Whether the grid row is contained in a virtual scroller. */ isVirtualized?: boolean, + /** Whether the grid row is selected. */ isSelected?: boolean, + /** Whether the grid row is disabled. */ isDisabled?: boolean, + /** Whether selection should occur on press up instead of press down. */ shouldSelectOnPressUp?: boolean } export interface GridRowAria { + /** Props for the grid row element. */ rowProps: HTMLAttributes } diff --git a/packages/@react-aria/table/docs/TableAnatomy.svg b/packages/@react-aria/table/docs/TableAnatomy.svg new file mode 100644 index 00000000000..034141a4dca --- /dev/null +++ b/packages/@react-aria/table/docs/TableAnatomy.svg @@ -0,0 +1,1727 @@ + + + +Table anatomy diagram +Shows a table component with labels pointing to its parts, including the row, row divider, column, column header, column divider, sort icon, and cell elements. + + + diff --git a/packages/@react-aria/table/docs/useTable.mdx b/packages/@react-aria/table/docs/useTable.mdx index 755eefdf2f5..1a1384850e1 100644 --- a/packages/@react-aria/table/docs/useTable.mdx +++ b/packages/@react-aria/table/docs/useTable.mdx @@ -11,11 +11,15 @@ import {Layout} from '@react-spectrum/docs'; export default Layout; import docs from 'docs:@react-aria/table'; +import collectionsDocs from 'docs:@react-types/shared/src/collections.d.ts'; +import selectionDocs from 'docs:@react-stately/selection'; +import statelyDocs from 'docs:@react-stately/table'; import {HeaderInfo, FunctionAPI, TypeContext, InterfaceType, TypeLink} from '@react-spectrum/docs'; import packageData from '@react-aria/table/package.json'; +import Anatomy from './TableAnatomy.svg'; ```jsx import -import {useTable, useTableColumnHeader, useTableRow, useTableRowHeader, useTableSelectionCheckbox} from '@react-aria/table'; +import {useTable, useTableCell, useTableColumnHeader, useTableRow, useTableRowGroup, useTableRowHeader, useTableSelectAllCheckbox, useTableSelectionCheckbox} from '@react-aria/table'; ``` --- @@ -29,33 +33,119 @@ keywords: [table, aria] ## API +TODO figure out how to get the api for useTableCell and useTableRowGroup to work, FunctionApi doesn't play nice with those perhaps because they are grid hooks that are renamed in the export? + + ## Features -// Talk about implementing a table via table tr etc and the challenges - -// add list of things the hooks offer +A table can be built using the [<table>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/table), [<thead>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/thead), +[<tbody>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/tbody), and other table specific HTML elements, but this is very limited in functionality especially when it comes to user interactions. +`useTable` helps achieve accessible and interactive table components that can be styled as needed. + +* Exposed to assistive technology as a `grid` using ARIA +* Support for single, multiple, or no row selection +* Support for disabled rows +* Labeling support for accessibility +* Support for mouse, touch, and keyboard interactions +* Support for keyboard navigation between columns, rows, cells, and in-cell focusable items +* Column sorting +* Multiple row header support +* Automatic scrolling support during keyboard navigation +* Typeahead to allow focusing rows by typing text +* Virtualized scrolling support for performance with large tables ## Anatomy +TODO: currently using the Table on the spectrum site, maybe should use the anatomy diagram on the contribution site? That one shows collapsible rows though... + + +A table consists of a container element, with columns and rows of cells containing data inside.The cells within a table may contain focusable elements or plain text content. +If the table supports row selection, each row includes a selection checkbox. Additionally, a "select all" checkbox is displayed as the first column header if the table supports multiple row selection. + +`useTable`, `useTableCell`, `useTableColumnHeader`, `useTableRow`, and `useTableRowHeader` handle keyboard, mouse, and other interactions to support +row selection, in table navigation, and overall focus behavior. Those hooks, along with `useTableRowGroup`, also handle exposing the table and its contents +to assistive technology using ARIA. `useTableSelectAllCheckbox` and `useTableSelectionCheckbox` handle row selection and associating each checkbox with its respective rows +for assistive technology. + +`useTable` returns props that you should spread onto the table container element: + + + + + +`useTableColumnHeader` returns props for an individual table column header: + + + + + +`useTableRow` returns props for an individual table row: + + + + + +`useTableRowGroup` returns props for a element containing one or more rows of column headers or cells. This is often equivalent +to the header, body, and footer of the table: + +TODO FIGURE OUT WHY THIS IMPORT DOESN"T WORK -// talk about anatomy of a table, column, rows, row headers, selection checkboxes -// do we have an anatomy image? -// mention state object +`useTableRowHeader` returns props for the table cell that serves as a row's header: + + + + + +`useTableCell` returns props for an individual table cell: + +TODO FIGURE OUT WHY THIS IMPORT DOESN"T WORK + +`useTableSelectionCheckbox` returns props for an individual table row selection checkbox: + + + + + +`useTableSelectAllCheckbox` returns props for the table's "select all" checkbox: + + + + + +State is managed by the +hook from `@react-stately/table`. The state object should be passed as an option to each of the above hooks where applicable. + +Note that an `aria-label` or `aria-labelledby` must be passed to the table to identify the element to assistive technology. ## State management -// mention useTableState + +`useTable` requires knowledge of the rows, cells, and columns in the table in order to handle keyboard +navigation and other interactions. It does this using +the +interface, which is a generic interface to access sequential unique keyed data. You can +implement this interface yourself, e.g. by using a prop to pass a list of item objects, +but from +`@react-stately/table` implements a JSX based interface for building collections instead. +See [Collection Components](/react-stately/collections.html) for more information, +and [Collection Interface](/react-stately/Collection.html) for internal details. + +In addition, +manages the state necessary for multiple selection and exposes +a , +which makes use of the collection to provide an interface to update the selection state. +For more information, see [Selection](/react-stately/selection.html). ## Example @@ -68,5 +158,13 @@ keywords: [table, aria] ## Internationalization +`useTable` handles some aspects of internationalization automatically. +For example, type to select is implemented with an +[Intl.Collator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Collator) +for internationalized string matching. You are responsible for localizing all text content within the table. +Make sure that some types of content (e.g. file extensions) are not translated. ### RTL + +In right-to-left languages, the table layout should be mirrored. The columns should be ordered from right to left and the +individual column text alignment should be inverted. Ensure that your CSS accounts for this. diff --git a/packages/@react-aria/table/src/useTable.ts b/packages/@react-aria/table/src/useTable.ts index fc3b267548a..4b818c812f3 100644 --- a/packages/@react-aria/table/src/useTable.ts +++ b/packages/@react-aria/table/src/useTable.ts @@ -21,6 +21,7 @@ import {useId} from '@react-aria/utils'; import {useMemo} from 'react'; interface TableProps extends GridProps { + /** The layout object for the table. Computes what content is visible and how to position and style them. */ layout?: Layout> } diff --git a/packages/@react-aria/table/src/useTableColumnHeader.ts b/packages/@react-aria/table/src/useTableColumnHeader.ts index d5910cdab47..a828ec7eb90 100644 --- a/packages/@react-aria/table/src/useTableColumnHeader.ts +++ b/packages/@react-aria/table/src/useTableColumnHeader.ts @@ -21,14 +21,20 @@ import {usePress} from '@react-aria/interactions'; interface ColumnHeaderProps { + /** An object representing the column header. Contains all the relevant information that makes up the column header. */ node: Node, + /** The ref attached to the column header. */ ref: RefObject, + /** Whether the column header is contained in a virtual scroller. */ isVirtualized?: boolean, + /** The number of columns the column header should span. */ colspan?: number, + /** Whether the column header is disabled. */ isDisabled?: boolean } interface ColumnHeaderAria { + /** Props for the column header element. */ columnHeaderProps: HTMLAttributes } diff --git a/packages/@react-aria/table/src/useTableRowHeader.ts b/packages/@react-aria/table/src/useTableRowHeader.ts index 9c5406c199b..53d52adf051 100644 --- a/packages/@react-aria/table/src/useTableRowHeader.ts +++ b/packages/@react-aria/table/src/useTableRowHeader.ts @@ -17,13 +17,18 @@ import {TableState} from '@react-stately/table'; import {useGridCell} from '@react-aria/grid'; interface RowHeaderProps { + /** An object representing the row header. Contains all the relevant information that makes up the row header. */ node: GridNode, + /** The ref attached to the row header. */ ref: RefObject, + /** Whether the row header is contained in a virtual scroller. */ isVirtualized?: boolean, + /** Whether the row header is disabled. */ isDisabled?: boolean } interface RowHeaderAria { + /** Props for the row header element. */ rowHeaderProps: HTMLAttributes } diff --git a/packages/@react-aria/table/src/useTableSelectionCheckbox.ts b/packages/@react-aria/table/src/useTableSelectionCheckbox.ts index 72c9b3ac76f..1a715a41b01 100644 --- a/packages/@react-aria/table/src/useTableSelectionCheckbox.ts +++ b/packages/@react-aria/table/src/useTableSelectionCheckbox.ts @@ -17,11 +17,14 @@ import {TableState} from '@react-stately/table'; import {useId} from '@react-aria/utils'; interface SelectionCheckboxProps { + /** A unique key for the checkbox. */ key: Key, + /** Whether the checkbox is disabled. */ isDisabled?: boolean } interface SelectionCheckboxAria { + /** Props for the checkbox element. */ checkboxProps: AriaCheckboxProps } From 6e411f018ec6c80f3ca9851cc3dd0e174637f995 Mon Sep 17 00:00:00 2001 From: Daniel Lu Date: Tue, 27 Apr 2021 12:30:56 -0700 Subject: [PATCH 17/62] progress local docs are busted --- packages/@react-aria/table/docs/useTable.mdx | 388 +++++++++++++++- .../tooltip/stories/Tooltip.stories.tsx | 432 +++++++++++++++++- 2 files changed, 815 insertions(+), 5 deletions(-) diff --git a/packages/@react-aria/table/docs/useTable.mdx b/packages/@react-aria/table/docs/useTable.mdx index 1a1384850e1..d1df86e2c85 100644 --- a/packages/@react-aria/table/docs/useTable.mdx +++ b/packages/@react-aria/table/docs/useTable.mdx @@ -149,12 +149,392 @@ For more information, see [Selection](/react-stately/selection.html). ## Example -// Perhaps a checkbox vs non checkbox version? +This example uses HTML `
` elements to construct the table. It uses `useTableState` to construct the table's collection of rows and columns, +applying the appropriate attributes to each `
` using the various table hooks listed above paired with the div's role in the table. +The table follows the [Collection Components API](../react-stately/collections.html), accepting both static and dynamic collections. +The example below shows a static collection, which can be used when the full set of table contents is known ahead of time. + +```tsx example export=true +import {Cell, Column, Row, TableBody, TableHeader, useTableState} from '@react-stately/table'; +import {mergeProps} from '@react-aria/utils'; +import {useCheckbox} from '@react-aria/checkbox'; +import {useContext, useRef} from 'react'; +import {useFocusRing} from '@react-aria/focus'; +import {useToggleState} from '@react-stately/toggle'; + +const TableContext = React.createContext(null); +function useTableContext() { + return useContext(TableContext); +} + +function Table(props) { + let state = useTableState({...props, showSelectionCheckboxes: true}); + let ref = useRef(); + + let {gridProps} = useTable({ + ...props, + ref: ref + }, state); + + let headerRows = state.collection.rows.filter(item => item.type === 'headerrow' && item); + let rows = state.collection.rows.filter(item => item.type === 'item' && item); + + let renderColumnNode = (node) => { + if (node.props.isSelectionCell) { + return ; + } + + return ; + }; + + let renderRowNode = (node) => { + if (node.props.isSelectionCell) { + return ; + } + + if (state.collection.rowHeaderColumnKeys.has(node.column.key)) { + return ; + } + + return ; + }; + + return ( + +
+ + {[...headerRows].map(headerRow => ( + + {[...headerRow.childNodes].map((column) => renderColumnNode(column))} + + ))} + +
+ + {[...rows].map(row => ( + + {[...row.childNodes].map((node) => renderRowNode(node))} + + ))} + +
+
+
+ ); +} + + +function TableHeaderGroup({children, ...otherProps}) { + let {rowGroupProps} = useTableRowGroup(); + + return ( +
+ {children} +
+ ); +} + +function TableColumnHeader({column}) { + let ref = useRef(); + let state = useTableContext(); + let {columnHeaderProps} = useTableColumnHeader({ + node: column, + ref, + colspan: column.colspan + }, state); + + let {isFocusVisible, focusProps} = useFocusRing(); + let columnProps = column.props; + let arrowIcon = state.sortDescriptor?.direction === 'ascending' ? '▲' : '▼'; + + return ( +
+ {column.rendered} + {columnProps.allowsSorting && state.sortDescriptor?.column === column.key && + + } +
+ ); +} + +function TableSelectAllCell({column}) { + let ref = useRef(); + let state = useTableContext(); + + let isSingleSelectionMode = state.selectionManager.selectionMode === 'single'; + let {columnHeaderProps} = useTableColumnHeader({ + node: column, + ref, + colspan: column.colspan, + isDisabled: isSingleSelectionMode + }, state); + + let {checkboxProps} = useTableSelectAllCheckbox(state); + let inputRef = useRef(null); + let {inputProps} = useCheckbox(checkboxProps, useToggleState(checkboxProps), inputRef); + + return ( +
+ +
+ ); +} + +function TableRowGroup({children, ...otherProps}) { + let {rowGroupProps} = useTableRowGroup(); + + return ( +
+ {children} +
+ ); +} + +function TableRow({item, children, ...otherProps}) { + let ref = useRef(); + let state = useTableContext(); + let isDisabled = state.disabledKeys.has(item.key); + let isSelected = state.selectionManager.isSelected(item.key) && !isDisabled; + let {rowProps} = useTableRow({ + node: item, + isSelected, + ref, + isDisabled + }, state); + + let props = mergeProps( + rowProps, + otherProps + ); + + return ( +
+ {children} +
+ ); +} + +function TableHeaderRow({item, children, ...otherProps}) { + return ( +
+ {children} +
+ ); +} + +function TableCheckboxCell({cell}) { + let ref = useRef(); + let state = useTableContext(); + let isDisabled = state.disabledKeys.has(cell.parentKey); + let {gridCellProps} = useTableCell({ + node: cell, + ref, + isDisabled + }, state); + + let {checkboxProps} = useTableSelectionCheckbox( + { + key: cell.parentKey, + isDisabled + }, + state + ); + + let inputRef = useRef(null); + let {inputProps} = useCheckbox({...checkboxProps, isDisabled}, useToggleState(checkboxProps), inputRef); + + return ( +
+ {state.selectionManager.selectionMode !== 'none' && + + } +
+ ); +} + +function TableCell({cell}) { + let ref = useRef(); + let state = useTableContext(); + let isDisabled = state.disabledKeys.has(cell.parentKey); + let {gridCellProps} = useTableCell({ + node: cell, + ref, + isDisabled + }, state); + + return ( + + ); +} + +function TableRowHeader({cell}) { + let ref = useRef(); + let state = useTableContext(); + let isDisabled = state.disabledKeys.has(cell.parentKey); + let {rowHeaderProps} = useTableRowHeader({ + node: cell, + ref, + isDisabled + }, state); + + return ( + + ); +} + +function TableCellBase({cell, cellRef, ...otherProps}) { + let {isFocusVisible, focusProps} = useFocusRing(); + + return ( +
+ + {cell.rendered} + +
+ ); +} + + + + Name + Type + Date Modified + + + + Games + File folder + 6/7/2020 + + + Program Files + File folder + 4/7/2021 + + + bootmgr + System file + 11/20/2010 + + + log.txt + Text Document + 1/18/2016 + + +
+``` ## Usage -// Add this section? -// show dynamic vs static -// see other Table examples in docs + +### Dynamic collections + +// TODO add description + +```tsx example +function Example() { + let columns = [ + {name: 'Name', key: 'name'}, + {name: 'Type', key: 'type'}, + {name: 'Date Modified', key: 'date'} + ]; + + let rows = [ + {id: '1', name: 'Games', date: '6/7/2020', type: 'File folder'}, + {id: '2', name: 'Program Files', date: '4/7/2021', type: 'File folder'}, + {id: '3', name: 'bootmgr', date: '11/20/2010', type: 'System file'}, + {id: '4', name: 'log.txt', date: '1/18/2016', type: 'Text Document'} + ]; + + return ( + + + {(column) => ( + + {column.name} + + )} + + + {(item) => ( + + {/* Note this key is equal to the key of the the column, + not the key set on the Row prior */} + {(key) => {item[key]}} + + )} + +
+ ); +} +``` + +### Selection + +### Sorting + +### Async +// show dynamic +// show single selection, no selection, multiple selection +// show sorting +// show async ## Internationalization diff --git a/packages/@react-spectrum/tooltip/stories/Tooltip.stories.tsx b/packages/@react-spectrum/tooltip/stories/Tooltip.stories.tsx index fcf41a0317f..adf51371ff7 100644 --- a/packages/@react-spectrum/tooltip/stories/Tooltip.stories.tsx +++ b/packages/@react-spectrum/tooltip/stories/Tooltip.stories.tsx @@ -10,9 +10,40 @@ * governing permissions and limitations under the License. */ -import React from 'react'; +import {action} from '@storybook/addon-actions'; +import React, {useContext, useRef} from 'react'; import {storiesOf} from '@storybook/react'; import {Tooltip} from '../src'; +import {useTable, useTableCell, useTableColumnHeader, useTableRow, useTableRowGroup, useTableRowHeader, useTableSelectAllCheckbox, useTableSelectionCheckbox} from '@react-aria/table'; +import {TableState, useTableState} from '@react-stately/table'; +import { mergeProps } from '@react-aria/utils'; + +import {useCheckbox} from '@react-aria/checkbox'; +import {useToggleState} from '@react-stately/toggle'; +import { + Cell, + Column, + Row, + TableBody, + TableHeader +} from '@react-stately/table'; + +let onSelectionChange = action('onSelectionChange'); +let columns = [ + {name: 'Foo', key: 'foo'}, + {name: 'Bar', key: 'bar'}, + {name: 'Baz', key: 'baz'} +]; +let items = [ + {test: 'Test 1', foo: 'Foo 1', bar: 'Bar 1', yay: 'Yay 1', baz: 'Baz 1'}, + {test: 'Test 2', foo: 'Foo 2', bar: 'Bar 2', yay: 'Yay 2', baz: 'Baz 2'}, + {test: 'Test 1', foo: 'Foo 3', bar: 'Bar 1', yay: 'Yay 1', baz: 'Baz 1'}, + {test: 'Test 2', foo: 'Foo 4', bar: 'Bar 2', yay: 'Yay 2', baz: 'Baz 2'}, + {test: 'Test 1', foo: 'Foo 5', bar: 'Bar 1', yay: 'Yay 1', baz: 'Baz 1'}, + {test: 'Test 2', foo: 'Foo 6', bar: 'Bar 2', yay: 'Yay 2', baz: 'Baz 2'}, + {test: 'Test 1', foo: 'Foo 7', bar: 'Bar 1', yay: 'Yay 1', baz: 'Baz 1'}, + {test: 'Test 2', foo: 'Foo 8', bar: 'Bar 2', yay: 'Yay 2', baz: 'Baz 2'} +]; storiesOf('Tooltip', module) .add( @@ -70,6 +101,49 @@ storiesOf('Tooltip', module) .add( 'long content', () => render(longMarkup) + ) + .add( + 'dynamic', + () => ( + // +
+ + {column => {column.name}} + + + {item => + ( + {key => {item[key]}} + ) + } + +
+ ) + ) + .add( + 'static', + () => ( + // onSelectionChange([...s])}> +
onSelectionChange([...s])} style={{width: '500px', height: '200px'}}> + + First Name + Last Name + Birthday + + + + Sam + Smith + May 3 + + + Julia + Jones + February 10 + + +
+ ) ); const longMarkup = ( @@ -93,3 +167,359 @@ function render(content, props = {}) {
); } + + +const TableContext = React.createContext(null); +function useTableContext() { + return useContext(TableContext); +} + +function Table(props) { + // NEEDED? + let {isQuiet} = props; + let state = useTableState({...props, showSelectionCheckboxes: true}); + // NEEDED? + let density = props.density || 'regular'; + let ref = useRef(); + // NEEDED? Maybe too much, just pass some basic values, no need for density and scale? + // layout is technically optional + // let layout = useMemo(() => new TableLayout({ + // // If props.rowHeight is auto, then use estimated heights based on scale, otherwise use fixed heights. + // rowHeight: props.overflowMode === 'wrap' + // ? null + // : ROW_HEIGHTS[density][scale], + // estimatedRowHeight: props.overflowMode === 'wrap' + // ? ROW_HEIGHTS[density][scale] + // : null, + // headingHeight: props.overflowMode === 'wrap' + // ? null + // : DEFAULT_HEADER_HEIGHT[scale], + // estimatedHeadingHeight: props.overflowMode === 'wrap' + // ? DEFAULT_HEADER_HEIGHT[scale] + // : null, + // getDefaultWidth: ({hideHeader, isSelectionCell, showDivider}) => { + // if (hideHeader) { + // let width = DEFAULT_HIDE_HEADER_CELL_WIDTH[scale]; + // return showDivider ? width + 1 : width; + // } else if (isSelectionCell) { + // return SELECTION_CELL_DEFAULT_WIDTH; + // } + // } + // }), [props.overflowMode, scale, density]); + + let {gridProps} = useTable({ + ...props, + // ref: domRef, + ref: ref, + // layout + }, state); + // console.log('state, state.collection', state, state.collection) + let headerRows = state.collection.rows.filter(item => item.type === 'headerrow' && item); + let rows = state.collection.rows.filter(item => item.type === 'item' && item); + + let renderColumnNode = (node) => { + if (node.props.isSelectionCell) { + return + } + + return + }; + + let renderRowNode = (node) => { + if (node.props.isSelectionCell) { + return ; + } + + if (state.collection.rowHeaderColumnKeys.has(node.column.key)) { + return ; + } + + return ; + }; + + // TODO check the extra div styles + return ( + +
+
+ + {[...headerRows].map(headerRow => ( + + {[...headerRow.childNodes].map((column) => renderColumnNode(column))} + + ))} + +
+
+
+ + {[...rows].map(row => ( + + {[...row.childNodes].map((node) => renderRowNode(node))} + + ))} + +
+
+
+
+ ); +} + + +function TableHeaderRENAME({children, ...otherProps}) { + let {rowGroupProps} = useTableRowGroup(); + + return ( +
+ {children} +
+ ); +} + +function TableColumnHeader({column}) { + let ref = useRef(); + let state = useTableContext(); + let {columnHeaderProps} = useTableColumnHeader({ + node: column, + ref, + colspan: column.colspan, + }, state); + + let columnProps = column.props; + // let {hoverProps, isHovered} = useHover({}); + + return ( +
+ {column.rendered} + {/* {columnProps.hideHeader ? + {column.rendered} : + column.rendered + } */} + {/* TODO: add style/element for sorting? ignore sorting? */} + {/* {columnProps.allowsSorting && + + } */} +
+ ); +} + +function TableSelectAllCell({column}) { + let ref = useRef(); + let state = useTableContext(); + + let isSingleSelectionMode = state.selectionManager.selectionMode === 'single'; + let {columnHeaderProps} = useTableColumnHeader({ + node: column, + ref, + colspan: column.colspan, + isDisabled: isSingleSelectionMode + }, state); + + let {checkboxProps} = useTableSelectAllCheckbox(state); + // let {hoverProps, isHovered} = useHover({}); + // TODO + let inputRef = useRef(null); + let {inputProps} = useCheckbox(checkboxProps, useToggleState(checkboxProps), inputRef) + return ( +
+ {/* */} + +
+ ); +} + +function TableRowGroup({children, ...otherProps}) { + let {rowGroupProps} = useTableRowGroup(); + + return ( +
+ {children} +
+ ); +} + +function TableRow({item, children, ...otherProps}) { + let ref = useRef(); + let state = useTableContext(); + let isDisabled = state.disabledKeys.has(item.key); + let isSelected = state.selectionManager.isSelected(item.key) && !isDisabled; + let {rowProps} = useTableRow({ + node: item, + isSelected, + ref, + isDisabled + }, state); + + // TODO: do we need the below for the aria example? + // The row should show the focus background style when any cell inside it is focused. + // If the row itself is focused, then it should have a blue focus indicator on the left. + // let { + // isFocusVisible: isFocusVisibleWithin, + // focusProps: focusWithinProps + // } = useFocusRing({within: true}); + // let {isFocusVisible, focusProps} = useFocusRing(); + // let {hoverProps, isHovered} = useHover({isDisabled}); + let props = mergeProps( + rowProps, + otherProps, + // focusWithinProps, + // focusProps, + // hoverProps + ); + + return ( +
+ {/* // className={ + // classNames( + // styles, + // 'spectrum-Table-row', + // { + // 'is-selected': isSelected, + // 'is-focused': isFocusVisibleWithin, + // 'focus-ring': isFocusVisible, + // 'is-hovered': isHovered, + // 'is-disabled': isDisabled + // } + // ) + // }> */} + {children} +
+ ); +} + +function TableHeaderRow({item, children, ...otherProps}) { + return ( +
+ {children} +
+ ); +} + +function TableCheckboxCell({cell}) { + let ref = useRef(); + let state = useTableContext(); + let isDisabled = state.disabledKeys.has(cell.parentKey); + let {gridCellProps} = useTableCell({ + node: cell, + ref, + isDisabled + }, state); + + let {checkboxProps} = useTableSelectionCheckbox( + { + key: cell.parentKey, + isDisabled + }, + state + ); + + let inputRef = useRef(null); + let {inputProps} = useCheckbox(checkboxProps, useToggleState(checkboxProps), inputRef) + + return ( +
+ {state.selectionManager.selectionMode !== 'none' && + + } +
+ ); +} + +function TableCell({cell}) { + let ref = useRef(); + let state = useTableContext(); + let isDisabled = state.disabledKeys.has(cell.parentKey); + let {gridCellProps} = useTableCell({ + node: cell, + ref, + isDisabled + }, state); + + return ( + + ); +} + +function TableRowHeader({cell}) { + let ref = useRef(); + let state = useTableContext(); + let isDisabled = state.disabledKeys.has(cell.parentKey); + let {rowHeaderProps} = useTableRowHeader({ + node: cell, + ref, + isDisabled + }, state); + + return ( + + ); +} + +function TableCellBase({cell, cellRef, ...otherProps}) { + let state = useTableContext(); + let columnProps = cell.column.props; + let isDisabled = state.disabledKeys.has(cell.parentKey); + + return ( +
+ > + + {/* // className={ + // classNames( + // styles, + // 'spectrum-Table-cellContents' + // ) + // }> */} + {cell.rendered} + +
+ ); +} From 065343e24da386ce90d966305b342970252f1bb2 Mon Sep 17 00:00:00 2001 From: Daniel Lu Date: Tue, 27 Apr 2021 12:52:23 -0700 Subject: [PATCH 18/62] removing stuff from tooltip.stories --- .../tooltip/stories/Tooltip.stories.tsx | 432 +----------------- 1 file changed, 1 insertion(+), 431 deletions(-) diff --git a/packages/@react-spectrum/tooltip/stories/Tooltip.stories.tsx b/packages/@react-spectrum/tooltip/stories/Tooltip.stories.tsx index adf51371ff7..fcf41a0317f 100644 --- a/packages/@react-spectrum/tooltip/stories/Tooltip.stories.tsx +++ b/packages/@react-spectrum/tooltip/stories/Tooltip.stories.tsx @@ -10,40 +10,9 @@ * governing permissions and limitations under the License. */ -import {action} from '@storybook/addon-actions'; -import React, {useContext, useRef} from 'react'; +import React from 'react'; import {storiesOf} from '@storybook/react'; import {Tooltip} from '../src'; -import {useTable, useTableCell, useTableColumnHeader, useTableRow, useTableRowGroup, useTableRowHeader, useTableSelectAllCheckbox, useTableSelectionCheckbox} from '@react-aria/table'; -import {TableState, useTableState} from '@react-stately/table'; -import { mergeProps } from '@react-aria/utils'; - -import {useCheckbox} from '@react-aria/checkbox'; -import {useToggleState} from '@react-stately/toggle'; -import { - Cell, - Column, - Row, - TableBody, - TableHeader -} from '@react-stately/table'; - -let onSelectionChange = action('onSelectionChange'); -let columns = [ - {name: 'Foo', key: 'foo'}, - {name: 'Bar', key: 'bar'}, - {name: 'Baz', key: 'baz'} -]; -let items = [ - {test: 'Test 1', foo: 'Foo 1', bar: 'Bar 1', yay: 'Yay 1', baz: 'Baz 1'}, - {test: 'Test 2', foo: 'Foo 2', bar: 'Bar 2', yay: 'Yay 2', baz: 'Baz 2'}, - {test: 'Test 1', foo: 'Foo 3', bar: 'Bar 1', yay: 'Yay 1', baz: 'Baz 1'}, - {test: 'Test 2', foo: 'Foo 4', bar: 'Bar 2', yay: 'Yay 2', baz: 'Baz 2'}, - {test: 'Test 1', foo: 'Foo 5', bar: 'Bar 1', yay: 'Yay 1', baz: 'Baz 1'}, - {test: 'Test 2', foo: 'Foo 6', bar: 'Bar 2', yay: 'Yay 2', baz: 'Baz 2'}, - {test: 'Test 1', foo: 'Foo 7', bar: 'Bar 1', yay: 'Yay 1', baz: 'Baz 1'}, - {test: 'Test 2', foo: 'Foo 8', bar: 'Bar 2', yay: 'Yay 2', baz: 'Baz 2'} -]; storiesOf('Tooltip', module) .add( @@ -101,49 +70,6 @@ storiesOf('Tooltip', module) .add( 'long content', () => render(longMarkup) - ) - .add( - 'dynamic', - () => ( - // -
- - {column => {column.name}} - - - {item => - ( - {key => {item[key]}} - ) - } - -
- ) - ) - .add( - 'static', - () => ( - // onSelectionChange([...s])}> -
onSelectionChange([...s])} style={{width: '500px', height: '200px'}}> - - First Name - Last Name - Birthday - - - - Sam - Smith - May 3 - - - Julia - Jones - February 10 - - -
- ) ); const longMarkup = ( @@ -167,359 +93,3 @@ function render(content, props = {}) {
); } - - -const TableContext = React.createContext(null); -function useTableContext() { - return useContext(TableContext); -} - -function Table(props) { - // NEEDED? - let {isQuiet} = props; - let state = useTableState({...props, showSelectionCheckboxes: true}); - // NEEDED? - let density = props.density || 'regular'; - let ref = useRef(); - // NEEDED? Maybe too much, just pass some basic values, no need for density and scale? - // layout is technically optional - // let layout = useMemo(() => new TableLayout({ - // // If props.rowHeight is auto, then use estimated heights based on scale, otherwise use fixed heights. - // rowHeight: props.overflowMode === 'wrap' - // ? null - // : ROW_HEIGHTS[density][scale], - // estimatedRowHeight: props.overflowMode === 'wrap' - // ? ROW_HEIGHTS[density][scale] - // : null, - // headingHeight: props.overflowMode === 'wrap' - // ? null - // : DEFAULT_HEADER_HEIGHT[scale], - // estimatedHeadingHeight: props.overflowMode === 'wrap' - // ? DEFAULT_HEADER_HEIGHT[scale] - // : null, - // getDefaultWidth: ({hideHeader, isSelectionCell, showDivider}) => { - // if (hideHeader) { - // let width = DEFAULT_HIDE_HEADER_CELL_WIDTH[scale]; - // return showDivider ? width + 1 : width; - // } else if (isSelectionCell) { - // return SELECTION_CELL_DEFAULT_WIDTH; - // } - // } - // }), [props.overflowMode, scale, density]); - - let {gridProps} = useTable({ - ...props, - // ref: domRef, - ref: ref, - // layout - }, state); - // console.log('state, state.collection', state, state.collection) - let headerRows = state.collection.rows.filter(item => item.type === 'headerrow' && item); - let rows = state.collection.rows.filter(item => item.type === 'item' && item); - - let renderColumnNode = (node) => { - if (node.props.isSelectionCell) { - return - } - - return - }; - - let renderRowNode = (node) => { - if (node.props.isSelectionCell) { - return ; - } - - if (state.collection.rowHeaderColumnKeys.has(node.column.key)) { - return ; - } - - return ; - }; - - // TODO check the extra div styles - return ( - -
-
- - {[...headerRows].map(headerRow => ( - - {[...headerRow.childNodes].map((column) => renderColumnNode(column))} - - ))} - -
-
-
- - {[...rows].map(row => ( - - {[...row.childNodes].map((node) => renderRowNode(node))} - - ))} - -
-
-
-
- ); -} - - -function TableHeaderRENAME({children, ...otherProps}) { - let {rowGroupProps} = useTableRowGroup(); - - return ( -
- {children} -
- ); -} - -function TableColumnHeader({column}) { - let ref = useRef(); - let state = useTableContext(); - let {columnHeaderProps} = useTableColumnHeader({ - node: column, - ref, - colspan: column.colspan, - }, state); - - let columnProps = column.props; - // let {hoverProps, isHovered} = useHover({}); - - return ( -
- {column.rendered} - {/* {columnProps.hideHeader ? - {column.rendered} : - column.rendered - } */} - {/* TODO: add style/element for sorting? ignore sorting? */} - {/* {columnProps.allowsSorting && - - } */} -
- ); -} - -function TableSelectAllCell({column}) { - let ref = useRef(); - let state = useTableContext(); - - let isSingleSelectionMode = state.selectionManager.selectionMode === 'single'; - let {columnHeaderProps} = useTableColumnHeader({ - node: column, - ref, - colspan: column.colspan, - isDisabled: isSingleSelectionMode - }, state); - - let {checkboxProps} = useTableSelectAllCheckbox(state); - // let {hoverProps, isHovered} = useHover({}); - // TODO - let inputRef = useRef(null); - let {inputProps} = useCheckbox(checkboxProps, useToggleState(checkboxProps), inputRef) - return ( -
- {/* */} - -
- ); -} - -function TableRowGroup({children, ...otherProps}) { - let {rowGroupProps} = useTableRowGroup(); - - return ( -
- {children} -
- ); -} - -function TableRow({item, children, ...otherProps}) { - let ref = useRef(); - let state = useTableContext(); - let isDisabled = state.disabledKeys.has(item.key); - let isSelected = state.selectionManager.isSelected(item.key) && !isDisabled; - let {rowProps} = useTableRow({ - node: item, - isSelected, - ref, - isDisabled - }, state); - - // TODO: do we need the below for the aria example? - // The row should show the focus background style when any cell inside it is focused. - // If the row itself is focused, then it should have a blue focus indicator on the left. - // let { - // isFocusVisible: isFocusVisibleWithin, - // focusProps: focusWithinProps - // } = useFocusRing({within: true}); - // let {isFocusVisible, focusProps} = useFocusRing(); - // let {hoverProps, isHovered} = useHover({isDisabled}); - let props = mergeProps( - rowProps, - otherProps, - // focusWithinProps, - // focusProps, - // hoverProps - ); - - return ( -
- {/* // className={ - // classNames( - // styles, - // 'spectrum-Table-row', - // { - // 'is-selected': isSelected, - // 'is-focused': isFocusVisibleWithin, - // 'focus-ring': isFocusVisible, - // 'is-hovered': isHovered, - // 'is-disabled': isDisabled - // } - // ) - // }> */} - {children} -
- ); -} - -function TableHeaderRow({item, children, ...otherProps}) { - return ( -
- {children} -
- ); -} - -function TableCheckboxCell({cell}) { - let ref = useRef(); - let state = useTableContext(); - let isDisabled = state.disabledKeys.has(cell.parentKey); - let {gridCellProps} = useTableCell({ - node: cell, - ref, - isDisabled - }, state); - - let {checkboxProps} = useTableSelectionCheckbox( - { - key: cell.parentKey, - isDisabled - }, - state - ); - - let inputRef = useRef(null); - let {inputProps} = useCheckbox(checkboxProps, useToggleState(checkboxProps), inputRef) - - return ( -
- {state.selectionManager.selectionMode !== 'none' && - - } -
- ); -} - -function TableCell({cell}) { - let ref = useRef(); - let state = useTableContext(); - let isDisabled = state.disabledKeys.has(cell.parentKey); - let {gridCellProps} = useTableCell({ - node: cell, - ref, - isDisabled - }, state); - - return ( - - ); -} - -function TableRowHeader({cell}) { - let ref = useRef(); - let state = useTableContext(); - let isDisabled = state.disabledKeys.has(cell.parentKey); - let {rowHeaderProps} = useTableRowHeader({ - node: cell, - ref, - isDisabled - }, state); - - return ( - - ); -} - -function TableCellBase({cell, cellRef, ...otherProps}) { - let state = useTableContext(); - let columnProps = cell.column.props; - let isDisabled = state.disabledKeys.has(cell.parentKey); - - return ( -
- > - - {/* // className={ - // classNames( - // styles, - // 'spectrum-Table-cellContents' - // ) - // }> */} - {cell.rendered} - -
- ); -} From 848227292817fa2439b16c124e8f24b509e428c8 Mon Sep 17 00:00:00 2001 From: Daniel Lu Date: Tue, 27 Apr 2021 14:05:38 -0700 Subject: [PATCH 19/62] adding examples to useTable --- packages/@react-aria/table/docs/useTable.mdx | 190 +++++++++++++++++- .../table/docs/useTableState.mdx | 5 + 2 files changed, 185 insertions(+), 10 deletions(-) diff --git a/packages/@react-aria/table/docs/useTable.mdx b/packages/@react-aria/table/docs/useTable.mdx index d1df86e2c85..82c9c3b08d9 100644 --- a/packages/@react-aria/table/docs/useTable.mdx +++ b/packages/@react-aria/table/docs/useTable.mdx @@ -152,7 +152,10 @@ For more information, see [Selection](/react-stately/selection.html). This example uses HTML `
` elements to construct the table. It uses `useTableState` to construct the table's collection of rows and columns, applying the appropriate attributes to each `
` using the various table hooks listed above paired with the div's role in the table. The table follows the [Collection Components API](../react-stately/collections.html), accepting both static and dynamic collections. -The example below shows a static collection, which can be used when the full set of table contents is known ahead of time. + +The example below shows a static collection, which can be used when the full set of table contents is known ahead of time. Be sure to note the usage of +`Cell`, `Column`, `Row`, `TableBody`, and `TableHeader` to construct the table structure. These elements are similar to `Item` and `Section` used in other components +following the [Collection Components API](../react-stately/collections.html) mentioned previously, serving as node generators for the table collection. ```tsx example export=true import {Cell, Column, Row, TableBody, TableHeader, useTableState} from '@react-stately/table'; @@ -444,14 +447,14 @@ function TableCellBase({cell, cellRef, ...otherProps}) { outline: isFocusVisible ? '2px solid -webkit-focus-ring-color' : 'none' }} ref={cellRef}> - + {cell.rendered}
); } - +
Name Type @@ -486,7 +489,8 @@ function TableCellBase({cell, cellRef, ...otherProps}) { ### Dynamic collections -// TODO add description +Dynamic collections, as shown below, can be used when the table data comes from an external data source such as an API call, or update over time. +In the example below, both the columns and the rows are provided to the table via a render function. ```tsx example function Example() { @@ -504,7 +508,7 @@ function Example() { ]; return ( -
+
{(column) => ( @@ -528,13 +532,179 @@ function Example() { ### Selection +By default, `useTableState` doesn't allow row selection but this can be modified using the `selectionMode` prop. Use `defaultSelectedKeys` to provide a default set of selected rows. +Note that the value of the selected keys must match the `key` prop of the row. + +The example below uses `defaultSelectedKeys` to select the row with key equal to "2". + +```tsx example +
+ + Name + Type + Level + + + + Charizard + Fire, Flying + 67 + + + Blastoise + Water + 56 + + + Venusaur + Grass, Poison + 83 + + + Pikachu + Electric + 100 + + +
+``` + +To programmatically control row selection, use the `selectedKeys` prop paired with the `onSelectionChange` callback. The `key` prop from the selected row will +be passed into the callback when the row is pressed, allowing you to update `selectedKeys` accordingly. + +Here is how you would control selection for the above example. + +```tsx example export=true +function PokemonTable(props) { + let columns = [ + {name: 'Name', uid: 'name'}, + {name: 'Type', uid: 'type'}, + {name: 'Level', uid: 'level'} + ]; + let rows = [ + {id: '1', name: 'Charizard', type: 'Fire, Flying', level: '67'}, + {id: '2', name: 'Blastoise', type: 'Water', level: '56'}, + {id: '3', name: 'Venusaur', type: 'Grass, Poison', level: '83'}, + {id: '4', name: 'Pikachu', type: 'Electric', level: '100'} + ]; + let [selected, setSelected] = React.useState(new Set(['2'])); + return ( + + + {column => ( + + {column.name} + + )} + + + {item => ( + + {key => {item[key]}} + + )} + +
+ ); +} +``` + +Multiple selection can be enabled by setting `selectionMode` to `multiple`. + +```tsx example +// Using the same table as above + +``` + +Table also supports a `disallowEmptySelection` prop which forces the user to have at least one row in the Table selected at all times. + +```tsx example +// Using the same table as above + +``` + +### Disabled rows + +You can disable specific rows by providing an array of keys to `useTableState` via the `disabledKeys` prop. This will prevent rows from being selectable as shown in the example below. +Note that you are responsible for the styling of disabled rows. + +```tsx example +// Using the same table as above + +``` + ### Sorting -### Async -// show dynamic -// show single selection, no selection, multiple selection -// show sorting -// show async +Table supports sorting its data when a column header is pressed. To designate that a Column should support sorting, provide it with +the `allowsSorting` prop. The Table accepts a `SortDescriptor` prop that defines the current column key to sort by and the sort direction (ascending/descending). +When the user presses a column's sort icon, the column's key and sort direction is passed into the `onSortChange` callback, allowing you to update +the `SortDescriptor` appropriately. + +This example performs client side sorting by passing a `sort` function to the [useAsyncList](../react-stately/useAsyncList.html) hook. +See the docs for more information on how to perform server side sorting. + +```tsx example +import {useAsyncList} from '@react-stately/data'; + +function AsyncSortTable() { + let columns = [ + {name: 'Name', key: 'name'}, + {name: 'Height', key: 'height'}, + {name: 'Mass', key: 'mass'}, + {name: 'Birth Year', key: 'birth_year'} + ]; + let list = useAsyncList({ + async load({signal, cursor}) { + if (cursor) { + cursor = cursor.replace(/^http:\/\//i, 'https://'); + } + let res = await fetch(cursor || `https://swapi.dev/api/people/?search`, {signal}); + let json = await res.json(); + return { + items: json.results, + cursor: json.next + }; + }, + async sort({items, sortDescriptor}) { + let sorted = items.sort((a, b) => { + let cmp; + let first = a[sortDescriptor.column].replace("BBY", ""); + let second = b[sortDescriptor.column].replace("BBY", ""); + if (+first || +second) { + cmp = +first < +second ? -1 : 1; + } else { + cmp = first <= second ? -1 : 1; + } + if (sortDescriptor.direction === 'descending') { + cmp *= -1; + } + return cmp; + }); + return { + items: sorted + }; + } + }); + + return ( + + + {(column) => ( + + {column.name} + + )} + + + {(item) => ( + {(key) => {item[key]}} + )} + +
+ ); +} +``` ## Internationalization diff --git a/packages/@react-stately/table/docs/useTableState.mdx b/packages/@react-stately/table/docs/useTableState.mdx index 5523a080c1c..a2d2abcd434 100644 --- a/packages/@react-stately/table/docs/useTableState.mdx +++ b/packages/@react-stately/table/docs/useTableState.mdx @@ -30,6 +30,11 @@ keywords: [table, state] ## API + + + + + ## Interface From 5cd8fe555a1ead21820117f8b71f4538119519a6 Mon Sep 17 00:00:00 2001 From: Daniel Lu Date: Tue, 27 Apr 2021 16:16:46 -0700 Subject: [PATCH 20/62] adding loadmore functionality --- packages/@react-aria/table/docs/useTable.mdx | 28 +++++++++++++++++-- .../table/docs/useTableState.mdx | 3 +- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/packages/@react-aria/table/docs/useTable.mdx b/packages/@react-aria/table/docs/useTable.mdx index 82c9c3b08d9..041c162a1ae 100644 --- a/packages/@react-aria/table/docs/useTable.mdx +++ b/packages/@react-aria/table/docs/useTable.mdx @@ -156,12 +156,13 @@ The table follows the [Collection Components API](../react-stately/collections.h The example below shows a static collection, which can be used when the full set of table contents is known ahead of time. Be sure to note the usage of `Cell`, `Column`, `Row`, `TableBody`, and `TableHeader` to construct the table structure. These elements are similar to `Item` and `Section` used in other components following the [Collection Components API](../react-stately/collections.html) mentioned previously, serving as node generators for the table collection. +See [useTableState](/react-stately/useTableState.html) for more info on the props they support. ```tsx example export=true import {Cell, Column, Row, TableBody, TableHeader, useTableState} from '@react-stately/table'; import {mergeProps} from '@react-aria/utils'; import {useCheckbox} from '@react-aria/checkbox'; -import {useContext, useRef} from 'react'; +import {useContext, useLayoutEffect, useRef} from 'react'; import {useFocusRing} from '@react-aria/focus'; import {useToggleState} from '@react-stately/toggle'; @@ -173,6 +174,8 @@ function useTableContext() { function Table(props) { let state = useTableState({...props, showSelectionCheckboxes: true}); let ref = useRef(); + let scrollViewRef = useRef(); + let {collection, selectionManager} = state; let {gridProps} = useTable({ ...props, @@ -202,9 +205,26 @@ function Table(props) { return ; }; + let onScroll = (e) => { + if (e.target.scrollHeight - e.target.scrollTop - e.target.clientHeight < 10) { + collection.body.props.onLoadMore && collection.body.props.onLoadMore(); + } + }; + + useLayoutEffect(() => { + if (scrollViewRef.current) { + let scrollHeight = scrollViewRef.current.scrollHeight; + let clientHeight = scrollViewRef.current?.clientHeight; + + if (!(scrollHeight > clientHeight)) { + collection.body.props.onLoadMore && collection.body.props.onLoadMore(); + } + } + }, [collection.body.props, scrollViewRef]); + return ( -
+
{[...headerRows].map(headerRow => ( @@ -213,6 +233,7 @@ function Table(props) { ))}
+ items={list.items} + onLoadMore={list.loadMore}> {(item) => ( {(key) => {item[key]}} )} diff --git a/packages/@react-stately/table/docs/useTableState.mdx b/packages/@react-stately/table/docs/useTableState.mdx index a2d2abcd434..da5405e430c 100644 --- a/packages/@react-stately/table/docs/useTableState.mdx +++ b/packages/@react-stately/table/docs/useTableState.mdx @@ -42,4 +42,5 @@ keywords: [table, state] ## Example -See the docs for [useTable](/react-aria/useTable.html) in react-aria for an example of `useTableState`. +See the docs for [useTable](/react-aria/useTable.html) in react-aria for an example of `useTableState`, `Cell`, `Column`, +`Row`, `TableBody`, and `TableHeader`. From 4c1eb93d324019386a9c76c4809b9766856144a6 Mon Sep 17 00:00:00 2001 From: Daniel Lu Date: Wed, 28 Apr 2021 18:13:43 -0700 Subject: [PATCH 21/62] update transformer and packager so that renamed exports work in the docs api table --- packages/dev/parcel-packager-docs/DocsPackager.js | 11 ++++++++++- .../dev/parcel-transformer-docs/DocsTransformer.js | 4 ++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/packages/dev/parcel-packager-docs/DocsPackager.js b/packages/dev/parcel-packager-docs/DocsPackager.js index 630fa6da7b5..fa46f64cfb9 100644 --- a/packages/dev/parcel-packager-docs/DocsPackager.js +++ b/packages/dev/parcel-packager-docs/DocsPackager.js @@ -44,12 +44,21 @@ module.exports = new Packager({ function _processAsset(asset, res) { let obj = processCode(asset, code.get(asset.id)); for (let [exported] of asset.symbols) { + let {asset: resolvedAsset, exportSymbol} = bundleGraph.resolveSymbol(asset, exported); let processed = resolvedAsset.id === asset.id ? obj : processAsset(resolvedAsset); + if (exportSymbol === '*') { Object.assign(res, processed); } else { - res[exported] = processed[exportSymbol]; + // Re-exported with different name (e.g. export {useGridCell as useTableCell}) + if (exportSymbol !== exported) { + let clone = {...processed[exportSymbol]}; + clone.name = exported; + res[exported] = clone; + } else { + res[exported] = processed[exportSymbol]; + } } } diff --git a/packages/dev/parcel-transformer-docs/DocsTransformer.js b/packages/dev/parcel-transformer-docs/DocsTransformer.js index 5bdcdc0eebb..f33a81a3a35 100644 --- a/packages/dev/parcel-transformer-docs/DocsTransformer.js +++ b/packages/dev/parcel-transformer-docs/DocsTransformer.js @@ -41,8 +41,8 @@ module.exports = new Transformer({ if (path.node.source) { let symbols = new Map(); for (let specifier of path.node.specifiers) { - symbols.set(specifier.exported.name, {local: specifier.local.name}); - asset.symbols.set(specifier.exported.name, specifier.local.name); + symbols.set(specifier.local.name, {local: specifier.exported.name}); + asset.symbols.set(specifier.exported.name, specifier.exported.name); } asset.addDependency({ From 2ff09418107acb949edc1ba356e9fea1d995eb6b Mon Sep 17 00:00:00 2001 From: Daniel Lu Date: Thu, 29 Apr 2021 10:25:34 -0700 Subject: [PATCH 22/62] updating docs now that I can pull in useTablecell props --- packages/@react-aria/table/docs/useTable.mdx | 63 +++++++------------ .../dev/parcel-packager-docs/DocsPackager.js | 1 - 2 files changed, 22 insertions(+), 42 deletions(-) diff --git a/packages/@react-aria/table/docs/useTable.mdx b/packages/@react-aria/table/docs/useTable.mdx index 041c162a1ae..9a41e04c153 100644 --- a/packages/@react-aria/table/docs/useTable.mdx +++ b/packages/@react-aria/table/docs/useTable.mdx @@ -40,19 +40,19 @@ keywords: [table, aria] ## API -TODO figure out how to get the api for useTableCell and useTableRowGroup to work, FunctionApi doesn't play nice with those perhaps because they are grid hooks that are renamed in the export? - + + ## Features A table can be built using the [<table>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/table), [<thead>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/thead), -[<tbody>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/tbody), and other table specific HTML elements, but this is very limited in functionality especially when it comes to user interactions. +[<tbody>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/tbody), and other table specific HTML elements, but is very limited in functionality especially when it comes to user interactions. `useTable` helps achieve accessible and interactive table components that can be styled as needed. * Exposed to assistive technology as a `grid` using ARIA @@ -68,7 +68,7 @@ A table can be built using the [<table>](https://developer.mozilla.org/en- * Virtualized scrolling support for performance with large tables ## Anatomy -TODO: currently using the Table on the spectrum site, maybe should use the anatomy diagram on the contribution site? That one shows collapsible rows though... + A table consists of a container element, with columns and rows of cells containing data inside.The cells within a table may contain focusable elements or plain text content. @@ -100,7 +100,9 @@ for assistive technology. `useTableRowGroup` returns props for a element containing one or more rows of column headers or cells. This is often equivalent to the header, body, and footer of the table: -TODO FIGURE OUT WHY THIS IMPORT DOESN"T WORK + + + `useTableRowHeader` returns props for the table cell that serves as a row's header: @@ -110,7 +112,9 @@ TODO FIGURE OUT WHY THIS IMPORT DOESN"T WORK `useTableCell` returns props for an individual table cell: -TODO FIGURE OUT WHY THIS IMPORT DOESN"T WORK + + + `useTableSelectionCheckbox` returns props for an individual table row selection checkbox: @@ -162,7 +166,7 @@ See [useTableState](/react-stately/useTableState.html) for more info on the prop import {Cell, Column, Row, TableBody, TableHeader, useTableState} from '@react-stately/table'; import {mergeProps} from '@react-aria/utils'; import {useCheckbox} from '@react-aria/checkbox'; -import {useContext, useLayoutEffect, useRef} from 'react'; +import {useContext, useRef} from 'react'; import {useFocusRing} from '@react-aria/focus'; import {useToggleState} from '@react-stately/toggle'; @@ -175,15 +179,14 @@ function Table(props) { let state = useTableState({...props, showSelectionCheckboxes: true}); let ref = useRef(); let scrollViewRef = useRef(); - let {collection, selectionManager} = state; - + let {collection} = state; let {gridProps} = useTable({ ...props, ref: ref }, state); - let headerRows = state.collection.rows.filter(item => item.type === 'headerrow' && item); - let rows = state.collection.rows.filter(item => item.type === 'item' && item); + let headerRows = collection.rows.filter(item => item.type === 'headerrow' && item); + let rows = collection.rows.filter(item => item.type === 'item' && item); let renderColumnNode = (node) => { if (node.props.isSelectionCell) { @@ -198,33 +201,16 @@ function Table(props) { return ; } - if (state.collection.rowHeaderColumnKeys.has(node.column.key)) { + if (collection.rowHeaderColumnKeys.has(node.column.key)) { return ; } return ; }; - let onScroll = (e) => { - if (e.target.scrollHeight - e.target.scrollTop - e.target.clientHeight < 10) { - collection.body.props.onLoadMore && collection.body.props.onLoadMore(); - } - }; - - useLayoutEffect(() => { - if (scrollViewRef.current) { - let scrollHeight = scrollViewRef.current.scrollHeight; - let clientHeight = scrollViewRef.current?.clientHeight; - - if (!(scrollHeight > clientHeight)) { - collection.body.props.onLoadMore && collection.body.props.onLoadMore(); - } - } - }, [collection.body.props, scrollViewRef]); - return ( -
+
{[...headerRows].map(headerRow => ( @@ -233,7 +219,6 @@ function Table(props) { ))}
+ {(column) => ( @@ -717,8 +699,7 @@ function AsyncSortTable() { )} + items={list.items}> {(item) => ( {(key) => {item[key]}} )} diff --git a/packages/dev/parcel-packager-docs/DocsPackager.js b/packages/dev/parcel-packager-docs/DocsPackager.js index fa46f64cfb9..6b44d53859f 100644 --- a/packages/dev/parcel-packager-docs/DocsPackager.js +++ b/packages/dev/parcel-packager-docs/DocsPackager.js @@ -44,7 +44,6 @@ module.exports = new Packager({ function _processAsset(asset, res) { let obj = processCode(asset, code.get(asset.id)); for (let [exported] of asset.symbols) { - let {asset: resolvedAsset, exportSymbol} = bundleGraph.resolveSymbol(asset, exported); let processed = resolvedAsset.id === asset.id ? obj : processAsset(resolvedAsset); From 323f1664fa674fda0cd74df260a55b7a91c32f03 Mon Sep 17 00:00:00 2001 From: Daniel Lu Date: Tue, 11 May 2021 13:20:41 -0700 Subject: [PATCH 23/62] updating svg --- .../@react-aria/table/docs/TableAnatomy.svg | 1840 +---------------- 1 file changed, 114 insertions(+), 1726 deletions(-) diff --git a/packages/@react-aria/table/docs/TableAnatomy.svg b/packages/@react-aria/table/docs/TableAnatomy.svg index 034141a4dca..ae2a58e96fe 100644 --- a/packages/@react-aria/table/docs/TableAnatomy.svg +++ b/packages/@react-aria/table/docs/TableAnatomy.svg @@ -1,1727 +1,115 @@ - - - -Table anatomy diagram -Shows a table component with labels pointing to its parts, including the row, row divider, column, column header, column divider, sort icon, and cell elements. - - + + Table anatomy diagram + Shows a table component with labels pointing to its parts, including the row, row divider, column, column header, column divider, sort icon, and cell elements. + + + + + + + + + + Columnheader + + + SENT + + + + + 6,554 + + + + + 2,875 + 3,679 + 1,308 + Tree item + Tree item + Tree item + Tree item + ITEM NAME + + + Column + + + Cell + + + + Sort icon + + + + + Rowdivider + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Checkboxselection + + + + + Columndivider + + From b8b42486265f097f77c326d921cead3fbca44db0 Mon Sep 17 00:00:00 2001 From: Daniel Lu Date: Tue, 11 May 2021 14:29:39 -0700 Subject: [PATCH 24/62] fixing colors --- .../@react-aria/table/docs/TableAnatomy.svg | 50 +++++-------------- 1 file changed, 13 insertions(+), 37 deletions(-) diff --git a/packages/@react-aria/table/docs/TableAnatomy.svg b/packages/@react-aria/table/docs/TableAnatomy.svg index ae2a58e96fe..08836534427 100644 --- a/packages/@react-aria/table/docs/TableAnatomy.svg +++ b/packages/@react-aria/table/docs/TableAnatomy.svg @@ -1,4 +1,4 @@ - + Table anatomy diagram Shows a table component with labels pointing to its parts, including the row, row divider, column, column header, column divider, sort icon, and cell elements. @@ -7,23 +7,18 @@ - + - Columnheader SENT - + 6,554 - - - - 2,875 3,679 1,308 @@ -33,7 +28,6 @@ header" transform="translate(402 -360)" fill="var(--anatomy-gray-700)" font-size Tree item ITEM NAME - Column @@ -45,58 +39,42 @@ header" transform="translate(402 -360)" fill="var(--anatomy-gray-700)" font-size + + + + + Rowdivider - - - - - - + - - - - - + - - - - - + - - - - - + - - - - - + @@ -104,10 +82,8 @@ divider" transform="translate(402 -355)" fill="var(--anatomy-gray-700)" font-siz Checkboxselection - - Columndivider From 1543d96f936a9f8c4dc5eab790dc8fab71579d4b Mon Sep 17 00:00:00 2001 From: Daniel Lu Date: Tue, 11 May 2021 17:31:59 -0700 Subject: [PATCH 25/62] stopgap to get the dividers to show up --- packages/@react-aria/table/docs/TableAnatomy.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@react-aria/table/docs/TableAnatomy.svg b/packages/@react-aria/table/docs/TableAnatomy.svg index 08836534427..9f0779b9577 100644 --- a/packages/@react-aria/table/docs/TableAnatomy.svg +++ b/packages/@react-aria/table/docs/TableAnatomy.svg @@ -14,7 +14,7 @@ header" transform="translate(402 -360)" fill="var(--anatomy-gray-700)" font-size SENT - + From d4be58f3d796261c273196ba156a5cd5eb9c42e8 Mon Sep 17 00:00:00 2001 From: Daniel Lu Date: Thu, 13 May 2021 15:23:31 -0700 Subject: [PATCH 26/62] allowing styles to be passed to PropTable --- packages/@react-spectrum/table/docs/TableView.mdx | 10 +++++----- packages/dev/docs/src/PropTable.js | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/@react-spectrum/table/docs/TableView.mdx b/packages/@react-spectrum/table/docs/TableView.mdx index a96bff6a476..f4075c5dbad 100644 --- a/packages/@react-spectrum/table/docs/TableView.mdx +++ b/packages/@react-spectrum/table/docs/TableView.mdx @@ -403,7 +403,7 @@ function AsyncSortTable() { ### TableView props - + ### TableHeader props @@ -411,7 +411,7 @@ function AsyncSortTable() { props: { properties: Object.fromEntries(Object.entries(tableTypes.exports.TableHeaderProps.properties)) } -}} links={tableTypes.links} /> +}} links={tableTypes.links} style={{marginBottom: '40px'}} /> ### Column props @@ -419,7 +419,7 @@ function AsyncSortTable() { props: { properties: Object.fromEntries(Object.entries(tableTypes.exports.SpectrumColumnProps.properties)) } -}} links={tableTypes.links} /> +}} links={tableTypes.links} style={{marginBottom: '40px'}} /> ### TableBody props @@ -427,7 +427,7 @@ function AsyncSortTable() { props: { properties: Object.fromEntries(Object.entries(tableTypes.exports.TableBodyProps.properties)) } -}} links={tableTypes.links} /> +}} links={tableTypes.links} style={{marginBottom: '40px'}} /> ### Row props @@ -435,7 +435,7 @@ function AsyncSortTable() { props: { properties: Object.fromEntries(Object.entries(tableTypes.exports.RowProps.properties)) } -}} links={tableTypes.links} /> +}} links={tableTypes.links} style={{marginBottom: '40px'}} /> ### Cell props diff --git a/packages/dev/docs/src/PropTable.js b/packages/dev/docs/src/PropTable.js index ece058810ca..74445f50ac1 100644 --- a/packages/dev/docs/src/PropTable.js +++ b/packages/dev/docs/src/PropTable.js @@ -75,11 +75,11 @@ const GROUPS = { ] }; -export function PropTable({component, links}) { +export function PropTable({component, links, style}) { let [ungrouped, groups] = groupProps(component.props.properties); return ( - <> +
{Object.keys(groups).map(group => ( @@ -92,7 +92,7 @@ export function PropTable({component, links}) { ))} - +
); } From a13dc23d584c899c23e50163a657af64ff165110 Mon Sep 17 00:00:00 2001 From: Daniel Lu Date: Thu, 13 May 2021 15:31:14 -0700 Subject: [PATCH 27/62] moving aria and stately table docs to collections --- packages/@react-aria/table/docs/useTable.mdx | 2 +- packages/@react-stately/table/docs/useTableState.mdx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/@react-aria/table/docs/useTable.mdx b/packages/@react-aria/table/docs/useTable.mdx index 9a41e04c153..4166b086597 100644 --- a/packages/@react-aria/table/docs/useTable.mdx +++ b/packages/@react-aria/table/docs/useTable.mdx @@ -23,7 +23,7 @@ import {useTable, useTableCell, useTableColumnHeader, useTableRow, useTableRowGr ``` --- -category: Table +category: Collections keywords: [table, aria] --- diff --git a/packages/@react-stately/table/docs/useTableState.mdx b/packages/@react-stately/table/docs/useTableState.mdx index da5405e430c..f8aa7f3627f 100644 --- a/packages/@react-stately/table/docs/useTableState.mdx +++ b/packages/@react-stately/table/docs/useTableState.mdx @@ -15,7 +15,7 @@ import {ClassAPI, HeaderInfo, FunctionAPI} from '@react-spectrum/docs'; import packageData from '@react-stately/table/package.json'; --- -category: Table +category: Collections keywords: [table, state] --- From d31aa4c23b9b9844b19c3616107ab9f403eee022 Mon Sep 17 00:00:00 2001 From: Daniel Lu Date: Thu, 13 May 2021 15:43:02 -0700 Subject: [PATCH 28/62] adding some missing types --- packages/@react-aria/grid/src/useGridCell.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/@react-aria/grid/src/useGridCell.ts b/packages/@react-aria/grid/src/useGridCell.ts index e4d31ba4710..e0f5dd60232 100644 --- a/packages/@react-aria/grid/src/useGridCell.ts +++ b/packages/@react-aria/grid/src/useGridCell.ts @@ -22,13 +22,17 @@ import {useLocale} from '@react-aria/i18n'; import {useSelectableItem} from '@react-aria/selection'; interface GridCellProps { + /** An object representing the grid cell. Contains all the relevant information that makes up the grid cell. */ node: RSNode, + /** The ref attached to the grid cell element. */ ref: RefObject, + /** Whether the grid cell is contained in a virtual scroller. */ isVirtualized?: boolean, + /** Whether the grid cell is disabled. */ isDisabled?: boolean, - - /* when a cell is focused, should the cell or it's first focusable item be focused */ + /** Whether the cell or it's first focusable item be focused when the grid cell is focused. */ focusMode?: 'child' | 'cell', + /** Whether selection should occur on press up instead of press down. */ shouldSelectOnPressUp?: boolean } From e77d309ffacbbdbaeda0cbd3ec05c1375c691a51 Mon Sep 17 00:00:00 2001 From: Daniel Lu Date: Mon, 17 May 2021 15:54:13 -0700 Subject: [PATCH 29/62] partial review comment addressing --- packages/@react-aria/grid/src/useGridCell.ts | 1 + packages/@react-aria/grid/src/useGridRowGroup.ts | 1 + packages/@react-aria/table/docs/useTable.mdx | 4 ++-- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/@react-aria/grid/src/useGridCell.ts b/packages/@react-aria/grid/src/useGridCell.ts index e0f5dd60232..64925142560 100644 --- a/packages/@react-aria/grid/src/useGridCell.ts +++ b/packages/@react-aria/grid/src/useGridCell.ts @@ -37,6 +37,7 @@ interface GridCellProps { } interface GridCellAria { + /** Props for the grid cell element. */ gridCellProps: HTMLAttributes } diff --git a/packages/@react-aria/grid/src/useGridRowGroup.ts b/packages/@react-aria/grid/src/useGridRowGroup.ts index 0439507ad03..fcff2aa2d7f 100644 --- a/packages/@react-aria/grid/src/useGridRowGroup.ts +++ b/packages/@react-aria/grid/src/useGridRowGroup.ts @@ -13,6 +13,7 @@ import {HTMLAttributes} from 'react'; interface GridRowGroupAria { + /** Props for the row group element. */ rowGroupProps: HTMLAttributes } diff --git a/packages/@react-aria/table/docs/useTable.mdx b/packages/@react-aria/table/docs/useTable.mdx index 4166b086597..dd06e266479 100644 --- a/packages/@react-aria/table/docs/useTable.mdx +++ b/packages/@react-aria/table/docs/useTable.mdx @@ -71,7 +71,7 @@ A table can be built using the [<table>](https://developer.mozilla.org/en- -A table consists of a container element, with columns and rows of cells containing data inside.The cells within a table may contain focusable elements or plain text content. +A table consists of a container element, with columns and rows of cells containing data inside. The cells within a table may contain focusable elements or plain text content. If the table supports row selection, each row includes a selection checkbox. Additionally, a "select all" checkbox is displayed as the first column header if the table supports multiple row selection. `useTable`, `useTableCell`, `useTableColumnHeader`, `useTableRow`, and `useTableRowHeader` handle keyboard, mouse, and other interactions to support @@ -460,7 +460,7 @@ function TableCellBase({cell, cellRef, ...otherProps}) { ); } -
+
Name Type From dbb1f6af11d88ea8710a46b8b1da2b867cc62f27 Mon Sep 17 00:00:00 2001 From: Daniel Lu Date: Tue, 18 May 2021 11:12:01 -0700 Subject: [PATCH 30/62] fixing table height and table checkbox description --- packages/@react-aria/table/docs/useTable.mdx | 6 +++--- .../@react-aria/table/src/useTableSelectionCheckbox.ts | 9 +++++++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/packages/@react-aria/table/docs/useTable.mdx b/packages/@react-aria/table/docs/useTable.mdx index dd06e266479..29b8602c81c 100644 --- a/packages/@react-aria/table/docs/useTable.mdx +++ b/packages/@react-aria/table/docs/useTable.mdx @@ -514,7 +514,7 @@ function Example() { ]; return ( -
+
{(column) => ( @@ -544,7 +544,7 @@ Note that the value of the selected keys must match the `key` prop of the row. The example below uses `defaultSelectedKeys` to select the row with key equal to "2". ```tsx example -
+
Name Type @@ -595,7 +595,7 @@ function PokemonTable(props) { ]; let [selected, setSelected] = React.useState(new Set(['2'])); return ( -
+
{column => ( diff --git a/packages/@react-aria/table/src/useTableSelectionCheckbox.ts b/packages/@react-aria/table/src/useTableSelectionCheckbox.ts index 1a715a41b01..80cd1e8e2bf 100644 --- a/packages/@react-aria/table/src/useTableSelectionCheckbox.ts +++ b/packages/@react-aria/table/src/useTableSelectionCheckbox.ts @@ -24,7 +24,12 @@ interface SelectionCheckboxProps { } interface SelectionCheckboxAria { - /** Props for the checkbox element. */ + /** Props for the row selection checkbox element. */ + checkboxProps: AriaCheckboxProps +} + +interface SelectAllCheckboxAria { + /** Props for the select all checkbox element. */ checkboxProps: AriaCheckboxProps } @@ -51,7 +56,7 @@ export function useTableSelectionCheckbox(props: SelectionCheckboxProps, stat }; } -export function useTableSelectAllCheckbox(state: TableState): SelectionCheckboxAria { +export function useTableSelectAllCheckbox(state: TableState): SelectAllCheckboxAria { let {isEmpty, isSelectAll} = state.selectionManager; return { checkboxProps: { From 1a453bbbcbe35ca5041c5876c3688682f9bc6a3a Mon Sep 17 00:00:00 2001 From: Daniel Lu Date: Tue, 18 May 2021 11:19:15 -0700 Subject: [PATCH 31/62] adding links explaining rowheader column and column headers --- packages/@react-aria/table/docs/useTable.mdx | 2 +- .../@react-aria/table/src/useTableColumnHeader.ts | 12 ++++++------ packages/@react-aria/table/src/useTableRowHeader.ts | 10 +++++----- packages/@react-spectrum/table/docs/TableView.mdx | 2 +- packages/@react-types/table/src/index.d.ts | 5 ++--- 5 files changed, 15 insertions(+), 16 deletions(-) diff --git a/packages/@react-aria/table/docs/useTable.mdx b/packages/@react-aria/table/docs/useTable.mdx index 29b8602c81c..e82c4ec9519 100644 --- a/packages/@react-aria/table/docs/useTable.mdx +++ b/packages/@react-aria/table/docs/useTable.mdx @@ -62,7 +62,7 @@ A table can be built using the [<table>](https://developer.mozilla.org/en- * Support for mouse, touch, and keyboard interactions * Support for keyboard navigation between columns, rows, cells, and in-cell focusable items * Column sorting -* Multiple row header support +* Multiple [row header](https://www.w3.org/TR/wai-aria-1.1/#rowheader) support * Automatic scrolling support during keyboard navigation * Typeahead to allow focusing rows by typing text * Virtualized scrolling support for performance with large tables diff --git a/packages/@react-aria/table/src/useTableColumnHeader.ts b/packages/@react-aria/table/src/useTableColumnHeader.ts index a828ec7eb90..7b1476060a3 100644 --- a/packages/@react-aria/table/src/useTableColumnHeader.ts +++ b/packages/@react-aria/table/src/useTableColumnHeader.ts @@ -21,20 +21,20 @@ import {usePress} from '@react-aria/interactions'; interface ColumnHeaderProps { - /** An object representing the column header. Contains all the relevant information that makes up the column header. */ + /** An object representing the [column header](https://www.w3.org/TR/wai-aria-1.1/#columnheader). Contains all the relevant information that makes up the column header. */ node: Node, - /** The ref attached to the column header. */ + /** The ref attached to the [column header](https://www.w3.org/TR/wai-aria-1.1/#columnheader). */ ref: RefObject, - /** Whether the column header is contained in a virtual scroller. */ + /** Whether the [column header](https://www.w3.org/TR/wai-aria-1.1/#columnheader) is contained in a virtual scroller. */ isVirtualized?: boolean, - /** The number of columns the column header should span. */ + /** The number of columns the [column header](https://www.w3.org/TR/wai-aria-1.1/#columnheader) should span. */ colspan?: number, - /** Whether the column header is disabled. */ + /** Whether the [column header](https://www.w3.org/TR/wai-aria-1.1/#columnheader) is disabled. */ isDisabled?: boolean } interface ColumnHeaderAria { - /** Props for the column header element. */ + /** Props for the [column header](https://www.w3.org/TR/wai-aria-1.1/#columnheader) element. */ columnHeaderProps: HTMLAttributes } diff --git a/packages/@react-aria/table/src/useTableRowHeader.ts b/packages/@react-aria/table/src/useTableRowHeader.ts index 53d52adf051..f6657b3093c 100644 --- a/packages/@react-aria/table/src/useTableRowHeader.ts +++ b/packages/@react-aria/table/src/useTableRowHeader.ts @@ -17,18 +17,18 @@ import {TableState} from '@react-stately/table'; import {useGridCell} from '@react-aria/grid'; interface RowHeaderProps { - /** An object representing the row header. Contains all the relevant information that makes up the row header. */ + /** An object representing the [row header](https://www.w3.org/TR/wai-aria-1.1/#rowheader). Contains all the relevant information that makes up the row header. */ node: GridNode, - /** The ref attached to the row header. */ + /** The ref attached to the [row header](https://www.w3.org/TR/wai-aria-1.1/#rowheader). */ ref: RefObject, - /** Whether the row header is contained in a virtual scroller. */ + /** Whether the [row header](https://www.w3.org/TR/wai-aria-1.1/#rowheader) is contained in a virtual scroller. */ isVirtualized?: boolean, - /** Whether the row header is disabled. */ + /** Whether the [row header](https://www.w3.org/TR/wai-aria-1.1/#rowheader) is disabled. */ isDisabled?: boolean } interface RowHeaderAria { - /** Props for the row header element. */ + /** Props for the [row header](https://www.w3.org/TR/wai-aria-1.1/#rowheader) element. */ rowHeaderProps: HTMLAttributes } diff --git a/packages/@react-spectrum/table/docs/TableView.mdx b/packages/@react-spectrum/table/docs/TableView.mdx index f4075c5dbad..b75affd1607 100644 --- a/packages/@react-spectrum/table/docs/TableView.mdx +++ b/packages/@react-spectrum/table/docs/TableView.mdx @@ -132,7 +132,7 @@ For languages that are read right-to-left (e.g. Hebrew and Arabic), the layout o ### Accessibility An `aria-label` must be provided to the TableView for accessibility. If the TableView is labeled by a separate element, an `aria-labelledby` prop must be provided using the id of the labeling element instead. -By default, the first column of the TableView is used as the row header and is announced by assistive technology when navigating through the rows. You can override this behavior by providing the `isRowHeader` prop +By default, the first column of the TableView is used as the [row header](https://www.w3.org/TR/wai-aria-1.1/#rowheader) and is announced by assistive technology when navigating through the rows. You can override this behavior by providing the `isRowHeader` prop to one or more Columns, allowing you to customize which columns should label the rows of the TableView. The example below applies `isRowHeader` to the "First Name" and "Last Name" columns so that each row is announced with the person's full name (e.g. "John Doe"). diff --git a/packages/@react-types/table/src/index.d.ts b/packages/@react-types/table/src/index.d.ts index b9cf155289b..4e905a45171 100644 --- a/packages/@react-types/table/src/index.d.ts +++ b/packages/@react-types/table/src/index.d.ts @@ -83,7 +83,7 @@ export interface SpectrumColumnProps extends ColumnProps { allowsSorting?: boolean, // /** Whether the column should stick to the viewport when scrolling. */ // isSticky?: boolean, // shouldStick?? Not implemented yet? - /** Whether a column is a row header and should be announced by assistive technology during row navigation. */ + /** Whether a column is a [row header](https://www.w3.org/TR/wai-aria-1.1/#rowheader) and should be announced by assistive technology during row navigation. */ isRowHeader?: boolean, /** Whether the column should render a divider between it and the next column. */ showDivider?: boolean, @@ -136,8 +136,7 @@ export interface TableCollection extends GridCollection { headerRows: GridNode[], /** A list of column nodes in the table. */ columns: GridNode[], - // TODO perhaps elaborate here, link to rowheader docs? https://www.digitala11y.com/rowheader-role/? - /** A set of column keys that serve as the row header. */ + /** A set of column keys that serve as the [row header](https://www.w3.org/TR/wai-aria-1.1/#rowheader). */ rowHeaderColumnKeys: Set, /** The node that makes up the body of the table. */ body: GridNode From 4c45c71415a5609a57825340a9319918a5da1689 Mon Sep 17 00:00:00 2001 From: Daniel Lu Date: Tue, 18 May 2021 11:30:21 -0700 Subject: [PATCH 32/62] removing some todos --- packages/@react-stately/table/src/useTableState.ts | 2 -- packages/@react-types/table/src/index.d.ts | 1 - 2 files changed, 3 deletions(-) diff --git a/packages/@react-stately/table/src/useTableState.ts b/packages/@react-stately/table/src/useTableState.ts index a2d422465cc..1cceedca618 100644 --- a/packages/@react-stately/table/src/useTableState.ts +++ b/packages/@react-stately/table/src/useTableState.ts @@ -34,8 +34,6 @@ export interface CollectionBuilderContext { columns: Node[] } -// TODO: perhaps define things like `items`, `selectedKeys` etc here instead of relying on extend -// since the table type descriptions for these props should be more specific to table? export interface TableStateProps extends CollectionBase, MultipleSelection, Sortable { /** Whether the row selection checkboxes should be displayed. */ showSelectionCheckboxes?: boolean diff --git a/packages/@react-types/table/src/index.d.ts b/packages/@react-types/table/src/index.d.ts index 4e905a45171..7a5059628d0 100644 --- a/packages/@react-types/table/src/index.d.ts +++ b/packages/@react-types/table/src/index.d.ts @@ -129,7 +129,6 @@ export interface CellProps { export type CellElement = ReactElement; export type CellRenderer = (columnKey: Key) => CellElement; -// TODO: perhaps defined `rows`, etc here so that we can table specific definitions export interface TableCollection extends GridCollection { // TODO perhaps elaborate on this? maybe not clear enought, essentially returns the table header rows (e.g. in a tiered headers table, will return the nodes containing the top tier column, next tier, etc) /** A list of header row nodes in the table. */ From 4fc0dc832f9e8dcf6263312eb92994ab4d2ca0d0 Mon Sep 17 00:00:00 2001 From: Daniel Lu Date: Mon, 24 May 2021 10:38:38 -0700 Subject: [PATCH 33/62] updating docs to reflect isLoading -> loadingState change --- packages/@react-spectrum/table/docs/TableView.mdx | 6 +++--- packages/@react-types/table/src/index.d.ts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/@react-spectrum/table/docs/TableView.mdx b/packages/@react-spectrum/table/docs/TableView.mdx index f4075c5dbad..e5db2e757e5 100644 --- a/packages/@react-spectrum/table/docs/TableView.mdx +++ b/packages/@react-spectrum/table/docs/TableView.mdx @@ -168,7 +168,7 @@ The example below applies `isRowHeader` to the "First Name" and "Last Name" colu ## Asynchronous loading TableView supports loading data asynchronously, and will display a progress circle reflecting the current load state, -set by the `isLoading` prop. It also supports infinite scrolling to load more data on demand as the user scrolls, via the `onLoadMore` prop. +set by the `loadingState` prop. It also supports infinite scrolling to load more data on demand as the user scrolls, via the `onLoadMore` prop. This example uses the [useAsyncList](../react-stately/useAsyncList.html) hook to handle loading the data. See the docs for more information. @@ -212,7 +212,7 @@ function AsyncTable() { {(item) => ( {(key) => {item[key]}} @@ -388,7 +388,7 @@ function AsyncSortTable() { + loadingState={list.loadingState}> {(item) => ( {(key) => {item[key]}} )} diff --git a/packages/@react-types/table/src/index.d.ts b/packages/@react-types/table/src/index.d.ts index f652defe528..ae60790cb00 100644 --- a/packages/@react-types/table/src/index.d.ts +++ b/packages/@react-types/table/src/index.d.ts @@ -98,7 +98,7 @@ export interface TableBodyProps extends Omit { /** The contents of the table body. Supports static items or a function for dynamic rendering. */ children: CollectionChildren, /** A list of row objects in the table body used when dynamically rendering rows. */ - items?: Iterable + items?: Iterable, /** The current loading state of the TableView. */ loadingState?: LoadingState } From 9335e4b00f3d08a568fc6296fbe4bbaf2c5eb072 Mon Sep 17 00:00:00 2001 From: Daniel Lu Date: Tue, 25 May 2021 15:02:18 -0700 Subject: [PATCH 34/62] addressing review comments --- packages/@react-types/table/src/index.d.ts | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/packages/@react-types/table/src/index.d.ts b/packages/@react-types/table/src/index.d.ts index ae60790cb00..df398fabd4d 100644 --- a/packages/@react-types/table/src/index.d.ts +++ b/packages/@react-types/table/src/index.d.ts @@ -15,9 +15,8 @@ import {GridCollection, GridNode} from '@react-types/grid'; import {Key, ReactElement, ReactNode} from 'react'; export interface TableProps extends MultipleSelection, Sortable { - // TODO: I think this shouldn't have Section and row? Also need to fix prop table rendering for this /** The elements that make up the Table. Includes the TableHeader, TableBody, Columns, and Rows. */ - children: ReactElement | TableBodyProps>[], + children: [ReactElement>, ReactElement>], /** A list of row keys to disable. */ disabledKeys?: Iterable } @@ -55,9 +54,6 @@ export interface ColumnProps { children: ReactNode | ColumnElement | ColumnElement[], /** A list of child columns used when dynamically rendering nested child columns. */ childColumns?: T[], - // TODO Not sure if this is a thing, doesn't seem to get passed through? - /** An accessibility label for the column. */ - // 'aria-label'?: string, /** The width of the column. */ width?: number | string, /** The minimum width of the column. */ @@ -75,10 +71,6 @@ export interface SpectrumColumnProps extends ColumnProps { * @default 'start' */ align?: 'start' | 'center' | 'end', - // /** TODO: Don't think this is implemented yet */ - // allowsResizing?: boolean, - // /** TODO: Don't think this is implemented yet */ - // allowsReordering?: boolean, /** Whether the column allows sorting. */ allowsSorting?: boolean, // /** Whether the column should stick to the viewport when scrolling. */ @@ -113,9 +105,6 @@ export interface RowProps { children: CellElement | CellElement[] | CellRenderer, /** A string representation of the row's contents, used for features like typeahead. */ textValue?: string // ??? - // TODO Not sure if this is a thing, doesn't seem to get passed through? - /** An accessibility label for the row. */ - // 'aria-label'?: string // ??? } export interface CellProps { @@ -123,9 +112,6 @@ export interface CellProps { children: ReactNode, /** A string representation of the cell's contents, used for features like typeahead. */ textValue?: string - // TODO Not sure if this is a thing, doesn't seem to get passed through? - /** An accessibility label for the cell. */ - // 'aria-label'?: string } export type CellElement = ReactElement; From 59637b3b278aeb9b80cc4cddf909200b019ac5f7 Mon Sep 17 00:00:00 2001 From: Daniel Lu Date: Tue, 8 Jun 2021 10:36:07 -0700 Subject: [PATCH 35/62] addressing review comments --- packages/@react-aria/table/docs/useTable.mdx | 7 ++++--- packages/@react-stately/table/docs/useTableState.mdx | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/@react-aria/table/docs/useTable.mdx b/packages/@react-aria/table/docs/useTable.mdx index e82c4ec9519..ea2ce207bf5 100644 --- a/packages/@react-aria/table/docs/useTable.mdx +++ b/packages/@react-aria/table/docs/useTable.mdx @@ -24,7 +24,7 @@ import {useTable, useTableCell, useTableColumnHeader, useTableRow, useTableRowGr --- category: Collections -keywords: [table, aria] +keywords: [table, aria, grid] --- # useTable @@ -35,7 +35,7 @@ keywords: [table, aria] packageData={packageData} componentNames={['useTable', 'useTableCell', 'useTableColumnHeader', 'useTableRow', 'useTableRowGroup', 'useTableRowHeader', 'useTableSelectAllCheckbox', 'useTableSelectionCheckbox']} sourceData={[ - {type: 'W3C', url: 'https://www.w3.org/TR/wai-aria-practices/#menu'} + {type: 'W3C', url: 'https://www.w3.org/TR/wai-aria-practices/#grid} ]} /> ## API @@ -72,7 +72,7 @@ A table can be built using the [<table>](https://developer.mozilla.org/en- A table consists of a container element, with columns and rows of cells containing data inside. The cells within a table may contain focusable elements or plain text content. -If the table supports row selection, each row includes a selection checkbox. Additionally, a "select all" checkbox is displayed as the first column header if the table supports multiple row selection. +If the table supports row selection, each row includes a selection checkbox in the first column. Additionally, a "select all" checkbox is displayed as the first column header if the table supports multiple row selection. `useTable`, `useTableCell`, `useTableColumnHeader`, `useTableRow`, and `useTableRowHeader` handle keyboard, mouse, and other interactions to support row selection, in table navigation, and overall focus behavior. Those hooks, along with `useTableRowGroup`, also handle exposing the table and its contents @@ -311,6 +311,7 @@ function TableSelectAllCell({column}) { ref={ref}> diff --git a/packages/@react-stately/table/docs/useTableState.mdx b/packages/@react-stately/table/docs/useTableState.mdx index f8aa7f3627f..eb586fc2250 100644 --- a/packages/@react-stately/table/docs/useTableState.mdx +++ b/packages/@react-stately/table/docs/useTableState.mdx @@ -16,7 +16,7 @@ import packageData from '@react-stately/table/package.json'; --- category: Collections -keywords: [table, state] +keywords: [table, state, grid] --- # useTableState From 9d2758cac53f463b38d3fc88971aad7424c8d8e5 Mon Sep 17 00:00:00 2001 From: Daniel Lu Date: Tue, 8 Jun 2021 10:57:31 -0700 Subject: [PATCH 36/62] fixing docs --- packages/@react-aria/table/docs/useTable.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@react-aria/table/docs/useTable.mdx b/packages/@react-aria/table/docs/useTable.mdx index ea2ce207bf5..cfc97d7f5ab 100644 --- a/packages/@react-aria/table/docs/useTable.mdx +++ b/packages/@react-aria/table/docs/useTable.mdx @@ -35,7 +35,7 @@ keywords: [table, aria, grid] packageData={packageData} componentNames={['useTable', 'useTableCell', 'useTableColumnHeader', 'useTableRow', 'useTableRowGroup', 'useTableRowHeader', 'useTableSelectAllCheckbox', 'useTableSelectionCheckbox']} sourceData={[ - {type: 'W3C', url: 'https://www.w3.org/TR/wai-aria-practices/#grid} + {type: 'W3C', url: 'https://www.w3.org/TR/wai-aria-practices/#grid'} ]} /> ## API From 69bea2a66d0a84cccb3829cd24cbd6a0ba5915bf Mon Sep 17 00:00:00 2001 From: Daniel Lu Date: Wed, 9 Jun 2021 11:34:22 -0700 Subject: [PATCH 37/62] adding note about hideheader and its expected column content --- packages/@react-spectrum/table/docs/TableView.mdx | 3 ++- packages/@react-types/table/src/index.d.ts | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/@react-spectrum/table/docs/TableView.mdx b/packages/@react-spectrum/table/docs/TableView.mdx index e5db2e757e5..1c3a177dcff 100644 --- a/packages/@react-spectrum/table/docs/TableView.mdx +++ b/packages/@react-spectrum/table/docs/TableView.mdx @@ -665,7 +665,8 @@ import Delete from '@spectrum-icons/workflow/Delete'; ### Hide header Individual column headers can be hidden by providing the `hideHeader` prop to the Column. A tooltip is rendered when the column header is focused -to compensate for the lack of a visual title. +to compensate for the lack of a visual title. Note that the `hideHeader` prop is specifically intended for columns that contain ActionButtons instead +of text content. ```tsx example export=true function TableExample(props) { diff --git a/packages/@react-types/table/src/index.d.ts b/packages/@react-types/table/src/index.d.ts index df398fabd4d..b37319b82f3 100644 --- a/packages/@react-types/table/src/index.d.ts +++ b/packages/@react-types/table/src/index.d.ts @@ -81,7 +81,8 @@ export interface SpectrumColumnProps extends ColumnProps { showDivider?: boolean, /** * Whether the column should hide its header text. A tooltip with the column's header text - * will be displayed when the column header is focused instead. + * will be displayed when the column header is focused instead. Note that this prop is specifically for columns + * that contain ActionButtons in place of text content. */ hideHeader?: boolean } From c1eb348aaccbddcb10de1c366b774a8a7b8e4336 Mon Sep 17 00:00:00 2001 From: Daniel Lu Date: Fri, 11 Jun 2021 13:47:12 -0700 Subject: [PATCH 38/62] splitting out tableview mdx from type updates for release --- .../@react-spectrum/table/docs/TableView.mdx | 786 ------------------ 1 file changed, 786 deletions(-) delete mode 100644 packages/@react-spectrum/table/docs/TableView.mdx diff --git a/packages/@react-spectrum/table/docs/TableView.mdx b/packages/@react-spectrum/table/docs/TableView.mdx deleted file mode 100644 index 1c3a177dcff..00000000000 --- a/packages/@react-spectrum/table/docs/TableView.mdx +++ /dev/null @@ -1,786 +0,0 @@ - - -import {Layout} from '@react-spectrum/docs'; -export default Layout; - -import docs from 'docs:@react-spectrum/table'; -import tableTypes from 'docs:@react-types/table/src/index.d.ts'; -import {HeaderInfo, PropTable} from '@react-spectrum/docs'; -import packageData from '@react-spectrum/table/package.json'; - -```jsx import -import {ActionButton} from '@react-spectrum/button'; -import Add from '@spectrum-icons/workflow/Add'; -import {Cell, Column, Row, TableView, TableBody, TableHeader} from '@react-spectrum/table'; -import {Flex} from '@react-spectrum/layout'; -``` - ---- -category: Collections -keywords: [table, grid] ---- - -# TableView - -

{docs.exports.TableView.description}

- - - -## Example -```tsx example - - - - Name - Type - Date Modified - - - - Games - File folder - 6/7/2020 - - - Program Files - File folder - 4/7/2021 - - - bootmgr - System file - 11/20/2010 - - - log.txt - Text Document - 1/18/2016 - - - - -``` - -## Content -TableView expects a TableHeader and a TableBody as children. In turn, TableHeader and TableBody follow the [Collection Components](../react-stately/collections.html) API. -The TableHeader accepts either static Columns or a `columns` prop with a renderer function for dynamic rendering. Similarly, TableBody accepts either static Rows or a `items` prop with a renderer function. -Row follows the same pattern, accepting Cells as children instead. - -Basic usage of TableView, seen in the example above, shows multiple Columns and Rows populated with strings and Cells respectively. Static collections, as in this example, can be used when the contents of the TableView is known ahead of time. - -Dynamic collections, as shown below, can be used when the options come from an external data source such as an API call, or update over time. Providing the data in this way allows TableView to automatically cache the rendering of each item, -which dramatically improves performance. Make sure that each rendered property in the Row object matches with a Column's key. In the example below, the `uid` of each Column is set as its key and matches with a -property within each Row object. - -```tsx example -function Example() { - let columns = [ - {name: 'Name', uid: 'name'}, - {name: 'Type', uid: 'type'}, - {name: 'Date Modified', uid: 'date'} - ]; - - let rows = [ - {id: '1', name: 'Games', date: '6/7/2020', type: 'File folder'}, - {id: '2', name: 'Program Files', date: '4/7/2021', type: 'File folder'}, - {id: '3', name: 'bootmgr', date: '11/20/2010', type: 'System file'}, - {id: '4', name: 'log.txt', date: '1/18/2016', type: 'Text Document'} - ]; - - return ( - - - - {column => ( - - {column.name} - - )} - - - {item => ( - - {/* Note this key is equal to the key of the the column, - not the key set on the Row prior */} - {key => {item[key]}} - - )} - - - - ); -} -``` - -### Internationalization -To internationalize a TableView, all text content within the TableView should be replaced with localized strings. This includes the `aria-label` provided to the TableView if any. -For languages that are read right-to-left (e.g. Hebrew and Arabic), the layout of TableView is automatically flipped. - -## Labeling -### Accessibility -An `aria-label` must be provided to the TableView for accessibility. If the TableView is labeled by a separate element, an `aria-labelledby` prop must be provided using the id of the labeling element instead. - -By default, the first column of the TableView is used as the row header and is announced by assistive technology when navigating through the rows. You can override this behavior by providing the `isRowHeader` prop -to one or more Columns, allowing you to customize which columns should label the rows of the TableView. - -The example below applies `isRowHeader` to the "First Name" and "Last Name" columns so that each row is announced with the person's full name (e.g. "John Doe"). - -```tsx example - - - - First Name - Last Name - Age - - - - John - Doe - 45 - - - Jane - Doe - 37 - - - Joe - Schmoe - 67 - - - - -``` - -## Asynchronous loading -TableView supports loading data asynchronously, and will display a progress circle reflecting the current load state, -set by the `loadingState` prop. It also supports infinite scrolling to load more data on demand as the user scrolls, via the `onLoadMore` prop. - -This example uses the [useAsyncList](../react-stately/useAsyncList.html) hook to handle loading the data. -See the docs for more information. - -```tsx example -import {useAsyncList} from '@react-stately/data'; - -function AsyncTable() { - let columns = [ - {name: 'Name', key: 'name'}, - {name: 'Height', key: 'height'}, - {name: 'Mass', key: 'mass'}, - {name: 'Birth Year', key: 'birth_year'} - ]; - - let list = useAsyncList({ - async load({signal, cursor}) { - if (cursor) { - cursor = cursor.replace(/^http:\/\//i, 'https://'); - } - - let res = await fetch(cursor || `https://swapi.dev/api/people/?search=`, {signal}); - let json = await res.json(); - - return { - items: json.results, - cursor: json.next - }; - } - }); - - return ( - - - - {(column) => ( - - {column.name} - - )} - - - {(item) => ( - {(key) => {item[key]}} - )} - - - - ); -} -``` - -## Events - -### Selection -TableView supports multiple selection modes. By default, selection is disabled, but this can be modified using the `selectionMode` prop. -Use `defaultSelectedKeys` to provide a default set of selected rows. Note that the value of the selected keys must match the `key` prop of the Row. - -The example below uses `defaultSelectedKeys` to select the row with key equal to "2". - -```tsx example - - - - Name - Type - Level - - - - Charizard - Fire, Flying - 67 - - - Blastoise - Water - 56 - - - Venusaur - Grass, Poison - 83 - - - Pikachu - Electric - 100 - - - - -``` - -To programmatically control row selection, use the `selectedKeys` prop paired with the `onSelectionChange` callback. The `key` prop from the selected row will -be passed into the callback when the row is pressed, allowing you to update `selectedKeys` accordingly. - -Here is how you would control selection for the above example. - -```tsx example export=true -function PokemonTable(props) { - let columns = [ - {name: 'Name', uid: 'name'}, - {name: 'Type', uid: 'type'}, - {name: 'Level', uid: 'level'} - ]; - - let rows = [ - {id: '1', name: 'Charizard', type: 'Fire, Flying', level: '67'}, - {id: '2', name: 'Blastoise', type: 'Water', level: '56'}, - {id: '3', name: 'Venusaur', type: 'Grass, Poison', level: '83'}, - {id: '4', name: 'Pikachu', type: 'Electric', level: '100'} - ]; - - let [selected, setSelected] = React.useState(new Set(['2'])); - - return ( - - - - {column => ( - - {column.name} - - )} - - - {item => ( - - {key => {item[key]}} - - )} - - - - ); -} -``` - -Multiple selection can be enabled by setting `selectionMode` to `multiple`. - -```tsx example -// Using the same table as above - -``` - -TableView also supports a `disallowEmptySelection` prop which forces the user to have at least one row in the TableView selected at all times. - -```tsx example -// Using the same table as above - -``` - -### Sorting -TableView supports sorting its data when a column header is pressed. To designate that a Column should support sorting, provide it with -the `allowsSorting` prop. The TableView accepts a `SortDescriptor` prop that defines the current column key to sort by and the sort direction (ascending/descending). -When the user presses a column's sort icon, the column's key and sort direction is passed into the `onSortChange` callback, allowing you to update -the `SortDescriptor` appropriately. - -This example performs client side sorting by passing a `sort` function to the [useAsyncList](../react-stately/useAsyncList.html) hook. -See the docs for more information on how to perform server side sorting. - -```tsx example -function AsyncSortTable() { - let columns = [ - {name: 'Name', key: 'name'}, - {name: 'Height', key: 'height'}, - {name: 'Mass', key: 'mass'}, - {name: 'Birth Year', key: 'birth_year'} - ]; - - let list = useAsyncList({ - async load({signal}) { - let res = await fetch(`https://swapi.dev/api/people/?search`, {signal}); - let json = await res.json(); - - return { - items: json.results - }; - }, - async sort({items, sortDescriptor}) { - let sorted = items.sort((a, b) => { - let cmp; - let first = a[sortDescriptor.column].replace("BBY", ""); - let second = b[sortDescriptor.column].replace("BBY", ""); - - if (+first || +second) { - cmp = +first < +second ? -1 : 1; - } else { - cmp = first <= second ? -1 : 1; - } - - if (sortDescriptor.direction === 'descending') { - cmp *= -1; - } - return cmp; - }); - - return { - items: sorted - }; - } - }); - - return ( - - - - {(column) => ( - - {column.name} - - )} - - - {(item) => ( - {(key) => {item[key]}} - )} - - - - ); -} -``` - -## Props - -### TableView props - - - -### TableHeader props - - - -### Column props - - - -### TableBody props - - - -### Row props - - - -### Cell props - - - -## Visual options -### Column alignment -[View guidelines](https://spectrum.adobe.com/page/table/#Usage-guidelines) - -```tsx example - - - - Name - Type - Size - - - - 2021406_Proposal - PDF - 86 KB - - - Budget Template - XLS - 120 KB - - - Onboarding - PPT - 472 KB - - - Welcome - TXT - 24 KB - - - - -``` - -### Column widths -Columns support three different width props: `minWidth`, `width`, and `maxWidth`. - -```tsx example - - - - Name - Type - Size - - - - 2021406_Proposal - PDF - 86 KB - - - Budget Template - XLS - 120 KB - - - Onboarding - PPT - 472 KB - - - Welcome - TXT - 24 KB - - - - -``` - -### Column dividers -[View guidelines](https://spectrum.adobe.com/page/table/#Column-dividers) - -```tsx example - - - - Name - Type - Size - - - - 2021406_Proposal - PDF - 86 KB - - - Budget Template - XLS - 120 KB - - - Onboarding - PPT - 472 KB - - - Welcome - TXT - 24 KB - - - - -``` - -### Nested columns -TableView supports nesting columns, allowing you to render "tiered" column headers. - -```tsx example - - - - - Name - Type - Size - - - - - 2021406_Proposal - PDF - 86 KB - - - Budget Template - XLS - 120 KB - - - Onboarding - PPT - 472 KB - - - - -``` - -### Focusable cells -Cells accept any renderable node, allowing you to have focusable children within the TableView. - -```tsx example -import Edit from '@spectrum-icons/workflow/Edit'; -import Delete from '@spectrum-icons/workflow/Delete'; - - - - - Name - Add - Delete - - - - Red Panda - - - - - - - - - - - - - Harbor Seal - - - - - - - - - - - - - Groundhog - - - - - - - - - - - - - Otter - - - - - - - - - - - - - - -``` - -### Hide header -Individual column headers can be hidden by providing the `hideHeader` prop to the Column. A tooltip is rendered when the column header is focused -to compensate for the lack of a visual title. Note that the `hideHeader` prop is specifically intended for columns that contain ActionButtons instead -of text content. - -```tsx example export=true -function TableExample(props) { - let columns = [ - {name: 'First Name', key: 'firstName'}, - {name: 'Last Name', key: 'lastName'}, - {name: 'Add Info', key: 'addInfo'}, - {name: 'Age', key: 'age'} - ]; - - let rows = [ - {id: '1', firstName: 'John', lastName: 'Doe', age: '45'}, - {id: '2', firstName: 'Jane', lastName: 'Doe', age: '37'}, - {id: '3', firstName: 'Joe', lastName: 'Schmoe', age: '67'}, - {id: '4', firstName: 'Joe', lastName: 'Bloggs', age: '12'}, - {id: '5', firstName: 'Longggggggggggg Wrapping', lastName: 'Name', age: '56'} - ]; - - return ( - - - - {column => ( - - {column.name} - - )} - - - {item => - ( - {key => - key === 'addInfo' - ? - : {item[key]} - } - ) - } - - - - ); -} -``` - -### Quiet -[View guidelines](https://spectrum.adobe.com/page/table/#Standard-or-quiet) - -```tsx example -// Using same setup as hide header example - -``` - -### Disabled -Use the `disabledKeys` prop to specify which rows to disable selection for in the TableView. - -```tsx example -// Using same setup as hide header example - -``` - -### Density -The amount of vertical padding that each row contains can be modified by providing the `density` prop. - -```tsx example -// Using same setup as hide header example - - - - -``` - -### Overflow mode -By default, text content that overflows its table cell will be truncated. You can have it wrap instead by passing `overflowMode="wrap"` -to the TableView. - -```tsx example -// Using same setup as hide header example - -``` - -### Empty state -Use the `renderEmptyState` prop to customize what the TableView will display if there are no rows provided. - -```tsx example -import {Content} from '@react-spectrum/view'; -import {IllustratedMessage} from '@react-spectrum/illustratedmessage'; -import {Heading} from '@react-spectrum/text'; - -function renderEmptyState() { - return ( - - - - - No results - No results found - - ); -} - - - - - Name - Type - Size - - - {[]} - - - -``` From c2c40fe4413608eb7a4585b0dbacdcf952018ed8 Mon Sep 17 00:00:00 2001 From: Daniel Lu Date: Fri, 11 Jun 2021 13:48:39 -0700 Subject: [PATCH 39/62] adding tableview docs --- .../@react-types/table/docs/TableView.mdx | 786 ++++++++++++++++++ 1 file changed, 786 insertions(+) create mode 100644 packages/@react-types/table/docs/TableView.mdx diff --git a/packages/@react-types/table/docs/TableView.mdx b/packages/@react-types/table/docs/TableView.mdx new file mode 100644 index 00000000000..1c3a177dcff --- /dev/null +++ b/packages/@react-types/table/docs/TableView.mdx @@ -0,0 +1,786 @@ + + +import {Layout} from '@react-spectrum/docs'; +export default Layout; + +import docs from 'docs:@react-spectrum/table'; +import tableTypes from 'docs:@react-types/table/src/index.d.ts'; +import {HeaderInfo, PropTable} from '@react-spectrum/docs'; +import packageData from '@react-spectrum/table/package.json'; + +```jsx import +import {ActionButton} from '@react-spectrum/button'; +import Add from '@spectrum-icons/workflow/Add'; +import {Cell, Column, Row, TableView, TableBody, TableHeader} from '@react-spectrum/table'; +import {Flex} from '@react-spectrum/layout'; +``` + +--- +category: Collections +keywords: [table, grid] +--- + +# TableView + +

{docs.exports.TableView.description}

+ + + +## Example +```tsx example + + + + Name + Type + Date Modified + + + + Games + File folder + 6/7/2020 + + + Program Files + File folder + 4/7/2021 + + + bootmgr + System file + 11/20/2010 + + + log.txt + Text Document + 1/18/2016 + + + + +``` + +## Content +TableView expects a TableHeader and a TableBody as children. In turn, TableHeader and TableBody follow the [Collection Components](../react-stately/collections.html) API. +The TableHeader accepts either static Columns or a `columns` prop with a renderer function for dynamic rendering. Similarly, TableBody accepts either static Rows or a `items` prop with a renderer function. +Row follows the same pattern, accepting Cells as children instead. + +Basic usage of TableView, seen in the example above, shows multiple Columns and Rows populated with strings and Cells respectively. Static collections, as in this example, can be used when the contents of the TableView is known ahead of time. + +Dynamic collections, as shown below, can be used when the options come from an external data source such as an API call, or update over time. Providing the data in this way allows TableView to automatically cache the rendering of each item, +which dramatically improves performance. Make sure that each rendered property in the Row object matches with a Column's key. In the example below, the `uid` of each Column is set as its key and matches with a +property within each Row object. + +```tsx example +function Example() { + let columns = [ + {name: 'Name', uid: 'name'}, + {name: 'Type', uid: 'type'}, + {name: 'Date Modified', uid: 'date'} + ]; + + let rows = [ + {id: '1', name: 'Games', date: '6/7/2020', type: 'File folder'}, + {id: '2', name: 'Program Files', date: '4/7/2021', type: 'File folder'}, + {id: '3', name: 'bootmgr', date: '11/20/2010', type: 'System file'}, + {id: '4', name: 'log.txt', date: '1/18/2016', type: 'Text Document'} + ]; + + return ( + + + + {column => ( + + {column.name} + + )} + + + {item => ( + + {/* Note this key is equal to the key of the the column, + not the key set on the Row prior */} + {key => {item[key]}} + + )} + + + + ); +} +``` + +### Internationalization +To internationalize a TableView, all text content within the TableView should be replaced with localized strings. This includes the `aria-label` provided to the TableView if any. +For languages that are read right-to-left (e.g. Hebrew and Arabic), the layout of TableView is automatically flipped. + +## Labeling +### Accessibility +An `aria-label` must be provided to the TableView for accessibility. If the TableView is labeled by a separate element, an `aria-labelledby` prop must be provided using the id of the labeling element instead. + +By default, the first column of the TableView is used as the row header and is announced by assistive technology when navigating through the rows. You can override this behavior by providing the `isRowHeader` prop +to one or more Columns, allowing you to customize which columns should label the rows of the TableView. + +The example below applies `isRowHeader` to the "First Name" and "Last Name" columns so that each row is announced with the person's full name (e.g. "John Doe"). + +```tsx example + + + + First Name + Last Name + Age + + + + John + Doe + 45 + + + Jane + Doe + 37 + + + Joe + Schmoe + 67 + + + + +``` + +## Asynchronous loading +TableView supports loading data asynchronously, and will display a progress circle reflecting the current load state, +set by the `loadingState` prop. It also supports infinite scrolling to load more data on demand as the user scrolls, via the `onLoadMore` prop. + +This example uses the [useAsyncList](../react-stately/useAsyncList.html) hook to handle loading the data. +See the docs for more information. + +```tsx example +import {useAsyncList} from '@react-stately/data'; + +function AsyncTable() { + let columns = [ + {name: 'Name', key: 'name'}, + {name: 'Height', key: 'height'}, + {name: 'Mass', key: 'mass'}, + {name: 'Birth Year', key: 'birth_year'} + ]; + + let list = useAsyncList({ + async load({signal, cursor}) { + if (cursor) { + cursor = cursor.replace(/^http:\/\//i, 'https://'); + } + + let res = await fetch(cursor || `https://swapi.dev/api/people/?search=`, {signal}); + let json = await res.json(); + + return { + items: json.results, + cursor: json.next + }; + } + }); + + return ( + + + + {(column) => ( + + {column.name} + + )} + + + {(item) => ( + {(key) => {item[key]}} + )} + + + + ); +} +``` + +## Events + +### Selection +TableView supports multiple selection modes. By default, selection is disabled, but this can be modified using the `selectionMode` prop. +Use `defaultSelectedKeys` to provide a default set of selected rows. Note that the value of the selected keys must match the `key` prop of the Row. + +The example below uses `defaultSelectedKeys` to select the row with key equal to "2". + +```tsx example + + + + Name + Type + Level + + + + Charizard + Fire, Flying + 67 + + + Blastoise + Water + 56 + + + Venusaur + Grass, Poison + 83 + + + Pikachu + Electric + 100 + + + + +``` + +To programmatically control row selection, use the `selectedKeys` prop paired with the `onSelectionChange` callback. The `key` prop from the selected row will +be passed into the callback when the row is pressed, allowing you to update `selectedKeys` accordingly. + +Here is how you would control selection for the above example. + +```tsx example export=true +function PokemonTable(props) { + let columns = [ + {name: 'Name', uid: 'name'}, + {name: 'Type', uid: 'type'}, + {name: 'Level', uid: 'level'} + ]; + + let rows = [ + {id: '1', name: 'Charizard', type: 'Fire, Flying', level: '67'}, + {id: '2', name: 'Blastoise', type: 'Water', level: '56'}, + {id: '3', name: 'Venusaur', type: 'Grass, Poison', level: '83'}, + {id: '4', name: 'Pikachu', type: 'Electric', level: '100'} + ]; + + let [selected, setSelected] = React.useState(new Set(['2'])); + + return ( + + + + {column => ( + + {column.name} + + )} + + + {item => ( + + {key => {item[key]}} + + )} + + + + ); +} +``` + +Multiple selection can be enabled by setting `selectionMode` to `multiple`. + +```tsx example +// Using the same table as above + +``` + +TableView also supports a `disallowEmptySelection` prop which forces the user to have at least one row in the TableView selected at all times. + +```tsx example +// Using the same table as above + +``` + +### Sorting +TableView supports sorting its data when a column header is pressed. To designate that a Column should support sorting, provide it with +the `allowsSorting` prop. The TableView accepts a `SortDescriptor` prop that defines the current column key to sort by and the sort direction (ascending/descending). +When the user presses a column's sort icon, the column's key and sort direction is passed into the `onSortChange` callback, allowing you to update +the `SortDescriptor` appropriately. + +This example performs client side sorting by passing a `sort` function to the [useAsyncList](../react-stately/useAsyncList.html) hook. +See the docs for more information on how to perform server side sorting. + +```tsx example +function AsyncSortTable() { + let columns = [ + {name: 'Name', key: 'name'}, + {name: 'Height', key: 'height'}, + {name: 'Mass', key: 'mass'}, + {name: 'Birth Year', key: 'birth_year'} + ]; + + let list = useAsyncList({ + async load({signal}) { + let res = await fetch(`https://swapi.dev/api/people/?search`, {signal}); + let json = await res.json(); + + return { + items: json.results + }; + }, + async sort({items, sortDescriptor}) { + let sorted = items.sort((a, b) => { + let cmp; + let first = a[sortDescriptor.column].replace("BBY", ""); + let second = b[sortDescriptor.column].replace("BBY", ""); + + if (+first || +second) { + cmp = +first < +second ? -1 : 1; + } else { + cmp = first <= second ? -1 : 1; + } + + if (sortDescriptor.direction === 'descending') { + cmp *= -1; + } + return cmp; + }); + + return { + items: sorted + }; + } + }); + + return ( + + + + {(column) => ( + + {column.name} + + )} + + + {(item) => ( + {(key) => {item[key]}} + )} + + + + ); +} +``` + +## Props + +### TableView props + + + +### TableHeader props + + + +### Column props + + + +### TableBody props + + + +### Row props + + + +### Cell props + + + +## Visual options +### Column alignment +[View guidelines](https://spectrum.adobe.com/page/table/#Usage-guidelines) + +```tsx example + + + + Name + Type + Size + + + + 2021406_Proposal + PDF + 86 KB + + + Budget Template + XLS + 120 KB + + + Onboarding + PPT + 472 KB + + + Welcome + TXT + 24 KB + + + + +``` + +### Column widths +Columns support three different width props: `minWidth`, `width`, and `maxWidth`. + +```tsx example + + + + Name + Type + Size + + + + 2021406_Proposal + PDF + 86 KB + + + Budget Template + XLS + 120 KB + + + Onboarding + PPT + 472 KB + + + Welcome + TXT + 24 KB + + + + +``` + +### Column dividers +[View guidelines](https://spectrum.adobe.com/page/table/#Column-dividers) + +```tsx example + + + + Name + Type + Size + + + + 2021406_Proposal + PDF + 86 KB + + + Budget Template + XLS + 120 KB + + + Onboarding + PPT + 472 KB + + + Welcome + TXT + 24 KB + + + + +``` + +### Nested columns +TableView supports nesting columns, allowing you to render "tiered" column headers. + +```tsx example + + + + + Name + Type + Size + + + + + 2021406_Proposal + PDF + 86 KB + + + Budget Template + XLS + 120 KB + + + Onboarding + PPT + 472 KB + + + + +``` + +### Focusable cells +Cells accept any renderable node, allowing you to have focusable children within the TableView. + +```tsx example +import Edit from '@spectrum-icons/workflow/Edit'; +import Delete from '@spectrum-icons/workflow/Delete'; + + + + + Name + Add + Delete + + + + Red Panda + + + + + + + + + + + + + Harbor Seal + + + + + + + + + + + + + Groundhog + + + + + + + + + + + + + Otter + + + + + + + + + + + + + + +``` + +### Hide header +Individual column headers can be hidden by providing the `hideHeader` prop to the Column. A tooltip is rendered when the column header is focused +to compensate for the lack of a visual title. Note that the `hideHeader` prop is specifically intended for columns that contain ActionButtons instead +of text content. + +```tsx example export=true +function TableExample(props) { + let columns = [ + {name: 'First Name', key: 'firstName'}, + {name: 'Last Name', key: 'lastName'}, + {name: 'Add Info', key: 'addInfo'}, + {name: 'Age', key: 'age'} + ]; + + let rows = [ + {id: '1', firstName: 'John', lastName: 'Doe', age: '45'}, + {id: '2', firstName: 'Jane', lastName: 'Doe', age: '37'}, + {id: '3', firstName: 'Joe', lastName: 'Schmoe', age: '67'}, + {id: '4', firstName: 'Joe', lastName: 'Bloggs', age: '12'}, + {id: '5', firstName: 'Longggggggggggg Wrapping', lastName: 'Name', age: '56'} + ]; + + return ( + + + + {column => ( + + {column.name} + + )} + + + {item => + ( + {key => + key === 'addInfo' + ? + : {item[key]} + } + ) + } + + + + ); +} +``` + +### Quiet +[View guidelines](https://spectrum.adobe.com/page/table/#Standard-or-quiet) + +```tsx example +// Using same setup as hide header example + +``` + +### Disabled +Use the `disabledKeys` prop to specify which rows to disable selection for in the TableView. + +```tsx example +// Using same setup as hide header example + +``` + +### Density +The amount of vertical padding that each row contains can be modified by providing the `density` prop. + +```tsx example +// Using same setup as hide header example + + + + +``` + +### Overflow mode +By default, text content that overflows its table cell will be truncated. You can have it wrap instead by passing `overflowMode="wrap"` +to the TableView. + +```tsx example +// Using same setup as hide header example + +``` + +### Empty state +Use the `renderEmptyState` prop to customize what the TableView will display if there are no rows provided. + +```tsx example +import {Content} from '@react-spectrum/view'; +import {IllustratedMessage} from '@react-spectrum/illustratedmessage'; +import {Heading} from '@react-spectrum/text'; + +function renderEmptyState() { + return ( + + + + + No results + No results found + + ); +} + + + + + Name + Type + Size + + + {[]} + + + +``` From 8dd7345c72ed76d89b84929cb41a48833b1b69ca Mon Sep 17 00:00:00 2001 From: Daniel Lu Date: Fri, 11 Jun 2021 14:05:20 -0700 Subject: [PATCH 40/62] splitting out the mdx files --- .../@react-aria/table/docs/TableAnatomy.svg | 91 --- packages/@react-aria/table/docs/useTable.mdx | 724 ------------------ .../table/docs/useTableState.mdx | 46 -- 3 files changed, 861 deletions(-) delete mode 100644 packages/@react-aria/table/docs/TableAnatomy.svg delete mode 100644 packages/@react-aria/table/docs/useTable.mdx delete mode 100644 packages/@react-stately/table/docs/useTableState.mdx diff --git a/packages/@react-aria/table/docs/TableAnatomy.svg b/packages/@react-aria/table/docs/TableAnatomy.svg deleted file mode 100644 index 9f0779b9577..00000000000 --- a/packages/@react-aria/table/docs/TableAnatomy.svg +++ /dev/null @@ -1,91 +0,0 @@ - - Table anatomy diagram - Shows a table component with labels pointing to its parts, including the row, row divider, column, column header, column divider, sort icon, and cell elements. - - - - - - - - - Columnheader - - - SENT - - - - - 6,554 - 2,875 - 3,679 - 1,308 - Tree item - Tree item - Tree item - Tree item - ITEM NAME - - Column - - - Cell - - - - Sort icon - - - - - - - - - - Rowdivider - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Checkboxselection - - - Columndivider - - - diff --git a/packages/@react-aria/table/docs/useTable.mdx b/packages/@react-aria/table/docs/useTable.mdx deleted file mode 100644 index cfc97d7f5ab..00000000000 --- a/packages/@react-aria/table/docs/useTable.mdx +++ /dev/null @@ -1,724 +0,0 @@ - - -import {Layout} from '@react-spectrum/docs'; -export default Layout; - -import docs from 'docs:@react-aria/table'; -import collectionsDocs from 'docs:@react-types/shared/src/collections.d.ts'; -import selectionDocs from 'docs:@react-stately/selection'; -import statelyDocs from 'docs:@react-stately/table'; -import {HeaderInfo, FunctionAPI, TypeContext, InterfaceType, TypeLink} from '@react-spectrum/docs'; -import packageData from '@react-aria/table/package.json'; -import Anatomy from './TableAnatomy.svg'; - -```jsx import -import {useTable, useTableCell, useTableColumnHeader, useTableRow, useTableRowGroup, useTableRowHeader, useTableSelectAllCheckbox, useTableSelectionCheckbox} from '@react-aria/table'; -``` - ---- -category: Collections -keywords: [table, aria, grid] ---- - -# useTable - -

{docs.exports.useTable.description}

- - - -## API - - - - - - - - - - -## Features - -A table can be built using the [<table>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/table), [<thead>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/thead), -[<tbody>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/tbody), and other table specific HTML elements, but is very limited in functionality especially when it comes to user interactions. -`useTable` helps achieve accessible and interactive table components that can be styled as needed. - -* Exposed to assistive technology as a `grid` using ARIA -* Support for single, multiple, or no row selection -* Support for disabled rows -* Labeling support for accessibility -* Support for mouse, touch, and keyboard interactions -* Support for keyboard navigation between columns, rows, cells, and in-cell focusable items -* Column sorting -* Multiple [row header](https://www.w3.org/TR/wai-aria-1.1/#rowheader) support -* Automatic scrolling support during keyboard navigation -* Typeahead to allow focusing rows by typing text -* Virtualized scrolling support for performance with large tables - -## Anatomy - - - -A table consists of a container element, with columns and rows of cells containing data inside. The cells within a table may contain focusable elements or plain text content. -If the table supports row selection, each row includes a selection checkbox in the first column. Additionally, a "select all" checkbox is displayed as the first column header if the table supports multiple row selection. - -`useTable`, `useTableCell`, `useTableColumnHeader`, `useTableRow`, and `useTableRowHeader` handle keyboard, mouse, and other interactions to support -row selection, in table navigation, and overall focus behavior. Those hooks, along with `useTableRowGroup`, also handle exposing the table and its contents -to assistive technology using ARIA. `useTableSelectAllCheckbox` and `useTableSelectionCheckbox` handle row selection and associating each checkbox with its respective rows -for assistive technology. - -`useTable` returns props that you should spread onto the table container element: - - - - - -`useTableColumnHeader` returns props for an individual table column header: - - - - - -`useTableRow` returns props for an individual table row: - - - - - -`useTableRowGroup` returns props for a element containing one or more rows of column headers or cells. This is often equivalent -to the header, body, and footer of the table: - - - - - -`useTableRowHeader` returns props for the table cell that serves as a row's header: - - - - - -`useTableCell` returns props for an individual table cell: - - - - - -`useTableSelectionCheckbox` returns props for an individual table row selection checkbox: - - - - - -`useTableSelectAllCheckbox` returns props for the table's "select all" checkbox: - - - - - -State is managed by the -hook from `@react-stately/table`. The state object should be passed as an option to each of the above hooks where applicable. - -Note that an `aria-label` or `aria-labelledby` must be passed to the table to identify the element to assistive technology. - -## State management - -`useTable` requires knowledge of the rows, cells, and columns in the table in order to handle keyboard -navigation and other interactions. It does this using -the -interface, which is a generic interface to access sequential unique keyed data. You can -implement this interface yourself, e.g. by using a prop to pass a list of item objects, -but from -`@react-stately/table` implements a JSX based interface for building collections instead. -See [Collection Components](/react-stately/collections.html) for more information, -and [Collection Interface](/react-stately/Collection.html) for internal details. - -In addition, -manages the state necessary for multiple selection and exposes -a , -which makes use of the collection to provide an interface to update the selection state. -For more information, see [Selection](/react-stately/selection.html). - -## Example - -This example uses HTML `
` elements to construct the table. It uses `useTableState` to construct the table's collection of rows and columns, -applying the appropriate attributes to each `
` using the various table hooks listed above paired with the div's role in the table. -The table follows the [Collection Components API](../react-stately/collections.html), accepting both static and dynamic collections. - -The example below shows a static collection, which can be used when the full set of table contents is known ahead of time. Be sure to note the usage of -`Cell`, `Column`, `Row`, `TableBody`, and `TableHeader` to construct the table structure. These elements are similar to `Item` and `Section` used in other components -following the [Collection Components API](../react-stately/collections.html) mentioned previously, serving as node generators for the table collection. -See [useTableState](/react-stately/useTableState.html) for more info on the props they support. - -```tsx example export=true -import {Cell, Column, Row, TableBody, TableHeader, useTableState} from '@react-stately/table'; -import {mergeProps} from '@react-aria/utils'; -import {useCheckbox} from '@react-aria/checkbox'; -import {useContext, useRef} from 'react'; -import {useFocusRing} from '@react-aria/focus'; -import {useToggleState} from '@react-stately/toggle'; - -const TableContext = React.createContext(null); -function useTableContext() { - return useContext(TableContext); -} - -function Table(props) { - let state = useTableState({...props, showSelectionCheckboxes: true}); - let ref = useRef(); - let scrollViewRef = useRef(); - let {collection} = state; - let {gridProps} = useTable({ - ...props, - ref: ref - }, state); - - let headerRows = collection.rows.filter(item => item.type === 'headerrow' && item); - let rows = collection.rows.filter(item => item.type === 'item' && item); - - let renderColumnNode = (node) => { - if (node.props.isSelectionCell) { - return ; - } - - return ; - }; - - let renderRowNode = (node) => { - if (node.props.isSelectionCell) { - return ; - } - - if (collection.rowHeaderColumnKeys.has(node.column.key)) { - return ; - } - - return ; - }; - - return ( - -
- - {[...headerRows].map(headerRow => ( - - {[...headerRow.childNodes].map((column) => renderColumnNode(column))} - - ))} - -
- - {[...rows].map(row => ( - - {[...row.childNodes].map((node) => renderRowNode(node))} - - ))} - -
-
-
- ); -} - - -function TableHeaderGroup({children, ...otherProps}) { - let {rowGroupProps} = useTableRowGroup(); - - return ( -
- {children} -
- ); -} - -function TableColumnHeader({column}) { - let ref = useRef(); - let state = useTableContext(); - let {columnHeaderProps} = useTableColumnHeader({ - node: column, - ref, - colspan: column.colspan - }, state); - - let {isFocusVisible, focusProps} = useFocusRing(); - let columnProps = column.props; - let arrowIcon = state.sortDescriptor?.direction === 'ascending' ? '▲' : '▼'; - - return ( -
- {column.rendered} - {columnProps.allowsSorting && state.sortDescriptor?.column === column.key && - - } -
- ); -} - -function TableSelectAllCell({column}) { - let ref = useRef(); - let state = useTableContext(); - - let isSingleSelectionMode = state.selectionManager.selectionMode === 'single'; - let {columnHeaderProps} = useTableColumnHeader({ - node: column, - ref, - colspan: column.colspan, - isDisabled: isSingleSelectionMode - }, state); - - let {checkboxProps} = useTableSelectAllCheckbox(state); - let inputRef = useRef(null); - let {inputProps} = useCheckbox(checkboxProps, useToggleState(checkboxProps), inputRef); - - return ( -
- -
- ); -} - -function TableRowGroup({children, ...otherProps}) { - let {rowGroupProps} = useTableRowGroup(); - - return ( -
- {children} -
- ); -} - -function TableRow({item, children, ...otherProps}) { - let ref = useRef(); - let state = useTableContext(); - let isDisabled = state.disabledKeys.has(item.key); - let isSelected = state.selectionManager.isSelected(item.key) && !isDisabled; - let {rowProps} = useTableRow({ - node: item, - isSelected, - ref, - isDisabled - }, state); - - let props = mergeProps( - rowProps, - otherProps - ); - - return ( -
- {children} -
- ); -} - -function TableHeaderRow({item, children, ...otherProps}) { - return ( -
- {children} -
- ); -} - -function TableCheckboxCell({cell}) { - let ref = useRef(); - let state = useTableContext(); - let isDisabled = state.disabledKeys.has(cell.parentKey); - let {gridCellProps} = useTableCell({ - node: cell, - ref, - isDisabled - }, state); - - let {checkboxProps} = useTableSelectionCheckbox( - { - key: cell.parentKey, - isDisabled - }, - state - ); - - let inputRef = useRef(null); - let {inputProps} = useCheckbox({...checkboxProps, isDisabled}, useToggleState(checkboxProps), inputRef); - - return ( -
- {state.selectionManager.selectionMode !== 'none' && - - } -
- ); -} - -function TableCell({cell}) { - let ref = useRef(); - let state = useTableContext(); - let isDisabled = state.disabledKeys.has(cell.parentKey); - let {gridCellProps} = useTableCell({ - node: cell, - ref, - isDisabled - }, state); - - return ( - - ); -} - -function TableRowHeader({cell}) { - let ref = useRef(); - let state = useTableContext(); - let isDisabled = state.disabledKeys.has(cell.parentKey); - let {rowHeaderProps} = useTableRowHeader({ - node: cell, - ref, - isDisabled - }, state); - - return ( - - ); -} - -function TableCellBase({cell, cellRef, ...otherProps}) { - let {isFocusVisible, focusProps} = useFocusRing(); - - return ( -
- - {cell.rendered} - -
- ); -} - -
- - Name - Type - Date Modified - - - - Games - File folder - 6/7/2020 - - - Program Files - File folder - 4/7/2021 - - - bootmgr - System file - 11/20/2010 - - - log.txt - Text Document - 1/18/2016 - - -
-``` - -## Usage - -### Dynamic collections - -Dynamic collections, as shown below, can be used when the table data comes from an external data source such as an API call, or update over time. -In the example below, both the columns and the rows are provided to the table via a render function. - -```tsx example -function Example() { - let columns = [ - {name: 'Name', key: 'name'}, - {name: 'Type', key: 'type'}, - {name: 'Date Modified', key: 'date'} - ]; - - let rows = [ - {id: '1', name: 'Games', date: '6/7/2020', type: 'File folder'}, - {id: '2', name: 'Program Files', date: '4/7/2021', type: 'File folder'}, - {id: '3', name: 'bootmgr', date: '11/20/2010', type: 'System file'}, - {id: '4', name: 'log.txt', date: '1/18/2016', type: 'Text Document'} - ]; - - return ( - - - {(column) => ( - - {column.name} - - )} - - - {(item) => ( - - {/* Note this key is equal to the key of the the column, - not the key set on the Row prior */} - {(key) => {item[key]}} - - )} - -
- ); -} -``` - -### Selection - -By default, `useTableState` doesn't allow row selection but this can be modified using the `selectionMode` prop. Use `defaultSelectedKeys` to provide a default set of selected rows. -Note that the value of the selected keys must match the `key` prop of the row. - -The example below uses `defaultSelectedKeys` to select the row with key equal to "2". - -```tsx example - - - Name - Type - Level - - - - Charizard - Fire, Flying - 67 - - - Blastoise - Water - 56 - - - Venusaur - Grass, Poison - 83 - - - Pikachu - Electric - 100 - - -
-``` - -To programmatically control row selection, use the `selectedKeys` prop paired with the `onSelectionChange` callback. The `key` prop from the selected row will -be passed into the callback when the row is pressed, allowing you to update `selectedKeys` accordingly. - -Here is how you would control selection for the above example. - -```tsx example export=true -function PokemonTable(props) { - let columns = [ - {name: 'Name', uid: 'name'}, - {name: 'Type', uid: 'type'}, - {name: 'Level', uid: 'level'} - ]; - let rows = [ - {id: '1', name: 'Charizard', type: 'Fire, Flying', level: '67'}, - {id: '2', name: 'Blastoise', type: 'Water', level: '56'}, - {id: '3', name: 'Venusaur', type: 'Grass, Poison', level: '83'}, - {id: '4', name: 'Pikachu', type: 'Electric', level: '100'} - ]; - let [selected, setSelected] = React.useState(new Set(['2'])); - return ( - - - {column => ( - - {column.name} - - )} - - - {item => ( - - {key => {item[key]}} - - )} - -
- ); -} -``` - -Multiple selection can be enabled by setting `selectionMode` to `multiple`. - -```tsx example -// Using the same table as above - -``` - -Table also supports a `disallowEmptySelection` prop which forces the user to have at least one row in the Table selected at all times. - -```tsx example -// Using the same table as above - -``` - -### Disabled rows - -You can disable specific rows by providing an array of keys to `useTableState` via the `disabledKeys` prop. This will prevent rows from being selectable as shown in the example below. -Note that you are responsible for the styling of disabled rows. - -```tsx example -// Using the same table as above - -``` - -### Sorting - -Table supports sorting its data when a column header is pressed. To designate that a Column should support sorting, provide it with -the `allowsSorting` prop. The Table accepts a `SortDescriptor` prop that defines the current column key to sort by and the sort direction (ascending/descending). -When the user presses a column's sort icon, the column's key and sort direction is passed into the `onSortChange` callback, allowing you to update -the `SortDescriptor` appropriately. - -This example performs client side sorting by passing a `sort` function to the [useAsyncList](../react-stately/useAsyncList.html) hook. -See the docs for more information on how to perform server side sorting. - -```tsx example -import {useAsyncList} from '@react-stately/data'; - -function AsyncSortTable() { - let columns = [ - {name: 'Name', key: 'name'}, - {name: 'Height', key: 'height'}, - {name: 'Mass', key: 'mass'}, - {name: 'Birth Year', key: 'birth_year'} - ]; - - let list = useAsyncList({ - async load({signal}) { - let res = await fetch(`https://swapi.dev/api/people/?search`, {signal}); - let json = await res.json(); - return { - items: json.results - }; - }, - async sort({items, sortDescriptor}) { - let sorted = items.sort((a, b) => { - let cmp; - let first = a[sortDescriptor.column].replace("BBY", ""); - let second = b[sortDescriptor.column].replace("BBY", ""); - if (+first || +second) { - cmp = +first < +second ? -1 : 1; - } else { - cmp = first <= second ? -1 : 1; - } - if (sortDescriptor.direction === 'descending') { - cmp *= -1; - } - return cmp; - }); - return { - items: sorted - }; - } - }); - - return ( - - - {(column) => ( - - {column.name} - - )} - - - {(item) => ( - {(key) => {item[key]}} - )} - -
- ); -} -``` - -## Internationalization - -`useTable` handles some aspects of internationalization automatically. -For example, type to select is implemented with an -[Intl.Collator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Collator) -for internationalized string matching. You are responsible for localizing all text content within the table. -Make sure that some types of content (e.g. file extensions) are not translated. - -### RTL - -In right-to-left languages, the table layout should be mirrored. The columns should be ordered from right to left and the -individual column text alignment should be inverted. Ensure that your CSS accounts for this. diff --git a/packages/@react-stately/table/docs/useTableState.mdx b/packages/@react-stately/table/docs/useTableState.mdx deleted file mode 100644 index eb586fc2250..00000000000 --- a/packages/@react-stately/table/docs/useTableState.mdx +++ /dev/null @@ -1,46 +0,0 @@ - - -import {Layout} from '@react-spectrum/docs'; -export default Layout; - -import docs from 'docs:@react-stately/table'; -import {ClassAPI, HeaderInfo, FunctionAPI} from '@react-spectrum/docs'; -import packageData from '@react-stately/table/package.json'; - ---- -category: Collections -keywords: [table, state, grid] ---- - -# useTableState - -

{docs.exports.useTableState.description}

- - - -## API - - - - - - - - -## Interface - - - -## Example - -See the docs for [useTable](/react-aria/useTable.html) in react-aria for an example of `useTableState`, `Cell`, `Column`, -`Row`, `TableBody`, and `TableHeader`. From f592b071d8e91020c86414684de72606c0795c57 Mon Sep 17 00:00:00 2001 From: Daniel Lu Date: Fri, 11 Jun 2021 14:07:06 -0700 Subject: [PATCH 41/62] removing tableview docs --- .../@react-spectrum/table/docs/TableView.mdx | 786 ------------------ 1 file changed, 786 deletions(-) delete mode 100644 packages/@react-spectrum/table/docs/TableView.mdx diff --git a/packages/@react-spectrum/table/docs/TableView.mdx b/packages/@react-spectrum/table/docs/TableView.mdx deleted file mode 100644 index b110a74babb..00000000000 --- a/packages/@react-spectrum/table/docs/TableView.mdx +++ /dev/null @@ -1,786 +0,0 @@ - - -import {Layout} from '@react-spectrum/docs'; -export default Layout; - -import docs from 'docs:@react-spectrum/table'; -import tableTypes from 'docs:@react-types/table/src/index.d.ts'; -import {HeaderInfo, PropTable} from '@react-spectrum/docs'; -import packageData from '@react-spectrum/table/package.json'; - -```jsx import -import {ActionButton} from '@react-spectrum/button'; -import Add from '@spectrum-icons/workflow/Add'; -import {Cell, Column, Row, TableView, TableBody, TableHeader} from '@react-spectrum/table'; -import {Flex} from '@react-spectrum/layout'; -``` - ---- -category: Collections -keywords: [table, grid] ---- - -# TableView - -

{docs.exports.TableView.description}

- - - -## Example -```tsx example - - - - Name - Type - Date Modified - - - - Games - File folder - 6/7/2020 - - - Program Files - File folder - 4/7/2021 - - - bootmgr - System file - 11/20/2010 - - - log.txt - Text Document - 1/18/2016 - - - - -``` - -## Content -TableView expects a TableHeader and a TableBody as children. In turn, TableHeader and TableBody follow the [Collection Components](../react-stately/collections.html) API. -The TableHeader accepts either static Columns or a `columns` prop with a renderer function for dynamic rendering. Similarly, TableBody accepts either static Rows or a `items` prop with a renderer function. -Row follows the same pattern, accepting Cells as children instead. - -Basic usage of TableView, seen in the example above, shows multiple Columns and Rows populated with strings and Cells respectively. Static collections, as in this example, can be used when the contents of the TableView is known ahead of time. - -Dynamic collections, as shown below, can be used when the options come from an external data source such as an API call, or update over time. Providing the data in this way allows TableView to automatically cache the rendering of each item, -which dramatically improves performance. Make sure that each rendered property in the Row object matches with a Column's key. In the example below, the `uid` of each Column is set as its key and matches with a -property within each Row object. - -```tsx example -function Example() { - let columns = [ - {name: 'Name', uid: 'name'}, - {name: 'Type', uid: 'type'}, - {name: 'Date Modified', uid: 'date'} - ]; - - let rows = [ - {id: '1', name: 'Games', date: '6/7/2020', type: 'File folder'}, - {id: '2', name: 'Program Files', date: '4/7/2021', type: 'File folder'}, - {id: '3', name: 'bootmgr', date: '11/20/2010', type: 'System file'}, - {id: '4', name: 'log.txt', date: '1/18/2016', type: 'Text Document'} - ]; - - return ( - - - - {column => ( - - {column.name} - - )} - - - {item => ( - - {/* Note this key is equal to the key of the the column, - not the key set on the Row prior */} - {key => {item[key]}} - - )} - - - - ); -} -``` - -### Internationalization -To internationalize a TableView, all text content within the TableView should be replaced with localized strings. This includes the `aria-label` provided to the TableView if any. -For languages that are read right-to-left (e.g. Hebrew and Arabic), the layout of TableView is automatically flipped. - -## Labeling -### Accessibility -An `aria-label` must be provided to the TableView for accessibility. If the TableView is labeled by a separate element, an `aria-labelledby` prop must be provided using the id of the labeling element instead. - -By default, the first column of the TableView is used as the [row header](https://www.w3.org/TR/wai-aria-1.1/#rowheader) and is announced by assistive technology when navigating through the rows. You can override this behavior by providing the `isRowHeader` prop -to one or more Columns, allowing you to customize which columns should label the rows of the TableView. - -The example below applies `isRowHeader` to the "First Name" and "Last Name" columns so that each row is announced with the person's full name (e.g. "John Doe"). - -```tsx example - - - - First Name - Last Name - Age - - - - John - Doe - 45 - - - Jane - Doe - 37 - - - Joe - Schmoe - 67 - - - - -``` - -## Asynchronous loading -TableView supports loading data asynchronously, and will display a progress circle reflecting the current load state, -set by the `loadingState` prop. It also supports infinite scrolling to load more data on demand as the user scrolls, via the `onLoadMore` prop. - -This example uses the [useAsyncList](../react-stately/useAsyncList.html) hook to handle loading the data. -See the docs for more information. - -```tsx example -import {useAsyncList} from '@react-stately/data'; - -function AsyncTable() { - let columns = [ - {name: 'Name', key: 'name'}, - {name: 'Height', key: 'height'}, - {name: 'Mass', key: 'mass'}, - {name: 'Birth Year', key: 'birth_year'} - ]; - - let list = useAsyncList({ - async load({signal, cursor}) { - if (cursor) { - cursor = cursor.replace(/^http:\/\//i, 'https://'); - } - - let res = await fetch(cursor || `https://swapi.dev/api/people/?search=`, {signal}); - let json = await res.json(); - - return { - items: json.results, - cursor: json.next - }; - } - }); - - return ( - - - - {(column) => ( - - {column.name} - - )} - - - {(item) => ( - {(key) => {item[key]}} - )} - - - - ); -} -``` - -## Events - -### Selection -TableView supports multiple selection modes. By default, selection is disabled, but this can be modified using the `selectionMode` prop. -Use `defaultSelectedKeys` to provide a default set of selected rows. Note that the value of the selected keys must match the `key` prop of the Row. - -The example below uses `defaultSelectedKeys` to select the row with key equal to "2". - -```tsx example - - - - Name - Type - Level - - - - Charizard - Fire, Flying - 67 - - - Blastoise - Water - 56 - - - Venusaur - Grass, Poison - 83 - - - Pikachu - Electric - 100 - - - - -``` - -To programmatically control row selection, use the `selectedKeys` prop paired with the `onSelectionChange` callback. The `key` prop from the selected row will -be passed into the callback when the row is pressed, allowing you to update `selectedKeys` accordingly. - -Here is how you would control selection for the above example. - -```tsx example export=true -function PokemonTable(props) { - let columns = [ - {name: 'Name', uid: 'name'}, - {name: 'Type', uid: 'type'}, - {name: 'Level', uid: 'level'} - ]; - - let rows = [ - {id: '1', name: 'Charizard', type: 'Fire, Flying', level: '67'}, - {id: '2', name: 'Blastoise', type: 'Water', level: '56'}, - {id: '3', name: 'Venusaur', type: 'Grass, Poison', level: '83'}, - {id: '4', name: 'Pikachu', type: 'Electric', level: '100'} - ]; - - let [selected, setSelected] = React.useState(new Set(['2'])); - - return ( - - - - {column => ( - - {column.name} - - )} - - - {item => ( - - {key => {item[key]}} - - )} - - - - ); -} -``` - -Multiple selection can be enabled by setting `selectionMode` to `multiple`. - -```tsx example -// Using the same table as above - -``` - -TableView also supports a `disallowEmptySelection` prop which forces the user to have at least one row in the TableView selected at all times. - -```tsx example -// Using the same table as above - -``` - -### Sorting -TableView supports sorting its data when a column header is pressed. To designate that a Column should support sorting, provide it with -the `allowsSorting` prop. The TableView accepts a `SortDescriptor` prop that defines the current column key to sort by and the sort direction (ascending/descending). -When the user presses a column's sort icon, the column's key and sort direction is passed into the `onSortChange` callback, allowing you to update -the `SortDescriptor` appropriately. - -This example performs client side sorting by passing a `sort` function to the [useAsyncList](../react-stately/useAsyncList.html) hook. -See the docs for more information on how to perform server side sorting. - -```tsx example -function AsyncSortTable() { - let columns = [ - {name: 'Name', key: 'name'}, - {name: 'Height', key: 'height'}, - {name: 'Mass', key: 'mass'}, - {name: 'Birth Year', key: 'birth_year'} - ]; - - let list = useAsyncList({ - async load({signal}) { - let res = await fetch(`https://swapi.dev/api/people/?search`, {signal}); - let json = await res.json(); - - return { - items: json.results - }; - }, - async sort({items, sortDescriptor}) { - let sorted = items.sort((a, b) => { - let cmp; - let first = a[sortDescriptor.column].replace("BBY", ""); - let second = b[sortDescriptor.column].replace("BBY", ""); - - if (+first || +second) { - cmp = +first < +second ? -1 : 1; - } else { - cmp = first <= second ? -1 : 1; - } - - if (sortDescriptor.direction === 'descending') { - cmp *= -1; - } - return cmp; - }); - - return { - items: sorted - }; - } - }); - - return ( - - - - {(column) => ( - - {column.name} - - )} - - - {(item) => ( - {(key) => {item[key]}} - )} - - - - ); -} -``` - -## Props - -### TableView props - - - -### TableHeader props - - - -### Column props - - - -### TableBody props - - - -### Row props - - - -### Cell props - - - -## Visual options -### Column alignment -[View guidelines](https://spectrum.adobe.com/page/table/#Usage-guidelines) - -```tsx example - - - - Name - Type - Size - - - - 2021406_Proposal - PDF - 86 KB - - - Budget Template - XLS - 120 KB - - - Onboarding - PPT - 472 KB - - - Welcome - TXT - 24 KB - - - - -``` - -### Column widths -Columns support three different width props: `minWidth`, `width`, and `maxWidth`. - -```tsx example - - - - Name - Type - Size - - - - 2021406_Proposal - PDF - 86 KB - - - Budget Template - XLS - 120 KB - - - Onboarding - PPT - 472 KB - - - Welcome - TXT - 24 KB - - - - -``` - -### Column dividers -[View guidelines](https://spectrum.adobe.com/page/table/#Column-dividers) - -```tsx example - - - - Name - Type - Size - - - - 2021406_Proposal - PDF - 86 KB - - - Budget Template - XLS - 120 KB - - - Onboarding - PPT - 472 KB - - - Welcome - TXT - 24 KB - - - - -``` - -### Nested columns -TableView supports nesting columns, allowing you to render "tiered" column headers. - -```tsx example - - - - - Name - Type - Size - - - - - 2021406_Proposal - PDF - 86 KB - - - Budget Template - XLS - 120 KB - - - Onboarding - PPT - 472 KB - - - - -``` - -### Focusable cells -Cells accept any renderable node, allowing you to have focusable children within the TableView. - -```tsx example -import Edit from '@spectrum-icons/workflow/Edit'; -import Delete from '@spectrum-icons/workflow/Delete'; - - - - - Name - Add - Delete - - - - Red Panda - - - - - - - - - - - - - Harbor Seal - - - - - - - - - - - - - Groundhog - - - - - - - - - - - - - Otter - - - - - - - - - - - - - - -``` - -### Hide header -Individual column headers can be hidden by providing the `hideHeader` prop to the Column. A tooltip is rendered when the column header is focused -to compensate for the lack of a visual title. Note that the `hideHeader` prop is specifically intended for columns that contain ActionButtons instead -of text content. - -```tsx example export=true -function TableExample(props) { - let columns = [ - {name: 'First Name', key: 'firstName'}, - {name: 'Last Name', key: 'lastName'}, - {name: 'Add Info', key: 'addInfo'}, - {name: 'Age', key: 'age'} - ]; - - let rows = [ - {id: '1', firstName: 'John', lastName: 'Doe', age: '45'}, - {id: '2', firstName: 'Jane', lastName: 'Doe', age: '37'}, - {id: '3', firstName: 'Joe', lastName: 'Schmoe', age: '67'}, - {id: '4', firstName: 'Joe', lastName: 'Bloggs', age: '12'}, - {id: '5', firstName: 'Longggggggggggg Wrapping', lastName: 'Name', age: '56'} - ]; - - return ( - - - - {column => ( - - {column.name} - - )} - - - {item => - ( - {key => - key === 'addInfo' - ? - : {item[key]} - } - ) - } - - - - ); -} -``` - -### Quiet -[View guidelines](https://spectrum.adobe.com/page/table/#Standard-or-quiet) - -```tsx example -// Using same setup as hide header example - -``` - -### Disabled -Use the `disabledKeys` prop to specify which rows to disable selection for in the TableView. - -```tsx example -// Using same setup as hide header example - -``` - -### Density -The amount of vertical padding that each row contains can be modified by providing the `density` prop. - -```tsx example -// Using same setup as hide header example - - - - -``` - -### Overflow mode -By default, text content that overflows its table cell will be truncated. You can have it wrap instead by passing `overflowMode="wrap"` -to the TableView. - -```tsx example -// Using same setup as hide header example - -``` - -### Empty state -Use the `renderEmptyState` prop to customize what the TableView will display if there are no rows provided. - -```tsx example -import {Content} from '@react-spectrum/view'; -import {IllustratedMessage} from '@react-spectrum/illustratedmessage'; -import {Heading} from '@react-spectrum/text'; - -function renderEmptyState() { - return ( - - - - - No results - No results found - - ); -} - - - - - Name - Type - Size - - - {[]} - - - -``` From 5395577d91e18c8dac03be1fe6a3da755907f123 Mon Sep 17 00:00:00 2001 From: Daniel Lu Date: Fri, 11 Jun 2021 14:14:21 -0700 Subject: [PATCH 42/62] adding aria and stately docs --- .../@react-aria/table/docs/TableAnatomy.svg | 91 +++ packages/@react-aria/table/docs/useTable.mdx | 683 ++++++++++++++++++ .../table/docs/useTableState.mdx | 46 ++ .../@react-types/table/docs/TableView.mdx | 2 +- 4 files changed, 821 insertions(+), 1 deletion(-) create mode 100644 packages/@react-aria/table/docs/TableAnatomy.svg create mode 100644 packages/@react-aria/table/docs/useTable.mdx create mode 100644 packages/@react-stately/table/docs/useTableState.mdx diff --git a/packages/@react-aria/table/docs/TableAnatomy.svg b/packages/@react-aria/table/docs/TableAnatomy.svg new file mode 100644 index 00000000000..9f0779b9577 --- /dev/null +++ b/packages/@react-aria/table/docs/TableAnatomy.svg @@ -0,0 +1,91 @@ + + Table anatomy diagram + Shows a table component with labels pointing to its parts, including the row, row divider, column, column header, column divider, sort icon, and cell elements. + + + + + + + + + Columnheader + + + SENT + + + + + 6,554 + 2,875 + 3,679 + 1,308 + Tree item + Tree item + Tree item + Tree item + ITEM NAME + + Column + + + Cell + + + + Sort icon + + + + + + + + + + Rowdivider + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Checkboxselection + + + Columndivider + + + diff --git a/packages/@react-aria/table/docs/useTable.mdx b/packages/@react-aria/table/docs/useTable.mdx new file mode 100644 index 00000000000..8105f1ebbe5 --- /dev/null +++ b/packages/@react-aria/table/docs/useTable.mdx @@ -0,0 +1,683 @@ + + +import {Layout} from '@react-spectrum/docs'; +export default Layout; + +import docs from 'docs:@react-aria/table'; +import collectionsDocs from 'docs:@react-types/shared/src/collections.d.ts'; +import selectionDocs from 'docs:@react-stately/selection'; +import statelyDocs from 'docs:@react-stately/table'; +import {HeaderInfo, FunctionAPI, TypeContext, InterfaceType, TypeLink} from '@react-spectrum/docs'; +import packageData from '@react-aria/table/package.json'; +import Anatomy from './TableAnatomy.svg'; + +```jsx import +import {useTable, useTableCell, useTableColumnHeader, useTableRow, useTableRowGroup, useTableRowHeader, useTableSelectAllCheckbox, useTableSelectionCheckbox} from '@react-aria/table'; +``` + +--- +category: Collections +keywords: [table, aria, grid] +--- + +# useTable + +

{docs.exports.useTable.description}

+ + + +## API + + + + + + + + + + +## Features + +A table can be built using the [<table>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/table), [<thead>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/thead), +[<tbody>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/tbody), and other table specific HTML elements, but is very limited in functionality especially when it comes to user interactions. +`useTable` helps achieve accessible and interactive table components that can be styled as needed. + +* Exposed to assistive technology as a `grid` using ARIA +* Support for single, multiple, or no row selection +* Support for disabled rows +* Labeling support for accessibility +* Support for mouse, touch, and keyboard interactions +* Support for keyboard navigation between columns, rows, cells, and in-cell focusable items +* Column sorting +* Multiple [row header](https://www.w3.org/TR/wai-aria-1.1/#rowheader) support +* Automatic scrolling support during keyboard navigation +* Typeahead to allow focusing rows by typing text +* Virtualized scrolling support for performance with large tables + +## Anatomy + + + +A table consists of a container element, with columns and rows of cells containing data inside. The cells within a table may contain focusable elements or plain text content. +If the table supports row selection, each row includes a selection checkbox in the first column. Additionally, a "select all" checkbox is displayed as the first column header if the table supports multiple row selection. + +`useTable`, `useTableCell`, `useTableColumnHeader`, `useTableRow`, and `useTableRowHeader` handle keyboard, mouse, and other interactions to support +row selection, in table navigation, and overall focus behavior. Those hooks, along with `useTableRowGroup`, also handle exposing the table and its contents +to assistive technology using ARIA. `useTableSelectAllCheckbox` and `useTableSelectionCheckbox` handle row selection and associating each checkbox with its respective rows +for assistive technology. + +`useTable` returns props that you should spread onto the table container element: + + + + + +`useTableColumnHeader` returns props for an individual table column header: + + + + + +`useTableRow` returns props for an individual table row: + + + + + +`useTableRowGroup` returns props for a element containing one or more rows of column headers or cells. This is often equivalent +to the header, body, and footer of the table: + + + + + +`useTableRowHeader` returns props for the table cell that serves as a row's header: + + + + + +`useTableCell` returns props for an individual table cell: + + + + + +`useTableSelectionCheckbox` returns props for an individual table row selection checkbox: + + + + + +`useTableSelectAllCheckbox` returns props for the table's "select all" checkbox: + + + + + +State is managed by the +hook from `@react-stately/table`. The state object should be passed as an option to each of the above hooks where applicable. + +Note that an `aria-label` or `aria-labelledby` must be passed to the table to identify the element to assistive technology. + +## State management + +`useTable` requires knowledge of the rows, cells, and columns in the table in order to handle keyboard +navigation and other interactions. It does this using +the +interface, which is a generic interface to access sequential unique keyed data. You can +implement this interface yourself, e.g. by using a prop to pass a list of item objects, +but from +`@react-stately/table` implements a JSX based interface for building collections instead. +See [Collection Components](/react-stately/collections.html) for more information, +and [Collection Interface](/react-stately/Collection.html) for internal details. + +In addition, +manages the state necessary for multiple selection and exposes +a , +which makes use of the collection to provide an interface to update the selection state. +For more information, see [Selection](/react-stately/selection.html). + +## Example + +This example uses HTML `
` elements to construct the table. It uses `useTableState` to construct the table's collection of rows and columns, +applying the appropriate attributes to each `
` using the various table hooks listed above paired with the div's role in the table. +The table follows the [Collection Components API](../react-stately/collections.html), accepting both static and dynamic collections. + +The example below shows a static collection, which can be used when the full set of table contents is known ahead of time. Be sure to note the usage of +`Cell`, `Column`, `Row`, `TableBody`, and `TableHeader` to construct the table structure. These elements are similar to `Item` and `Section` used in other components +following the [Collection Components API](../react-stately/collections.html) mentioned previously, serving as node generators for the table collection. +See [useTableState](/react-stately/useTableState.html) for more info on the props they support. + +```tsx example export=true +import {Cell, Column, Row, TableBody, TableHeader, useTableState} from '@react-stately/table'; +import {mergeProps} from '@react-aria/utils'; +import {useCheckbox} from '@react-aria/checkbox'; +import {useContext, useRef} from 'react'; +import {useFocusRing} from '@react-aria/focus'; +import {useToggleState} from '@react-stately/toggle'; +const TableContext = React.createContext(null); +function useTableContext() { + return useContext(TableContext); +} +function Table(props) { + let state = useTableState({...props, showSelectionCheckboxes: true}); + let ref = useRef(); + let scrollViewRef = useRef(); + let {collection} = state; + let {gridProps} = useTable({ + ...props, + ref: ref + }, state); + let headerRows = collection.rows.filter(item => item.type === 'headerrow' && item); + let rows = collection.rows.filter(item => item.type === 'item' && item); + let renderColumnNode = (node) => { + if (node.props.isSelectionCell) { + return ; + } + return ; + }; + let renderRowNode = (node) => { + if (node.props.isSelectionCell) { + return ; + } + if (collection.rowHeaderColumnKeys.has(node.column.key)) { + return ; + } + return ; + }; + return ( + +
+ + {[...headerRows].map(headerRow => ( + + {[...headerRow.childNodes].map((column) => renderColumnNode(column))} + + ))} + +
+ + {[...rows].map(row => ( + + {[...row.childNodes].map((node) => renderRowNode(node))} + + ))} + +
+
+
+ ); +} +function TableHeaderGroup({children, ...otherProps}) { + let {rowGroupProps} = useTableRowGroup(); + return ( +
+ {children} +
+ ); +} +function TableColumnHeader({column}) { + let ref = useRef(); + let state = useTableContext(); + let {columnHeaderProps} = useTableColumnHeader({ + node: column, + ref, + colspan: column.colspan + }, state); + let {isFocusVisible, focusProps} = useFocusRing(); + let columnProps = column.props; + let arrowIcon = state.sortDescriptor?.direction === 'ascending' ? '▲' : '▼'; + return ( +
+ {column.rendered} + {columnProps.allowsSorting && state.sortDescriptor?.column === column.key && + + } +
+ ); +} +function TableSelectAllCell({column}) { + let ref = useRef(); + let state = useTableContext(); + let isSingleSelectionMode = state.selectionManager.selectionMode === 'single'; + let {columnHeaderProps} = useTableColumnHeader({ + node: column, + ref, + colspan: column.colspan, + isDisabled: isSingleSelectionMode + }, state); + let {checkboxProps} = useTableSelectAllCheckbox(state); + let inputRef = useRef(null); + let {inputProps} = useCheckbox(checkboxProps, useToggleState(checkboxProps), inputRef); + return ( +
+ +
+ ); +} +function TableRowGroup({children, ...otherProps}) { + let {rowGroupProps} = useTableRowGroup(); + return ( +
+ {children} +
+ ); +} +function TableRow({item, children, ...otherProps}) { + let ref = useRef(); + let state = useTableContext(); + let isDisabled = state.disabledKeys.has(item.key); + let isSelected = state.selectionManager.isSelected(item.key) && !isDisabled; + let {rowProps} = useTableRow({ + node: item, + isSelected, + ref, + isDisabled + }, state); + let props = mergeProps( + rowProps, + otherProps + ); + return ( +
+ {children} +
+ ); +} +function TableHeaderRow({item, children, ...otherProps}) { + return ( +
+ {children} +
+ ); +} +function TableCheckboxCell({cell}) { + let ref = useRef(); + let state = useTableContext(); + let isDisabled = state.disabledKeys.has(cell.parentKey); + let {gridCellProps} = useTableCell({ + node: cell, + ref, + isDisabled + }, state); + let {checkboxProps} = useTableSelectionCheckbox( + { + key: cell.parentKey, + isDisabled + }, + state + ); + let inputRef = useRef(null); + let {inputProps} = useCheckbox({...checkboxProps, isDisabled}, useToggleState(checkboxProps), inputRef); + return ( +
+ {state.selectionManager.selectionMode !== 'none' && + + } +
+ ); +} +function TableCell({cell}) { + let ref = useRef(); + let state = useTableContext(); + let isDisabled = state.disabledKeys.has(cell.parentKey); + let {gridCellProps} = useTableCell({ + node: cell, + ref, + isDisabled + }, state); + return ( + + ); +} +function TableRowHeader({cell}) { + let ref = useRef(); + let state = useTableContext(); + let isDisabled = state.disabledKeys.has(cell.parentKey); + let {rowHeaderProps} = useTableRowHeader({ + node: cell, + ref, + isDisabled + }, state); + return ( + + ); +} +function TableCellBase({cell, cellRef, ...otherProps}) { + let {isFocusVisible, focusProps} = useFocusRing(); + return ( +
+ + {cell.rendered} + +
+ ); +} + + + Name + Type + Date Modified + + + + Games + File folder + 6/7/2020 + + + Program Files + File folder + 4/7/2021 + + + bootmgr + System file + 11/20/2010 + + + log.txt + Text Document + 1/18/2016 + + +
+``` + +## Usage + +### Dynamic collections + +Dynamic collections, as shown below, can be used when the table data comes from an external data source such as an API call, or update over time. +In the example below, both the columns and the rows are provided to the table via a render function. + +```tsx example +function Example() { + let columns = [ + {name: 'Name', key: 'name'}, + {name: 'Type', key: 'type'}, + {name: 'Date Modified', key: 'date'} + ]; + let rows = [ + {id: '1', name: 'Games', date: '6/7/2020', type: 'File folder'}, + {id: '2', name: 'Program Files', date: '4/7/2021', type: 'File folder'}, + {id: '3', name: 'bootmgr', date: '11/20/2010', type: 'System file'}, + {id: '4', name: 'log.txt', date: '1/18/2016', type: 'Text Document'} + ]; + return ( + + + {(column) => ( + + {column.name} + + )} + + + {(item) => ( + + {/* Note this key is equal to the key of the the column, + not the key set on the Row prior */} + {(key) => {item[key]}} + + )} + +
+ ); +} +``` + +### Selection + +By default, `useTableState` doesn't allow row selection but this can be modified using the `selectionMode` prop. Use `defaultSelectedKeys` to provide a default set of selected rows. +Note that the value of the selected keys must match the `key` prop of the row. + +The example below uses `defaultSelectedKeys` to select the row with key equal to "2". + +```tsx example + + + Name + Type + Level + + + + Charizard + Fire, Flying + 67 + + + Blastoise + Water + 56 + + + Venusaur + Grass, Poison + 83 + + + Pikachu + Electric + 100 + + +
+``` + +To programmatically control row selection, use the `selectedKeys` prop paired with the `onSelectionChange` callback. The `key` prop from the selected row will +be passed into the callback when the row is pressed, allowing you to update `selectedKeys` accordingly. + +Here is how you would control selection for the above example. + +```tsx example export=true +function PokemonTable(props) { + let columns = [ + {name: 'Name', uid: 'name'}, + {name: 'Type', uid: 'type'}, + {name: 'Level', uid: 'level'} + ]; + let rows = [ + {id: '1', name: 'Charizard', type: 'Fire, Flying', level: '67'}, + {id: '2', name: 'Blastoise', type: 'Water', level: '56'}, + {id: '3', name: 'Venusaur', type: 'Grass, Poison', level: '83'}, + {id: '4', name: 'Pikachu', type: 'Electric', level: '100'} + ]; + let [selected, setSelected] = React.useState(new Set(['2'])); + return ( + + + {column => ( + + {column.name} + + )} + + + {item => ( + + {key => {item[key]}} + + )} + +
+ ); +} +``` + +Multiple selection can be enabled by setting `selectionMode` to `multiple`. + +```tsx example +// Using the same table as above + +``` + +Table also supports a `disallowEmptySelection` prop which forces the user to have at least one row in the Table selected at all times. + +```tsx example +// Using the same table as above + +``` + +### Disabled rows + +You can disable specific rows by providing an array of keys to `useTableState` via the `disabledKeys` prop. This will prevent rows from being selectable as shown in the example below. +Note that you are responsible for the styling of disabled rows. + +```tsx example +// Using the same table as above + +``` + +### Sorting + +Table supports sorting its data when a column header is pressed. To designate that a Column should support sorting, provide it with +the `allowsSorting` prop. The Table accepts a `SortDescriptor` prop that defines the current column key to sort by and the sort direction (ascending/descending). +When the user presses a column's sort icon, the column's key and sort direction is passed into the `onSortChange` callback, allowing you to update +the `SortDescriptor` appropriately. + +This example performs client side sorting by passing a `sort` function to the [useAsyncList](../react-stately/useAsyncList.html) hook. +See the docs for more information on how to perform server side sorting. + +```tsx example +import {useAsyncList} from '@react-stately/data'; +function AsyncSortTable() { + let columns = [ + {name: 'Name', key: 'name'}, + {name: 'Height', key: 'height'}, + {name: 'Mass', key: 'mass'}, + {name: 'Birth Year', key: 'birth_year'} + ]; + let list = useAsyncList({ + async load({signal}) { + let res = await fetch(`https://swapi.dev/api/people/?search`, {signal}); + let json = await res.json(); + return { + items: json.results + }; + }, + async sort({items, sortDescriptor}) { + let sorted = items.sort((a, b) => { + let cmp; + let first = a[sortDescriptor.column].replace("BBY", ""); + let second = b[sortDescriptor.column].replace("BBY", ""); + if (+first || +second) { + cmp = +first < +second ? -1 : 1; + } else { + cmp = first <= second ? -1 : 1; + } + if (sortDescriptor.direction === 'descending') { + cmp *= -1; + } + return cmp; + }); + return { + items: sorted + }; + } + }); + return ( + + + {(column) => ( + + {column.name} + + )} + + + {(item) => ( + {(key) => {item[key]}} + )} + +
+ ); +} +``` + +## Internationalization + +`useTable` handles some aspects of internationalization automatically. +For example, type to select is implemented with an +[Intl.Collator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Collator) +for internationalized string matching. You are responsible for localizing all text content within the table. +Make sure that some types of content (e.g. file extensions) are not translated. + +### RTL + +In right-to-left languages, the table layout should be mirrored. The columns should be ordered from right to left and the +individual column text alignment should be inverted. Ensure that your CSS accounts for this. diff --git a/packages/@react-stately/table/docs/useTableState.mdx b/packages/@react-stately/table/docs/useTableState.mdx new file mode 100644 index 00000000000..eb586fc2250 --- /dev/null +++ b/packages/@react-stately/table/docs/useTableState.mdx @@ -0,0 +1,46 @@ + + +import {Layout} from '@react-spectrum/docs'; +export default Layout; + +import docs from 'docs:@react-stately/table'; +import {ClassAPI, HeaderInfo, FunctionAPI} from '@react-spectrum/docs'; +import packageData from '@react-stately/table/package.json'; + +--- +category: Collections +keywords: [table, state, grid] +--- + +# useTableState + +

{docs.exports.useTableState.description}

+ + + +## API + + + + + + + + +## Interface + + + +## Example + +See the docs for [useTable](/react-aria/useTable.html) in react-aria for an example of `useTableState`, `Cell`, `Column`, +`Row`, `TableBody`, and `TableHeader`. diff --git a/packages/@react-types/table/docs/TableView.mdx b/packages/@react-types/table/docs/TableView.mdx index 1c3a177dcff..b110a74babb 100644 --- a/packages/@react-types/table/docs/TableView.mdx +++ b/packages/@react-types/table/docs/TableView.mdx @@ -132,7 +132,7 @@ For languages that are read right-to-left (e.g. Hebrew and Arabic), the layout o ### Accessibility An `aria-label` must be provided to the TableView for accessibility. If the TableView is labeled by a separate element, an `aria-labelledby` prop must be provided using the id of the labeling element instead. -By default, the first column of the TableView is used as the row header and is announced by assistive technology when navigating through the rows. You can override this behavior by providing the `isRowHeader` prop +By default, the first column of the TableView is used as the [row header](https://www.w3.org/TR/wai-aria-1.1/#rowheader) and is announced by assistive technology when navigating through the rows. You can override this behavior by providing the `isRowHeader` prop to one or more Columns, allowing you to customize which columns should label the rows of the TableView. The example below applies `isRowHeader` to the "First Name" and "Last Name" columns so that each row is announced with the person's full name (e.g. "John Doe"). From 2f27852db9b94fc53c54fd5e4c6fb8e535b3b9a5 Mon Sep 17 00:00:00 2001 From: Daniel Lu Date: Fri, 11 Jun 2021 14:25:06 -0700 Subject: [PATCH 43/62] moving tableview docs to the right place --- .../{@react-types => @react-spectrum}/table/docs/TableView.mdx | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename packages/{@react-types => @react-spectrum}/table/docs/TableView.mdx (100%) diff --git a/packages/@react-types/table/docs/TableView.mdx b/packages/@react-spectrum/table/docs/TableView.mdx similarity index 100% rename from packages/@react-types/table/docs/TableView.mdx rename to packages/@react-spectrum/table/docs/TableView.mdx From 20128b5c61ecb3b425cdb04b21023d5066580153 Mon Sep 17 00:00:00 2001 From: Daniel Lu Date: Fri, 11 Jun 2021 14:59:48 -0700 Subject: [PATCH 44/62] getting rid of table column defaultWidth prop and row child items not supported yet so removing for now --- .../@react-stately/layout/src/TableLayout.ts | 2 +- packages/@react-stately/table/src/Row.ts | 12 +------- packages/@react-types/table/src/index.d.ts | 29 +++++++++---------- 3 files changed, 16 insertions(+), 27 deletions(-) diff --git a/packages/@react-stately/layout/src/TableLayout.ts b/packages/@react-stately/layout/src/TableLayout.ts index 5b1cb9be50b..99bf5760662 100644 --- a/packages/@react-stately/layout/src/TableLayout.ts +++ b/packages/@react-stately/layout/src/TableLayout.ts @@ -72,7 +72,7 @@ export class TableLayout extends ListLayout { let remainingSpace = this.virtualizer.visibleRect.width; for (let column of this.collection.columns) { let props = column.props as ColumnProps; - let width = props.width ?? props.defaultWidth ?? this.getDefaultWidth(props); + let width = props.width ?? this.getDefaultWidth(props); if (width != null) { let w = this.parseWidth(width); this.columnWidths.set(column.key, w); diff --git a/packages/@react-stately/table/src/Row.ts b/packages/@react-stately/table/src/Row.ts index 2cdcdead8f5..52193c325de 100644 --- a/packages/@react-stately/table/src/Row.ts +++ b/packages/@react-stately/table/src/Row.ts @@ -20,7 +20,7 @@ function Row(props: RowProps): ReactElement { // eslint-disable-line @type } Row.getCollectionNode = function* getCollectionNode(props: RowProps, context: CollectionBuilderContext): Generator> { - let {childItems, children, textValue} = props; + let {children, textValue} = props; yield { type: 'item', @@ -63,16 +63,6 @@ Row.getCollectionNode = function* getCollectionNode(props: RowProps, conte yield* cells; } - - // Then process child rows (e.g. treeble) - if (childItems) { - for (let child of childItems) { - yield { - type: 'item', - value: child - }; - } - } }, shouldInvalidate(newContext: CollectionBuilderContext) { // Invalidate all rows if the columns changed. diff --git a/packages/@react-types/table/src/index.d.ts b/packages/@react-types/table/src/index.d.ts index b37319b82f3..31369eef2bf 100644 --- a/packages/@react-types/table/src/index.d.ts +++ b/packages/@react-types/table/src/index.d.ts @@ -15,7 +15,7 @@ import {GridCollection, GridNode} from '@react-types/grid'; import {Key, ReactElement, ReactNode} from 'react'; export interface TableProps extends MultipleSelection, Sortable { - /** The elements that make up the Table. Includes the TableHeader, TableBody, Columns, and Rows. */ + /** The elements that make up the table. Includes the TableHeader, TableBody, Columns, and Rows. */ children: [ReactElement>, ReactElement>], /** A list of row keys to disable. */ disabledKeys?: Iterable @@ -23,23 +23,23 @@ export interface TableProps extends MultipleSelection, Sortable { export interface SpectrumTableProps extends TableProps, DOMProps, AriaLabelingProps, StyleProps { /** - * Sets the amount of vertical padding within each Table cell. + * Sets the amount of vertical padding within each cell. * @default 'regular' */ density?: 'compact' | 'regular' | 'spacious', /** - * Sets the overflow behavior for the Table cell contents. + * Sets the overflow behavior for the cell contents. * @default 'truncate' */ overflowMode?: 'wrap' | 'truncate', - /** Whether the Table should be displayed with a quiet style. */ + /** Whether the TableView should be displayed with a quiet style. */ isQuiet?: boolean, - /** Sets what the Table should render when there is no content to display. */ + /** Sets what the TableView should render when there is no content to display. */ renderEmptyState?: () => JSX.Element } export interface TableHeaderProps { - /** A list of Table columns. */ + /** A list of table columns. */ columns?: T[], /** A list of `Column(s)` or a function. If the latter, a list of columns must be provided using the `columns` prop. */ children: ColumnElement | ColumnElement[] | ColumnRenderer @@ -59,9 +59,8 @@ export interface ColumnProps { /** The minimum width of the column. */ minWidth?: number | string, /** The maximum width of the column. */ - maxWidth?: number | string, - /** The default width of the column. */ - defaultWidth?: number | string + maxWidth?: number | string + // defaultWidth?: number | string } // TODO: how to support these in CollectionBuilder... @@ -92,16 +91,16 @@ export interface TableBodyProps extends Omit { children: CollectionChildren, /** A list of row objects in the table body used when dynamically rendering rows. */ items?: Iterable, - /** The current loading state of the TableView. */ + /** The current loading state of the table. */ loadingState?: LoadingState } export interface RowProps { - // treeble case? - /** A list of child item objects used when dynamically rendering row children. */ - childItems?: Iterable, - /** Whether this row has children, even if not loaded yet. */ - hasChildItems?: boolean, + // treeble case? Unsupported props for now + // /** A list of child item objects used when dynamically rendering row children. */ + // childItems?: Iterable, + // /** Whether this row has children, even if not loaded yet. */ + // hasChildItems?: boolean, /** Rendered contents of the row or row child items. */ children: CellElement | CellElement[] | CellRenderer, /** A string representation of the row's contents, used for features like typeahead. */ From 91324a929cdfefc462a8c2a5739f8b3802c433e7 Mon Sep 17 00:00:00 2001 From: Daniel Lu Date: Fri, 11 Jun 2021 15:04:01 -0700 Subject: [PATCH 45/62] fixing lint --- packages/@react-stately/table/src/Row.ts | 6 +++--- packages/@react-types/table/src/index.d.ts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/@react-stately/table/src/Row.ts b/packages/@react-stately/table/src/Row.ts index 52193c325de..3267e4fa871 100644 --- a/packages/@react-stately/table/src/Row.ts +++ b/packages/@react-stately/table/src/Row.ts @@ -15,11 +15,11 @@ import {PartialNode} from '@react-stately/collections'; import React, {ReactElement} from 'react'; import {RowProps} from '@react-types/table'; -function Row(props: RowProps): ReactElement { // eslint-disable-line @typescript-eslint/no-unused-vars +function Row(props: RowProps): ReactElement { // eslint-disable-line @typescript-eslint/no-unused-vars return null; } -Row.getCollectionNode = function* getCollectionNode(props: RowProps, context: CollectionBuilderContext): Generator> { +Row.getCollectionNode = function* getCollectionNode(props: RowProps, context: CollectionBuilderContext): Generator> { let {children, textValue} = props; yield { @@ -75,5 +75,5 @@ Row.getCollectionNode = function* getCollectionNode(props: RowProps, conte }; // We don't want getCollectionNode to show up in the type definition -let _Row = Row as (props: RowProps) => JSX.Element; +let _Row = Row as (props: RowProps) => JSX.Element; export {_Row as Row}; diff --git a/packages/@react-types/table/src/index.d.ts b/packages/@react-types/table/src/index.d.ts index 31369eef2bf..b9b33b7dded 100644 --- a/packages/@react-types/table/src/index.d.ts +++ b/packages/@react-types/table/src/index.d.ts @@ -95,7 +95,7 @@ export interface TableBodyProps extends Omit { loadingState?: LoadingState } -export interface RowProps { +export interface RowProps { // treeble case? Unsupported props for now // /** A list of child item objects used when dynamically rendering row children. */ // childItems?: Iterable, From aadf95bacc681617bfcd0d1d25785498bc39e0f9 Mon Sep 17 00:00:00 2001 From: Daniel Lu Date: Fri, 11 Jun 2021 15:51:52 -0700 Subject: [PATCH 46/62] adding doc comments for grid and table hooks --- packages/@react-aria/grid/src/useGrid.ts | 8 +++++++- packages/@react-aria/grid/src/useGridCell.ts | 7 ++++++- packages/@react-aria/grid/src/useGridRow.ts | 5 +++++ packages/@react-aria/grid/src/useGridRowGroup.ts | 3 +++ packages/@react-aria/table/src/useTable.ts | 6 ++++++ packages/@react-aria/table/src/useTableColumnHeader.ts | 5 +++++ packages/@react-aria/table/src/useTableRowHeader.ts | 5 +++++ .../@react-aria/table/src/useTableSelectionCheckbox.ts | 5 +++++ packages/@react-stately/grid/src/useGridState.ts | 3 +++ 9 files changed, 45 insertions(+), 2 deletions(-) diff --git a/packages/@react-aria/grid/src/useGrid.ts b/packages/@react-aria/grid/src/useGrid.ts index c6e09412da7..d08f304e54e 100644 --- a/packages/@react-aria/grid/src/useGrid.ts +++ b/packages/@react-aria/grid/src/useGrid.ts @@ -39,7 +39,7 @@ export interface GridProps extends DOMProps, AriaLabelingProps { */ focusMode?: 'row' | 'cell', /** - * A function that returns the text that should be announced by accessibility tools when a row is added or removed from selection. + * A function that returns the text that should be announced by assistive technology when a row is added or removed from selection. * @default (key) => state.collection.getItem(key)?.textValue */ getRowText?: (key: Key) => string @@ -50,6 +50,12 @@ export interface GridAria { gridProps: HTMLAttributes } +/** + * Provides the behavior and accessibility implementation for a grid component. + * A grid displays data in one or more rows and columns and enables a user to navigate its contents via directional navigation keys. + * @param props - Props for the grid. + * @param state - State for the grid, as returned by `useGridState`. + */ export function useGrid(props: GridProps, state: GridState>): GridAria { let { ref, diff --git a/packages/@react-aria/grid/src/useGridCell.ts b/packages/@react-aria/grid/src/useGridCell.ts index a63ae3f9946..55a68365057 100644 --- a/packages/@react-aria/grid/src/useGridCell.ts +++ b/packages/@react-aria/grid/src/useGridCell.ts @@ -30,7 +30,7 @@ interface GridCellProps { isVirtualized?: boolean, /** Whether the grid cell is disabled. */ isDisabled?: boolean, - /** Whether the cell or it's first focusable item be focused when the grid cell is focused. */ + /** Whether the cell or its first focusable child element should be focused when the grid cell is focused. */ focusMode?: 'child' | 'cell', /** Whether selection should occur on press up instead of press down. */ shouldSelectOnPressUp?: boolean @@ -41,6 +41,11 @@ interface GridCellAria { gridCellProps: HTMLAttributes } +/** + * Provides the behavior and accessibility implementation for a cell in a grid. + * @param props - Props for the cell. + * @param state - State of the parent grid, as returned by `useGridState`. + */ export function useGridCell>(props: GridCellProps, state: GridState): GridCellAria { let { node, diff --git a/packages/@react-aria/grid/src/useGridRow.ts b/packages/@react-aria/grid/src/useGridRow.ts index fad2f8cfd61..91df542deb1 100644 --- a/packages/@react-aria/grid/src/useGridRow.ts +++ b/packages/@react-aria/grid/src/useGridRow.ts @@ -37,6 +37,11 @@ export interface GridRowAria { rowProps: HTMLAttributes } +/** + * Provides the behavior and accessibility implementation for a row in a grid. + * @param props - Props for the row. + * @param state - State of the parent grid, as returned by `useGridState`. + */ export function useGridRow, S extends GridState>(props: GridRowProps, state: S): GridRowAria { let { node, diff --git a/packages/@react-aria/grid/src/useGridRowGroup.ts b/packages/@react-aria/grid/src/useGridRowGroup.ts index fcff2aa2d7f..90b2da378de 100644 --- a/packages/@react-aria/grid/src/useGridRowGroup.ts +++ b/packages/@react-aria/grid/src/useGridRowGroup.ts @@ -17,6 +17,9 @@ interface GridRowGroupAria { rowGroupProps: HTMLAttributes } +/** + * Provides the accessibility implementation for a row group in a grid. + */ export function useGridRowGroup(): GridRowGroupAria { return { rowGroupProps: { diff --git a/packages/@react-aria/table/src/useTable.ts b/packages/@react-aria/table/src/useTable.ts index e5b7bc27bb4..67bf7da4474 100644 --- a/packages/@react-aria/table/src/useTable.ts +++ b/packages/@react-aria/table/src/useTable.ts @@ -25,6 +25,12 @@ interface TableProps extends GridProps { layout?: Layout> } +/** + * Provides the behavior and accessibility implementation for a table component. + * A table displays data in one or more rows and columns and enables a user to navigate its contents via directional navigation keys. + * @param props - Props for the table. + * @param state - State for the table, as returned by `useTableState`. + */ export function useTable(props: TableProps, state: TableState): GridAria { let { ref, diff --git a/packages/@react-aria/table/src/useTableColumnHeader.ts b/packages/@react-aria/table/src/useTableColumnHeader.ts index 7b1476060a3..3a98bb0e03c 100644 --- a/packages/@react-aria/table/src/useTableColumnHeader.ts +++ b/packages/@react-aria/table/src/useTableColumnHeader.ts @@ -38,6 +38,11 @@ interface ColumnHeaderAria { columnHeaderProps: HTMLAttributes } +/** + * Provides the behavior and accessibility implementation for a column header in a table. + * @param props - Props for the column header. + * @param state - State of the table, as returned by `useTableState`. + */ export function useTableColumnHeader(props: ColumnHeaderProps, state: TableState): ColumnHeaderAria { let {node, colspan, ref, isDisabled} = props; let {gridCellProps} = useGridCell(props, state); diff --git a/packages/@react-aria/table/src/useTableRowHeader.ts b/packages/@react-aria/table/src/useTableRowHeader.ts index f6657b3093c..25b5404384f 100644 --- a/packages/@react-aria/table/src/useTableRowHeader.ts +++ b/packages/@react-aria/table/src/useTableRowHeader.ts @@ -32,6 +32,11 @@ interface RowHeaderAria { rowHeaderProps: HTMLAttributes } +/** + * Provides the behavior and accessibility implementation for a row header in a table. + * @param props - Props for the row header. + * @param state - State of the table, as returned by `useTableState`. + */ export function useTableRowHeader(props: RowHeaderProps, state: TableState): RowHeaderAria { let {gridCellProps} = useGridCell(props, state); diff --git a/packages/@react-aria/table/src/useTableSelectionCheckbox.ts b/packages/@react-aria/table/src/useTableSelectionCheckbox.ts index 80cd1e8e2bf..16ea547d685 100644 --- a/packages/@react-aria/table/src/useTableSelectionCheckbox.ts +++ b/packages/@react-aria/table/src/useTableSelectionCheckbox.ts @@ -33,6 +33,11 @@ interface SelectAllCheckboxAria { checkboxProps: AriaCheckboxProps } +/** + * Provides the behavior and accessibility implementation for a selection checkbox in a table. + * @param props - Props for the selection checkbox. + * @param state - State of the table, as returned by `useTableState`. + */ export function useTableSelectionCheckbox(props: SelectionCheckboxProps, state: TableState): SelectionCheckboxAria { let { key, diff --git a/packages/@react-stately/grid/src/useGridState.ts b/packages/@react-stately/grid/src/useGridState.ts index 172f9769c3c..9b77dcf73fd 100644 --- a/packages/@react-stately/grid/src/useGridState.ts +++ b/packages/@react-stately/grid/src/useGridState.ts @@ -17,6 +17,9 @@ interface GridStateOptions> extends MultipleSelec focusMode?: 'row' | 'cell' } +/** + * Provides state management for a grid component. Handles row selection and focusing a grid cell's focusable child if applicable. + */ export function useGridState>(props: GridStateOptions): GridState { let {collection, focusMode} = props; let selectionState = useMultipleSelectionState(props); From ed1df88781614db68d1c2839239033b5b29657c7 Mon Sep 17 00:00:00 2001 From: Daniel Lu Date: Fri, 11 Jun 2021 15:55:41 -0700 Subject: [PATCH 47/62] missed usetablerow doc comment --- packages/@react-aria/table/src/useTableRow.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/@react-aria/table/src/useTableRow.ts b/packages/@react-aria/table/src/useTableRow.ts index 39bc0ba0f32..7de1366dfdc 100644 --- a/packages/@react-aria/table/src/useTableRow.ts +++ b/packages/@react-aria/table/src/useTableRow.ts @@ -15,6 +15,11 @@ import {GridRowAria, GridRowProps, useGridRow} from '@react-aria/grid'; import {TableCollection} from '@react-types/table'; import {TableState} from '@react-stately/table'; +/** + * Provides the behavior and accessibility implementation for a row in a table. + * @param props - Props for the row. + * @param state - State of the table, as returned by `useTableState`. + */ export function useTableRow(props: GridRowProps, state: TableState): GridRowAria { let {node} = props; let {rowProps} = useGridRow, TableState>(props, state); From 4f5a66df848b83baeea76ee6c0dd6af9a3ca14d2 Mon Sep 17 00:00:00 2001 From: Daniel Lu Date: Fri, 11 Jun 2021 16:11:26 -0700 Subject: [PATCH 48/62] adding missing description for useTableSelectAllCheckbox hook so sneaky --- packages/@react-aria/table/src/useTableSelectionCheckbox.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/@react-aria/table/src/useTableSelectionCheckbox.ts b/packages/@react-aria/table/src/useTableSelectionCheckbox.ts index 16ea547d685..bbbee61bf58 100644 --- a/packages/@react-aria/table/src/useTableSelectionCheckbox.ts +++ b/packages/@react-aria/table/src/useTableSelectionCheckbox.ts @@ -61,6 +61,11 @@ export function useTableSelectionCheckbox(props: SelectionCheckboxProps, stat }; } +/** + * Provides the behavior and accessibility implementation for the select all checkbox in a table. + * @param props - Props for the select all checkbox. + * @param state - State of the table, as returned by `useTableState`. + */ export function useTableSelectAllCheckbox(state: TableState): SelectAllCheckboxAria { let {isEmpty, isSelectAll} = state.selectionManager; return { From 95a09ef70a1ff505a978dae447bf4d8f7e03117f Mon Sep 17 00:00:00 2001 From: Daniel Lu Date: Thu, 17 Jun 2021 10:12:57 -0700 Subject: [PATCH 49/62] replacing table css hover pseudo class with is-hovered fixes doc examples where hovering was highlighting rows for tableview w/o selection enebaled --- .../@adobe/spectrum-css-temp/components/table/skin.css | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/@adobe/spectrum-css-temp/components/table/skin.css b/packages/@adobe/spectrum-css-temp/components/table/skin.css index 8f142bc0aa7..bcb23e07c09 100644 --- a/packages/@adobe/spectrum-css-temp/components/table/skin.css +++ b/packages/@adobe/spectrum-css-temp/components/table/skin.css @@ -19,7 +19,7 @@ governing permissions and limitations under the License. color: var(--spectrum-table-header-sort-icon-color); } - &:hover { + &.is-hovered { color: var(--spectrum-table-header-text-color-hover); .spectrum-Table-sortedIcon { @@ -119,7 +119,7 @@ tbody.spectrum-Table-body { /* We apply background color to the cell rather than the row so that * cells can overlap (e.g. sticky row headers). */ - &:hover .spectrum-Table-cell { + &.is-hovered .spectrum-Table-cell { background-color: var(--spectrum-table-row-background-color-hover); } @@ -143,7 +143,7 @@ tbody.spectrum-Table-body { background-color: var(--spectrum-table-row-background-color-selected); } - &:hover .spectrum-Table-cell { + &.is-hovered .spectrum-Table-cell { background-color: var(--spectrum-table-row-background-color-selected-hover); } @@ -191,7 +191,7 @@ tbody.spectrum-Table-body { background-color: var(--spectrum-alias-background-color-default); } - &:hover .spectrum-Table-cell { + &.is-hovered .spectrum-Table-cell { background-color: var(--spectrum-table-quiet-row-background-color-hover); } @@ -209,7 +209,7 @@ tbody.spectrum-Table-body { background-color: var(--spectrum-table-quiet-row-background-color-selected); } - &:hover .spectrum-Table-cell { + &.is-hovered .spectrum-Table-cell { background-color: var(--spectrum-table-quiet-row-background-color-selected-hover); } From 17589c44fa569e78e248b1304afc4323d58ebe34 Mon Sep 17 00:00:00 2001 From: Devon Govett Date: Thu, 17 Jun 2021 12:44:08 -0700 Subject: [PATCH 50/62] Update some examples --- .../@react-spectrum/table/docs/TableView.mdx | 416 ++++++++++-------- 1 file changed, 226 insertions(+), 190 deletions(-) diff --git a/packages/@react-spectrum/table/docs/TableView.mdx b/packages/@react-spectrum/table/docs/TableView.mdx index b110a74babb..5b6ff53ce58 100644 --- a/packages/@react-spectrum/table/docs/TableView.mdx +++ b/packages/@react-spectrum/table/docs/TableView.mdx @@ -85,43 +85,40 @@ which dramatically improves performance. Make sure that each rendered property i property within each Row object. ```tsx example -function Example() { - let columns = [ - {name: 'Name', uid: 'name'}, - {name: 'Type', uid: 'type'}, - {name: 'Date Modified', uid: 'date'} - ]; - - let rows = [ - {id: '1', name: 'Games', date: '6/7/2020', type: 'File folder'}, - {id: '2', name: 'Program Files', date: '4/7/2021', type: 'File folder'}, - {id: '3', name: 'bootmgr', date: '11/20/2010', type: 'System file'}, - {id: '4', name: 'log.txt', date: '1/18/2016', type: 'Text Document'} - ]; - - return ( - - - - {column => ( - - {column.name} - - )} - - - {item => ( - - {/* Note this key is equal to the key of the the column, - not the key set on the Row prior */} - {key => {item[key]}} - - )} - - - - ); -} +let columns = [ + {name: 'Name', uid: 'name'}, + {name: 'Type', uid: 'type'}, + {name: 'Date Modified', uid: 'date'} +]; + +let rows = [ + {id: 1, name: 'Games', date: '6/7/2020', type: 'File folder'}, + {id: 2, name: 'Program Files', date: '4/7/2021', type: 'File folder'}, + {id: 3, name: 'bootmgr', date: '11/20/2010', type: 'System file'}, + {id: 4, name: 'log.txt', date: '1/18/2016', type: 'Text Document'} +]; + + + + {column => ( + + {column.name} + + )} + + + {item => ( + + {columnKey => {item[columnKey]}} + + )} + + ``` ### Internationalization @@ -224,50 +221,50 @@ function AsyncTable() { } ``` -## Events -### Selection -TableView supports multiple selection modes. By default, selection is disabled, but this can be modified using the `selectionMode` prop. +## Selection + +By default, TableView doesn't allow row selection but this can be enabled using the `selectionMode` prop. Use `defaultSelectedKeys` to provide a default set of selected rows. Note that the value of the selected keys must match the `key` prop of the Row. -The example below uses `defaultSelectedKeys` to select the row with key equal to "2". +The example below enables multiple selection mode, and uses `defaultSelectedKeys` to select the rows with keys "2" and "4". ```tsx example - - - - Name - Type - Level - - - - Charizard - Fire, Flying - 67 - - - Blastoise - Water - 56 - - - Venusaur - Grass, Poison - 83 - - - Pikachu - Electric - 100 - - - - + + + Name + Type + Level + + + + Charizard + Fire, Flying + 67 + + + Blastoise + Water + 56 + + + Venusaur + Grass, Poison + 83 + + + Pikachu + Electric + 100 + + + ``` -To programmatically control row selection, use the `selectedKeys` prop paired with the `onSelectionChange` callback. The `key` prop from the selected row will -be passed into the callback when the row is pressed, allowing you to update `selectedKeys` accordingly. +### Controlled selection + +To programmatically control row selection, use the `selectedKeys` prop paired with the `onSelectionChange` callback. The `key` prop from the selected rows will +be passed into the callback when the row is pressed, allowing you to update state accordingly. Here is how you would control selection for the above example. @@ -280,121 +277,117 @@ function PokemonTable(props) { ]; let rows = [ - {id: '1', name: 'Charizard', type: 'Fire, Flying', level: '67'}, - {id: '2', name: 'Blastoise', type: 'Water', level: '56'}, - {id: '3', name: 'Venusaur', type: 'Grass, Poison', level: '83'}, - {id: '4', name: 'Pikachu', type: 'Electric', level: '100'} + {id: 1, name: 'Charizard', type: 'Fire, Flying', level: '67'}, + {id: 2, name: 'Blastoise', type: 'Water', level: '56'}, + {id: 3, name: 'Venusaur', type: 'Grass, Poison', level: '83'}, + {id: 4, name: 'Pikachu', type: 'Electric', level: '100'} ]; - let [selected, setSelected] = React.useState(new Set(['2'])); + let [selectedKeys, setSelectedKeys] = React.useState(new Set([2])); return ( - - - - {column => ( - - {column.name} - - )} - - - {item => ( - - {key => {item[key]}} - - )} - - - + + + {column => ( + + {column.name} + + )} + + + {item => ( + + {columnKey => {item[columnKey]}} + + )} + + ); } ``` -Multiple selection can be enabled by setting `selectionMode` to `multiple`. +### Single selection + +To limit users to selecting only a single item at a time, `selectionMode` can be set to `single`. ```tsx example // Using the same table as above - + ``` +### Disallow empty selection + TableView also supports a `disallowEmptySelection` prop which forces the user to have at least one row in the TableView selected at all times. +In this mode, if a single row is selected and the user presses it, it will not be deselected. ```tsx example // Using the same table as above - + ``` -### Sorting +### Disabled rows + +You can disable specific rows by providing an array of keys to TableView via the `disabledKeys` prop. This will prevent rows from being +selectable as shown in the example below. + +```tsx example +// Using the same table as above + +``` + +## Sorting + TableView supports sorting its data when a column header is pressed. To designate that a Column should support sorting, provide it with -the `allowsSorting` prop. The TableView accepts a `SortDescriptor` prop that defines the current column key to sort by and the sort direction (ascending/descending). -When the user presses a column's sort icon, the column's key and sort direction is passed into the `onSortChange` callback, allowing you to update -the `SortDescriptor` appropriately. +the `allowsSorting` prop. The TableView accepts a `sortDescriptor` prop that defines the current column key to sort by and the sort direction (ascending/descending). +When the user presses a sortable column header, the column's key and sort direction is passed into the `onSortChange` callback, allowing you to update +the `sortDescriptor` appropriately. This example performs client side sorting by passing a `sort` function to the [useAsyncList](../react-stately/useAsyncList.html) hook. See the docs for more information on how to perform server side sorting. ```tsx example function AsyncSortTable() { - let columns = [ - {name: 'Name', key: 'name'}, - {name: 'Height', key: 'height'}, - {name: 'Mass', key: 'mass'}, - {name: 'Birth Year', key: 'birth_year'} - ]; - let list = useAsyncList({ async load({signal}) { let res = await fetch(`https://swapi.dev/api/people/?search`, {signal}); let json = await res.json(); - return { items: json.results }; }, async sort({items, sortDescriptor}) { - let sorted = items.sort((a, b) => { - let cmp; - let first = a[sortDescriptor.column].replace("BBY", ""); - let second = b[sortDescriptor.column].replace("BBY", ""); - - if (+first || +second) { - cmp = +first < +second ? -1 : 1; - } else { - cmp = first <= second ? -1 : 1; - } - - if (sortDescriptor.direction === 'descending') { - cmp *= -1; - } - return cmp; - }); - return { - items: sorted + items: items.sort((a, b) => { + let first = a[sortDescriptor.column]; + let second = b[sortDescriptor.column]; + let cmp = (parseInt(first) || first) < (parseInt(second) || second) ? -1 : 1; + if (sortDescriptor.direction === 'descending') { + cmp *= -1; + } + return cmp; + }) }; } }); return ( - - - - {(column) => ( - - {column.name} - - )} - - - {(item) => ( - {(key) => {item[key]}} - )} - - - + + + Name + Height + Mass + Birth Year + + + {item => ( + + {columnKey => {item[columnKey]}} + + )} + + ); } ``` @@ -557,40 +550,6 @@ Columns support three different width props: `minWidth`, `width`, and `maxWidth` ``` -### Nested columns -TableView supports nesting columns, allowing you to render "tiered" column headers. - -```tsx example - - - - - Name - Type - Size - - - - - 2021406_Proposal - PDF - 86 KB - - - Budget Template - XLS - 120 KB - - - Onboarding - PPT - 472 KB - - - - -``` - ### Focusable cells Cells accept any renderable node, allowing you to have focusable children within the TableView. @@ -723,14 +682,6 @@ function TableExample(props) { ``` -### Disabled -Use the `disabledKeys` prop to specify which rows to disable selection for in the TableView. - -```tsx example -// Using same setup as hide header example - -``` - ### Density The amount of vertical padding that each row contains can be modified by providing the `density` prop. @@ -784,3 +735,88 @@ function renderEmptyState() { ``` + +### Nested columns + +TableView supports nesting columns, allowing you to create column groups, or "tiered" column headers. + +```tsx example + + + + First Name + Last Name + + + Age + Birthday + + + + + Sam + Smith + 36 + May 3 + + + Julia + Jones + 24 + February 10 + + + Peter + Parker + 28 + September 7 + + + Bruce + Wayne + 32 + December 18 + + + +``` + +Nested columns can also be defined dynamically using the function syntax and the `childColumns` prop. +The following example is the same as the example above, but defined dynamically. + +```tsx example +let columns = [ + {name: 'Name', key: 'name', children: [ + {name: 'First Name', key: 'first', isRowHeader: true}, + {name: 'Last Name', key: 'last', isRowHeader: true} + ]}, + {name: 'Information', key: 'info', children: [ + {name: 'Age', key: 'age'}, + {name: 'Birthday', key: 'birthday'} + ]} +]; + +let rows = [ + {id: 1, first: 'Sam', last: 'Smith', age: 36, birthday: 'May 3'}, + {id: 2, first: 'Julia', last: 'Jones', age: 24, birthday: 'February 10'}, + {id: 3, first: 'Peter', last: 'Parker', age: 28, birthday: 'September 7'}, + {id: 4, first: 'Bruce', last: 'Wayne', age: 32, birthday: 'December 18'} +]; + + + + {column => ( + + {column.name} + + )} + + + {item => ( + + {columnKey => {item[columnKey]}} + + )} + + +``` From 7c1a9254b45d4ab70de340a618f4aa2d3008ccd2 Mon Sep 17 00:00:00 2001 From: Devon Govett Date: Thu, 17 Jun 2021 15:25:31 -0700 Subject: [PATCH 51/62] Update table anatomy diagram So that it matches up with the ARIA roles better --- .../@react-aria/table/docs/TableAnatomy.svg | 226 ++++++++++++------ 1 file changed, 150 insertions(+), 76 deletions(-) diff --git a/packages/@react-aria/table/docs/TableAnatomy.svg b/packages/@react-aria/table/docs/TableAnatomy.svg index 9f0779b9577..89841bcdfd5 100644 --- a/packages/@react-aria/table/docs/TableAnatomy.svg +++ b/packages/@react-aria/table/docs/TableAnatomy.svg @@ -1,91 +1,165 @@ - + Table anatomy diagram - Shows a table component with labels pointing to its parts, including the row, row divider, column, column header, column divider, sort icon, and cell elements. - - - - - - - - - Columnheader - - - SENT - - - - - 6,554 - 2,875 - 3,679 - 1,308 - Tree item - Tree item - Tree item - Tree item - ITEM NAME - - Column - - - Cell - - - - Sort icon - - - - - - - - - - Rowdivider - + Shows a table component with labels pointing to its parts, including the row, column, column header, and cell elements. + + + + Column + header + + + + SIZE + + + + + + 214 KB + + + + + 120 KB + + 139 KB + + 24 KB + + Proposal + + Budget + + Welcome + + Onboarding + + FILE NAME + + + + Cell + + + + + + + + + + + + - - - - + + + + + + + + - - - - + + + + + + + + - - - - + + + + + + + + - - - - + + + + + + + + - - - - + + Select all + checkbox + + + + + + Selection + checkbox + + + + Row + group + + + + + + + + Header + row + + + + + + + + Row + + + + + + + + + + + + + + + - - Checkboxselection - + + + + + + + + + + + Row + group + + + + + + - Columndivider - + From 4c5b2ba7a1f61d47ffd72990368872b3edcbf2ef Mon Sep 17 00:00:00 2001 From: Devon Govett Date: Thu, 17 Jun 2021 15:35:17 -0700 Subject: [PATCH 52/62] ARIA docs updates Breaks apart the large example into separate components with individual code blocks. --- packages/@react-aria/table/docs/useTable.mdx | 850 ++++++++++-------- .../MDXTransformer.js | 17 +- 2 files changed, 468 insertions(+), 399 deletions(-) diff --git a/packages/@react-aria/table/docs/useTable.mdx b/packages/@react-aria/table/docs/useTable.mdx index 8105f1ebbe5..174b4affd64 100644 --- a/packages/@react-aria/table/docs/useTable.mdx +++ b/packages/@react-aria/table/docs/useTable.mdx @@ -14,12 +14,14 @@ import docs from 'docs:@react-aria/table'; import collectionsDocs from 'docs:@react-types/shared/src/collections.d.ts'; import selectionDocs from 'docs:@react-stately/selection'; import statelyDocs from 'docs:@react-stately/table'; +import focusDocs from 'docs:@react-aria/focus'; +import checkboxDocs from 'docs:@react-aria/checkbox'; import {HeaderInfo, FunctionAPI, TypeContext, InterfaceType, TypeLink} from '@react-spectrum/docs'; import packageData from '@react-aria/table/package.json'; import Anatomy from './TableAnatomy.svg'; ```jsx import -import {useTable, useTableCell, useTableColumnHeader, useTableRow, useTableRowGroup, useTableRowHeader, useTableSelectAllCheckbox, useTableSelectionCheckbox} from '@react-aria/table'; +import {useTable, useTableCell, useTableColumnHeader, useTableRow, useTableHeaderRow, useTableRowGroup, useTableSelectAllCheckbox, useTableSelectionCheckbox} from '@react-aria/table'; ``` --- @@ -33,7 +35,7 @@ keywords: [table, aria, grid] @@ -41,30 +43,35 @@ keywords: [table, aria, grid] ## API + + - - ## Features -A table can be built using the [<table>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/table), [<thead>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/thead), -[<tbody>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/tbody), and other table specific HTML elements, but is very limited in functionality especially when it comes to user interactions. +A table can be built using the [<table>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/table), [<tr>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/tr), +[<td>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/td), and other table specific HTML elements, but is very limited in functionality especially when it comes to user interactions. +HTML tables are meant for static content, rather than tables with rich interactions like focusable elements within cells, keyboard navigation, row selection, sorting, etc. `useTable` helps achieve accessible and interactive table components that can be styled as needed. * Exposed to assistive technology as a `grid` using ARIA -* Support for single, multiple, or no row selection -* Support for disabled rows -* Labeling support for accessibility -* Support for mouse, touch, and keyboard interactions -* Support for keyboard navigation between columns, rows, cells, and in-cell focusable items -* Column sorting -* Multiple [row header](https://www.w3.org/TR/wai-aria-1.1/#rowheader) support -* Automatic scrolling support during keyboard navigation +* Keyboard navigation between columns, rows, cells, and in-cell focusable elements via the arrow keys +* Single, multiple, or no row selection via mouse, touch, or keyboard interactions +* Support for disabled rows, which cannot be selected +* Optional support for checkboxes in each row for selection, as well as in the header to select all rows +* Column sorting support +* Async loading, infinite scrolling, filtering, and sorting support +* Support for column groups via nested columns * Typeahead to allow focusing rows by typing text +* Automatic scrolling support during keyboard navigation +* Labeling support for accessibility +* Support for marking columns as [row headers](https://www.w3.org/TR/wai-aria-1.1/#rowheader), which will be read when navigating the rows with a screen reader +* Ensures that selections are announced using an ARIA live region +* Support for using HTML table elements, or custom element types (e.g. `
`) for layout flexibility * Virtualized scrolling support for performance with large tables ## Anatomy @@ -72,61 +79,12 @@ A table can be built using the [<table>](https://developer.mozilla.org/en- A table consists of a container element, with columns and rows of cells containing data inside. The cells within a table may contain focusable elements or plain text content. -If the table supports row selection, each row includes a selection checkbox in the first column. Additionally, a "select all" checkbox is displayed as the first column header if the table supports multiple row selection. - -`useTable`, `useTableCell`, `useTableColumnHeader`, `useTableRow`, and `useTableRowHeader` handle keyboard, mouse, and other interactions to support -row selection, in table navigation, and overall focus behavior. Those hooks, along with `useTableRowGroup`, also handle exposing the table and its contents -to assistive technology using ARIA. `useTableSelectAllCheckbox` and `useTableSelectionCheckbox` handle row selection and associating each checkbox with its respective rows -for assistive technology. - -`useTable` returns props that you should spread onto the table container element: - - - - - -`useTableColumnHeader` returns props for an individual table column header: - - - - - -`useTableRow` returns props for an individual table row: - - - - - -`useTableRowGroup` returns props for a element containing one or more rows of column headers or cells. This is often equivalent -to the header, body, and footer of the table: - - - - - -`useTableRowHeader` returns props for the table cell that serves as a row's header: - - - - +If the table supports row selection, each row can optionally include a selection checkbox in the first column. Additionally, a "select all" checkbox is displayed as the first column header if the table supports multiple row selection. -`useTableCell` returns props for an individual table cell: - - - - - -`useTableSelectionCheckbox` returns props for an individual table row selection checkbox: - - - - - -`useTableSelectAllCheckbox` returns props for the table's "select all" checkbox: - - - - +The , , , and hooks handle keyboard, mouse, and other interactions to support +row selection, in table navigation, and overall focus behavior. Those hooks, along with and , also handle exposing the table and its contents +to assistive technology using ARIA. and handle row selection and associating each checkbox with its respective rows +for assistive technology. Each of these hooks returns props to be spread onto the appropriate HTML element. State is managed by the hook from `@react-stately/table`. The state object should be passed as an option to each of the above hooks where applicable. @@ -145,6 +103,9 @@ but , , , , and components, which support both static and dynamic data. +See the examples in the [usage](#usage) section below for details on how to use these components. + In addition, manages the state necessary for multiple selection and exposes a , @@ -153,278 +114,208 @@ For more information, see [Selection](/react-stately/selection.html). ## Example -This example uses HTML `
` elements to construct the table. It uses `useTableState` to construct the table's collection of rows and columns, -applying the appropriate attributes to each `
` using the various table hooks listed above paired with the div's role in the table. -The table follows the [Collection Components API](../react-stately/collections.html), accepting both static and dynamic collections. +Tables are complex [collection components](../react-stately/collections.html) that are built up from many child elements +including columns, rows, and cells. In this example, we'll use the standard HTML table elements along with hooks from React +Aria for each child. You may also use other elements like `
` to render these components as appropriate. +Since there are many pieces, we'll walk through each of them one by one. -The example below shows a static collection, which can be used when the full set of table contents is known ahead of time. Be sure to note the usage of -`Cell`, `Column`, `Row`, `TableBody`, and `TableHeader` to construct the table structure. These elements are similar to `Item` and `Section` used in other components -following the [Collection Components API](../react-stately/collections.html) mentioned previously, serving as node generators for the table collection. -See [useTableState](/react-stately/useTableState.html) for more info on the props they support. +The hook will be used to render the outer most table element. It uses +the hook to construct the table's collection of rows and columns, +and manage state such as the focused row/cell, selection, and sort column/direction. We'll use the collection to iterate through +the rows and cells of the table and render the relevant components, which we'll define below. -```tsx example export=true +```tsx example export=true render=false import {Cell, Column, Row, TableBody, TableHeader, useTableState} from '@react-stately/table'; import {mergeProps} from '@react-aria/utils'; -import {useCheckbox} from '@react-aria/checkbox'; -import {useContext, useRef} from 'react'; +import {useRef} from 'react'; import {useFocusRing} from '@react-aria/focus'; -import {useToggleState} from '@react-stately/toggle'; -const TableContext = React.createContext(null); -function useTableContext() { - return useContext(TableContext); -} + function Table(props) { - let state = useTableState({...props, showSelectionCheckboxes: true}); + let state = useTableState({...props, showSelectionCheckboxes: props.selectionMode === 'multiple'}); let ref = useRef(); - let scrollViewRef = useRef(); let {collection} = state; - let {gridProps} = useTable({ - ...props, - ref: ref - }, state); - let headerRows = collection.rows.filter(item => item.type === 'headerrow' && item); - let rows = collection.rows.filter(item => item.type === 'item' && item); - let renderColumnNode = (node) => { - if (node.props.isSelectionCell) { - return ; - } - return ; - }; - let renderRowNode = (node) => { - if (node.props.isSelectionCell) { - return ; - } - if (collection.rowHeaderColumnKeys.has(node.column.key)) { - return ; - } - return ; - }; + let {gridProps} = useTable(props, state, ref); + return ( - -
- - {[...headerRows].map(headerRow => ( - - {[...headerRow.childNodes].map((column) => renderColumnNode(column))} - - ))} - -
- - {[...rows].map(row => ( - - {[...row.childNodes].map((node) => renderRowNode(node))} - - ))} - -
-
-
+ + + {collection.headerRows.map(headerRow => ( + + {[...headerRow.childNodes].map(column => + column.props.isSelectionCell + ? + : + )} + + ))} + + + {[...collection.body.childNodes].map(row => ( + + {[...row.childNodes].map(cell => + cell.props.isSelectionCell + ? + : + )} + + ))} + +
); } -function TableHeaderGroup({children, ...otherProps}) { +``` + +### Table header + +A hook will be used to group the rows in the table header and table body. In this example, +we're using HTML table elements, so this will be either a `` or `` element, as passed from the +above `Table` component via the `type` prop. + +```tsx example export=true render=false +function TableRowGroup({type: Element, style, children}) { let {rowGroupProps} = useTableRowGroup(); return ( -
+ {children} -
+ ); } -function TableColumnHeader({column}) { +``` + +The hook will be used to render a header row. Header rows are similar to other rows, +but they don't support user interaction like selection. In this example, there's only one header +row, but there could be multiple in the case of nested columns. See the [example below](#nested-columns) for details. + +```tsx example export=true render=false +function TableHeaderRow({item, state, children}) { + let ref = useRef(); + let {rowProps} = useTableHeaderRow({node: item}, state, ref); + + return ( + + {children} + + ); +} +``` + +The hook will be used to render each column header. Column headers act as a label +for all of the cells in that column, and can optionally support user interaction to sort by the column +and change the sort order. + +The `allowsSorting` property of the column object can be used to determine +if the column supports sorting at all. + +The `sortDescriptor` object stored in the `state` object indicates which column the table is currently sorted by, +as well as the sort direction (ascending or descending). This is used to render an arrow icon to visually +indicate the sort direction. When not sorted by this column, we use `visibility: hidden` to ensure that +we reserve space for this icon at all times. That way the table's layout doesn't shift when we change the +column we're sorting by. See the [example below](#sorting) of all of this in action. + +Finally, we use the hook to ensure that a focus ring is rendered when +the cell is navigated to with the keyboard. + +```tsx example export=true render=false +function TableColumnHeader({column, state}) { let ref = useRef(); - let state = useTableContext(); - let {columnHeaderProps} = useTableColumnHeader({ - node: column, - ref, - colspan: column.colspan - }, state); + let {columnHeaderProps} = useTableColumnHeader({node: column}, state, ref); let {isFocusVisible, focusProps} = useFocusRing(); - let columnProps = column.props; let arrowIcon = state.sortDescriptor?.direction === 'ascending' ? '▲' : '▼'; + return ( -
1 ? 'center' : 'left', + padding: '5px 10px', + outline: isFocusVisible ? '2px solid orange' : 'none', + cursor: 'default' }} ref={ref}> {column.rendered} - {columnProps.allowsSorting && state.sortDescriptor?.column === column.key && -
+ ); } -function TableSelectAllCell({column}) { - let ref = useRef(); - let state = useTableContext(); - let isSingleSelectionMode = state.selectionManager.selectionMode === 'single'; - let {columnHeaderProps} = useTableColumnHeader({ - node: column, - ref, - colspan: column.colspan, - isDisabled: isSingleSelectionMode - }, state); - let {checkboxProps} = useTableSelectAllCheckbox(state); - let inputRef = useRef(null); - let {inputProps} = useCheckbox(checkboxProps, useToggleState(checkboxProps), inputRef); - return ( -
- -
- ); -} -function TableRowGroup({children, ...otherProps}) { - let {rowGroupProps} = useTableRowGroup(); - return ( -
- {children} -
- ); -} -function TableRow({item, children, ...otherProps}) { +``` + +### Table body + +Now that we've covered the table header, let's move on to the body. We'll use +the hook to render each row in the table. +Table rows can be focused and navigated to using the keyboard via the arrow keys. In addition, table rows +can optionally support selection via mouse, touch, or keyboard. Clicking, tapping, or pressing the Space +key anywhere in the row selects it. + +We'll use the object exposed +by the `state` to determine if a row is selected, and render a pink background if so. We'll also use the +hook to render a focus ring when the user navigates to the row with the keyboard. + +```tsx example export=true render=false +function TableRow({item, children, state}) { let ref = useRef(); - let state = useTableContext(); - let isDisabled = state.disabledKeys.has(item.key); - let isSelected = state.selectionManager.isSelected(item.key) && !isDisabled; - let {rowProps} = useTableRow({ - node: item, - isSelected, - ref, - isDisabled - }, state); - let props = mergeProps( - rowProps, - otherProps - ); + let isSelected = state.selectionManager.isSelected(item.key); + let {rowProps} = useTableRow({node: item}, state, ref); + let {isFocusVisible, focusProps} = useFocusRing(); + return ( -
{children} -
- ); -} -function TableHeaderRow({item, children, ...otherProps}) { - return ( -
- {children} -
- ); -} -function TableCheckboxCell({cell}) { - let ref = useRef(); - let state = useTableContext(); - let isDisabled = state.disabledKeys.has(cell.parentKey); - let {gridCellProps} = useTableCell({ - node: cell, - ref, - isDisabled - }, state); - let {checkboxProps} = useTableSelectionCheckbox( - { - key: cell.parentKey, - isDisabled - }, - state - ); - let inputRef = useRef(null); - let {inputProps} = useCheckbox({...checkboxProps, isDisabled}, useToggleState(checkboxProps), inputRef); - return ( -
- {state.selectionManager.selectionMode !== 'none' && - - } -
- ); -} -function TableCell({cell}) { - let ref = useRef(); - let state = useTableContext(); - let isDisabled = state.disabledKeys.has(cell.parentKey); - let {gridCellProps} = useTableCell({ - node: cell, - ref, - isDisabled - }, state); - return ( - + ); } -function TableRowHeader({cell}) { +``` + +Finally, we'll use the hook to render each cell. +Users can use the left and right arrow keys to navigate to each cell in a row, as well as any focusable elements +within a cell. This is indicated by the focus ring, as created with the +hook. The cell's contents are available in the `rendered` property of the cell +object. + +```tsx example export=true render=false +function TableCell({cell, state}) { let ref = useRef(); - let state = useTableContext(); - let isDisabled = state.disabledKeys.has(cell.parentKey); - let {rowHeaderProps} = useTableRowHeader({ - node: cell, - ref, - isDisabled - }, state); - return ( - - ); -} -function TableCellBase({cell, cellRef, ...otherProps}) { + let {gridCellProps} = useTableCell({node: cell}, state, ref); let {isFocusVisible, focusProps} = useFocusRing(); + return ( -
- - {cell.rendered} - -
+ ref={ref}> + {cell.rendered} + ); } +``` + +With all of the above components in place, we can render an example of our Table in action. +This example shows a static collection, where all of the data is hard coded. [See below](#dynamic-collections) +for examples of using this Table component with dynamic collections (e.g. from a server). + +Try tabbing into the table and navigating using the arrow keys. + +```tsx example Name @@ -456,58 +347,75 @@ function TableCellBase({cell, cellRef, ...otherProps}) {
``` -## Usage +### Adding selection -### Dynamic collections +Next, let's add support for selection. For multiple selection, we'll want to add a column of checkboxes to the left +of the table to allow the user to select rows. This is done using the +hook. It is passed the `parentKey` of the cell, which refers to the row the cell is contained within. When the user +checks or unchecks the checkbox, the row will be added or removed from the Table's selection. -Dynamic collections, as shown below, can be used when the table data comes from an external data source such as an API call, or update over time. -In the example below, both the columns and the rows are provided to the table via a render function. +In this example, we pass the result of the `checkboxProps` into the +hook and render an `` element directly, but it's likely you'll have a `Checkbox` component in your component library that uses these hooks already. +See the [useCheckbox docs](useCheckbox.html) for more information. + +```tsx example export=true render=false +import {useToggleState} from '@react-stately/toggle'; +import {useCheckbox} from '@react-aria/checkbox'; + +function TableCheckboxCell({cell, state}) { + let ref = useRef(); + let {gridCellProps} = useTableCell({node: cell}, state, ref); + let {checkboxProps} = useTableSelectionCheckbox({key: cell.parentKey}, state); + + let inputRef = useRef(null); + let {inputProps} = useCheckbox(checkboxProps, useToggleState(checkboxProps), inputRef); -```tsx example -function Example() { - let columns = [ - {name: 'Name', key: 'name'}, - {name: 'Type', key: 'type'}, - {name: 'Date Modified', key: 'date'} - ]; - let rows = [ - {id: '1', name: 'Games', date: '6/7/2020', type: 'File folder'}, - {id: '2', name: 'Program Files', date: '4/7/2021', type: 'File folder'}, - {id: '3', name: 'bootmgr', date: '11/20/2010', type: 'System file'}, - {id: '4', name: 'log.txt', date: '1/18/2016', type: 'Text Document'} - ]; return ( - - - {(column) => ( - - {column.name} - - )} - - - {(item) => ( - - {/* Note this key is equal to the key of the the column, - not the key set on the Row prior */} - {(key) => {item[key]}} - - )} - -
+ + + ); } ``` -### Selection +We also want the user to be able to select all rows in the table at once. This is possible using the ⌘ Cmd + A +keyboard shortcut, but we'll also add a checkbox into the table header to do this and represent the selection state visually. +This is done using the hook. When all rows are selected, +the checkbox will be shown as checked, and when only some rows are selected, the checkbox will be rendered in an indeterminate state. +The user can check or uncheck the checkbox to select all or clear the selection, respectively. -By default, `useTableState` doesn't allow row selection but this can be modified using the `selectionMode` prop. Use `defaultSelectedKeys` to provide a default set of selected rows. -Note that the value of the selected keys must match the `key` prop of the row. +```tsx example export=true render=false +function TableSelectAllCell({column, state}) { + let ref = useRef(); + let isSingleSelectionMode = state.selectionManager.selectionMode === 'single'; + let {columnHeaderProps} = useTableColumnHeader({node: column}, state, ref); + + let {checkboxProps} = useTableSelectAllCheckbox(state); + let inputRef = useRef(null); + let {inputProps} = useCheckbox(checkboxProps, useToggleState(checkboxProps), inputRef); + + return ( + + + + ); +} +``` -The example below uses `defaultSelectedKeys` to select the row with key equal to "2". +The following example shows how to enable multiple selection support using the Table component we built above. +It's as simple as setting the `selectionMode` prop to `"multiple"`. Because we set the `showSelectionCheckboxes` +option of `useTableState` to true when multiple selection is enabled, an extra column for these checkboxes is +automatically added for us. ```tsx example - +
Name Type @@ -538,38 +446,47 @@ The example below uses `defaultSelectedKeys` to select the row with key equal to
``` -To programmatically control row selection, use the `selectedKeys` prop paired with the `onSelectionChange` callback. The `key` prop from the selected row will -be passed into the callback when the row is pressed, allowing you to update `selectedKeys` accordingly. +And that's it! We now have a fully interactive table component that can support keyboard navigation, single or multiple selection, +as well as column sorting. In addition, it is fully accessible for screen readers and other assistive technology. See below for more +examples of how to use the Table component that we've built. -Here is how you would control selection for the above example. +## Usage + +### Dynamic collections + +So far, our examples have shown static collections, where the data is hard coded. +Dynamic collections, as shown below, can be used when the table data comes from an external data source such as an API, or updates over time. +In the example below, both the columns and the rows are provided to the table via a render function. You can also make the columns static and +only the rows dynamic. ```tsx example export=true -function PokemonTable(props) { +function ExampleTable(props) { let columns = [ - {name: 'Name', uid: 'name'}, - {name: 'Type', uid: 'type'}, - {name: 'Level', uid: 'level'} + {name: 'Name', key: 'name'}, + {name: 'Type', key: 'type'}, + {name: 'Date Modified', key: 'date'} ]; + let rows = [ - {id: '1', name: 'Charizard', type: 'Fire, Flying', level: '67'}, - {id: '2', name: 'Blastoise', type: 'Water', level: '56'}, - {id: '3', name: 'Venusaur', type: 'Grass, Poison', level: '83'}, - {id: '4', name: 'Pikachu', type: 'Electric', level: '100'} + {id: '1', name: 'Games', date: '6/7/2020', type: 'File folder'}, + {id: '2', name: 'Program Files', date: '4/7/2021', type: 'File folder'}, + {id: '3', name: 'bootmgr', date: '11/20/2010', type: 'System file'}, + {id: '4', name: 'log.txt', date: '1/18/2016', type: 'Text Document'} ]; - let [selected, setSelected] = React.useState(new Set(['2'])); + return ( - +
{column => ( - + {column.name} )} {item => ( - - {key => {item[key]}} + + {columnKey => {item[columnKey]}} )} @@ -578,49 +495,105 @@ function PokemonTable(props) { } ``` +### Single selection + +By default, `useTableState` doesn't allow row selection but this can be enabled using the `selectionMode` prop. Use `defaultSelectedKeys` to provide a default set of selected rows. +Note that the value of the selected keys must match the `key` prop of the row. + +The example below enables single selection mode, and uses `defaultSelectedKeys` to select the row with key equal to "2". +A user can click on a different row to change the selection, or click on the same row again to deselect it entirely. + +```tsx example +// Using the example above + +``` + +### Multiple selection + Multiple selection can be enabled by setting `selectionMode` to `multiple`. ```tsx example -// Using the same table as above - +// Using the example above + ``` +### Disallow empty selection + Table also supports a `disallowEmptySelection` prop which forces the user to have at least one row in the Table selected at all times. +In this mode, if a single row is selected and the user presses it, it will not be deselected. ```tsx example -// Using the same table as above - +// Using the example above + +``` + +### Controlled selection + +To programmatically control row selection, use the `selectedKeys` prop paired with the `onSelectionChange` callback. The `key` prop from the selected rows will +be passed into the callback when the row is pressed, allowing you to update state accordingly. + +```tsx example export=true +function PokemonTable(props) { + let columns = [ + {name: 'Name', uid: 'name'}, + {name: 'Type', uid: 'type'}, + {name: 'Level', uid: 'level'} + ]; + + let rows = [ + {id: 1, name: 'Charizard', type: 'Fire, Flying', level: '67'}, + {id: 2, name: 'Blastoise', type: 'Water', level: '56'}, + {id: 3, name: 'Venusaur', type: 'Grass, Poison', level: '83'}, + {id: 4, name: 'Pikachu', type: 'Electric', level: '100'} + ]; + + let [selectedKeys, setSelectedKeys] = React.useState(new Set([2])); + + return ( +
+ + {column => ( + + {column.name} + + )} + + + {item => ( + + {columnKey => {item[columnKey]}} + + )} + +
+ ); +} ``` ### Disabled rows You can disable specific rows by providing an array of keys to `useTableState` via the `disabledKeys` prop. This will prevent rows from being selectable as shown in the example below. -Note that you are responsible for the styling of disabled rows. +Note that you are responsible for the styling of disabled rows, however, the selection checkbox will be automatically disabled. ```tsx example // Using the same table as above - + ``` ### Sorting Table supports sorting its data when a column header is pressed. To designate that a Column should support sorting, provide it with -the `allowsSorting` prop. The Table accepts a `SortDescriptor` prop that defines the current column key to sort by and the sort direction (ascending/descending). -When the user presses a column's sort icon, the column's key and sort direction is passed into the `onSortChange` callback, allowing you to update -the `SortDescriptor` appropriately. +the `allowsSorting` prop. The Table accepts a `sortDescriptor` prop that defines the current column key to sort by and the sort direction (ascending/descending). +When the user presses a sortable column header, the column's key and sort direction is passed into the `onSortChange` callback, allowing you to update +the `sortDescriptor` appropriately. This example performs client side sorting by passing a `sort` function to the [useAsyncList](../react-stately/useAsyncList.html) hook. See the docs for more information on how to perform server side sorting. ```tsx example import {useAsyncList} from '@react-stately/data'; + function AsyncSortTable() { - let columns = [ - {name: 'Name', key: 'name'}, - {name: 'Height', key: 'height'}, - {name: 'Mass', key: 'mass'}, - {name: 'Birth Year', key: 'birth_year'} - ]; let list = useAsyncList({ async load({signal}) { let res = await fetch(`https://swapi.dev/api/people/?search`, {signal}); @@ -630,38 +603,33 @@ function AsyncSortTable() { }; }, async sort({items, sortDescriptor}) { - let sorted = items.sort((a, b) => { - let cmp; - let first = a[sortDescriptor.column].replace("BBY", ""); - let second = b[sortDescriptor.column].replace("BBY", ""); - if (+first || +second) { - cmp = +first < +second ? -1 : 1; - } else { - cmp = first <= second ? -1 : 1; - } - if (sortDescriptor.direction === 'descending') { - cmp *= -1; - } - return cmp; - }); return { - items: sorted + items: items.sort((a, b) => { + let first = a[sortDescriptor.column]; + let second = b[sortDescriptor.column]; + let cmp = (parseInt(first) || first) < (parseInt(second) || second) ? -1 : 1; + if (sortDescriptor.direction === 'descending') { + cmp *= -1; + } + return cmp; + }) }; } }); + return ( - - - {(column) => ( - - {column.name} - - )} +
+ + Name + Height + Mass + Birth Year - - {(item) => ( - {(key) => {item[key]}} + + {item => ( + + {columnKey => {item[columnKey]}} + )}
@@ -669,13 +637,107 @@ function AsyncSortTable() { } ``` +### Nested columns + +Columns can be nested to create column groups. This will result in more than one header row to be created, with the `colspan` +attribute of each column header cell set to the appropriate value so that the columns line up. Data for the leaf columns +appears in each row of the table body. + +This example also shows the use of the `isRowHeader` prop for `Column`, which controls which columns are included in the +accessibility name for each row. By default, only the first column is included, but in some cases more than one column may +be used to represent the row. In this example, the first and last name columns are combined to form the ARIA label for the row. +Only leaf columns may be marked as row headers. + +```tsx example + + + + First Name + Last Name + + + Age + Birthday + + + + + Sam + Smith + 36 + May 3 + + + Julia + Jones + 24 + February 10 + + + Peter + Parker + 28 + September 7 + + + Bruce + Wayne + 32 + December 18 + + +
+``` + +### Dynamic nested columns + +Nested columns can also be defined dynamically using the function syntax and the `childColumns` prop. +The following example is the same as the example above, but defined dynamically. + +```tsx example +let columns = [ + {name: 'Name', key: 'name', children: [ + {name: 'First Name', key: 'first', isRowHeader: true}, + {name: 'Last Name', key: 'last', isRowHeader: true} + ]}, + {name: 'Information', key: 'info', children: [ + {name: 'Age', key: 'age'}, + {name: 'Birthday', key: 'birthday'} + ]} +]; + +let rows = [ + {id: 1, first: 'Sam', last: 'Smith', age: 36, birthday: 'May 3'}, + {id: 2, first: 'Julia', last: 'Jones', age: 24, birthday: 'February 10'}, + {id: 3, first: 'Peter', last: 'Parker', age: 28, birthday: 'September 7'}, + {id: 4, first: 'Bruce', last: 'Wayne', age: 32, birthday: 'December 18'} +]; + + + + {column => ( + + {column.name} + + )} + + + {item => ( + + {columnKey => {item[columnKey]}} + + )} + +
+``` + ## Internationalization `useTable` handles some aspects of internationalization automatically. For example, type to select is implemented with an [Intl.Collator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Collator) -for internationalized string matching. You are responsible for localizing all text content within the table. -Make sure that some types of content (e.g. file extensions) are not translated. +for internationalized string matching, and keyboard navigation is mirrored in right-to-left languages. +You are responsible for localizing all text content within the table. ### RTL diff --git a/packages/dev/parcel-transformer-mdx-docs/MDXTransformer.js b/packages/dev/parcel-transformer-mdx-docs/MDXTransformer.js index 84526ee9771..0e32c313d9f 100644 --- a/packages/dev/parcel-transformer-mdx-docs/MDXTransformer.js +++ b/packages/dev/parcel-transformer-mdx-docs/MDXTransformer.js @@ -65,11 +65,13 @@ module.exports = new Transformer({ provider = 'ExampleThemeSwitcher'; } - if (/^\s*function (.|\n)*}\s*$/.test(code)) { - let name = code.match(/^\s*function (.*?)\s*\(/)[1]; - code = `${code}\nReactDOM.render(<${provider}><${name} />, document.getElementById("${id}"));`; - } else if (/^<(.|\n)*>$/m.test(code)) { - code = code.replace(/^(<(.|\n)*>)$/m, `ReactDOM.render(<${provider}>$1, document.getElementById("${id}"));`); + if (!options.includes('render=false')) { + if (/^\s*function (.|\n)*}\s*$/.test(code)) { + let name = code.match(/^\s*function (.*?)\s*\(/)[1]; + code = `${code}\nReactDOM.render(<${provider}><${name} />, document.getElementById("${id}"));`; + } else if (/^<(.|\n)*>$/m.test(code)) { + code = code.replace(/^(<(.|\n)*>)$/m, `ReactDOM.render(<${provider}>$1, document.getElementById("${id}"));`); + } } if (!options.includes('export=true')) { @@ -78,6 +80,11 @@ module.exports = new Transformer({ exampleCode.push(code); + if (options.includes('render=false')) { + node.meta = null; + return transformExample(node, preRelease); + } + if (meta === 'snippet') { node.meta = null; return [ From 4e5d96c59862ccc164ab22dbadc494d40dd370f7 Mon Sep 17 00:00:00 2001 From: Devon Govett Date: Thu, 17 Jun 2021 17:57:24 -0700 Subject: [PATCH 53/62] Fix keys --- packages/@react-aria/table/docs/useTable.mdx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/@react-aria/table/docs/useTable.mdx b/packages/@react-aria/table/docs/useTable.mdx index 174b4affd64..e36b75a2281 100644 --- a/packages/@react-aria/table/docs/useTable.mdx +++ b/packages/@react-aria/table/docs/useTable.mdx @@ -468,10 +468,10 @@ function ExampleTable(props) { ]; let rows = [ - {id: '1', name: 'Games', date: '6/7/2020', type: 'File folder'}, - {id: '2', name: 'Program Files', date: '4/7/2021', type: 'File folder'}, - {id: '3', name: 'bootmgr', date: '11/20/2010', type: 'System file'}, - {id: '4', name: 'log.txt', date: '1/18/2016', type: 'Text Document'} + {id: 1, name: 'Games', date: '6/7/2020', type: 'File folder'}, + {id: 2, name: 'Program Files', date: '4/7/2021', type: 'File folder'}, + {id: 3, name: 'bootmgr', date: '11/20/2010', type: 'System file'}, + {id: 4, name: 'log.txt', date: '1/18/2016', type: 'Text Document'} ]; return ( From 1cd39f79be54e57ecb207d2d686ae6541198865e Mon Sep 17 00:00:00 2001 From: danni Date: Mon, 21 Jun 2021 13:25:28 -0700 Subject: [PATCH 54/62] updating table docs --- .../@react-spectrum/table/docs/TableView.mdx | 87 ++----------------- 1 file changed, 6 insertions(+), 81 deletions(-) diff --git a/packages/@react-spectrum/table/docs/TableView.mdx b/packages/@react-spectrum/table/docs/TableView.mdx index 5b6ff53ce58..f0dac1181b4 100644 --- a/packages/@react-spectrum/table/docs/TableView.mdx +++ b/packages/@react-spectrum/table/docs/TableView.mdx @@ -122,7 +122,7 @@ let rows = [ ``` ### Internationalization -To internationalize a TableView, all text content within the TableView should be replaced with localized strings. This includes the `aria-label` provided to the TableView if any. +To internationalize a TableView, all text content within the TableView should be localized. This includes the `aria-label` provided to the TableView if any. For languages that are read right-to-left (e.g. Hebrew and Arabic), the layout of TableView is automatically flipped. ## Labeling @@ -327,8 +327,7 @@ In this mode, if a single row is selected and the user presses it, it will not b ### Disabled rows -You can disable specific rows by providing an array of keys to TableView via the `disabledKeys` prop. This will prevent rows from being -selectable as shown in the example below. +You can disable specific rows by providing an array of keys to TableView via the `disabledKeys` prop. This will prevent rows from being selectable as shown in the example below. ```tsx example // Using the same table as above @@ -339,8 +338,7 @@ selectable as shown in the example below. TableView supports sorting its data when a column header is pressed. To designate that a Column should support sorting, provide it with the `allowsSorting` prop. The TableView accepts a `sortDescriptor` prop that defines the current column key to sort by and the sort direction (ascending/descending). -When the user presses a sortable column header, the column's key and sort direction is passed into the `onSortChange` callback, allowing you to update -the `sortDescriptor` appropriately. +When the user presses a sortable column header, the column's key and sort direction is passed into the `onSortChange` callback, allowing you to update the `sortDescriptor` appropriately. This example performs client side sorting by passing a `sort` function to the [useAsyncList](../react-stately/useAsyncList.html) hook. See the docs for more information on how to perform server side sorting. @@ -477,7 +475,7 @@ function AsyncSortTable() { ``` ### Column widths -Columns support three different width props: `minWidth`, `width`, and `maxWidth`. +Columns support three different width props: `minWidth`, `width`, and `maxWidth`. Each of these props accepts fixed values or percentages. TableView prioritizes columns with defined widths and divides the remaining space evenly amongst the other columns. ```tsx example @@ -550,78 +548,6 @@ Columns support three different width props: `minWidth`, `width`, and `maxWidth` ``` -### Focusable cells -Cells accept any renderable node, allowing you to have focusable children within the TableView. - -```tsx example -import Edit from '@spectrum-icons/workflow/Edit'; -import Delete from '@spectrum-icons/workflow/Delete'; - - - - - Name - Add - Delete - - - - Red Panda - - - - - - - - - - - - - Harbor Seal - - - - - - - - - - - - - Groundhog - - - - - - - - - - - - - Otter - - - - - - - - - - - - - - -``` - ### Hide header Individual column headers can be hidden by providing the `hideHeader` prop to the Column. A tooltip is rendered when the column header is focused to compensate for the lack of a visual title. Note that the `hideHeader` prop is specifically intended for columns that contain ActionButtons instead @@ -708,14 +634,13 @@ Use the `renderEmptyState` prop to customize what the TableView will display if ```tsx example import {Content} from '@react-spectrum/view'; import {IllustratedMessage} from '@react-spectrum/illustratedmessage'; +import NotFound from '@spectrum-icons/illustrations/NotFound'; import {Heading} from '@react-spectrum/text'; function renderEmptyState() { return ( - - - + No results No results found From 1be9ab1b62f8d3724d3bd5ba20cd2a810b3cb538 Mon Sep 17 00:00:00 2001 From: danni Date: Mon, 21 Jun 2021 13:35:16 -0700 Subject: [PATCH 55/62] update content --- .../@react-spectrum/table/docs/TableView.mdx | 73 +++++++++---------- 1 file changed, 35 insertions(+), 38 deletions(-) diff --git a/packages/@react-spectrum/table/docs/TableView.mdx b/packages/@react-spectrum/table/docs/TableView.mdx index f0dac1181b4..c058471365c 100644 --- a/packages/@react-spectrum/table/docs/TableView.mdx +++ b/packages/@react-spectrum/table/docs/TableView.mdx @@ -40,49 +40,46 @@ keywords: [table, grid] ## Example ```tsx example - - - - Name - Type - Date Modified - - - - Games - File folder - 6/7/2020 - - - Program Files - File folder - 4/7/2021 - - - bootmgr - System file - 11/20/2010 - - - log.txt - Text Document - 1/18/2016 - - - - + + + Name + Type + Date Modified + + + + Games + File folder + 6/7/2020 + + + Program Files + File folder + 4/7/2021 + + + bootmgr + System file + 11/20/2010 + + + log.txt + Text Document + 1/18/2016 + + + ``` +fix this hey , ## Content -TableView expects a TableHeader and a TableBody as children. In turn, TableHeader and TableBody follow the [Collection Components](../react-stately/collections.html) API. -The TableHeader accepts either static Columns or a `columns` prop with a renderer function for dynamic rendering. Similarly, TableBody accepts either static Rows or a `items` prop with a renderer function. -Row follows the same pattern, accepting Cells as children instead. +TableView is a complex [collection component](../react-stately/collections.html) that is built up from many child elements including columns, rows, and cells. Columns are defined within a TableHeader element and rows are defined within a TableBody element. Rows contain Cell elements that correspond to each column. Cells accept any element, allowing you to have focusable children within the TableView. + +Basic usage of TableView, seen in the example above, shows the use of a static collection where the contents of the TableView is hard coded. Dynamic collections, as shown below, can be used when the options come from an external data source such as an API, or update over time. Providing the data in this way allows TableView to automatically cache the rendering of each item, which dramatically improves performance. -Basic usage of TableView, seen in the example above, shows multiple Columns and Rows populated with strings and Cells respectively. Static collections, as in this example, can be used when the contents of the TableView is known ahead of time. +Columns and rows can be statically defined as children, or generated dynamically using a function based on the data passed to the `columns` or `items` prop respectively. Cells can also be statically defined as children, or generated dynamically based on the columns defined in the TableHeader. -Dynamic collections, as shown below, can be used when the options come from an external data source such as an API call, or update over time. Providing the data in this way allows TableView to automatically cache the rendering of each item, -which dramatically improves performance. Make sure that each rendered property in the Row object matches with a Column's key. In the example below, the `uid` of each Column is set as its key and matches with a -property within each Row object. +Each column and row has a unique key defined by the data. In the example below, the `uid` property of the column object is used as the `key` for the Column element within the TableHeader. The `key` of each row element is implicitly defined by the id property of the row object. See [collections](../react-stately/collections.html#unique-keys) to learn more keys in dynamic collections. ```tsx example let columns = [ From c2646c51896a28ec1130186a68fe1b111f3a784f Mon Sep 17 00:00:00 2001 From: danni Date: Mon, 21 Jun 2021 14:14:14 -0700 Subject: [PATCH 56/62] tables? --- .../@react-spectrum/table/docs/TableView.mdx | 191 +++++++++++++++--- 1 file changed, 162 insertions(+), 29 deletions(-) diff --git a/packages/@react-spectrum/table/docs/TableView.mdx b/packages/@react-spectrum/table/docs/TableView.mdx index c058471365c..0c44264a217 100644 --- a/packages/@react-spectrum/table/docs/TableView.mdx +++ b/packages/@react-spectrum/table/docs/TableView.mdx @@ -40,7 +40,7 @@ keywords: [table, grid] ## Example ```tsx example - + Name Type @@ -70,7 +70,6 @@ keywords: [table, grid] ``` -fix this hey , ## Content TableView is a complex [collection component](../react-stately/collections.html) that is built up from many child elements including columns, rows, and cells. Columns are defined within a TableHeader element and rows are defined within a TableBody element. Rows contain Cell elements that correspond to each column. Cells accept any element, allowing you to have focusable children within the TableView. @@ -97,7 +96,6 @@ let rows = [ {column => ( @@ -117,6 +115,143 @@ let rows = [ ``` +### Layout +```tsx example +///- begin collapse -/// +let columns = [ + {name: 'First name', id: 'first_name'}, + {name: 'Last name', id: 'last_name'}, + {name: 'City', id: 'city'} +]; + +let rows = [ +{"id":1,"first_name":"Andras","last_name":"Rodmell","city":"Tilburg"}, +{"id":2,"first_name":"Hansiain","last_name":"Muino","city":"Hollola"}, +{"id":3,"first_name":"Northrop","last_name":"Adnet","city":"Lai Cách"}, +{"id":4,"first_name":"Giana","last_name":"Phython","city":"Laspezia"}, +{"id":5,"first_name":"Maud","last_name":"Jaram","city":"Tipaz"}, +{"id":6,"first_name":"Gasparo","last_name":"Wiggin","city":"Feuknoni"}, +{"id":7,"first_name":"Phillie","last_name":"Lezemere","city":"Krajan Sidodadi"}, +{"id":8,"first_name":"Kailey","last_name":"Du Plantier","city":"Shangping"}, +{"id":9,"first_name":"Brady","last_name":"Oxtarby","city":"Bang Mun Nak"}, +{"id":10,"first_name":"Ekaterina","last_name":"Crennan","city":"Santo Antônio do Amparo"}, +{"id":11,"first_name":"Jaine","last_name":"Trembey","city":"Manūjān"}, +{"id":12,"first_name":"Emmey","last_name":"Dunguy","city":"Garhi Yāsīn"}, +{"id":13,"first_name":"Camille","last_name":"Millwall","city":"Orion"}, +{"id":14,"first_name":"Staci","last_name":"Glusby","city":"Alofi"}, +{"id":15,"first_name":"Ned","last_name":"Crumbleholme","city":"Ban Bueng"}, +{"id":16,"first_name":"Tana","last_name":"Beardsworth","city":"Puerto Aisén"}, +{"id":17,"first_name":"Dewain","last_name":"Fladgate","city":"London"}, +{"id":18,"first_name":"Thurstan","last_name":"Trembath","city":"Orléans"}, +{"id":19,"first_name":"Vaclav","last_name":"Fitzpayn","city":"Huangchen"}, +{"id":20,"first_name":"Keven","last_name":"Monkeman","city":"Medenychi"}, +{"id":21,"first_name":"Talia","last_name":"Ryman","city":"Piteå"}, +{"id":22,"first_name":"Percy","last_name":"Le Teve","city":"Terny"}, +{"id":23,"first_name":"Jackson","last_name":"Anten","city":"Beiling"}, +{"id":24,"first_name":"Jakob","last_name":"Goullee","city":"Pelym"}, +{"id":25,"first_name":"Dru","last_name":"Klainer","city":"Zavrč"}, +{"id":26,"first_name":"Lucie","last_name":"Donahue","city":"Kiryū"}, +{"id":27,"first_name":"Marc","last_name":"McPeck","city":"Nong Muang Khai"}, +{"id":28,"first_name":"Vivianna","last_name":"Allport","city":"Kajatian"}, +{"id":29,"first_name":"Drud","last_name":"Hurn","city":"Bambuí"}, +{"id":30,"first_name":"Trever","last_name":"Ambrodi","city":"Xiangtan"}, +{"id":31,"first_name":"Gwennie","last_name":"Kingswold","city":"San Benito"}, +{"id":32,"first_name":"Karlan","last_name":"Tilby","city":"Patrída"}, +{"id":33,"first_name":"Heddie","last_name":"Sneath","city":"Esperanza"}, +{"id":34,"first_name":"Harlen","last_name":"Sandells","city":"Harrismith"}, +{"id":35,"first_name":"Gavan","last_name":"Halward","city":"Al Ḩayfah"}, +{"id":36,"first_name":"Andre","last_name":"Everest","city":"Bahui"}, +{"id":37,"first_name":"Merilyn","last_name":"Rowbrey","city":"Imishli"}, +{"id":38,"first_name":"Abe","last_name":"Pecht","city":"Pangkalan Kasai"}, +{"id":39,"first_name":"Britt","last_name":"Collingridge","city":"Érd"}, +{"id":40,"first_name":"Leticia","last_name":"Thorndycraft","city":"Paita"}, +{"id":41,"first_name":"Eward","last_name":"Tigwell","city":"Aral"}, +{"id":42,"first_name":"Torrie","last_name":"Curzon","city":"Stockholm"}, +{"id":43,"first_name":"Jenifer","last_name":"Swalwel","city":"Jinniu"}, +{"id":44,"first_name":"Marianna","last_name":"Radley","city":"Hedi"}, +{"id":45,"first_name":"Antoine","last_name":"Tyers","city":"Hewa"}, +{"id":46,"first_name":"Darline","last_name":"Gallehawk","city":"København"}, +{"id":47,"first_name":"Rikki","last_name":"Rosenzveig","city":"Affery"}, +{"id":48,"first_name":"Debera","last_name":"Vedenichev","city":"Żywiec"}, +{"id":49,"first_name":"Morena","last_name":"Hewins","city":"Las Lajas"}, +{"id":50,"first_name":"Cordy","last_name":"Reimer","city":"Derbent"}, +{"id":51,"first_name":"Quint","last_name":"Thoresbie","city":"Guyang"}, +{"id":52,"first_name":"Christean","last_name":"Deere","city":"Waegwan"}, +{"id":53,"first_name":"Moyra","last_name":"Battelle","city":"Villa Presidente Frei, Ñuñoa, Santiago, Chile"}, +{"id":54,"first_name":"Fayth","last_name":"Gallafant","city":"Kedungharjo"}, +{"id":55,"first_name":"Thedrick","last_name":"Duddy","city":"Thị Trấn Mường Lát"}, +{"id":56,"first_name":"George","last_name":"Rickerd","city":"Zarqa"}, +{"id":57,"first_name":"Nikos","last_name":"Rideout","city":"Yuanqiao"}, +{"id":58,"first_name":"Alejandra","last_name":"Le Port","city":"Il’ichëvo"}, +{"id":59,"first_name":"Eleonora","last_name":"Gibberd","city":"Sua"}, +{"id":60,"first_name":"Archibaldo","last_name":"Place","city":"Sidayu"}, +{"id":61,"first_name":"Helen","last_name":"Brenton","city":"Kuressaare"}, +{"id":62,"first_name":"Leyla","last_name":"Armstead","city":"Haifa"}, +{"id":63,"first_name":"Bridget","last_name":"Strotone","city":"Karasuk"}, +{"id":64,"first_name":"Jarid","last_name":"Packer","city":"Студеничани"}, +{"id":65,"first_name":"Christos","last_name":"Natt","city":"Nova Russas"}, +{"id":66,"first_name":"Alwyn","last_name":"Mingaud","city":"Conde"}, +{"id":67,"first_name":"Archy","last_name":"Thorneywork","city":"Gulu"}, +{"id":68,"first_name":"Iolanthe","last_name":"Spurgeon","city":"Ayrihuanca"}, +{"id":69,"first_name":"Rossy","last_name":"Axford","city":"Ledeč nad Sázavou"}, +{"id":70,"first_name":"Consuela","last_name":"Lillegard","city":"Finote Selam"}, +{"id":71,"first_name":"Salomon","last_name":"Buckney","city":"Kampokpok"}, +{"id":72,"first_name":"Celene","last_name":"Espley","city":"Sinubong"}, +{"id":73,"first_name":"Kristos","last_name":"Denyukhin","city":"Las Palmas"}, +{"id":74,"first_name":"Bertha","last_name":"Mallabon","city":"Vera"}, +{"id":75,"first_name":"Jorry","last_name":"Yuryev","city":"Carletonville"}, +{"id":76,"first_name":"Holly-anne","last_name":"Wagstaffe","city":"Sukadana"}, +{"id":77,"first_name":"Lara","last_name":"Shears","city":"Gambēla"}, +{"id":78,"first_name":"Romonda","last_name":"Glanville","city":"Donglu"}, +{"id":79,"first_name":"Felice","last_name":"Pryde","city":"Sapadun"}, +{"id":80,"first_name":"Nick","last_name":"Kidney","city":"Chernigovka"}, +{"id":81,"first_name":"Hermina","last_name":"Dooley","city":"New Agutaya"}, +{"id":82,"first_name":"Ketty","last_name":"FitzGeorge","city":"Abaza"}, +{"id":83,"first_name":"Patrizio","last_name":"Bovingdon","city":"‘Ayn al ‘Arab"}, +{"id":84,"first_name":"Caitrin","last_name":"Braine","city":"Il’inskiy"}, +{"id":85,"first_name":"Ian","last_name":"De Few","city":"Jatinagara"}, +{"id":86,"first_name":"Eben","last_name":"Adan","city":"Bolong"}, +{"id":87,"first_name":"Peder","last_name":"Innott","city":"Gampaha"}, +{"id":88,"first_name":"Selie","last_name":"Cruise","city":"Mariscala"}, +{"id":89,"first_name":"Melania","last_name":"Meredyth","city":"La’ershan"}, +{"id":90,"first_name":"Antonina","last_name":"Proby","city":"Shantoudian"}, +{"id":91,"first_name":"Sabra","last_name":"Dreng","city":"Dzhankoy"}, +{"id":92,"first_name":"Sibeal","last_name":"Hall-Gough","city":"Mengxi"}, +{"id":93,"first_name":"Fidel","last_name":"Maisey","city":"Gus’-Khrustal’nyy"}, +{"id":94,"first_name":"Alejandro","last_name":"Devey","city":"Charata"}, +{"id":95,"first_name":"Norina","last_name":"Stoyle","city":"Malaya Dubna"}, +{"id":96,"first_name":"Lari","last_name":"Kiezler","city":"Guaíba"}, +{"id":97,"first_name":"Percival","last_name":"Geffinger","city":"Ngeni"}, +{"id":98,"first_name":"Jo","last_name":"Spoure","city":"Karata"}, +{"id":99,"first_name":"Karlie","last_name":"Gooddy","city":"Pelem"}, +{"id":100,"first_name":"Edmon","last_name":"Alsopp","city":"Sandu"}]; +///- end collapse -/// + +// Table data hidden for example +// Display table in flex with actiongroup above. without height + +
sdghsfgiudsfglibdfilbfg
+ + + {column => ( + + {column.name} + + )} + + + {item => ( + + {columnKey => {item[columnKey]}} + + )} + + +
+``` ### Internationalization To internationalize a TableView, all text content within the TableView should be localized. This includes the `aria-label` provided to the TableView if any. @@ -132,32 +267,30 @@ to one or more Columns, allowing you to customize which columns should label the The example below applies `isRowHeader` to the "First Name" and "Last Name" columns so that each row is announced with the person's full name (e.g. "John Doe"). ```tsx example - - - - First Name - Last Name - Age - - - - John - Doe - 45 - - - Jane - Doe - 37 - - - Joe - Schmoe - 67 - - - - + + + First Name + Last Name + Age + + + + John + Doe + 45 + + + Jane + Doe + 37 + + + Joe + Schmoe + 67 + + + ``` ## Asynchronous loading From 4ff7aedf5393e71a8aa0a6b1616cde077cf0c5c8 Mon Sep 17 00:00:00 2001 From: danni Date: Mon, 21 Jun 2021 14:46:19 -0700 Subject: [PATCH 57/62] minheight 0 --- packages/@react-spectrum/table/docs/TableView.mdx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/@react-spectrum/table/docs/TableView.mdx b/packages/@react-spectrum/table/docs/TableView.mdx index 0c44264a217..d7a57396dd5 100644 --- a/packages/@react-spectrum/table/docs/TableView.mdx +++ b/packages/@react-spectrum/table/docs/TableView.mdx @@ -116,6 +116,11 @@ let rows = [
``` ### Layout +TableViews are often contained within a page layout that defines the size of the table. For example, a page might have a header or toolbar with a TableView below that fills the remaining vertical space. TableView is designed to scroll internally while the column headers remain fixed. Because of this, TableViews should not be placed within a scrollable container. + +The example below shows how to use a Flex component with to size a TableView, filling the remainder of the available space. + + ```tsx example ///- begin collapse -/// let columns = [ @@ -233,6 +238,7 @@ let rows = [
sdghsfgiudsfglibdfilbfg
{column => ( From 279cacc83d6068b17634517b93518d192572eb0c Mon Sep 17 00:00:00 2001 From: danni Date: Mon, 21 Jun 2021 15:36:18 -0700 Subject: [PATCH 58/62] remove flex from examples --- .../@react-spectrum/table/docs/TableView.mdx | 309 +++++++++--------- 1 file changed, 150 insertions(+), 159 deletions(-) diff --git a/packages/@react-spectrum/table/docs/TableView.mdx b/packages/@react-spectrum/table/docs/TableView.mdx index d7a57396dd5..f03e5f00dc5 100644 --- a/packages/@react-spectrum/table/docs/TableView.mdx +++ b/packages/@react-spectrum/table/docs/TableView.mdx @@ -20,6 +20,7 @@ import {ActionButton} from '@react-spectrum/button'; import Add from '@spectrum-icons/workflow/Add'; import {Cell, Column, Row, TableView, TableBody, TableHeader} from '@react-spectrum/table'; import {Flex} from '@react-spectrum/layout'; +import {ActionGroup, Item} from '@react-spectrum/actiongroup' ``` --- @@ -118,7 +119,7 @@ let rows = [ ### Layout TableViews are often contained within a page layout that defines the size of the table. For example, a page might have a header or toolbar with a TableView below that fills the remaining vertical space. TableView is designed to scroll internally while the column headers remain fixed. Because of this, TableViews should not be placed within a scrollable container. -The example below shows how to use a Flex component with to size a TableView, filling the remainder of the available space. +The example below shows how to use a [Flex](Flex.html) component to achieve the layout described above. Note the TableView uses the `flex` prop to fill the remainder of the available space. ```tsx example @@ -232,10 +233,12 @@ let rows = [ {"id":100,"first_name":"Edmon","last_name":"Alsopp","city":"Sandu"}]; ///- end collapse -/// -// Table data hidden for example -// Display table in flex with actiongroup above. without height - -
sdghsfgiudsfglibdfilbfg
+ + + Add + Delete + Edit + - - - {(column) => ( - - {column.name} - - )} - - - {(item) => ( - {(key) => {item[key]}} - )} - - - + + + {(column) => ( + + {column.name} + + )} + + + {(item) => ( + {(key) => {item[key]}} + )} + + ); } ``` @@ -366,7 +367,7 @@ Use `defaultSelectedKeys` to provide a default set of selected rows. Note that t The example below enables multiple selection mode, and uses `defaultSelectedKeys` to select the rows with keys "2" and "4". ```tsx example - + Name Type @@ -422,7 +423,7 @@ function PokemonTable(props) { let [selectedKeys, setSelectedKeys] = React.useState(new Set([2])); return ( - + {column => ( @@ -505,7 +506,7 @@ function AsyncSortTable() { }); return ( - + Name Height @@ -577,111 +578,105 @@ function AsyncSortTable() { [View guidelines](https://spectrum.adobe.com/page/table/#Usage-guidelines) ```tsx example - - - - Name - Type - Size - - - - 2021406_Proposal - PDF - 86 KB - - - Budget Template - XLS - 120 KB - - - Onboarding - PPT - 472 KB - - - Welcome - TXT - 24 KB - - - - + + + Name + Type + Size + + + + 2021406_Proposal + PDF + 86 KB + + + Budget Template + XLS + 120 KB + + + Onboarding + PPT + 472 KB + + + Welcome + TXT + 24 KB + + + ``` ### Column widths Columns support three different width props: `minWidth`, `width`, and `maxWidth`. Each of these props accepts fixed values or percentages. TableView prioritizes columns with defined widths and divides the remaining space evenly amongst the other columns. ```tsx example - - - - Name - Type - Size - - - - 2021406_Proposal - PDF - 86 KB - - - Budget Template - XLS - 120 KB - - - Onboarding - PPT - 472 KB - - - Welcome - TXT - 24 KB - - - - + + + Name + Type + Size + + + + 2021406_Proposal + PDF + 86 KB + + + Budget Template + XLS + 120 KB + + + Onboarding + PPT + 472 KB + + + Welcome + TXT + 24 KB + + + ``` ### Column dividers [View guidelines](https://spectrum.adobe.com/page/table/#Column-dividers) ```tsx example - - - - Name - Type - Size - - - - 2021406_Proposal - PDF - 86 KB - - - Budget Template - XLS - 120 KB - - - Onboarding - PPT - 472 KB - - - Welcome - TXT - 24 KB - - - - + + + Name + Type + Size + + + + 2021406_Proposal + PDF + 86 KB + + + Budget Template + XLS + 120 KB + + + Onboarding + PPT + 472 KB + + + Welcome + TXT + 24 KB + + + ``` ### Hide header @@ -707,31 +702,29 @@ function TableExample(props) { ]; return ( - - - - {column => ( - - {column.name} - - )} - - - {item => - ( - {key => - key === 'addInfo' - ? - : {item[key]} - } - ) - } - - - + + + {column => ( + + {column.name} + + )} + + + {item => + ( + {key => + key === 'addInfo' + ? + : {item[key]} + } + ) + } + + ); } ``` @@ -783,18 +776,16 @@ function renderEmptyState() { ); } - - - - Name - Type - Size - - - {[]} - - - + + + Name + Type + Size + + + {[]} + + ``` ### Nested columns @@ -802,7 +793,7 @@ function renderEmptyState() { TableView supports nesting columns, allowing you to create column groups, or "tiered" column headers. ```tsx example - + First Name @@ -864,7 +855,7 @@ let rows = [ {id: 4, first: 'Bruce', last: 'Wayne', age: 32, birthday: 'December 18'} ]; - + {column => ( From d1e6d46303cf0384a5340e947f6814089b9548f1 Mon Sep 17 00:00:00 2001 From: Devon Govett Date: Mon, 21 Jun 2021 16:08:37 -0700 Subject: [PATCH 59/62] minor updates --- packages/@react-spectrum/table/docs/TableView.mdx | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/@react-spectrum/table/docs/TableView.mdx b/packages/@react-spectrum/table/docs/TableView.mdx index f03e5f00dc5..685fd30dfa1 100644 --- a/packages/@react-spectrum/table/docs/TableView.mdx +++ b/packages/@react-spectrum/table/docs/TableView.mdx @@ -73,7 +73,7 @@ keywords: [table, grid] ``` ## Content -TableView is a complex [collection component](../react-stately/collections.html) that is built up from many child elements including columns, rows, and cells. Columns are defined within a TableHeader element and rows are defined within a TableBody element. Rows contain Cell elements that correspond to each column. Cells accept any element, allowing you to have focusable children within the TableView. +TableView is a complex [collection component](../react-stately/collections.html) that is built up from many child elements including columns, rows, and cells. Columns are defined within a TableHeader element and rows are defined within a TableBody element. Rows contain Cell elements that correspond to each column. Cells can contain any element, allowing you to have focusable children within the TableView. Basic usage of TableView, seen in the example above, shows the use of a static collection where the contents of the TableView is hard coded. Dynamic collections, as shown below, can be used when the options come from an external data source such as an API, or update over time. Providing the data in this way allows TableView to automatically cache the rendering of each item, which dramatically improves performance. @@ -697,8 +697,7 @@ function TableExample(props) { {id: '1', firstName: 'John', lastName: 'Doe', age: '45'}, {id: '2', firstName: 'Jane', lastName: 'Doe', age: '37'}, {id: '3', firstName: 'Joe', lastName: 'Schmoe', age: '67'}, - {id: '4', firstName: 'Joe', lastName: 'Bloggs', age: '12'}, - {id: '5', firstName: 'Longggggggggggg Wrapping', lastName: 'Name', age: '56'} + {id: '4', firstName: 'Joe', lastName: 'Bloggs', age: '12'} ]; return ( @@ -776,7 +775,7 @@ function renderEmptyState() { ); } - + Name Type @@ -790,7 +789,7 @@ function renderEmptyState() { ### Nested columns -TableView supports nesting columns, allowing you to create column groups, or "tiered" column headers. +TableView supports nesting columns, allowing you to create column groups, or "tiered" column headers. Data for the leaf columns appears in each row of the table body. ```tsx example From bd83d16519c097d1827c22a95716f7613cf969ab Mon Sep 17 00:00:00 2001 From: danni Date: Mon, 21 Jun 2021 16:29:47 -0700 Subject: [PATCH 60/62] add action button --- packages/@react-spectrum/table/docs/TableView.mdx | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/packages/@react-spectrum/table/docs/TableView.mdx b/packages/@react-spectrum/table/docs/TableView.mdx index f03e5f00dc5..fb69940ed2d 100644 --- a/packages/@react-spectrum/table/docs/TableView.mdx +++ b/packages/@react-spectrum/table/docs/TableView.mdx @@ -20,7 +20,6 @@ import {ActionButton} from '@react-spectrum/button'; import Add from '@spectrum-icons/workflow/Add'; import {Cell, Column, Row, TableView, TableBody, TableHeader} from '@react-spectrum/table'; import {Flex} from '@react-spectrum/layout'; -import {ActionGroup, Item} from '@react-spectrum/actiongroup' ``` --- @@ -234,14 +233,10 @@ let rows = [ ///- end collapse -/// - - Add - Delete - Edit - + Add {column => ( From f73967406c3fdbad6194160f31855aef9666c9e3 Mon Sep 17 00:00:00 2001 From: Devon Govett Date: Mon, 21 Jun 2021 17:09:38 -0700 Subject: [PATCH 61/62] Add a longer name that wraps --- packages/@react-spectrum/table/docs/TableView.mdx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/@react-spectrum/table/docs/TableView.mdx b/packages/@react-spectrum/table/docs/TableView.mdx index 0b5b028118c..15f1132de23 100644 --- a/packages/@react-spectrum/table/docs/TableView.mdx +++ b/packages/@react-spectrum/table/docs/TableView.mdx @@ -692,11 +692,12 @@ function TableExample(props) { {id: '1', firstName: 'John', lastName: 'Doe', age: '45'}, {id: '2', firstName: 'Jane', lastName: 'Doe', age: '37'}, {id: '3', firstName: 'Joe', lastName: 'Schmoe', age: '67'}, - {id: '4', firstName: 'Joe', lastName: 'Bloggs', age: '12'} + {id: '4', firstName: 'Joe', lastName: 'Bloggs', age: '12'}, + {id: '5', firstName: 'Taylor', lastName: 'Rodriguez Lloyd-Atkinson', age: '83'} ]; return ( - + {column => ( Date: Mon, 21 Jun 2021 17:32:26 -0700 Subject: [PATCH 62/62] remove minheight --- packages/@react-spectrum/table/docs/TableView.mdx | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/@react-spectrum/table/docs/TableView.mdx b/packages/@react-spectrum/table/docs/TableView.mdx index 15f1132de23..b10abf9ce98 100644 --- a/packages/@react-spectrum/table/docs/TableView.mdx +++ b/packages/@react-spectrum/table/docs/TableView.mdx @@ -236,7 +236,6 @@ let rows = [ Add {column => (