Skip to content

Commit 8058a37

Browse files
authored
fix(tab): cycle focus between the body and page tab sequence (testing-library#368)
* Cycle focus between the body and page tab sequence See testing-library/user-event#365 for context * Update documentation on `tab` method to reflect cycling with document.body Closes testing-library#365
1 parent f4eae9c commit 8058a37

File tree

3 files changed

+50
-7
lines changed

3 files changed

+50
-7
lines changed

README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -402,7 +402,11 @@ it('should cycle elements in document tab order', () => {
402402

403403
userEvent.tab()
404404

405-
// cycle goes back to first element
405+
// cycle goes back to the body element
406+
expect(document.body).toHaveFocus()
407+
408+
userEvent.tab()
409+
406410
expect(checkbox).toHaveFocus()
407411
})
408412
```
@@ -545,6 +549,7 @@ Thanks goes to these people ([emoji key][emojis]):
545549

546550
<!-- markdownlint-enable -->
547551
<!-- prettier-ignore-end -->
552+
548553
<!-- ALL-CONTRIBUTORS-LIST:END -->
549554

550555
This project follows the [all-contributors][all-contributors] specification.

src/__tests__/tab.js

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,11 @@ test('should cycle elements in document tab order', () => {
111111

112112
userEvent.tab()
113113

114-
// cycle goes back to first element
114+
// cycle goes back to the body
115+
expect(document.body).toHaveFocus()
116+
117+
userEvent.tab()
118+
115119
expect(checkbox).toHaveFocus()
116120
})
117121

@@ -127,14 +131,27 @@ test('should go backwards when shift = true', () => {
127131
'[data-testid="element"]',
128132
)
129133

130-
radio.focus()
134+
expect(document.body).toHaveFocus()
135+
136+
userEvent.tab({shift: true})
137+
138+
expect(number).toHaveFocus()
139+
140+
userEvent.tab({shift: true})
141+
142+
expect(radio).toHaveFocus()
131143

132144
userEvent.tab({shift: true})
133145

134146
expect(checkbox).toHaveFocus()
135147

136148
userEvent.tab({shift: true})
137149

150+
// cycle goes back to the body
151+
expect(document.body).toHaveFocus()
152+
153+
userEvent.tab({shift: true})
154+
138155
expect(number).toHaveFocus()
139156
})
140157

@@ -164,6 +181,10 @@ test('should respect tabindex, regardless of dom position', () => {
164181

165182
userEvent.tab()
166183

184+
expect(document.body).toHaveFocus()
185+
186+
userEvent.tab()
187+
167188
expect(radio).toHaveFocus()
168189
})
169190

@@ -193,6 +214,10 @@ test('should respect tab index order, then DOM order', () => {
193214

194215
userEvent.tab()
195216

217+
expect(document.body).toHaveFocus()
218+
219+
userEvent.tab()
220+
196221
expect(checkbox).toHaveFocus()
197222
})
198223

src/tab.js

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,22 @@ import {getActiveElement, FOCUSABLE_SELECTOR} from './utils'
33
import {focus} from './focus'
44
import {blur} from './blur'
55

6+
function getNextElement(currentIndex, shift, elements, focusTrap) {
7+
if (focusTrap === document && currentIndex === 0 && shift) {
8+
return document.body
9+
} else if (
10+
focusTrap === document &&
11+
currentIndex === elements.length - 1 &&
12+
!shift
13+
) {
14+
return document.body
15+
} else {
16+
const nextIndex = shift ? currentIndex - 1 : currentIndex + 1
17+
const defaultIndex = shift ? elements.length - 1 : 0
18+
return elements[nextIndex] || elements[defaultIndex]
19+
}
20+
}
21+
622
function tab({shift = false, focusTrap} = {}) {
723
const previousElement = getActiveElement(focusTrap?.ownerDocument ?? document)
824

@@ -57,10 +73,7 @@ function tab({shift = false, focusTrap} = {}) {
5773
el => el === el.ownerDocument.activeElement,
5874
)
5975

60-
const nextIndex = shift ? index - 1 : index + 1
61-
const defaultIndex = shift ? prunedElements.length - 1 : 0
62-
63-
const nextElement = prunedElements[nextIndex] || prunedElements[defaultIndex]
76+
const nextElement = getNextElement(index, shift, prunedElements, focusTrap)
6477

6578
const shiftKeyInit = {
6679
key: 'Shift',

0 commit comments

Comments
 (0)