From 8058a372d044c5b6e4016a9693b3b8e2ea104dcd Mon Sep 17 00:00:00 2001 From: Juan Carlos Medina Date: Sat, 20 Jun 2020 18:54:23 -0700 Subject: [PATCH] fix(tab): cycle focus between the body and page tab sequence (#368) * Cycle focus between the body and page tab sequence See https://github.com/testing-library/user-event/issues/365 for context * Update documentation on `tab` method to reflect cycling with document.body Closes #365 --- README.md | 7 ++++++- src/__tests__/tab.js | 29 +++++++++++++++++++++++++++-- src/tab.js | 21 +++++++++++++++++---- 3 files changed, 50 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 1b0d378e..c5108923 100644 --- a/README.md +++ b/README.md @@ -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() }) ``` @@ -545,6 +549,7 @@ Thanks goes to these people ([emoji key][emojis]): + This project follows the [all-contributors][all-contributors] specification. diff --git a/src/__tests__/tab.js b/src/__tests__/tab.js index 887408ce..649986e8 100644 --- a/src/__tests__/tab.js +++ b/src/__tests__/tab.js @@ -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() }) @@ -127,7 +131,15 @@ 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}) @@ -135,6 +147,11 @@ test('should go backwards when shift = true', () => { userEvent.tab({shift: true}) + // cycle goes back to the body + expect(document.body).toHaveFocus() + + userEvent.tab({shift: true}) + expect(number).toHaveFocus() }) @@ -164,6 +181,10 @@ test('should respect tabindex, regardless of dom position', () => { userEvent.tab() + expect(document.body).toHaveFocus() + + userEvent.tab() + expect(radio).toHaveFocus() }) @@ -193,6 +214,10 @@ test('should respect tab index order, then DOM order', () => { userEvent.tab() + expect(document.body).toHaveFocus() + + userEvent.tab() + expect(checkbox).toHaveFocus() }) diff --git a/src/tab.js b/src/tab.js index 964dbffe..49817240 100644 --- a/src/tab.js +++ b/src/tab.js @@ -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) @@ -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',