Skip to content

Commit 09a0489

Browse files
authored
Disabling TableView/ListView keyboard navigation and press interactions if collection is empty (#3422)
1 parent 68a7d57 commit 09a0489

File tree

16 files changed

+277
-136
lines changed

16 files changed

+277
-136
lines changed

packages/@adobe/spectrum-css-temp/components/table/skin.css

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ governing permissions and limitations under the License.
4141
}
4242
}
4343

44-
&:active {
44+
&.is-active {
4545
color: var(--spectrum-table-header-text-color-down);
4646

4747
.spectrum-Table-sortedIcon {
@@ -77,6 +77,10 @@ governing permissions and limitations under the License.
7777
border-color: var(--spectrum-table-border-color);
7878
background-color: var(--spectrum-table-background-color);
7979

80+
&:focus-ring {
81+
box-shadow: inset 0 0 0 2px var(--spectrum-table-cell-border-color-key-focus);
82+
}
83+
8084
&.is-drop-target {
8185
border-color: var(--spectrum-alias-border-color-focus);
8286
box-shadow: 0 0 0 1px var(--spectrum-alias-border-color-focus);

packages/@react-aria/grid/src/useGridCell.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,8 @@ export function useGridCell<T, C extends GridCollection<T>>(props: GridCellProps
9393
isVirtualized,
9494
focus,
9595
shouldSelectOnPressUp,
96-
onAction: onCellAction ? () => onCellAction(node.key) : onAction
96+
onAction: onCellAction ? () => onCellAction(node.key) : onAction,
97+
isDisabled: state.collection.size === 0
9798
});
9899

99100
let onKeyDownCapture = (e: ReactKeyboardEvent) => {

packages/@react-aria/grid/src/useGridRow.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@ export function useGridRow<T, C extends GridCollection<T>, S extends GridState<T
5959
ref,
6060
isVirtualized,
6161
shouldSelectOnPressUp,
62-
onAction: onRowAction ? () => onRowAction(node.key) : onAction
62+
onAction: onRowAction ? () => onRowAction(node.key) : onAction,
63+
isDisabled: state.collection.size === 0
6364
});
6465

6566
let isSelected = state.selectionManager.isSelected(node.key);

packages/@react-aria/gridlist/src/useGridList.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ export function useGridList<T>(props: AriaGridListOptions<T>, state: ListState<T
7878
id,
7979
'aria-multiselectable': state.selectionManager.selectionMode === 'multiple' ? 'true' : undefined
8080
},
81-
listProps,
81+
state.collection.size === 0 ? {} : listProps,
8282
descriptionProps
8383
);
8484

packages/@react-aria/table/src/useTable.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,8 @@ export function useTable<T>(props: AriaTableProps<T>, state: TableState<T>, ref:
119119
gridProps: mergeProps(
120120
gridProps,
121121
descriptionProps,
122+
// If table is empty, make sure the table is tabbable
123+
state.collection.size === 0 && {tabIndex: 0},
122124
{
123125
// merge sort description with long press information
124126
'aria-describedby': [descriptionProps['aria-describedby'], gridProps['aria-describedby']].filter(Boolean).join(' ')

packages/@react-aria/table/src/useTableColumnHeader.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,14 @@ export function useTableColumnHeader<T>(props: AriaTableColumnHeaderProps, state
9090

9191
return {
9292
columnHeaderProps: {
93-
...mergeProps(gridCellProps, pressProps, focusableProps, descriptionProps),
93+
...mergeProps(
94+
gridCellProps,
95+
pressProps,
96+
focusableProps,
97+
descriptionProps,
98+
// If the table is empty, make all column headers untabbable or programatically focusable
99+
state.collection.size === 0 && {tabIndex: null}
100+
),
94101
role: 'columnheader',
95102
id: getColumnHeaderId(state, node.key),
96103
'aria-colspan': node.colspan && node.colspan > 1 ? node.colspan : null,

packages/@react-aria/table/src/useTableColumnResize.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,12 @@ export interface TableColumnResizeAria {
3030
export interface AriaTableColumnResizeProps<T> {
3131
column: GridNode<T>,
3232
label: string,
33-
triggerRef: RefObject<HTMLDivElement>
33+
triggerRef: RefObject<HTMLDivElement>,
34+
isDisabled?: boolean
3435
}
3536

3637
export function useTableColumnResize<T>(props: AriaTableColumnResizeProps<T>, state: TableState<T>, columnState: TableColumnResizeState<T>, ref: RefObject<HTMLInputElement>): TableColumnResizeAria {
37-
let {column: item, triggerRef} = props;
38+
let {column: item, triggerRef, isDisabled} = props;
3839
const stateRef = useRef<TableColumnResizeState<T>>(null);
3940
// keep track of what the cursor on the body is so it can be restored back to that when done resizing
4041
const cursor = useRef<string | null>(null);
@@ -159,7 +160,8 @@ export function useTableColumnResize<T>(props: AriaTableColumnResizeProps<T>, st
159160
stateRef.current.onColumnResizeEnd(item);
160161
state.setKeyboardNavigationDisabled(false);
161162
},
162-
onChange
163+
onChange,
164+
disabled: isDisabled
163165
},
164166
ariaProps
165167
)

packages/@react-aria/table/src/useTableSelectionCheckbox.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ export function useTableSelectAllCheckbox<T>(state: TableState<T>): TableSelectA
6464
checkboxProps: {
6565
'aria-label': stringFormatter.format(selectionMode === 'single' ? 'select' : 'selectAll'),
6666
isSelected: isSelectAll,
67-
isDisabled: selectionMode !== 'multiple',
67+
isDisabled: selectionMode !== 'multiple' || state.collection.size === 0,
6868
isIndeterminate: !isEmpty && !isSelectAll,
6969
onChange: () => state.selectionManager.toggleSelectAll()
7070
}

packages/@react-spectrum/card/stories/GridCardView.stories.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {GridLayoutOptions} from '../src/GridLayout';
2121
import {Heading, Text} from '@react-spectrum/text';
2222
import {IllustratedMessage} from '@react-spectrum/illustratedmessage';
2323
import {Image} from '@react-spectrum/image';
24+
import {Link} from '@react-spectrum/link';
2425
import React, {Key, useMemo, useState} from 'react';
2526
import {Size} from '@react-stately/virtualizer';
2627
import {SpectrumCardViewProps} from '@react-types/card';
@@ -67,7 +68,7 @@ function renderEmptyState() {
6768
<path d="M133.7,8.5h-118c-1.9,0-3.5,1.6-3.5,3.5v27c0,0.8,0.7,1.5,1.5,1.5s1.5-0.7,1.5-1.5V23.5h119V92c0,0.3-0.2,0.5-0.5,0.5h-118c-0.3,0-0.5-0.2-0.5-0.5V69c0-0.8-0.7-1.5-1.5-1.5s-1.5,0.7-1.5,1.5v23c0,1.9,1.6,3.5,3.5,3.5h118c1.9,0,3.5-1.6,3.5-3.5V12C137.2,10.1,135.6,8.5,133.7,8.5z M15.2,21.5V12c0-0.3,0.2-0.5,0.5-0.5h118c0.3,0,0.5,0.2,0.5,0.5v9.5H15.2z M32.6,16.5c0,0.6-0.4,1-1,1h-10c-0.6,0-1-0.4-1-1s0.4-1,1-1h10C32.2,15.5,32.6,15.9,32.6,16.5z M13.6,56.1l-8.6,8.5C4.8,65,4.4,65.1,4,65.1c-0.4,0-0.8-0.1-1.1-0.4c-0.6-0.6-0.6-1.5,0-2.1l8.6-8.5l-8.6-8.5c-0.6-0.6-0.6-1.5,0-2.1c0.6-0.6,1.5-0.6,2.1,0l8.6,8.5l8.6-8.5c0.6-0.6,1.5-0.6,2.1,0c0.6,0.6,0.6,1.5,0,2.1L15.8,54l8.6,8.5c0.6,0.6,0.6,1.5,0,2.1c-0.3,0.3-0.7,0.4-1.1,0.4c-0.4,0-0.8-0.1-1.1-0.4L13.6,56.1z" />
6869
</svg>
6970
<Heading>No results</Heading>
70-
<Content>No results found</Content>
71+
<Content>No results found, press <Link onPress={action('linkPress')}>here</Link> for more info.</Content>
7172
</IllustratedMessage>
7273
);
7374
}
@@ -527,4 +528,3 @@ export function CustomLayout(props: SpectrumCardViewProps<object> & LayoutOption
527528
</div>
528529
);
529530
}
530-

packages/@react-spectrum/list/stories/ListView.stories.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {Image} from '@react-spectrum/image';
2020
import Info from '@spectrum-icons/workflow/Info';
2121
import {Item, ListView} from '../';
2222
import {ItemDropTarget} from '@react-types/shared';
23+
import {Link} from '@react-spectrum/link';
2324
import NoSearchResults from '@spectrum-icons/illustrations/NoSearchResults';
2425
import React, {useEffect, useState} from 'react';
2526
import {storiesOf} from '@storybook/react';
@@ -106,14 +107,14 @@ const itemsWithThumbs = [
106107
{key: '9', title: 'file of great boi', illustration: <File />}
107108
];
108109

109-
function renderEmptyState() {
110+
export function renderEmptyState() {
110111
return (
111112
<IllustratedMessage>
112113
<svg width="150" height="103" viewBox="0 0 150 103">
113114
<path d="M133.7,8.5h-118c-1.9,0-3.5,1.6-3.5,3.5v27c0,0.8,0.7,1.5,1.5,1.5s1.5-0.7,1.5-1.5V23.5h119V92c0,0.3-0.2,0.5-0.5,0.5h-118c-0.3,0-0.5-0.2-0.5-0.5V69c0-0.8-0.7-1.5-1.5-1.5s-1.5,0.7-1.5,1.5v23c0,1.9,1.6,3.5,3.5,3.5h118c1.9,0,3.5-1.6,3.5-3.5V12C137.2,10.1,135.6,8.5,133.7,8.5z M15.2,21.5V12c0-0.3,0.2-0.5,0.5-0.5h118c0.3,0,0.5,0.2,0.5,0.5v9.5H15.2z M32.6,16.5c0,0.6-0.4,1-1,1h-10c-0.6,0-1-0.4-1-1s0.4-1,1-1h10C32.2,15.5,32.6,15.9,32.6,16.5z M13.6,56.1l-8.6,8.5C4.8,65,4.4,65.1,4,65.1c-0.4,0-0.8-0.1-1.1-0.4c-0.6-0.6-0.6-1.5,0-2.1l8.6-8.5l-8.6-8.5c-0.6-0.6-0.6-1.5,0-2.1c0.6-0.6,1.5-0.6,2.1,0l8.6,8.5l8.6-8.5c0.6-0.6,1.5-0.6,2.1,0c0.6,0.6,0.6,1.5,0,2.1L15.8,54l8.6,8.5c0.6,0.6,0.6,1.5,0,2.1c-0.3,0.3-0.7,0.4-1.1,0.4c-0.4,0-0.8-0.1-1.1-0.4L13.6,56.1z" />
114115
</svg>
115116
<Heading>No results</Heading>
116-
<Content>No results found</Content>
117+
<Content>No results found, press <Link onPress={action('linkPress')}>here</Link> for more info.</Content>
117118
</IllustratedMessage>
118119
);
119120
}

0 commit comments

Comments
 (0)