From 498f64c943dd3986354ddb6021acfd380d877bf3 Mon Sep 17 00:00:00 2001 From: Amy Resnik Date: Thu, 6 Nov 2025 10:01:49 -0500 Subject: [PATCH 1/7] add logs --- packages/gamut/src/Tip/InfoTip/index.tsx | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/gamut/src/Tip/InfoTip/index.tsx b/packages/gamut/src/Tip/InfoTip/index.tsx index 9a39bae3fc..b9636af63f 100644 --- a/packages/gamut/src/Tip/InfoTip/index.tsx +++ b/packages/gamut/src/Tip/InfoTip/index.tsx @@ -107,6 +107,7 @@ export const InfoTip: React.FC = ({ }; const handleFocusOut = (event: FocusEvent) => { + console.log('in handleFocusOut'); const popoverContent = popoverContentRef.current; const button = buttonRef.current; const wrapper = wrapperRef.current; @@ -117,14 +118,21 @@ export const InfoTip: React.FC = ({ // If focus is moving back to the button or wrapper, allow it const movingToButton = button?.contains(relatedTarget) || wrapper?.contains(relatedTarget); - if (movingToButton) return; + if (movingToButton) { + console.log('focus moving to button or wrapper'); + return; + } // If focus is staying within the popover content, allow it - if (popoverContent?.contains(relatedTarget)) return; + if (popoverContent?.contains(relatedTarget)) { + console.log('focus staying within popover content'); + return; + } } // Return focus to button to maintain logical tab order setTimeout(() => { + console.log('in setTimeout'); buttonRef.current?.focus(); }, 0); }; From b11d0d6ddcbb7a889d61ceadbe1177e626a630c1 Mon Sep 17 00:00:00 2001 From: Amy Resnik Date: Thu, 6 Nov 2025 10:09:16 -0500 Subject: [PATCH 2/7] up timeout --- packages/gamut/src/Tip/InfoTip/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/gamut/src/Tip/InfoTip/index.tsx b/packages/gamut/src/Tip/InfoTip/index.tsx index b9636af63f..3a23b13c0b 100644 --- a/packages/gamut/src/Tip/InfoTip/index.tsx +++ b/packages/gamut/src/Tip/InfoTip/index.tsx @@ -134,7 +134,7 @@ export const InfoTip: React.FC = ({ setTimeout(() => { console.log('in setTimeout'); buttonRef.current?.focus(); - }, 0); + }, 300); }; // Wait for the popover ref to be set before attaching the listener From bb9a2dd19e8cb8d98781efa618cde7290a7899f4 Mon Sep 17 00:00:00 2001 From: Amy Resnik Date: Thu, 6 Nov 2025 10:09:53 -0500 Subject: [PATCH 3/7] add another log --- packages/gamut/src/Tip/InfoTip/index.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/gamut/src/Tip/InfoTip/index.tsx b/packages/gamut/src/Tip/InfoTip/index.tsx index 3a23b13c0b..dec1101e93 100644 --- a/packages/gamut/src/Tip/InfoTip/index.tsx +++ b/packages/gamut/src/Tip/InfoTip/index.tsx @@ -114,6 +114,7 @@ export const InfoTip: React.FC = ({ const { relatedTarget } = event; + console.log('relatedTarget', relatedTarget); if (relatedTarget instanceof Node) { // If focus is moving back to the button or wrapper, allow it const movingToButton = From c81bae1e66321cd5d82e52edc0cd7fbf9e1f3fc7 Mon Sep 17 00:00:00 2001 From: Amy Resnik Date: Thu, 6 Nov 2025 10:25:58 -0500 Subject: [PATCH 4/7] try something --- packages/gamut/src/Tip/InfoTip/index.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/gamut/src/Tip/InfoTip/index.tsx b/packages/gamut/src/Tip/InfoTip/index.tsx index dec1101e93..a3b0fbe51b 100644 --- a/packages/gamut/src/Tip/InfoTip/index.tsx +++ b/packages/gamut/src/Tip/InfoTip/index.tsx @@ -132,10 +132,8 @@ export const InfoTip: React.FC = ({ } // Return focus to button to maintain logical tab order - setTimeout(() => { - console.log('in setTimeout'); - buttonRef.current?.focus(); - }, 300); + console.log('focusing on button'); + buttonRef.current?.focus(); }; // Wait for the popover ref to be set before attaching the listener From 39830e87aba8caf20abb682dd009c506688ba867 Mon Sep 17 00:00:00 2001 From: Amy Resnik Date: Thu, 6 Nov 2025 10:26:35 -0500 Subject: [PATCH 5/7] longer timeout --- packages/gamut/src/Tip/InfoTip/index.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/gamut/src/Tip/InfoTip/index.tsx b/packages/gamut/src/Tip/InfoTip/index.tsx index a3b0fbe51b..e5a5a1d64f 100644 --- a/packages/gamut/src/Tip/InfoTip/index.tsx +++ b/packages/gamut/src/Tip/InfoTip/index.tsx @@ -132,8 +132,10 @@ export const InfoTip: React.FC = ({ } // Return focus to button to maintain logical tab order - console.log('focusing on button'); - buttonRef.current?.focus(); + setTimeout(() => { + console.log('in setTimeout'); + buttonRef.current?.focus(); + }, 1000); }; // Wait for the popover ref to be set before attaching the listener From 3513a21c425815fe4e977673229604ae54823565 Mon Sep 17 00:00:00 2001 From: Amy Resnik Date: Thu, 6 Nov 2025 10:43:56 -0500 Subject: [PATCH 6/7] change back --- packages/gamut/src/Tip/InfoTip/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/gamut/src/Tip/InfoTip/index.tsx b/packages/gamut/src/Tip/InfoTip/index.tsx index e5a5a1d64f..229c260681 100644 --- a/packages/gamut/src/Tip/InfoTip/index.tsx +++ b/packages/gamut/src/Tip/InfoTip/index.tsx @@ -135,7 +135,7 @@ export const InfoTip: React.FC = ({ setTimeout(() => { console.log('in setTimeout'); buttonRef.current?.focus(); - }, 1000); + }, 0); }; // Wait for the popover ref to be set before attaching the listener From d0e9ab11edc0adbbcb6a47f2855e8c8656fc588e Mon Sep 17 00:00:00 2001 From: Amy Resnik Date: Thu, 6 Nov 2025 10:54:21 -0500 Subject: [PATCH 7/7] try this --- packages/gamut/src/Tip/InfoTip/index.tsx | 55 +++++++++++------------- 1 file changed, 26 insertions(+), 29 deletions(-) diff --git a/packages/gamut/src/Tip/InfoTip/index.tsx b/packages/gamut/src/Tip/InfoTip/index.tsx index 229c260681..143c999f1a 100644 --- a/packages/gamut/src/Tip/InfoTip/index.tsx +++ b/packages/gamut/src/Tip/InfoTip/index.tsx @@ -106,36 +106,33 @@ export const InfoTip: React.FC = ({ } }; - const handleFocusOut = (event: FocusEvent) => { - console.log('in handleFocusOut'); - const popoverContent = popoverContentRef.current; - const button = buttonRef.current; - const wrapper = wrapperRef.current; - - const { relatedTarget } = event; - - console.log('relatedTarget', relatedTarget); - 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) { - console.log('focus moving to button or wrapper'); - 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 focus is staying within the popover content, allow it - if (popoverContent?.contains(relatedTarget)) { - console.log('focus staying within popover content'); - return; + // 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(() => { - console.log('in setTimeout'); - buttonRef.current?.focus(); - }, 0); }; // Wait for the popover ref to be set before attaching the listener @@ -143,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); @@ -152,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); };