diff --git a/packages/gamut/src/Tip/InfoTip/index.tsx b/packages/gamut/src/Tip/InfoTip/index.tsx index 9a39bae3fc6..143c999f1a5 100644 --- a/packages/gamut/src/Tip/InfoTip/index.tsx +++ b/packages/gamut/src/Tip/InfoTip/index.tsx @@ -106,27 +106,33 @@ export const InfoTip: React.FC = ({ } }; - const handleFocusOut = (event: FocusEvent) => { - const popoverContent = popoverContentRef.current; - const button = buttonRef.current; - const wrapper = wrapperRef.current; - - const { relatedTarget } = event; - - if (relatedTarget instanceof Node) { - // If focus is moving back to the button or wrapper, allow it - const movingToButton = - button?.contains(relatedTarget) || wrapper?.contains(relatedTarget); - if (movingToButton) return; - - // If focus is staying within the popover content, allow it - if (popoverContent?.contains(relatedTarget)) return; + const handleKeyDown = (event: KeyboardEvent) => { + if (event.key === 'Tab') { + const popoverContent = popoverContentRef.current; + if (!popoverContent) return; + + const focusableElements = + popoverContent.querySelectorAll( + 'a[href], button, textarea, input, select, [tabindex]:not([tabindex="-1"])' + ); + + if (focusableElements.length === 0) return; + + const firstElement = focusableElements[0]; + const lastElement = focusableElements[focusableElements.length - 1]; + const { activeElement } = document; + + // If tabbing forward from the last element, prevent default and move to button + if (!event.shiftKey && activeElement === lastElement) { + event.preventDefault(); + buttonRef.current?.focus(); + } + // If tabbing backward from the first element, prevent default and move to button + else if (event.shiftKey && activeElement === firstElement) { + event.preventDefault(); + buttonRef.current?.focus(); + } } - - // Return focus to button to maintain logical tab order - setTimeout(() => { - buttonRef.current?.focus(); - }, 0); }; // Wait for the popover ref to be set before attaching the listener @@ -134,7 +140,7 @@ export const InfoTip: React.FC = ({ const timeoutId = setTimeout(() => { popoverContent = popoverContentRef.current; if (popoverContent) { - popoverContent.addEventListener('focusout', handleFocusOut); + popoverContent.addEventListener('keydown', handleKeyDown); } }, 0); @@ -143,7 +149,7 @@ export const InfoTip: React.FC = ({ return () => { clearTimeout(timeoutId); if (popoverContent) { - popoverContent.removeEventListener('focusout', handleFocusOut); + popoverContent.removeEventListener('keydown', handleKeyDown); } document.removeEventListener('keydown', handleGlobalEscapeKey); };