Skip to content

Commit c6313b4

Browse files
[RAC]: Update Tabs navigation for arrow keys (#4821)
Allow tab item select via all arrow keys regardless of orientation. Co-authored-by: Daniel Lu <dl1644@gmail.com>
1 parent be50125 commit c6313b4

File tree

5 files changed

+23
-36
lines changed

5 files changed

+23
-36
lines changed

packages/@react-aria/tabs/docs/useTabList.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,7 @@ tab selection.
319319

320320
### Orientation
321321

322-
By default, tabs are horizontally oriented. The `orientation` prop can be set to `vertical` to change this. This affects keyboard navigation. You are responsible for styling your tabs accordingly.
322+
By default, tabs are horizontally oriented. The `orientation` prop can be set to `vertical` to change this. This does not affect keyboard navigation. You are responsible for styling your tabs accordingly.
323323

324324
```tsx example
325325
<Tabs aria-label="Chat log orientation example" orientation="vertical">

packages/@react-aria/tabs/src/TabsKeyboardDelegate.ts

Lines changed: 6 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -17,49 +17,33 @@ export class TabsKeyboardDelegate<T> implements KeyboardDelegate {
1717
private collection: Collection<T>;
1818
private flipDirection: boolean;
1919
private disabledKeys: Set<Key>;
20-
private orientation: Orientation;
2120

2221
constructor(collection: Collection<T>, direction: Direction, orientation: Orientation, disabledKeys: Set<Key> = new Set()) {
2322
this.collection = collection;
24-
this.flipDirection = direction === 'rtl' && orientation === 'horizontal';
25-
this.orientation = orientation;
23+
this.flipDirection = direction === 'rtl' && orientation === 'horizontal';
2624
this.disabledKeys = disabledKeys;
2725
}
2826

2927
getKeyLeftOf(key: Key) {
3028
if (this.flipDirection) {
3129
return this.getNextKey(key);
32-
} else {
33-
if (this.orientation === 'horizontal') {
34-
return this.getPreviousKey(key);
35-
}
36-
return null;
3730
}
31+
return this.getPreviousKey(key);
3832
}
3933

4034
getKeyRightOf(key: Key) {
4135
if (this.flipDirection) {
4236
return this.getPreviousKey(key);
43-
} else {
44-
if (this.orientation === 'horizontal') {
45-
return this.getNextKey(key);
46-
}
47-
return null;
48-
}
37+
}
38+
return this.getNextKey(key);
4939
}
5040

5141
getKeyAbove(key: Key) {
52-
if (this.orientation === 'vertical') {
53-
return this.getPreviousKey(key);
54-
}
55-
return null;
42+
return this.getPreviousKey(key);
5643
}
5744

5845
getKeyBelow(key: Key) {
59-
if (this.orientation === 'vertical') {
60-
return this.getNextKey(key);
61-
}
62-
return null;
46+
return this.getNextKey(key);
6347
}
6448

6549
getFirstKey() {

packages/@react-spectrum/tabs/docs/Tabs.mdx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,8 @@ function Example() {
469469
### Orientation
470470
[View guidelines](https://spectrum.adobe.com/page/tabs/#Orientation)
471471

472+
By default, tabs are horizontally oriented. The `orientation` prop can be set to `vertical` to change this. This does not affect keyboard navigation.
473+
472474
```tsx example
473475
<Tabs aria-label="Chat log orientation example" orientation="vertical">
474476
<TabList>

packages/@react-spectrum/tabs/test/Tabs.test.js

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -101,12 +101,11 @@ describe('Tabs', function () {
101101
expect(ref.current.UNSAFE_getDOMNode()).toBe(tablist.parentElement.parentElement);
102102
});
103103

104-
it('allows user to change tab item select via left/right arrow keys with horizontal tabs', function () {
104+
it('allows user to change tab item select via arrow keys with horizontal tabs', function () {
105105
let container = renderComponent({orientation: 'horizontal'});
106106
let tablist = container.getByRole('tablist');
107107
let tabs = within(tablist).getAllByRole('tab');
108108
let selectedItem = tabs[0];
109-
110109
expect(tablist).toHaveAttribute('aria-orientation', 'horizontal');
111110

112111
expect(selectedItem).toHaveAttribute('aria-selected', 'true');
@@ -117,14 +116,15 @@ describe('Tabs', function () {
117116
fireEvent.keyDown(nextSelectedItem, {key: 'ArrowLeft', code: 37, charCode: 37});
118117
expect(selectedItem).toHaveAttribute('aria-selected', 'true');
119118

120-
/** Doesn't change selection because it's horizontal tabs. */
119+
/** Changes selection regardless if it's horizontal tabs. */
121120
fireEvent.keyDown(selectedItem, {key: 'ArrowUp', code: 38, charCode: 38});
122-
expect(selectedItem).toHaveAttribute('aria-selected', 'true');
121+
nextSelectedItem = tabs[2];
122+
expect(nextSelectedItem).toHaveAttribute('aria-selected', 'true');
123123
fireEvent.keyDown(selectedItem, {key: 'ArrowDown', code: 40, charCode: 40});
124124
expect(selectedItem).toHaveAttribute('aria-selected', 'true');
125125
});
126126

127-
it('allows user to change tab item select via up/down arrow keys with vertical tabs', function () {
127+
it('allows user to change tab item select via arrow keys with vertical tabs', function () {
128128
let container = renderComponent({orientation: 'vertical'});
129129
let tablist = container.getByRole('tablist');
130130
let tabs = within(tablist).getAllByRole('tab');
@@ -133,18 +133,19 @@ describe('Tabs', function () {
133133

134134
expect(tablist).toHaveAttribute('aria-orientation', 'vertical');
135135

136-
/** Doesn't change selection because it's vertical tabs. */
137-
expect(selectedItem).toHaveAttribute('aria-selected', 'true');
138-
fireEvent.keyDown(selectedItem, {key: 'ArrowRight', code: 39, charCode: 39});
139-
expect(selectedItem).toHaveAttribute('aria-selected', 'true');
140-
fireEvent.keyDown(selectedItem, {key: 'ArrowLeft', code: 37, charCode: 37});
141-
expect(selectedItem).toHaveAttribute('aria-selected', 'true');
142-
143136
let nextSelectedItem = tabs[1];
137+
expect(selectedItem).toHaveAttribute('aria-selected', 'true');
144138
fireEvent.keyDown(selectedItem, {key: 'ArrowDown', code: 40, charCode: 40});
145139
expect(nextSelectedItem).toHaveAttribute('aria-selected', 'true');
146140
fireEvent.keyDown(nextSelectedItem, {key: 'ArrowUp', code: 38, charCode: 38});
147141
expect(selectedItem).toHaveAttribute('aria-selected', 'true');
142+
143+
/** Changes selection regardless if it's vertical tabs. */
144+
fireEvent.keyDown(selectedItem, {key: 'ArrowLeft', code: 37, charCode: 37});
145+
nextSelectedItem = tabs[2];
146+
expect(nextSelectedItem).toHaveAttribute('aria-selected', 'true');
147+
fireEvent.keyDown(selectedItem, {key: 'ArrowRight', code: 39, charCode: 39});
148+
expect(selectedItem).toHaveAttribute('aria-selected', 'true');
148149
});
149150

150151
it('wraps focus from first to last/last to first item', function () {

packages/react-aria-components/docs/Tabs.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -430,7 +430,7 @@ tab selection.
430430

431431
### Orientation
432432

433-
By default, tabs are horizontally oriented. The `orientation` prop can be set to `vertical` to change this. This affects keyboard navigation. You are responsible for styling your tabs accordingly.
433+
By default, tabs are horizontally oriented. The `orientation` prop can be set to `vertical` to change this. This does not affect keyboard navigation. You are responsible for styling your tabs accordingly.
434434

435435
```tsx example
436436
<Tabs orientation="vertical">

0 commit comments

Comments
 (0)