adobe / adobe/spectrum-web-components

[Bug]: interaction="manual" overlay — sp-opened never dispatches when interaction="modal" overlay is active, causing app freeze

Open
#6,380 3 comments 0 reactions 0 assignees View on GitHub
bug Component:Overlay Needs repro SEV 1
Dominant language
TypeScript
Stars
1.5k
Forks
262
Avg merge
3d 10h
Merged PRs (30d)
68

Description

### Code of conduct

- [x] I agree to follow this project's code of conduct.

### Impacted component(s)

@spectrum-web-components/overlay

### Library version

@spectrum-web-components/overlay: 1.9.0

### Expected behavior

`sp-opened `fires on bgTrigger within ~50ms, consistent with normal manual overlay behaviour.

### Actual behavior

When an interaction="modal" overlay is open, calling Overlay.open(trigger, "manual", content, opts) on a background element causes sp-opened to never dispatch on the trigger. The overlay gets permanently stuck in state="opening".

In applications that await sp-opened to resolve an open-state promise, this causes a complete and unrecoverable app freeze — Lit's update cycle hangs indefinitely and the page must be reloaded.

`sp-opened` never fires. The overlay is permanently stuck in state="opening". No error is surfaced to the caller.

### Screenshots

_No response_

### What browsers are you seeing the problem in?

Chrome

### How can we reproduce this issue?

1. Open an `interaction="modal"` overlay (e.g. via Overlay.open(trigger, "modal", content, opts)) and wait for sp-opened to fire on its trigger.
2. While that modal overlay is still open, call Overlay.open(bgTrigger, "manual", content, opts) on a background element
3. Listen for sp-opened on bgTrigger

### Sample code or abstract reproduction which illustrates the problem

showModal() — called internally by SWC when opening a type="modal" overlay — makes all background elements natively inert per the HTML spec. When SWC subsequently tries to open the manual overlay, it calls showPopover() on an element that is now in the inert subtree. The browser throws NotSupportedError. This throw is unhandled and aborts the open sequence before makeTransition() runs, so sp-opened is never dispatched.

Overlay.open(trigger, "manual", ...)
→ managePopoverOpen()
→ ensureOnDOM()
→ shouldShowPopover()
→ this.dialogEl.showPopover() // ← throws NotSupportedError, no try/catch
→ ensureOnDOM() aborts
→ makeTransition() never called
→ sp-opened never dispatched
We believe the fix belongs in `OverlayPopover.shouldShowPopover() `— wrapping showPopover() with a try/catch and setting this.open = false on NotSupportedError so sp-closed fires and callers can recover gracefully:

```
try {
this.dialogEl.showPopover();
this.managePosition();
} catch (err) {
if (err instanceof DOMException && err.name === 'NotSupportedError') {
this.open = false; // triggers sp-closed so callers can recover
return;
}
throw err;
}
```

### Severity

None

### Logs taken while reproducing problem

_No response_

### Would you like to track this issue in Jira?

- [ ] Yes, please tell me the ticket number!

Contributor guide

Open the contributing guide

Research direction

Start in OverlayPopover.shouldShowPopover() and trace the Overlay.open() path through managePopoverOpen() and ensureOnDOM(). Reproduce the modal-overlay then background manual-overlay sequence in Chrome, and verify that a NotSupportedError no longer leaves the overlay in state="opening" and that recovery events are dispatched as described.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
frontend
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.