Skip to content

Commit c4a783e

Browse files
authored
fix/bug useTablist #5996 (#6023)
* fix/bug useTabist #5996 and added tests
1 parent 6943a6b commit c4a783e

File tree

3 files changed

+59
-31
lines changed

3 files changed

+59
-31
lines changed

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

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -164,49 +164,57 @@ export function useSelectableCollection(options: AriaSelectableCollectionOptions
164164
switch (e.key) {
165165
case 'ArrowDown': {
166166
if (delegate.getKeyBelow) {
167-
e.preventDefault();
168167
let nextKey = manager.focusedKey != null
169-
? delegate.getKeyBelow(manager.focusedKey)
168+
? delegate.getKeyBelow?.(manager.focusedKey)
170169
: delegate.getFirstKey?.();
171170
if (nextKey == null && shouldFocusWrap) {
172171
nextKey = delegate.getFirstKey?.(manager.focusedKey);
173172
}
174-
navigateToKey(nextKey);
173+
if (nextKey != null) {
174+
e.preventDefault();
175+
navigateToKey(nextKey);
176+
}
175177
}
176178
break;
177179
}
178180
case 'ArrowUp': {
179181
if (delegate.getKeyAbove) {
180-
e.preventDefault();
181182
let nextKey = manager.focusedKey != null
182-
? delegate.getKeyAbove(manager.focusedKey)
183+
? delegate.getKeyAbove?.(manager.focusedKey)
183184
: delegate.getLastKey?.();
184185
if (nextKey == null && shouldFocusWrap) {
185186
nextKey = delegate.getLastKey?.(manager.focusedKey);
186187
}
187-
navigateToKey(nextKey);
188+
if (nextKey != null) {
189+
e.preventDefault();
190+
navigateToKey(nextKey);
191+
}
188192
}
189193
break;
190194
}
191195
case 'ArrowLeft': {
192196
if (delegate.getKeyLeftOf) {
193-
e.preventDefault();
194-
let nextKey = delegate.getKeyLeftOf(manager.focusedKey);
197+
let nextKey = delegate.getKeyLeftOf?.(manager.focusedKey);
195198
if (nextKey == null && shouldFocusWrap) {
196199
nextKey = direction === 'rtl' ? delegate.getFirstKey?.(manager.focusedKey) : delegate.getLastKey?.(manager.focusedKey);
197200
}
198-
navigateToKey(nextKey, direction === 'rtl' ? 'first' : 'last');
201+
if (nextKey != null) {
202+
e.preventDefault();
203+
navigateToKey(nextKey, direction === 'rtl' ? 'first' : 'last');
204+
}
199205
}
200206
break;
201207
}
202208
case 'ArrowRight': {
203209
if (delegate.getKeyRightOf) {
204-
e.preventDefault();
205-
let nextKey = delegate.getKeyRightOf(manager.focusedKey);
210+
let nextKey = delegate.getKeyRightOf?.(manager.focusedKey);
206211
if (nextKey == null && shouldFocusWrap) {
207212
nextKey = direction === 'rtl' ? delegate.getLastKey?.(manager.focusedKey) : delegate.getFirstKey?.(manager.focusedKey);
208213
}
209-
navigateToKey(nextKey, direction === 'rtl' ? 'last' : 'first');
214+
if (nextKey != null) {
215+
e.preventDefault();
216+
navigateToKey(nextKey, direction === 'rtl' ? 'last' : 'first');
217+
}
210218
}
211219
break;
212220
}
@@ -236,16 +244,20 @@ export function useSelectableCollection(options: AriaSelectableCollectionOptions
236244
break;
237245
case 'PageDown':
238246
if (delegate.getKeyPageBelow) {
239-
e.preventDefault();
240247
let nextKey = delegate.getKeyPageBelow(manager.focusedKey);
241-
navigateToKey(nextKey);
248+
if (nextKey != null) {
249+
e.preventDefault();
250+
navigateToKey(nextKey);
251+
}
242252
}
243253
break;
244254
case 'PageUp':
245255
if (delegate.getKeyPageAbove) {
246-
e.preventDefault();
247256
let nextKey = delegate.getKeyPageAbove(manager.focusedKey);
248-
navigateToKey(nextKey);
257+
if (nextKey != null) {
258+
e.preventDefault();
259+
navigateToKey(nextKey);
260+
}
249261
}
250262
break;
251263
case 'a':

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

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,13 @@ export class TabsKeyboardDelegate<T> implements KeyboardDelegate {
1616
private collection: Collection<Node<T>>;
1717
private flipDirection: boolean;
1818
private disabledKeys: Set<Key>;
19+
private tabDirection: boolean;
1920

2021
constructor(collection: Collection<Node<T>>, direction: Direction, orientation: Orientation, disabledKeys: Set<Key> = new Set()) {
2122
this.collection = collection;
2223
this.flipDirection = direction === 'rtl' && orientation === 'horizontal';
2324
this.disabledKeys = disabledKeys;
25+
this.tabDirection = orientation === 'horizontal';
2426
}
2527

2628
getKeyLeftOf(key: Key) {
@@ -37,13 +39,6 @@ export class TabsKeyboardDelegate<T> implements KeyboardDelegate {
3739
return this.getNextKey(key);
3840
}
3941

40-
getKeyAbove(key: Key) {
41-
return this.getPreviousKey(key);
42-
}
43-
44-
getKeyBelow(key: Key) {
45-
return this.getNextKey(key);
46-
}
4742

4843
private isDisabled(key: Key) {
4944
return this.disabledKeys.has(key) || !!this.collection.getItem(key)?.props?.isDisabled;
@@ -64,6 +59,20 @@ export class TabsKeyboardDelegate<T> implements KeyboardDelegate {
6459
}
6560
return key;
6661
}
62+
63+
getKeyAbove(key: Key) {
64+
if (this.tabDirection) {
65+
return null;
66+
}
67+
return this.getPreviousKey(key);
68+
}
69+
70+
getKeyBelow(key: Key) {
71+
if (this.tabDirection) {
72+
return null;
73+
}
74+
return this.getNextKey(key);
75+
}
6776

6877
getNextKey(key) {
6978
do {

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

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
* governing permissions and limitations under the License.
1111
*/
1212

13-
import {act, fireEvent, mockImplementation, pointerMap, render, waitFor, within} from '@react-spectrum/test-utils-internal';
13+
import {act, createEvent, fireEvent, mockImplementation, pointerMap, render, waitFor, within} from '@react-spectrum/test-utils-internal';
1414
import {Item, TabList, TabPanels, Tabs} from '../src';
1515
import {Links as LinksExample} from '../stories/Tabs.stories';
1616
import {Provider} from '@react-spectrum/provider';
@@ -113,18 +113,25 @@ describe('Tabs', function () {
113113

114114
expect(selectedItem).toHaveAttribute('aria-selected', 'true');
115115
act(() => {selectedItem.focus();});
116-
fireEvent.keyDown(selectedItem, {key: 'ArrowRight', code: 39, charCode: 39});
116+
let arrowRight = createEvent.keyDown(selectedItem, {key: 'ArrowRight', code: 39, charCode: 39});
117+
fireEvent(selectedItem, arrowRight);
117118
let nextSelectedItem = tabs[1];
118119
expect(nextSelectedItem).toHaveAttribute('aria-selected', 'true');
119-
fireEvent.keyDown(nextSelectedItem, {key: 'ArrowLeft', code: 37, charCode: 37});
120+
expect(arrowRight.defaultPrevented).toBe(true);
121+
let arrowLeft = createEvent.keyDown(nextSelectedItem, {key: 'ArrowLeft', code: 37, charCode: 37});
122+
fireEvent(nextSelectedItem, arrowLeft);
120123
expect(selectedItem).toHaveAttribute('aria-selected', 'true');
124+
expect(arrowLeft.defaultPrevented).toBe(true);
121125

122-
/** Changes selection regardless if it's horizontal tabs. */
123-
fireEvent.keyDown(selectedItem, {key: 'ArrowUp', code: 38, charCode: 38});
124-
nextSelectedItem = tabs[2];
125-
expect(nextSelectedItem).toHaveAttribute('aria-selected', 'true');
126-
fireEvent.keyDown(selectedItem, {key: 'ArrowDown', code: 40, charCode: 40});
126+
/** prevent changing tabs for horizontal orientations in aria-selected */
127+
let arrowUp = createEvent.keyDown(selectedItem, {key: 'ArrowUp', code: 38, charCode: 38});
128+
fireEvent(selectedItem, arrowUp);
129+
expect(selectedItem).toHaveAttribute('aria-selected', 'true');
130+
expect(arrowUp.defaultPrevented).toBe(false);
131+
let arrowDown = createEvent.keyDown(selectedItem, {key: 'ArrowDown', code: 40, charCode: 40});
132+
fireEvent(selectedItem, arrowDown);
127133
expect(selectedItem).toHaveAttribute('aria-selected', 'true');
134+
expect(arrowDown.defaultPrevented).toBe(false);
128135
});
129136

130137
it('allows user to change tab item select via arrow keys with vertical tabs', function () {

0 commit comments

Comments
 (0)