Skip to content

Commit ca9e92b

Browse files
nwidynskiLFDanLu
andauthored
Feat: Add support for horizontal orientation to GridList & ListBox (#8533)
* feat: add orientation to layout * feat: forward orientation to gridlist keyboard delegate * fix: remove duplicate keyboard delegate in gridlist * Feat: Add support for `orientation` to `KeyboardDelegate` interface * fix: implement layout delegate interface * fix: enumerable keys * fix: bidirectional overscan for table layout * chore: remove orientation fallback from domlayoutdelegate * fix: listbox stories * chore: review * fix: gridlist orientation * chore: naming * fix: content nodes * fix page up/down navigation for horizontal lists --------- Co-authored-by: Daniel Lu <dl1644@gmail.com>
1 parent b1d887c commit ca9e92b

10 files changed

Lines changed: 183 additions & 132 deletions

File tree

packages/@react-aria/listbox/src/useListBox.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
*/
1212

1313
import {AriaListBoxProps} from '@react-types/listbox';
14-
import {DOMAttributes, KeyboardDelegate, LayoutDelegate, RefObject} from '@react-types/shared';
14+
import {DOMAttributes, KeyboardDelegate, LayoutDelegate, Orientation, RefObject} from '@react-types/shared';
1515
import {filterDOMProps, mergeProps, useId} from '@react-aria/utils';
1616
import {listData} from './utils';
1717
import {ListState} from '@react-stately/list';
@@ -55,7 +55,13 @@ export interface AriaListBoxOptions<T> extends Omit<AriaListBoxProps<T>, 'childr
5555
* - 'override': links override all other interactions (link items are not selectable).
5656
* @default 'override'
5757
*/
58-
linkBehavior?: 'action' | 'selection' | 'override'
58+
linkBehavior?: 'action' | 'selection' | 'override',
59+
60+
/**
61+
* The primary orientation of the items. Usually this is the direction that the collection scrolls.
62+
* @default 'vertical'
63+
*/
64+
orientation?: Orientation
5965
}
6066

6167
/**
@@ -68,6 +74,7 @@ export function useListBox<T>(props: AriaListBoxOptions<T>, state: ListState<T>,
6874
let domProps = filterDOMProps(props, {labelable: true});
6975
// Use props instead of state here. We don't want this to change due to long press.
7076
let selectionBehavior = props.selectionBehavior || 'toggle';
77+
let orientation = props.orientation || 'vertical';
7178
let linkBehavior = props.linkBehavior || (selectionBehavior === 'replace' ? 'action' : 'override');
7279
if (selectionBehavior === 'toggle' && linkBehavior === 'action') {
7380
// linkBehavior="action" does not work with selectionBehavior="toggle" because there is no way
@@ -119,6 +126,7 @@ export function useListBox<T>(props: AriaListBoxOptions<T>, state: ListState<T>,
119126
'aria-multiselectable': 'true'
120127
} : {}, {
121128
role: 'listbox',
129+
'aria-orientation': orientation,
122130
...mergeProps(fieldProps, listProps)
123131
})
124132
};

packages/@react-aria/selection/src/ListKeyboardDelegate.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ export class ListKeyboardDelegate<T> implements KeyboardDelegate {
248248

249249
let nextKey: Key | null = key;
250250
if (this.orientation === 'horizontal') {
251-
let pageX = Math.min(this.layoutDelegate.getContentSize().width, itemRect.y - itemRect.width + this.layoutDelegate.getVisibleRect().width);
251+
let pageX = Math.min(this.layoutDelegate.getContentSize().width, itemRect.x - itemRect.width + this.layoutDelegate.getVisibleRect().width);
252252

253253
while (itemRect && itemRect.x < pageX && nextKey != null) {
254254
nextKey = this.getKeyBelow(nextKey);

packages/@react-aria/selection/src/useSelectableList.ts

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
*/
1212

1313
import {AriaSelectableCollectionOptions, useSelectableCollection} from './useSelectableCollection';
14-
import {Collection, DOMAttributes, Key, KeyboardDelegate, LayoutDelegate, Node} from '@react-types/shared';
14+
import {Collection, DOMAttributes, Key, KeyboardDelegate, LayoutDelegate, Node, Orientation} from '@react-types/shared';
1515
import {ListKeyboardDelegate} from './ListKeyboardDelegate';
1616
import {useCollator} from '@react-aria/i18n';
1717
import {useMemo} from 'react';
@@ -34,7 +34,12 @@ export interface AriaSelectableListOptions extends Omit<AriaSelectableCollection
3434
/**
3535
* The item keys that are disabled. These items cannot be selected, focused, or otherwise interacted with.
3636
*/
37-
disabledKeys: Set<Key>
37+
disabledKeys: Set<Key>,
38+
/**
39+
* The primary orientation of the items. Usually this is the direction that the collection scrolls.
40+
* @default 'vertical'
41+
*/
42+
orientation?: Orientation
3843
}
3944

4045
export interface SelectableListAria {
@@ -54,7 +59,8 @@ export function useSelectableList(props: AriaSelectableListOptions): SelectableL
5459
disabledKeys,
5560
ref,
5661
keyboardDelegate,
57-
layoutDelegate
62+
layoutDelegate,
63+
orientation
5864
} = props;
5965

6066
// By default, a KeyboardDelegate is provided which uses the DOM to query layout information (e.g. for page up/page down).
@@ -68,9 +74,10 @@ export function useSelectableList(props: AriaSelectableListOptions): SelectableL
6874
disabledBehavior,
6975
ref,
7076
collator,
71-
layoutDelegate
77+
layoutDelegate,
78+
orientation
7279
})
73-
), [keyboardDelegate, layoutDelegate, collection, disabledKeys, ref, collator, disabledBehavior]);
80+
), [keyboardDelegate, layoutDelegate, collection, disabledKeys, ref, collator, disabledBehavior, orientation]);
7481

7582
let {collectionProps} = useSelectableCollection({
7683
...props,

packages/@react-spectrum/table/test/TableTests.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1990,7 +1990,7 @@ export let tableTests = () => {
19901990
let row = cell.closest('[role=row]');
19911991
let cells = within(row).getAllByRole('gridcell');
19921992
let rowHeaders = within(row).getAllByRole('rowheader');
1993-
expect(cells).toHaveLength(9);
1993+
expect(cells).toHaveLength(10);
19941994
expect(rowHeaders).toHaveLength(1);
19951995
expect(cells[0]).toHaveAttribute('aria-colindex', '1'); // checkbox
19961996
expect(rowHeaders[0]).toHaveAttribute('aria-colindex', '2'); // rowheader

0 commit comments

Comments
 (0)