diff --git a/src/__tests__/tab.js b/src/__tests__/tab.js
index de9d0f46..fbd0493d 100644
--- a/src/__tests__/tab.js
+++ b/src/__tests__/tab.js
@@ -17,7 +17,7 @@ test('fires events when tabbing between two elements', () => {
userEvent.tab()
expect(getEventSnapshot()).toMatchInlineSnapshot(`
Events fired on: div
-
+
input#a[value=""] - keydown: Tab (9)
input#a[value=""] - focusout
input#b[value=""] - focusin
@@ -45,7 +45,7 @@ test('does not change focus if default prevented on keydown', () => {
userEvent.tab()
expect(getEventSnapshot()).toMatchInlineSnapshot(`
Events fired on: div
-
+
input#a[value=""] - keydown: Tab (9)
input#a[value=""] - keyup: Tab (9)
`)
@@ -418,6 +418,26 @@ test('should not focus disabled elements', () => {
expect(five).toHaveFocus()
})
+test('should not focus elements inside a hidden parent', () => {
+ setup(`
+
+
+
+
+
+
+
`)
+
+ const one = document.querySelector('[data-testid="one"]')
+ const three = document.querySelector('[data-testid="three"]')
+
+ userEvent.tab()
+ expect(one).toHaveFocus()
+
+ userEvent.tab()
+ expect(three).toHaveFocus()
+})
+
test('should keep focus on the document if there are no enabled, focusable elements', () => {
setup(``)
userEvent.tab()
diff --git a/src/tab.js b/src/tab.js
index 4bca7ff6..5205684b 100644
--- a/src/tab.js
+++ b/src/tab.js
@@ -31,7 +31,11 @@ function tab({shift = false, focusTrap} = {}) {
const enabledElements = [...focusableElements].filter(
el =>
el === previousElement ||
- (el.getAttribute('tabindex') !== '-1' && !el.disabled),
+ (el.getAttribute('tabindex') !== '-1' &&
+ !el.disabled &&
+ // Hidden elements are taken out of the
+ // document, along with all their children.
+ !el.closest('[hidden]')),
)
if (enabledElements.length === 0) return