-
Notifications
You must be signed in to change notification settings - Fork 236
fix(overlay): click-blocking behavior for type="modal" and "page" overlays #5907
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
🦋 Changeset detectedLatest commit: db515d8 The changes in this PR will be included in the next version bump. This PR includes changesets to release 78 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
📚 Branch Preview Links🔍 First Generation Visual Regression Test ResultsWhen a visual regression test fails (or has previously failed while working on this branch), its results can be found in the following URLs:
Deployed to Azure Blob Storage: If the changes are expected, update the |
…e/spectrum-web-components into rajdeep/modal-overlay-click
|
I have a question for my understanding... If I navigate to overlay elements - nested modal overlays story and open both outer and inner overlay and then click outside in the empty space, both the overlays close. Is that the expected behaviour? |
nikkimk
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the detailed PR instructions. It made testing super easy.
The small nit with this story being named modal when the type is auto.
Description
This PR restores the click-blocking behavior for modal and page overlays that was lost when migrating from
dialog.showModal()todialog.showPopover()for performance reasons. The fix manually implements the click-blocking functionality thatshowModal()provided automatically, ensuring that users cannot interact with elements outside of an open modal overlay.Key changes:
handlePointerdownandhandleClick) that intercept pointer and click events in the capture phaseevent.composedPath()preventDefault(),stopPropagation(), andstopImmediatePropagation()when clicks occur outside modal overlaysgetModalOverlays()andisEventInsideModal()) for better maintainabilityMotivation and context
In version 1.7.0, the overlay implementation migrated from using
dialog.showModal()todialog.showPopover()for performance improvements. However,showModal()provided native click-blocking behavior that prevented users from clicking elements outside the modal overlay, whileshowPopover()does not provide this behavior.This created a regression where modal overlays no longer blocked external clicks, allowing users to interact with elements behind the modal overlay. This violates accessibility best practices and user expectations for modal dialogs.
Expected behavior: When a modal overlay is open, users should not be able to click on elements outside of the overlay without first closing the overlay.
Previous behavior (1.4.0): Modal overlays correctly blocked external clicks using
showModal().Current behavior (1.7.0+): Modal overlays allowed external clicks, breaking the modal interaction pattern.
This fix restores the expected behavior while maintaining the performance benefits of using
showPopover().Related issue(s)
Screenshots (if appropriate)
Before (broken behavior):
DEMO: https://stackblitz.com/edit/vitejs-vite-nvgyzymb?file=package.json,src%2Fmy-element.ts
After (fixed behavior):
DEMO: https://swcpreviews.z13.web.core.windows.net/pr-5907/docs/first-gen-storybook/?path=/story/overlay-element--modal-click-blocking
Technical implementation details
Event handling strategy
The fix uses a two-pronged approach:
capture: trueto intercept events before they reach their target elementsevent.composedPath()to determine if the event originated inside a modal overlay dialogCode structure
getModalOverlays(): Helper method that filters the overlay stack to get only open modal/page overlaysisEventInsideModal(): Core logic that checks if an event path intersects with any modal overlay dialoghandlePointerdown(): Intercepts pointerdown events to block interactions early in the event chainhandleClick(): Intercepts click events as a secondary layer of protectionmanageModalBackdrop(): Creates/removes a transparent backdrop element to help catch external clicksBrowser compatibility
The implementation uses standard DOM APIs (
composedPath(),contains(),preventDefault(), etc.) that are supported across all modern browsers. The fix has been tested and verified to work in:Author's checklist
Reviewer's checklist
patch,minor, ormajorfeaturesManual review test cases
Modal overlay click blocking
overlay-element.stories.ts→modalClickBlockingallow-outside-clickis enabled)Nested modal overlays
overlay-element.stories.ts→nestedModalOverlaysPage overlays
type="page"overlaysNon-modal overlays
type="auto",type="hint", andtype="manual"overlaysKeyboard navigation
Device review
Testing
Automated tests
All existing overlay tests pass (191 tests across Firefox, Chromium, and Webkit):
yarn test:focus overlay- All tests passing ✅Breaking changes
None. This is a bug fix that restores expected behavior.
Additional notes