Skip to content

Commit

Permalink
fix(tab): cycle focus between the body and page tab sequence (#368)
Browse files Browse the repository at this point in the history
* Cycle focus between the body and page tab sequence

See #365 for context

* Update documentation on `tab` method to reflect cycling with document.body

Closes #365
  • Loading branch information
juanca committed Jun 21, 2020
1 parent f4eae9c commit 8058a37
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 7 deletions.
7 changes: 6 additions & 1 deletion README.md
Expand Up @@ -402,7 +402,11 @@ it('should cycle elements in document tab order', () => {

userEvent.tab()

// cycle goes back to first element
// cycle goes back to the body element
expect(document.body).toHaveFocus()

userEvent.tab()

expect(checkbox).toHaveFocus()
})
```
Expand Down Expand Up @@ -545,6 +549,7 @@ Thanks goes to these people ([emoji key][emojis]):

<!-- markdownlint-enable -->
<!-- prettier-ignore-end -->

<!-- ALL-CONTRIBUTORS-LIST:END -->

This project follows the [all-contributors][all-contributors] specification.
Expand Down
29 changes: 27 additions & 2 deletions src/__tests__/tab.js
Expand Up @@ -111,7 +111,11 @@ test('should cycle elements in document tab order', () => {

userEvent.tab()

// cycle goes back to first element
// cycle goes back to the body
expect(document.body).toHaveFocus()

userEvent.tab()

expect(checkbox).toHaveFocus()
})

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

radio.focus()
expect(document.body).toHaveFocus()

userEvent.tab({shift: true})

expect(number).toHaveFocus()

userEvent.tab({shift: true})

expect(radio).toHaveFocus()

userEvent.tab({shift: true})

expect(checkbox).toHaveFocus()

userEvent.tab({shift: true})

// cycle goes back to the body
expect(document.body).toHaveFocus()

userEvent.tab({shift: true})

expect(number).toHaveFocus()
})

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

userEvent.tab()

expect(document.body).toHaveFocus()

userEvent.tab()

expect(radio).toHaveFocus()
})

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

userEvent.tab()

expect(document.body).toHaveFocus()

userEvent.tab()

expect(checkbox).toHaveFocus()
})

Expand Down
21 changes: 17 additions & 4 deletions src/tab.js
Expand Up @@ -3,6 +3,22 @@ import {getActiveElement, FOCUSABLE_SELECTOR} from './utils'
import {focus} from './focus'
import {blur} from './blur'

function getNextElement(currentIndex, shift, elements, focusTrap) {
if (focusTrap === document && currentIndex === 0 && shift) {
return document.body
} else if (
focusTrap === document &&
currentIndex === elements.length - 1 &&
!shift
) {
return document.body
} else {
const nextIndex = shift ? currentIndex - 1 : currentIndex + 1
const defaultIndex = shift ? elements.length - 1 : 0
return elements[nextIndex] || elements[defaultIndex]
}
}

function tab({shift = false, focusTrap} = {}) {
const previousElement = getActiveElement(focusTrap?.ownerDocument ?? document)

Expand Down Expand Up @@ -57,10 +73,7 @@ function tab({shift = false, focusTrap} = {}) {
el => el === el.ownerDocument.activeElement,
)

const nextIndex = shift ? index - 1 : index + 1
const defaultIndex = shift ? prunedElements.length - 1 : 0

const nextElement = prunedElements[nextIndex] || prunedElements[defaultIndex]
const nextElement = getNextElement(index, shift, prunedElements, focusTrap)

const shiftKeyInit = {
key: 'Shift',
Expand Down

0 comments on commit 8058a37

Please sign in to comment.