From d04e70b27e40d0a04312d8c5e40127eceeefcd22 Mon Sep 17 00:00:00 2001 From: Devon Govett Date: Thu, 17 Jun 2021 15:16:55 -0700 Subject: [PATCH] Simplify ARIA table API and make more consistent (#2034) * Simplify ARIA table API and make more consistent * replacing table css hover pseudo class with is-hovered fixes doc examples where hovering was highlighting rows for tableview w/o selection enebaled * Fix ActionBar import Co-authored-by: Daniel Lu Co-authored-by: Danni --- .../components/table/skin.css | 10 +- .../dnd/stories/DraggableCollection.tsx | 12 +-- .../@react-aria/dnd/stories/DroppableGrid.tsx | 13 +-- .../@react-aria/dnd/stories/Reorderable.tsx | 13 +-- .../@react-aria/dnd/stories/dnd.stories.tsx | 12 +-- packages/@react-aria/grid/src/useGrid.ts | 6 +- packages/@react-aria/grid/src/useGridCell.ts | 9 +- packages/@react-aria/grid/src/useGridRow.ts | 14 +-- packages/@react-aria/grid/stories/example.tsx | 11 +- packages/@react-aria/table/src/index.ts | 4 +- packages/@react-aria/table/src/useTable.ts | 11 +- .../@react-aria/table/src/useTableCell.ts | 49 +++++++++ .../table/src/useTableColumnHeader.ts | 27 +++-- .../table/src/useTableHeaderRow.ts | 36 +++++++ packages/@react-aria/table/src/useTableRow.ts | 5 +- .../table/src/useTableRowHeader.ts | 51 --------- .../table/src/useTableSelectionCheckbox.ts | 16 ++- .../actionbar/src/ActionBar.tsx | 2 +- .../@react-spectrum/list/src/ListView.tsx | 5 +- .../@react-spectrum/list/src/ListViewItem.tsx | 8 +- .../@react-spectrum/overlays/src/index.ts | 1 + .../@react-spectrum/table/src/TableView.tsx | 100 +++++------------- .../@react-spectrum/table/test/Table.test.js | 2 +- .../selection/src/SelectionManager.ts | 10 +- packages/@react-stately/table/src/Cell.ts | 3 + packages/@react-stately/table/src/Column.ts | 5 + packages/@react-stately/table/src/Row.ts | 5 + .../@react-stately/table/src/TableBody.ts | 6 +- .../@react-stately/table/src/TableHeader.ts | 4 + packages/@react-types/table/src/index.d.ts | 11 +- 30 files changed, 211 insertions(+), 250 deletions(-) create mode 100644 packages/@react-aria/table/src/useTableCell.ts create mode 100644 packages/@react-aria/table/src/useTableHeaderRow.ts delete mode 100644 packages/@react-aria/table/src/useTableRowHeader.ts 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); } diff --git a/packages/@react-aria/dnd/stories/DraggableCollection.tsx b/packages/@react-aria/dnd/stories/DraggableCollection.tsx index 9aeb4c5d11d..d0637f9b128 100644 --- a/packages/@react-aria/dnd/stories/DraggableCollection.tsx +++ b/packages/@react-aria/dnd/stories/DraggableCollection.tsx @@ -116,10 +116,9 @@ function DraggableCollection(props) { let {gridProps} = useGrid({ ...props, - ref, 'aria-label': 'Draggable list', focusMode: 'cell' - }, gridState); + }, gridState, ref); return (
, /** Whether the grid uses virtual scrolling. */ isVirtualized?: boolean, /** @@ -55,10 +53,10 @@ export interface GridAria { * 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`. + * @param ref - The ref attached to the grid element. */ -export function useGrid(props: GridProps, state: GridState>): GridAria { +export function useGrid(props: GridProps, state: GridState>, ref: RefObject): GridAria { let { - ref, isVirtualized, keyboardDelegate, focusMode, diff --git a/packages/@react-aria/grid/src/useGridCell.ts b/packages/@react-aria/grid/src/useGridCell.ts index 55a68365057..6029aa9c2e2 100644 --- a/packages/@react-aria/grid/src/useGridCell.ts +++ b/packages/@react-aria/grid/src/useGridCell.ts @@ -24,12 +24,8 @@ 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, /** 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. */ @@ -46,12 +42,10 @@ interface GridCellAria { * @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 { +export function useGridCell>(props: GridCellProps, state: GridState, ref: RefObject): GridCellAria { let { node, - ref, isVirtualized, - isDisabled, focusMode = 'child', shouldSelectOnPressUp } = props; @@ -88,6 +82,7 @@ export function useGridCell>(props: GridCellProps }); // TODO: move into useSelectableItem? + let isDisabled = state.disabledKeys.has(node.key) || state.disabledKeys.has(node.parentKey); let {pressProps} = usePress({...itemProps, isDisabled}); let onKeyDown = (e: ReactKeyboardEvent) => { diff --git a/packages/@react-aria/grid/src/useGridRow.ts b/packages/@react-aria/grid/src/useGridRow.ts index 91df542deb1..1253b837d73 100644 --- a/packages/@react-aria/grid/src/useGridRow.ts +++ b/packages/@react-aria/grid/src/useGridRow.ts @@ -20,14 +20,8 @@ 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 } @@ -42,13 +36,10 @@ export interface GridRowAria { * @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 { +export function useGridRow, S extends GridState>(props: GridRowProps, state: S, ref: RefObject): GridRowAria { let { node, - ref, isVirtualized, - isSelected, - isDisabled, shouldSelectOnPressUp } = props; @@ -60,6 +51,9 @@ export function useGridRow, S extends GridState @@ -48,15 +47,11 @@ function Row({state, item, focusMode}) { let rowRef = React.useRef(); let cellRef = React.useRef(); let cellNode = [...item.childNodes][0]; - let {rowProps} = useGridRow({ - node: item, - ref: rowRef - }, state); + let {rowProps} = useGridRow({node: item}, state, rowRef); let {gridCellProps} = useGridCell({ node: cellNode, - ref: cellRef, focusMode - }, state); + }, state, cellRef); let [isRowFocused, setRowFocused] = React.useState(false); let {focusProps: rowFocusProps} = useFocus({ diff --git a/packages/@react-aria/table/src/index.ts b/packages/@react-aria/table/src/index.ts index 0fd45c7111e..33c1c208bf9 100644 --- a/packages/@react-aria/table/src/index.ts +++ b/packages/@react-aria/table/src/index.ts @@ -13,8 +13,8 @@ export * from './useTable'; export * from './useTableColumnHeader'; export * from './useTableRow'; -export * from './useTableRowHeader'; +export * from './useTableHeaderRow'; +export * from './useTableCell'; export * from './useTableSelectionCheckbox'; -export {useGridCell as useTableCell} from '@react-aria/grid'; export {useGridRowGroup as useTableRowGroup} from '@react-aria/grid'; diff --git a/packages/@react-aria/table/src/useTable.ts b/packages/@react-aria/table/src/useTable.ts index 67bf7da4474..7f884b49135 100644 --- a/packages/@react-aria/table/src/useTable.ts +++ b/packages/@react-aria/table/src/useTable.ts @@ -14,11 +14,11 @@ import {GridAria, GridProps, useGrid} from '@react-aria/grid'; import {gridIds} from './utils'; import {Layout} from '@react-stately/virtualizer'; import {Node} from '@react-types/shared'; +import {RefObject, useMemo} from 'react'; import {TableKeyboardDelegate} from './TableKeyboardDelegate'; import {TableState} from '@react-stately/table'; import {useCollator, useLocale} from '@react-aria/i18n'; 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. */ @@ -27,13 +27,14 @@ interface TableProps extends GridProps { /** * 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. + * A table displays data in rows and columns and enables a user to navigate its contents via directional navigation keys, + * and optionally supports row selection and sorting. * @param props - Props for the table. * @param state - State for the table, as returned by `useTableState`. + * @param ref - The ref attached to the table element. */ -export function useTable(props: TableProps, state: TableState): GridAria { +export function useTable(props: TableProps, state: TableState, ref: RefObject): GridAria { let { - ref, keyboardDelegate, isVirtualized, layout @@ -87,7 +88,7 @@ export function useTable(props: TableProps, state: TableState): GridAri return ''; } - }, state); + }, state, ref); // Override to include header rows if (isVirtualized) { diff --git a/packages/@react-aria/table/src/useTableCell.ts b/packages/@react-aria/table/src/useTableCell.ts new file mode 100644 index 00000000000..b8c66932330 --- /dev/null +++ b/packages/@react-aria/table/src/useTableCell.ts @@ -0,0 +1,49 @@ +/* + * Copyright 2020 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +import {getCellId} from './utils'; +import {GridNode} from '@react-types/grid'; +import {HTMLAttributes, RefObject} from 'react'; +import {TableState} from '@react-stately/table'; +import {useGridCell} from '@react-aria/grid'; + +interface TableCellProps { + /** An object representing the table cell. Contains all the relevant information that makes up the row header. */ + node: GridNode, + /** Whether the cell is contained in a virtual scroller. */ + isVirtualized?: boolean +} + +interface TableCellAria { + /** Props for the table cell element. */ + gridCellProps: HTMLAttributes +} + +/** + * Provides the behavior and accessibility implementation for a cell in a table. + * @param props - Props for the cell. + * @param state - State of the table, as returned by `useTableState`. + * @param ref - The ref attached to the cell element. + */ +export function useTableCell(props: TableCellProps, state: TableState, ref: RefObject): TableCellAria { + let {gridCellProps} = useGridCell(props, state, ref); + + let columnKey = props.node.column.key; + if (state.collection.rowHeaderColumnKeys.has(columnKey)) { + gridCellProps.role = 'rowheader'; + gridCellProps.id = getCellId(state, props.node.parentKey, columnKey); + } + + return { + gridCellProps + }; +} diff --git a/packages/@react-aria/table/src/useTableColumnHeader.ts b/packages/@react-aria/table/src/useTableColumnHeader.ts index 3a98bb0e03c..56b1c936c4d 100644 --- a/packages/@react-aria/table/src/useTableColumnHeader.ts +++ b/packages/@react-aria/table/src/useTableColumnHeader.ts @@ -11,9 +11,9 @@ */ import {getColumnHeaderId} from './utils'; +import {GridNode} from '@react-types/grid'; import {HTMLAttributes, RefObject} from 'react'; import {mergeProps} from '@react-aria/utils'; -import {Node} from '@react-types/shared'; import {TableState} from '@react-stately/table'; import {useFocusable} from '@react-aria/focus'; import {useGridCell} from '@react-aria/grid'; @@ -22,15 +22,9 @@ import {usePress} from '@react-aria/interactions'; interface ColumnHeaderProps { /** 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](https://www.w3.org/TR/wai-aria-1.1/#columnheader). */ - ref: RefObject, + node: GridNode, /** 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](https://www.w3.org/TR/wai-aria-1.1/#columnheader) should span. */ - colspan?: number, - /** Whether the [column header](https://www.w3.org/TR/wai-aria-1.1/#columnheader) is disabled. */ - isDisabled?: boolean + isVirtualized?: boolean } interface ColumnHeaderAria { @@ -42,13 +36,15 @@ interface ColumnHeaderAria { * 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`. + * @param ref - The ref attached to the column header element. */ -export function useTableColumnHeader(props: ColumnHeaderProps, state: TableState): ColumnHeaderAria { - let {node, colspan, ref, isDisabled} = props; - let {gridCellProps} = useGridCell(props, state); +export function useTableColumnHeader(props: ColumnHeaderProps, state: TableState, ref: RefObject): ColumnHeaderAria { + let {node} = props; + let {gridCellProps} = useGridCell(props, state, ref); + let isSelectionCellDisabled = node.props.isSelectionCell && state.selectionManager.selectionMode === 'single'; let {pressProps} = usePress({ - isDisabled: !node.props.allowsSorting || isDisabled, + isDisabled: !node.props.allowsSorting || isSelectionCellDisabled, onPress() { state.sort(node.key); } @@ -66,8 +62,9 @@ export function useTableColumnHeader(props: ColumnHeaderProps, state: TableSt ...mergeProps(gridCellProps, pressProps, focusableProps), role: 'columnheader', id: getColumnHeaderId(state, node.key), - 'aria-colspan': colspan && colspan > 1 ? colspan : null, - 'aria-sort': ariaSort + 'aria-colspan': node.colspan && node.colspan > 1 ? node.colspan : null, + 'aria-sort': ariaSort, + 'aria-disabled': isSelectionCellDisabled || undefined } }; } diff --git a/packages/@react-aria/table/src/useTableHeaderRow.ts b/packages/@react-aria/table/src/useTableHeaderRow.ts new file mode 100644 index 00000000000..d20751974c4 --- /dev/null +++ b/packages/@react-aria/table/src/useTableHeaderRow.ts @@ -0,0 +1,36 @@ +/* + * Copyright 2020 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +import {GridRowAria, GridRowProps} from '@react-aria/grid'; +import {RefObject} from 'react'; +import {TableState} from '@react-stately/table'; + +/** + * Provides the behavior and accessibility implementation for a header row in a table. + * @param props - Props for the row. + * @param state - State of the table, as returned by `useTableState`. + */ +// eslint-disable-next-line @typescript-eslint/no-unused-vars +export function useTableHeaderRow(props: GridRowProps, state: TableState, ref: RefObject): GridRowAria { + let {node, isVirtualized} = props; + let rowProps = { + role: 'row' + }; + + if (isVirtualized) { + rowProps['aria-rowindex'] = node.index + 1; // aria-rowindex is 1 based + } + + return { + rowProps + }; +} diff --git a/packages/@react-aria/table/src/useTableRow.ts b/packages/@react-aria/table/src/useTableRow.ts index 7de1366dfdc..217fbe28d48 100644 --- a/packages/@react-aria/table/src/useTableRow.ts +++ b/packages/@react-aria/table/src/useTableRow.ts @@ -12,6 +12,7 @@ import {getRowLabelledBy} from './utils'; import {GridRowAria, GridRowProps, useGridRow} from '@react-aria/grid'; +import {RefObject} from 'react'; import {TableCollection} from '@react-types/table'; import {TableState} from '@react-stately/table'; @@ -20,9 +21,9 @@ import {TableState} from '@react-stately/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 { +export function useTableRow(props: GridRowProps, state: TableState, ref: RefObject): GridRowAria { let {node} = props; - let {rowProps} = useGridRow, TableState>(props, state); + let {rowProps} = useGridRow, TableState>(props, state, ref); return { rowProps: { ...rowProps, diff --git a/packages/@react-aria/table/src/useTableRowHeader.ts b/packages/@react-aria/table/src/useTableRowHeader.ts deleted file mode 100644 index 25b5404384f..00000000000 --- a/packages/@react-aria/table/src/useTableRowHeader.ts +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2020 Adobe. All rights reserved. - * This file is licensed to you under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. You may obtain a copy - * of the License at http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under - * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS - * OF ANY KIND, either express or implied. See the License for the specific language - * governing permissions and limitations under the License. - */ - -import {getCellId} from './utils'; -import {GridNode} from '@react-types/grid'; -import {HTMLAttributes, RefObject} from 'react'; -import {TableState} from '@react-stately/table'; -import {useGridCell} from '@react-aria/grid'; - -interface RowHeaderProps { - /** 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](https://www.w3.org/TR/wai-aria-1.1/#rowheader). */ - ref: RefObject, - /** 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](https://www.w3.org/TR/wai-aria-1.1/#rowheader) is disabled. */ - isDisabled?: boolean -} - -interface RowHeaderAria { - /** Props for the [row header](https://www.w3.org/TR/wai-aria-1.1/#rowheader) element. */ - 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); - - let columnKey = props.node.column.key; - return { - rowHeaderProps: { - ...gridCellProps, - role: 'rowheader', - id: getCellId(state, props.node.parentKey, columnKey) - } - }; -} diff --git a/packages/@react-aria/table/src/useTableSelectionCheckbox.ts b/packages/@react-aria/table/src/useTableSelectionCheckbox.ts index bbbee61bf58..77a105d0336 100644 --- a/packages/@react-aria/table/src/useTableSelectionCheckbox.ts +++ b/packages/@react-aria/table/src/useTableSelectionCheckbox.ts @@ -18,9 +18,7 @@ import {useId} from '@react-aria/utils'; interface SelectionCheckboxProps { /** A unique key for the checkbox. */ - key: Key, - /** Whether the checkbox is disabled. */ - isDisabled?: boolean + key: Key } interface SelectionCheckboxAria { @@ -39,14 +37,12 @@ interface SelectAllCheckboxAria { * @param state - State of the table, as returned by `useTableState`. */ export function useTableSelectionCheckbox(props: SelectionCheckboxProps, state: TableState): SelectionCheckboxAria { - let { - key, - isDisabled - } = props; + let {key} = props; let manager = state.selectionManager; let checkboxId = useId(); - let isSelected = state.selectionManager.isSelected(key) && !isDisabled; + let isDisabled = state.disabledKeys.has(key); + let isSelected = state.selectionManager.isSelected(key); let onChange = () => manager.select(key); @@ -56,6 +52,7 @@ export function useTableSelectionCheckbox(props: SelectionCheckboxProps, stat 'aria-label': 'Select', 'aria-labelledby': `${checkboxId} ${getRowLabelledBy(state, key)}`, isSelected, + isDisabled: isDisabled || manager.selectionMode === 'none', onChange } }; @@ -67,11 +64,12 @@ export function useTableSelectionCheckbox(props: SelectionCheckboxProps, stat * @param state - State of the table, as returned by `useTableState`. */ export function useTableSelectAllCheckbox(state: TableState): SelectAllCheckboxAria { - let {isEmpty, isSelectAll} = state.selectionManager; + let {isEmpty, isSelectAll, selectionMode} = state.selectionManager; return { checkboxProps: { 'aria-label': 'Select All', isSelected: isSelectAll, + isDisabled: selectionMode !== 'multiple', isIndeterminate: !isEmpty && !isSelectAll, onChange: () => state.selectionManager.toggleSelectAll() } diff --git a/packages/@react-spectrum/actionbar/src/ActionBar.tsx b/packages/@react-spectrum/actionbar/src/ActionBar.tsx index 3c90a654633..ff27ca30f6a 100644 --- a/packages/@react-spectrum/actionbar/src/ActionBar.tsx +++ b/packages/@react-spectrum/actionbar/src/ActionBar.tsx @@ -20,7 +20,7 @@ import {filterDOMProps} from '@react-aria/utils'; import {FocusScope} from '@react-aria/focus'; // @ts-ignore import intlMessages from '../intl/*.json'; -import {OpenTransition} from '@react-spectrum/overlays/src/OpenTransition'; +import {OpenTransition} from '@react-spectrum/overlays'; import React, {ReactElement, useEffect, useRef} from 'react'; import {SpectrumActionBarProps} from '@react-types/actionbar'; import styles from './actionbar.css'; diff --git a/packages/@react-spectrum/list/src/ListView.tsx b/packages/@react-spectrum/list/src/ListView.tsx index 4b070593237..2bff970695f 100644 --- a/packages/@react-spectrum/list/src/ListView.tsx +++ b/packages/@react-spectrum/list/src/ListView.tsx @@ -88,9 +88,8 @@ function ListView(props: ListViewProps, ref: DOMRef(props: SpectrumTableProps, ref: DOMRef, unknown>; @@ -194,10 +193,6 @@ function TableView(props: SpectrumTableProps, ref: DOMRef; } - if (state.collection.rowHeaderColumnKeys.has(item.column.key)) { - return ; - } - return ; } case 'placeholder': @@ -390,10 +385,8 @@ function TableColumnHeader({column}) { let state = useTableContext(); let {columnHeaderProps} = useTableColumnHeader({ node: column, - ref, - colspan: column.colspan, isVirtualized: true - }, state); + }, state, ref); let columnProps = column.props as SpectrumColumnProps; let {hoverProps, isHovered} = useHover({}); @@ -443,12 +436,8 @@ function TableSelectAllCell({column}) { let isSingleSelectionMode = state.selectionManager.selectionMode === 'single'; let {columnHeaderProps} = useTableColumnHeader({ node: column, - ref, - colspan: column.colspan, - isVirtualized: true, - // Disable click from focusing the div for selectionMode = "single" since there won't be a "Select All" checkbox available - isDisabled: isSingleSelectionMode - }, state); + isVirtualized: true + }, state, ref); let {checkboxProps} = useTableSelectAllCheckbox(state); let {hoverProps, isHovered} = useHover({}); @@ -457,7 +446,6 @@ function TableSelectAllCell({column}) {
+
{children}
); @@ -560,18 +549,10 @@ function TableCheckboxCell({cell}) { let isDisabled = state.disabledKeys.has(cell.parentKey); let {gridCellProps} = useTableCell({ node: cell, - ref, - isVirtualized: true, - isDisabled - }, state); + isVirtualized: true + }, state, ref); - let {checkboxProps} = useTableSelectionCheckbox( - { - key: cell.parentKey, - isDisabled - }, - state - ); + let {checkboxProps} = useTableSelectionCheckbox({key: cell.parentKey}, state); return ( @@ -604,53 +585,20 @@ function TableCheckboxCell({cell}) { } function TableCell({cell}) { - let ref = useRef(); let state = useTableContext(); - let isDisabled = state.disabledKeys.has(cell.parentKey); - let {gridCellProps} = useTableCell({ - node: cell, - ref, - isVirtualized: true, - 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, - isVirtualized: true, - isDisabled - }, state); - - return ( - - ); -} - -function TableCellBase({cell, cellRef, ...otherProps}) { - let state = useTableContext(); let columnProps = cell.column.props as SpectrumColumnProps; let isDisabled = state.disabledKeys.has(cell.parentKey); + let {gridCellProps} = useTableCell({ + node: cell, + isVirtualized: true + }, state, ref); return (
0) { + if (!this.disallowEmptySelection && (this.state.selectedKeys === 'all' || this.state.selectedKeys.size > 0)) { this.state.setSelectedKeys(new Selection()); } } diff --git a/packages/@react-stately/table/src/Cell.ts b/packages/@react-stately/table/src/Cell.ts index 96508501684..64ee306d81f 100644 --- a/packages/@react-stately/table/src/Cell.ts +++ b/packages/@react-stately/table/src/Cell.ts @@ -32,6 +32,9 @@ Cell.getCollectionNode = function* getCollectionNode(props: CellProps): Gener }; }; +/** + * A Cell represents the value of a single Column within a Table Row. + */ // We don't want getCollectionNode to show up in the type definition let _Cell = Cell as (props: CellProps) => JSX.Element; export {_Cell as Cell}; diff --git a/packages/@react-stately/table/src/Column.ts b/packages/@react-stately/table/src/Column.ts index c6ec42cba7c..fa7836d769d 100644 --- a/packages/@react-stately/table/src/Column.ts +++ b/packages/@react-stately/table/src/Column.ts @@ -68,6 +68,11 @@ Column.getCollectionNode = function* getCollectionNode(props: ColumnProps, updateContext(context); }; +/** + * A Column represents a field of each item within a Table. Columns may also contain nested + * Column elements to represent column groups. Nested columns can be statically defined as + * children, or dynamically generated using a function based on the `childColumns` prop. + */ // We don't want getCollectionNode to show up in the type definition let _Column = Column as (props: ColumnProps) => JSX.Element; export {_Column as Column}; diff --git a/packages/@react-stately/table/src/Row.ts b/packages/@react-stately/table/src/Row.ts index 3267e4fa871..1bb56474226 100644 --- a/packages/@react-stately/table/src/Row.ts +++ b/packages/@react-stately/table/src/Row.ts @@ -74,6 +74,11 @@ Row.getCollectionNode = function* getCollectionNode(props: RowProps, context: }; }; +/** + * A Row represents a single item in a Table and contains Cell elements for each column. + * Cells can be statically defined as children, or generated dynamically using a function + * based on the columns defined in the TableHeader. + */ // We don't want getCollectionNode to show up in the type definition let _Row = Row as (props: RowProps) => JSX.Element; export {_Row as Row}; diff --git a/packages/@react-stately/table/src/TableBody.ts b/packages/@react-stately/table/src/TableBody.ts index 5a43c088e81..e5d37f734d3 100644 --- a/packages/@react-stately/table/src/TableBody.ts +++ b/packages/@react-stately/table/src/TableBody.ts @@ -29,7 +29,7 @@ TableBody.getCollectionNode = function* getCollectionNode(props: TableBodyPro if (!items) { throw new Error('props.children was a function but props.items is missing'); } - + for (let item of items) { yield { type: 'item', @@ -52,6 +52,10 @@ TableBody.getCollectionNode = function* getCollectionNode(props: TableBodyPro }; }; +/** + * A TableBody is a container for the Row elements of a Table. Rows can be statically defined + * as children, or generated dynamically using a function based on the data passed to the `items` prop. + */ // We don't want getCollectionNode to show up in the type definition let _TableBody = TableBody as (props: TableBodyProps) => JSX.Element; export {_TableBody as TableBody}; diff --git a/packages/@react-stately/table/src/TableHeader.ts b/packages/@react-stately/table/src/TableHeader.ts index 6ac282c155d..b56befa7523 100644 --- a/packages/@react-stately/table/src/TableHeader.ts +++ b/packages/@react-stately/table/src/TableHeader.ts @@ -45,6 +45,10 @@ TableHeader.getCollectionNode = function* getCollectionNode(props: TableHeade } }; +/** + * A TableHeader is a container for the Column elements in a Table. Columns can be statically defined + * as children, or generated dynamically using a function based on the data passed to the `columns` prop. + */ // We don't want getCollectionNode to show up in the type definition let _TableHeader = TableHeader as (props: TableHeaderProps) => JSX.Element; export {_TableHeader as TableHeader}; diff --git a/packages/@react-types/table/src/index.d.ts b/packages/@react-types/table/src/index.d.ts index b71ff6f3f69..388d2040ad9 100644 --- a/packages/@react-types/table/src/index.d.ts +++ b/packages/@react-types/table/src/index.d.ts @@ -59,8 +59,13 @@ export interface ColumnProps { /** The minimum width of the column. */ minWidth?: number | string, /** The maximum width of the column. */ - maxWidth?: number | string + maxWidth?: number | string, // defaultWidth?: number | string + /** 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. */ + isRowHeader?: boolean + } // TODO: how to support these in CollectionBuilder... @@ -70,12 +75,8 @@ export interface SpectrumColumnProps extends ColumnProps { * @default 'start' */ align?: 'start' | 'center' | 'end', - /** Whether the column allows sorting. */ - 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](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, /**