Migration: 2023/8/18 #
As of the 0.37.0 release of the Spectrum Web Components library, we will be leveraging a new version of our Overlay API. We've done our best to ensure a smooth transition from one version of the API to the next, including adding extended support for the argument signature from the previous version. In this way, consumption of elements from the library (e.g. <overlay-trigger>
, <sp-picker>
, <sp-tooltip>
, et al.) or the imperative Overlay API (e.g. Overlay.open()
) should continue affording a close facsimile of the functionality that was provided by the previous version. Under the covers, many important changes have been made and there are several things you can do to prepare within your application's lifecycle.
<active-overlay>
is no longer part of the API #
If you had previously done work in your application to interact directly with overlaid content from the application level by making CSS or JS reference to <active-overlay>
elements, there will be changes required in your application.
- for CSS application: this overlaid content will now continue to live at its point of origin whether it is projected onto the "top-layer" or not. That means that you can style the overlaid content directly in the parent context.
- in JS: an equivelent to managing references to
<active-overlay>
elements can be achieved by globally listening for thesp-opened
event and leveraging the composed target of that eventevent.composedPath()[0]
(which will be ansp-overlay
element) in a similar fashion. For more complex referencing to these elements, please jointhis discussion for support in refactoring said functionality to leverage these new APIs.
Remove the open
attribute from content that is meant to be overlaid. #
The open
property will be addressed directly by the Overlay API itself. Elements wanting to manage their visual transition from the closed to open state should respond reactively to that change. Management of this attribute by the parent application could prevent those transitions from occurring as expected in elements provided by the Spectrum Web Components library.
✅ DO exclude open
attributes from slot="*-content"
children of <overlay-trigger>
elements
<overlay-trigger> <sp-button slot="trigger">Trigger</sp-button> <sp-popover slot="click-content" placement="bottom"> <sp-dialog no-divider>Popover for the trigger</sp-dialog> </sp-popover> <sp-tooltip slot="hover-content" placement="right"> Tooltip for the trigger </sp-tooltip> <!-- etc. --> </overlay-trigger>
Remove usage of placement="none"
in both declarative and imperative API usages #
placement="none"
or placement: 'none'
was previously leveraged to outline an overlay that would take the full size of the viewport. This responsibility is now fulfilled by assigning this value as undefined
, or, better yet, by not including it at all.
✅ DO omit placement
when not specifically relating the overlaid content to its target with the imperative API
this.closeHoverOverlay = Overlay.open( triggerReference, 'modal', contentReference, { delayed: false, offset: 0, receivesFocus: 'auto', } );
Ensure that your consumption of sp-opened
and sp-closed
events are typed #
Some of the internal properties of these events are changing, see interaction: 'auto' | 'hint' | 'manual' | 'modal' | 'page'
. Ensuring you are consuming these types will allow Typescript to support your upgrade from one version of the API to the next.
✅ DO use the OverlayOpenCloseDetail
type when listening for these Custom Events
html` <element-containing-an-overlay @sp-closed=${(event: OverlayOpenCloseDetail) => { if (event.detail.interaction === 'auto') { // Do something when the event was dispatched for an overlay with type "auto". } }} @sp-opened=${(event: OverlayOpenCloseDetail) => { if (event.detail.interaction === 'modal') { // Do something when the event was dispatched for an overlay with type "modal". } }} ></element-containing-an-overlay> `;
Prepare for descendant overlays to exist in the same DOM tree #
Previously, the reparenting of overlay content prevented sp-opened
and sp-closed
events from propagating through the overlay's ancestor DOM tree. Going forward these events will propagate in a more native manner meaning that an ancestor will have the opportunity to hear and respond to ALL sp-opened
and sp-closed
events for all of its descendant overlays.
✅ DO be sure to gate your listeners if your experience stacks multiple overlays (see submenus) within each other.
function handleSpOpened(event: OverlayOpenCloseDetail) { // Return if the `sp-opened` event was not dispatched from the element to which this listener is attached. if (event.target !== event.currentTarget) return; }
Consider leveraging the API declaratively #
The new Overlay API will continue to surface the Overlay.open()
method to more readily support migration from previous verions. However, imperatively interacting with the API in the way will continue to require that the content you wish to overlay be reparented, which can have unexpected side effects. If you begin leveraging the V2 signature of the API, you content will be reparented into an <sp-overlay>
element, and, rather than immediately placing the element into the page, it will be returned so that you can decide where you would like to append this content to your application. While continuing to leverage the V1 signature, not only will you content be reparented into an <sp-overlay>
element, but that element will be inserted immediately after the trigger
element that you have provided in support of applying your overlaid content to the tab order of your content in a predictable manner.
Either of these processes can have negative effects on the application of CSS, the way that events will propagate through your application, and the values returned from DOM selections APIs (like querySelector(...)
). Leverage an <sp-overlay>
directly in your application for full control over where the overlaid content will live in the DOM. Choosing a static location in your DOM for the <sp-overlay>
element controlling your overlay will not only does this normalize interactions with CSS, events, or DOM selection APIs, but also empower you to make more sustainable decisions as to tab order by which keyboard and screen reader users will interact with your content.
Explanation #
The new version of the Overlay API no longer relies on portalling (moving content to the end of the <body>
element) to defeat CSS clipping and stacking. While this approach was good at overcoming those realities, the reparenting of the overlaid content required to complete this technique had complex performance costs, due to the constant reorientation of elements to a new parent, and broke encapsulation by moving the content ourside of the shadow DOM in which is was originally placed. This made it difficult to take full control of styling and delivering overlaid content. Instead, the new API will leverage <dialog>
elements and their showModal()
method for modal overlays, and the popover
attribute along with its showPopover()
method. Both of these APIs lift content onto the
While the <dialog>
elementpopover
attributeposition: fixed
approach to defeat CSS clipping and stacking. This approach has less of a guarantee in overcoming these realities than what was being used previously and