Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(tab): tabpanel is tabbable when content has no tabbable content #7894

Merged
merged 12 commits into from
Mar 16, 2021

Conversation

tay1orjones
Copy link
Member

@tay1orjones tay1orjones commented Feb 25, 2021

Closes #7078

Add hasNoFocusableContent prop to Tab to inform if the tabpanel container should be focusable. This preserves existing functionality and allows users to opt-in to the new focus behavior when their tab content is text only or has no focusable elements/content.

Add useTabbableContent to TabContent to automatically determine if the contents contains a tabbable element.

Changelog

New

  • Add useTabbableContent hook to TabContent

Changed

  • Update Tabs story to have a tabbable button in the contents of one tab
  • Refactor TabContent tests to use testing-library and test for tabindex/hook functionality

Testing / Reviewing

  1. Open the default Tabs story
  2. Type the Tab key - focus should move to the tabpanel/content container.
  3. Type Shift + Tab - focus moves back to "Tab Label 1"
  4. Type - focus should move to "Tab Label 2"
  5. Type the Tab key again - focus should move to the button inside the content (the tabpanel should not be focused as the content contains a focusable element)

@netlify
Copy link

netlify bot commented Feb 25, 2021

Deploy preview for carbon-elements ready!

Built with commit 3c7538d

https://deploy-preview-7894--carbon-elements.netlify.app

@netlify
Copy link

netlify bot commented Feb 25, 2021

Deploy preview for carbon-components-react ready!

Built without sensitive environment variables with commit 3c7538d

https://deploy-preview-7894--carbon-components-react.netlify.app

Copy link
Contributor

@joshblack joshblack left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would we want to do this automatically for users, potentially? Like in an effect. Something like:

function TabContent(props) {
  // ...
  const ref = useRef(null);
  const hasFocusableContent = useFocusableContent(ref);

  // ...
  return <div role="tabpanel" tabIndex={hasFocusableContent ? 0 : undefined}>...</div>
}

function useFocusableContent(ref) {
  const [hasFocusableContent, setFocusableContent] = useState(false);

  useEffect(() => {
    const elements = getFocusableElements(ref.current);
    if (elements.length > 0) {
      setFocusableContent(true);
    }
  }, [ref]);

  return hasFocusableContent;
}

@tay1orjones
Copy link
Member Author

I dig the magic there for sure. It would also work if content is passed in via renderContent.

My only concern would be performance of getFocusableElements(ref.current);. Iterating over every element recursively against a known list of focusable elements on every render feels ... expensive 😬

@tay1orjones
Copy link
Member Author

You know what... I forgot about querySelectorAll 😅 Yeah this sounds good, I'll update the PR.

@emyarod
Copy link
Member

emyarod commented Feb 25, 2021

this might be helpful for your querySelectorAll https://github.com/carbon-design-system/carbon/blob/master/packages/react/src/internal/keyboard/navigation.js#L64-L72

@tay1orjones tay1orjones changed the title fix(tab): tabpanel is focusable when content has no focusable content fix(tab): tabpanel is tabbable when content has no tabbable content Feb 25, 2021
@tay1orjones
Copy link
Member Author

Thanks @emyarod, I hadn't seen that. I think it's selectorTabbable that I really wanted, "focusable elements" was a misnomer.

I modified the PR body to reflect the new approach. Also added some tests and refactored the tests off enzyme.

Co-authored-by: emyarod <emyarod@users.noreply.github.com>
Copy link
Contributor

@jnm2377 jnm2377 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Tab content is now tabbable if there's no interactive elements. Thanks for adding the comments to the code too! Super helpful! 🔥

@joshblack
Copy link
Contributor

@tay1orjones definitely get what you mean, it might be that this abstraction doesn't make see if the tabpanel also needs to be explicitly labeled so would totally understand moving forward with the prop approach if this wouldn't be a good fit to do automatically.

@tay1orjones
Copy link
Member Author

@joshblack It works quite nicely. Explicitly labelling the tabpanel feels like a very unlikely use case, but we could allow it by placing the automatic tabindex above {...others} so it's overridable?

Base automatically changed from master to main March 8, 2021 16:35
Copy link
Contributor

@andreancardona andreancardona left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tay1orjones this looks awesome!

I'm so so sorry for the review delay... 👀

@kodiakhq kodiakhq bot merged commit 6610748 into carbon-design-system:main Mar 16, 2021
@tay1orjones tay1orjones deleted the 7078-tab-content-focus branch March 17, 2021 14:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Tabs control: tabpanel needs to be in tab order if there's no focusable tabpanel content
5 participants