Skip to content

isFocusable/isTabbable has incorrect selectors #8146

@jamiebuilds-signal

Description

@jamiebuilds-signal

Provide a general summary of the issue here

https://github.com/adobe/react-spectrum/blob/2e9061df69eb17f17b0fb7d64d318c2835aed145/packages/%40react-aria/utils/src/isFocusable.ts

FOCUSABLE_ELEMENT_SELECTOR

  input:not([disabled]):not([type=hidden]):not([hidden]),
  select:not([disabled]):not([hidden]),
  textarea:not([disabled]):not([hidden]),
  button:not([disabled]):not([hidden]),
  a[href]:not([hidden]),
  area[href]:not([hidden]),
  summary:not([hidden]),
  iframe:not([hidden]),
  object:not([hidden]),
  embed:not([hidden]),
  audio[controls]:not([hidden]),
  video[controls]:not([hidden]),
- [contenteditable]:not([contenteditable^="false"]), // actual: missing `:not([hidden])`
+ [contenteditable]:not([contenteditable^="false"]):not([hidden]), // expected
  [tabindex]:not([disabled]):not([hidden])

TABBABLE_ELEMENT_SELECTOR

  input:not([disabled]):not([type=hidden]):not([hidden]):not([tabindex="-1"]),
  select:not([disabled]):not([hidden]):not([tabindex="-1"]),
  textarea:not([disabled]):not([hidden]):not([tabindex="-1"]),
  button:not([disabled]):not([hidden]):not([tabindex="-1"]),
  a[href]:not([hidden]):not([tabindex="-1"]),
  area[href]:not([hidden]):not([tabindex="-1"]),
  summary:not([hidden]):not([tabindex="-1"]),
  iframe:not([hidden]):not([tabindex="-1"]),
  object:not([hidden]):not([tabindex="-1"]),
  embed:not([hidden]):not([tabindex="-1"]),
  audio[controls]:not([hidden]):not([tabindex="-1"]),
  video[controls]:not([hidden]):not([tabindex="-1"]),
  [contenteditable]:not([contenteditable^="false"]):not([hidden]):not([tabindex="-1"]),
- [tabindex]:not([tabindex="-1"]):not([disabled]) // actual: missing `:not([hidden])`
+ [tabindex]:not([tabindex="-1"]):not([disabled]):not([hidden]) // expected

🤔 Expected Behavior?

😯 Current Behavior

💁 Possible Solution

const focusableElements = [
  'input:not([disabled]):not([type=hidden])',
  'select:not([disabled])',
  'textarea:not([disabled])',
  'button:not([disabled])',
  'a[href]',
  'area[href]',
  'summary',
  'iframe',
  'object',
  'embed',
  'audio[controls]',
  'video[controls]',
  '[contenteditable]:not([contenteditable^="false"])',
  '[tabindex]:not([disabled])'
];

const FOCUSABLE_ELEMENT_SELECTOR = focusableElements
  .map(selector => `${selector}:not([hidden])`)
  .join(",");

const TABBABLE_ELEMENT_SELECTOR = focusableElements
  .map(selector => `${selector}:not([hidden]):not([tabindex="-1"])`)
  .join(",");

export function isFocusable(element: Element): boolean {
  return element.matches(FOCUSABLE_ELEMENT_SELECTOR);
}

export function isTabbable(element: Element): boolean {
  return element.matches(TABBABLE_ELEMENT_SELECTOR);
}

🔦 Context

No response

🖥️ Steps to Reproduce

Version

What browsers are you seeing the problem on?

Other

If other, please specify.

What operating system are you using?

🧢 Your Company/Team

No response

🕷 Tracking Issue

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions