diff --git a/packages/react/src/components/TabContent/TabContent-test.js b/packages/react/src/components/TabContent/TabContent-test.js index 6f43b8f46361..0ac53779850b 100644 --- a/packages/react/src/components/TabContent/TabContent-test.js +++ b/packages/react/src/components/TabContent/TabContent-test.js @@ -7,29 +7,54 @@ import React from 'react'; import TabContent from '../TabContent'; -import { shallow } from 'enzyme'; +import { render, screen } from '@testing-library/react'; +import '@testing-library/jest-dom'; describe('TabContent', () => { describe('renders as expected', () => { - const wrapper = shallow( - -
content
-
content
-
- ); - it('renders children as expected', () => { - expect(wrapper.props().children.length).toEqual(2); + render( + +
content
+
content
+
+ ); + expect(screen.getByRole('tabpanel').children.length).toEqual(2); }); - it('sets selected if passed in via props', () => { - wrapper.setProps({ selected: true }); - expect(wrapper.props().selected).toEqual(true); + it('sets selected and hidden props with opposite boolean values', () => { + const { rerender } = render( + +
content
+
content
+
+ ); + expect(screen.queryByRole('tabpanel')).not.toBeInTheDocument(); + rerender( + +
content
+
content
+
+ ); + expect(screen.getByRole('tabpanel')).toBeVisible(); }); - it('sets selected and hidden props with opposite boolean values', () => { - wrapper.setProps({ selected: true }); - expect(wrapper.props().hidden).toEqual(false); + it('includes the content container in the tabbable index when no tabble contents is provided', () => { + render( + +

content

+
+ ); + expect(screen.getByRole('tabpanel')).toHaveAttribute('tabindex', '0'); + }); + + it('does not include the content container in the tabbable index when tabble contents is provided', () => { + render( + + content + + ); + expect(screen.getByRole('tabpanel')).not.toHaveAttribute('tabindex', '0'); }); }); }); diff --git a/packages/react/src/components/TabContent/TabContent.js b/packages/react/src/components/TabContent/TabContent.js index abe8140d73d3..5269fe427ee9 100644 --- a/packages/react/src/components/TabContent/TabContent.js +++ b/packages/react/src/components/TabContent/TabContent.js @@ -6,24 +6,45 @@ */ import PropTypes from 'prop-types'; -import React from 'react'; +import React, { useState, useRef } from 'react'; import classNames from 'classnames'; import { settings } from 'carbon-components'; +import { selectorTabbable } from '../../internal/keyboard/navigation'; +import useIsomorphicEffect from '../../internal/useIsomorphicEffect'; const { prefix } = settings; +/** + * Determine if the node within the provided ref contains content that is tabbable. + */ +function useTabbableContent(ref) { + const [hasTabbableContent, setHasTabbableContent] = useState(false); + + useIsomorphicEffect(() => { + if (ref.current) { + setHasTabbableContent(ref.current.querySelector(selectorTabbable)); + } + }); + + return hasTabbableContent; +} + const TabContent = (props) => { const { className, selected, children, ...other } = props; const tabContentClasses = classNames(`${prefix}--tab-content`, { [className]: className, }); + const ref = useRef(null); + const hasTabbableContent = useTabbableContent(ref); return ( ); diff --git a/packages/react/src/components/Tabs/Tabs-story.js b/packages/react/src/components/Tabs/Tabs-story.js index 72ce156249f5..da9e59f22a0a 100644 --- a/packages/react/src/components/Tabs/Tabs-story.js +++ b/packages/react/src/components/Tabs/Tabs-story.js @@ -18,6 +18,7 @@ import { settings } from 'carbon-components'; import classNames from 'classnames'; import './Tabs-story.scss'; import CodeSnippet from '../CodeSnippet'; +import Button from '../Button'; import Tabs from '../Tabs'; import Tab from '../Tab'; import TabsSkeleton from '../Tabs/Tabs.Skeleton'; @@ -128,6 +129,7 @@ export const _Default = () => (

Content for second tab goes here.

+

Content for third tab goes here.

diff --git a/packages/react/src/internal/useIsomorphicEffect.js b/packages/react/src/internal/useIsomorphicEffect.js new file mode 100644 index 000000000000..93b9f7e5ec7a --- /dev/null +++ b/packages/react/src/internal/useIsomorphicEffect.js @@ -0,0 +1,7 @@ +import { useEffect, useLayoutEffect } from 'react'; + +// useLayoutEffect on the client, useEffect on the server +const useIsomorphicEffect = + typeof window !== 'undefined' ? useLayoutEffect : useEffect; + +export default useIsomorphicEffect;