Skip to content

Commit 3e9dd5e

Browse files
committed
fix(overlay): additional content not dismissible
Fix SWC-1047: hover overlays should close with the Esc key when trigger is not focus.
1 parent 25c18a1 commit 3e9dd5e

File tree

3 files changed

+94
-0
lines changed

3 files changed

+94
-0
lines changed

1st-gen/packages/overlay/src/OverlayStack.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,13 @@ class OverlayStack {
176176
if (event.code !== 'Escape') return;
177177
if (!this.stack.length) return;
178178
const last = this.stack[this.stack.length - 1];
179+
if (last?.type === 'hint') {
180+
// Close hint/tooltip overlays on "Escape" key and prevent further handling of the event.
181+
event.preventDefault();
182+
event.stopPropagation();
183+
this.closeOverlay(last);
184+
return;
185+
}
179186
if (last?.type === 'page') {
180187
event.preventDefault();
181188
return;

1st-gen/packages/overlay/test/overlay-trigger-hover.test.ts

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,81 @@ describe('Overlay Trigger - Hover', () => {
210210

211211
expect(el.open).to.be.undefined;
212212
});
213+
it('closes the "tooltip" on "escape" keyup', async () => {
214+
const opened = oneEvent(button, 'sp-opened');
215+
button.dispatchEvent(
216+
new MouseEvent('pointerenter', {
217+
bubbles: true,
218+
composed: true,
219+
})
220+
);
221+
await waitUntil(
222+
() => tooltip.open === true,
223+
'tooltip should open',
224+
{ timeout: 500 }
225+
);
226+
expect(tooltip.open).to.be.true;
227+
228+
button.dispatchEvent(
229+
new MouseEvent('pointerenter', {
230+
bubbles: true,
231+
composed: true,
232+
})
233+
);
234+
await waitUntil(
235+
() => tooltip.open === true,
236+
'tooltip should open',
237+
{ timeout: 500 }
238+
);
239+
expect(tooltip.open).to.be.true;
240+
241+
button.dispatchEvent(
242+
new MouseEvent('pointerleave', {
243+
bubbles: true,
244+
composed: true,
245+
})
246+
);
247+
await elementUpdated(tooltip);
248+
249+
button.dispatchEvent(
250+
new MouseEvent('pointerenter', {
251+
bubbles: true,
252+
composed: true,
253+
})
254+
);
255+
await elementUpdated(tooltip);
256+
257+
tooltip.dispatchEvent(
258+
new MouseEvent('pointerleave', {
259+
bubbles: true,
260+
composed: true,
261+
})
262+
);
263+
await elementUpdated(tooltip);
264+
265+
button.dispatchEvent(
266+
new MouseEvent('pointerenter', {
267+
bubbles: true,
268+
composed: true,
269+
})
270+
);
271+
await opened;
272+
273+
expect(el.open).to.equal('hover');
274+
275+
// make sure escape keyup closes the tooltip even when the button does not have focus
276+
const body = el.ownerDocument.body;
277+
body.focus();
278+
const closed = oneEvent(button, 'sp-closed');
279+
const escapeKeyup = new KeyboardEvent('keyup', {
280+
key: 'Escape',
281+
bubbles: true,
282+
composed: true,
283+
});
284+
body.dispatchEvent(escapeKeyup);
285+
await closed;
286+
expect(el.open).to.be.undefined;
287+
});
213288
});
214289
it('persists hover content', async () => {
215290
const el = await fixture<OverlayTrigger>(
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,14 @@
1+
/**
2+
* Copyright 2025 Adobe. All rights reserved.
3+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License. You may obtain a copy
5+
* of the License at http://www.apache.org/licenses/LICENSE-2.0
6+
*
7+
* Unless required by applicable law or agreed to in writing, software distributed under
8+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9+
* OF ANY KIND, either express or implied. See the License for the specific language
10+
* governing permissions and limitations under the License.
11+
*/
12+
113
// Generated by genversion.
214
export const version = '1.10.0';

0 commit comments

Comments
 (0)