From 5f4f0a6c486852ad9ce7eb36117fdd7b2cd7ae58 Mon Sep 17 00:00:00 2001 From: andrew Date: Wed, 23 Oct 2024 12:23:28 -0500 Subject: [PATCH] fix(Tabs): reset active index to selected index for manual activation (#17831) * refactor(Tabs): rename internal vars * fix(Tabs): reset active index to selected index for manual activation * test(Tabs): test manual activation blur handler --- packages/react/src/components/Tabs/Tabs.tsx | 38 ++++++++++++++++--- .../components/Tabs/__tests__/Tabs-test.js | 24 ++++++++++++ 2 files changed, 56 insertions(+), 6 deletions(-) diff --git a/packages/react/src/components/Tabs/Tabs.tsx b/packages/react/src/components/Tabs/Tabs.tsx index ee9ecb0d1f3d..d5c6281a635c 100644 --- a/packages/react/src/components/Tabs/Tabs.tsx +++ b/packages/react/src/components/Tabs/Tabs.tsx @@ -531,9 +531,9 @@ function TabList({ ) { event.preventDefault(); - const filtredTabs = tabs.current.filter((tab) => tab !== null); + const filteredTabs = tabs.current.filter((tab) => tab !== null); - const activeTabs: TabElement[] = filtredTabs.filter( + const activeTabs: TabElement[] = filteredTabs.filter( (tab) => !tab.disabled ); @@ -553,6 +553,18 @@ function TabList({ } } + function handleBlur({ + relatedTarget: currentActiveNode, + }: React.FocusEvent) { + if (ref.current?.contains(currentActiveNode)) { + return; + } + // reset active index to selected tab index for manual activation + if (activation === 'manual') { + setActiveIndex(selectedIndex); + } + } + useEffectOnce(() => { const tab = tabs.current[selectedIndex]; if (scrollIntoView && tab) { @@ -706,7 +718,8 @@ function TabList({ role="tablist" className={`${prefix}--tab--list`} onScroll={debouncedOnScroll} - onKeyDown={onKeyDown}> + onKeyDown={onKeyDown} + onBlur={handleBlur}> {React.Children.map(children, (child, index) => { return !isElement(child) ? null : ( tab !== null); + const filteredTabs = tabs.current.filter((tab) => tab !== null); - const activeTabs: TabElement[] = filtredTabs.filter( + const activeTabs: TabElement[] = filteredTabs.filter( (tab) => !tab.disabled ); @@ -898,6 +911,18 @@ function TabListVertical({ } } + function handleBlur({ + relatedTarget: currentActiveNode, + }: React.FocusEvent) { + if (ref.current?.contains(currentActiveNode)) { + return; + } + // reset active index to selected tab index for manual activation + if (activation === 'manual') { + setActiveIndex(selectedIndex); + } + } + useEffectOnce(() => { if (tabs.current[selectedIndex]?.disabled) { const activeTabs = tabs.current.filter((tab) => { @@ -991,7 +1016,8 @@ function TabListVertical({ ref={ref} role="tablist" className={`${prefix}--tab--list`} - onKeyDown={onKeyDown}> + onKeyDown={onKeyDown} + onBlur={handleBlur}> {React.Children.map(children, (child, index) => { return !isElement(child) ? null : ( { ); }); + it('should reset focus to the active tab on blur in manual activation', async () => { + render( + + + Tab Label 1 + Tab Label 2 + Tab Label 3 + + + Tab Panel 1 + Tab Panel 2 + Tab Panel 3 + + + ); + + await userEvent.tab(); + await userEvent.keyboard('[ArrowRight]'); + expect(screen.getByTestId('tab-testid-2')).toHaveFocus(); + await userEvent.keyboard('[Tab]'); + await userEvent.keyboard('[Shift][Tab]'); + expect(screen.getByTestId('tab-testid-1')).toHaveFocus(); + }); + it('should render close icon if dismissable', () => { render( {}}>