diff --git a/packages/react-aria-components/docs/Table.mdx b/packages/react-aria-components/docs/Table.mdx index a127ec94d90..f9323656ca1 100644 --- a/packages/react-aria-components/docs/Table.mdx +++ b/packages/react-aria-components/docs/Table.mdx @@ -225,7 +225,7 @@ HTML tables are meant for static content, rather than tables with rich interacti `Table` helps achieve accessible and interactive table components that can be styled as needed. * **Row selection** – Single or multiple selection, with optional checkboxes, disabled rows, and both `toggle` and `replace` selection behaviors. -* **Columns** – Support for column sorting, [row header](https://www.w3.org/TR/wai-aria-1.1/#rowheader) columns, and nested column groups. Columns may optionally allow user resizing via mouse, touch, and keyboard interactions. +* **Columns** – Support for column sorting and [row header](https://www.w3.org/TR/wai-aria-1.1/#rowheader) columns. Columns may optionally allow user resizing via mouse, touch, and keyboard interactions. * **Interactive children** – Table cells may include interactive elements such as buttons, menus, etc. * **Actions** – Rows and cells support optional actions such as navigation via click, tap, double click, or Enter key. * **Async loading** – Support for loading and sorting items asynchronously. @@ -239,7 +239,7 @@ HTML tables are meant for static content, rather than tables with rich interacti -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. Columns may be nested to create column groups. +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 can optionally include a selection checkbox. Additionally, a "select all" checkbox may be displayed in a column header if the table supports multiple row selection. A drag button may also be included within a cell if the row is draggable. @@ -336,7 +336,7 @@ The following example includes a custom Column component with a sort indicator. ```tsx example export=true render=false import type {ColumnProps} from 'react-aria-components'; -function MyColumn(props: ColumnProps) { +function MyColumn(props: ColumnProps) { return ( {({allowsSorting, sortDirection}) => <> @@ -760,125 +760,6 @@ 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. - -### Static - -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 - - -
-``` - -
- Show CSS - -```css -.react-aria-Column { - &[colspan] { - text-align: center; - } -} -``` - -
- -### Dynamic - -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 -interface ColumnDefinition { - name: string, - key: string, - children?: ColumnDefinition[], - isRowHeader?: boolean -} - -let columns: ColumnDefinition[] = [ - {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 => ( - - {item.first} - {item.last} - {item.age} - {item.birthday} - - )} - -
-``` - ## Empty state Use the `renderEmptyState` prop to customize what the `TableBody` will display if there are no items. @@ -1136,7 +1017,7 @@ This example shows how to create a reusable component that wraps `` to i ```tsx example export=true render=false import {MenuTrigger, Button, Popover, Menu, MenuItem} from 'react-aria-components'; -interface ResizableTableColumnProps extends Omit, 'children'> { +interface ResizableTableColumnProps extends Omit { children: React.ReactNode } diff --git a/packages/react-aria-components/src/Table.tsx b/packages/react-aria-components/src/Table.tsx index a3bf92a1444..d1d09787014 100644 --- a/packages/react-aria-components/src/Table.tsx +++ b/packages/react-aria-components/src/Table.tsx @@ -527,12 +527,8 @@ export interface ColumnRenderProps { startResize(): void } -export interface ColumnProps extends RenderProps { +export interface ColumnProps extends RenderProps { id?: Key, - /** Rendered contents of the column if `children` contains child columns. */ - title?: ReactNode, - /** A list of child columns used when dynamically rendering nested child columns. */ - childColumns?: Iterable, /** Whether the column allows sorting. */ allowsSorting?: boolean, /** 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. */ @@ -549,21 +545,9 @@ export interface ColumnProps extends RenderProps maxWidth?: ColumnStaticSize | null } -function Column(props: ColumnProps, ref: ForwardedRef): JSX.Element | null { - let render = useContext(CollectionRendererContext); - let childColumns: ReactNode | ((item: T) => ReactNode); - if (typeof render === 'function') { - childColumns = render; - } else if (typeof props.children !== 'function') { - childColumns = props.children; - } - - let children = useCollectionChildren({ - children: (props.title || props.childColumns) ? childColumns : null, - items: props.childColumns - }); +function Column(props: ColumnProps, ref: ForwardedRef): JSX.Element | null { - return useSSRCollectionNode('column', props, ref, props.title ?? props.children, children); + return useSSRCollectionNode('column', props, ref, props.children); } /** @@ -820,7 +804,7 @@ function TableColumnHeader({column}: {column: GridNode}) { } } - let props: ColumnProps = column.props; + let props: ColumnProps = column.props; let renderProps = useRenderProps({ ...props, id: undefined, diff --git a/packages/react-aria-components/stories/index.stories.tsx b/packages/react-aria-components/stories/index.stories.tsx index cd6cdc9101d..36c781edfee 100644 --- a/packages/react-aria-components/stories/index.stories.tsx +++ b/packages/react-aria-components/stories/index.stories.tsx @@ -874,7 +874,7 @@ export const TableDynamicExample = () => { 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: 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/20167', type: 'Text Document'} ]; diff --git a/packages/react-aria-components/test/Table.test.js b/packages/react-aria-components/test/Table.test.js index 655b727008c..23f57e9323b 100644 --- a/packages/react-aria-components/test/Table.test.js +++ b/packages/react-aria-components/test/Table.test.js @@ -471,72 +471,6 @@ describe('Table', () => { expect(columns[2]).not.toHaveTextContent('▲'); }); - it('should support nested column headers', async () => { - 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 leafColumns = [{key: 'first'}, {key: 'last'}, {key: 'age'}, {key: 'birthday'}]; - - let items = [ - {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'} - ]; - - let {getAllByRole} = render( - - ); - - let header = getAllByRole('rowgroup')[0]; - let rows = within(header).getAllByRole('row'); - expect(rows).toHaveLength(2); - - let cells = within(rows[0]).getAllByRole('columnheader'); - expect(cells).toHaveLength(2); - expect(cells[0]).toHaveAttribute('aria-colspan', '2'); - expect(cells[1]).toHaveAttribute('aria-colspan', '2'); - - await user.tab(); - fireEvent.keyDown(document.activeElement, {key: 'ArrowUp'}); - fireEvent.keyUp(document.activeElement, {key: 'ArrowUp'}); - - cells = within(rows[1]).getAllByRole('columnheader'); - expect(document.activeElement).toBe(cells[0]); - - fireEvent.keyDown(document.activeElement, {key: 'ArrowRight'}); - fireEvent.keyUp(document.activeElement, {key: 'ArrowRight'}); - expect(document.activeElement).toBe(cells[1]); - - fireEvent.keyDown(document.activeElement, {key: 'ArrowRight'}); - fireEvent.keyUp(document.activeElement, {key: 'ArrowRight'}); - expect(document.activeElement).toBe(cells[2]); - - fireEvent.keyDown(document.activeElement, {key: 'ArrowRight'}); - fireEvent.keyUp(document.activeElement, {key: 'ArrowRight'}); - expect(document.activeElement).toBe(cells[3]); - - fireEvent.keyDown(document.activeElement, {key: 'ArrowUp'}); - fireEvent.keyUp(document.activeElement, {key: 'ArrowUp'}); - cells = within(rows[0]).getAllByRole('columnheader'); - expect(document.activeElement).toBe(cells[1]); - - fireEvent.keyDown(document.activeElement, {key: 'ArrowLeft'}); - fireEvent.keyUp(document.activeElement, {key: 'ArrowLeft'}); - expect(document.activeElement).toBe(cells[0]); - }); - it('should support empty state', () => { let {getAllByRole, getByRole} = render(