web-components icon indicating copy to clipboard operation
web-components copied to clipboard

Make <vaadin-select> usable in native dialog element by allowing the overlay to attach to an element other than body

Open DevDuki opened this issue 2 years ago • 6 comments

Describe your motivation

In my Lit application, I am using the native dialog element and wanted to add a select inside it. I used the vaadin-select component for it.

Now when I open the select element, the dropdown list is shown at the correct position, however, since the overlay is attached to the body element it makes it not reachable and is rendered behind the dialog. I can imagine the same issue would appear in other similar components such as the vaadin-combo-box.

I know there is a vaadin dialog component, which would solve this issue, but I would prefer to keep using the native dialog element.

Describe the solution you'd like

In the source code the body element is hard coded as the parent element. Perhaps a property allowing the consumer of the component to define their own parent could help solve the problem.

For example: <vaadin-select overlay-parent="${this.dialog}"> or something like that.

And then basically treating that element as the new parent element, instead of the body element.

Describe alternatives you've considered

I have tried the mwc-select from Material Web Components, which correctly renders the list inside the dialog. By changing the dialog's overflow property, it will also make the list look like it's "on top" of the dialog.

Additional context

I have prepared a sandbox where this behaviour can be seen and tested on stackblitz.

DevDuki avatar Aug 09 '23 06:08 DevDuki

Thanks for the issue. We currently have no plans to change the overlay system but it might be worth researching. Using native <dialog> as a foundation for vaadin-dialog is something that we can consider in the future.

web-padawan avatar Aug 09 '23 07:08 web-padawan

This is unfortunate, since it means that the <vaadin-select> is not usable in native <dialog> elements. I understand that alternatives are provided in the collection with e.g. <vaadin-dialog>.

It might be worth noting this limitation somewhere in the docs?

peschee avatar Aug 09 '23 07:08 peschee

@peschee yes, overlays tend to be a generic problem when mixing components from different libraries as they tend to have different z-index plan. It was not long ago when I saw a question about mixing a Bootstrap or Material overlay component and Vaadin one, which has similar issue as the z-indexes are not compatible. This is a generic problem, not limited to our set of components. For example if you would put mwc-select on our vaadin-dialog, it would open behind it, as vaadin-overlay z-index base offset is 200, and default for mwc-select is 8.

image

So in the end what ever way you go, the best practice is not to mix design systems too much.

TatuLund avatar Aug 09 '23 08:08 TatuLund

Thanks for the detailed example. Yes, I am aware of these issues.

So in the end what ever way you go, the best practice is not to mix design systems too much.

Unfortunately, no DS is perfect and/or contains all components in a variant you might need. The biggest selling point for WCs is that you can mix & match various components. As long as they are themeable & configurable enough, you can mostly work around these issues.

peschee avatar Aug 09 '23 08:08 peschee

Assuming we allow changing the overlay parent element from <body> to a <dialog>, it’s then possible that the overlay gets clipped by the dialog. For example, with Safari 16.6, if you define a fixed height for the dialog, and the dialog has more content than can fit in it, Safari changes it to a scrolling container, which then clips the overlay. The same thing happens if you explicitly set overflow: auto on the dialog. Chrome and Firefox allow the overlay to show outside the scrolling container as expected.

jouni avatar Aug 09 '23 19:08 jouni

A workaround


// this event listener will move the select overlay to the same parent as the select is in, so that no modality curtain will "block" it. 
// it also enables the dialog interactivity
select.addEventListener("opened-changed", e=> {if(e.detail.value) {
    const o = document.querySelector("vaadin-select-overlay");
    if(o?.parentElement !== e.target.parentElement) {
        e.target.parentElement.append(o);
    }
    dialog.style.pointerEvents = "auto"; // since body has pointer events disabled, we need to enable them here
    dialog.__ignoreNextClick = true; // signal for the dialog to ignore the next click to not immediately close the select overlay
}
});

// since the modal dialog backdrop will prevent any "close on outside click" events from the select, we need to do that
// manuaylly now
// we also need to handle the "select opened" click, otherwise it would get closed immediately
dialog.addEventListener("click", e => {
    if(dialog.__ignoreNextClick) { 
        delete dialog.__ignoreNextClick;
    } else if(select.opened) {
        select.opened = false;
    }    
})

It basically does what the "overlay-parent" attribute shall achieve. It moves the overlay from the body to the same parent as the select is attached to. Might have issues with other constellations, but for your demo it works.

stefanuebe avatar Jun 07 '24 05:06 stefanuebe

Fixed by updating vaadin-select to use popover in https://github.com/vaadin/web-components/pull/9751 and released in 25.0.0-alpha8. Closing.

Image

web-padawan avatar Jul 31 '25 11:07 web-padawan