Skip to content

Commit eb541d6

Browse files
jackfranklinDevtools-frontend LUCI CQ
authored andcommitted
Tooltip: ensure tooltip stays if focus moves from anchor into content
The tooltip has behaviour to ensure that when the focus moves from the anchor to the tooltip that the tooltip does not hide, however it only worked if the focus was the immediate child of the tooltip. In our case (see attached bug) the focus was moving into a link deeply nested within the contents of the tooltip. This CL updates the code to make use of the blur target & relatedTarget properties to detect this case and not hide the tooltip. Fixed: 406543379 Change-Id: I7ad19fbde17e275663465e0bb86f8b2ea8ba768e Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/6402534 Commit-Queue: Alina Varkki <[email protected]> Auto-Submit: Jack Franklin <[email protected]> Reviewed-by: Alina Varkki <[email protected]>
1 parent c147873 commit eb541d6

File tree

2 files changed

+33
-0
lines changed

2 files changed

+33
-0
lines changed

front_end/ui/components/tooltips/Tooltip.test.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,28 @@ describe('Tooltip', () => {
199199
assert.isFalse(container.querySelector('devtools-tooltip')?.open);
200200
});
201201

202+
it('should not hide the tooltip if focus moves from the anchor into deep DOM within the tooltip', async () => {
203+
const container = renderTooltip({variant: 'rich', attribute: 'aria-details'});
204+
const anchor = container.querySelector('button');
205+
assert.exists(anchor);
206+
const tooltip = container.querySelector('devtools-tooltip');
207+
assert.exists(tooltip);
208+
// Make some nested DOM for this; this test exists because of a bug where
209+
// the tooltip only stayed open if the focused element was an immediate
210+
// child, so for this test we make a nested DOM structure and test on that.
211+
tooltip.innerHTML = '<div><span><p class="deep-nested">nested</p></span></div>';
212+
213+
anchor.dispatchEvent(new FocusEvent('focus'));
214+
await checkForPendingActivity();
215+
assert.isTrue(tooltip.open);
216+
const richContents = container.querySelector('devtools-tooltip')?.querySelector('p.deep-nested');
217+
assert.exists(richContents);
218+
219+
anchor.dispatchEvent(new FocusEvent('blur', {relatedTarget: richContents}));
220+
await checkForPendingActivity();
221+
assert.isTrue(tooltip.open); // tooltip should still be open
222+
});
223+
202224
it('automatically sets and updates jslog', () => {
203225
const container = renderTooltip({jslogContext: 'context'});
204226
const tooltip = container.querySelector('devtools-tooltip');

front_end/ui/components/tooltips/Tooltip.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,17 @@ export class Tooltip extends HTMLElement {
160160
if (this.#timeout) {
161161
window.clearTimeout(this.#timeout);
162162
}
163+
// If the event is a blur event, then:
164+
// 1. event.currentTarget = the element that got blurred
165+
// 2. event.relatedTarget = the element that gained focus
166+
// https://developer.mozilla.org/en-US/docs/Web/API/FocusEvent/relatedTarget
167+
// If the blurred element (1) was our anchor, and the newly focused element
168+
// (2) is within the tooltip, we do not want to hide the tooltip.
169+
if (event && this.variant === 'rich' && event.target === this.#anchor && event.relatedTarget instanceof Node &&
170+
this.contains(event.relatedTarget)) {
171+
return;
172+
}
173+
163174
// Don't hide a rich tooltip when hovering over the tooltip itself.
164175
if (event && this.variant === 'rich' &&
165176
(event.relatedTarget === this || (event.relatedTarget as Element)?.parentElement === this)) {

0 commit comments

Comments
 (0)