diff --git a/packages/components/src/tabs/tab.tsx b/packages/components/src/tabs/tab.tsx index 29f6111adc8397..70f56e52ad2627 100644 --- a/packages/components/src/tabs/tab.tsx +++ b/packages/components/src/tabs/tab.tsx @@ -1,3 +1,8 @@ +/** + * External dependencies + */ +import * as Ariakit from '@ariakit/react'; + /** * WordPress dependencies */ @@ -22,13 +27,27 @@ export const Tab = forwardRef< HTMLButtonElement, Omit< WordPressComponentProps< TabProps, 'button', false >, 'id' > >( function Tab( { children, tabId, disabled, render, ...otherProps }, ref ) { - const context = useTabsContext(); - if ( ! context ) { + const { store, instanceId } = useTabsContext() ?? {}; + + // If the active item is not connected, the tablist may end up in a state + // where none of the tabs are tabbable. In this case, we force all tabs to + // be tabbable, so that as soon as an item received focus, it becomes active + // and Tablist goes back to working as expected. + // eslint-disable-next-line @wordpress/no-unused-vars-before-return + const tabbable = Ariakit.useStoreState( store, ( state ) => { + return ( + state?.activeId !== null && + ! store?.item( state?.activeId )?.element?.isConnected + ); + } ); + + if ( ! store ) { warning( '`Tabs.Tab` must be wrapped in a `Tabs` component.' ); return null; } - const { store, instanceId } = context; + const instancedTabId = `${ instanceId }-${ tabId }`; + return ( { children }