Skip to content
This repository has been archived by the owner on Dec 19, 2024. It is now read-only.

paper-dialog appears behind backdrop when inside a <app-header-layout> #152

Open
simonfuhrmann opened this issue Jun 29, 2017 · 19 comments
Open

Comments

@simonfuhrmann
Copy link

simonfuhrmann commented Jun 29, 2017

Description

When opening a <paper-dialog> that is nested inside <app-header-layout>, its backdrop (e.g., when using attributes with-backdrop or modal) appears on top of the dialog, rendering the dialog inoperable.

The z-index of the dialog and backdrop element are in the correct order.

Defining all dialogs outside of <app-header-layout> is generally not possible in more complex UIs. Usually the <app-header-layout> appears at a top level component, nesting the main app components. If components further down the DOM need to define dialogs, they will basically end up not working.

Expected outcome

Backdrop should appear behind the dialog.

Actual outcome

Backdrop appears in front of the dialog. The dialog is inoperable. Clicking anywhere closes the dialog. In modal mode, the application is stuck forever as a dialog cannot be closed by clicking on the backdrop.

Steps to reproduce

Create an app layout as follows and add dialogs.

<app-header-layout fullbleed>
  <app-header slot="header">
    <app-toolbar>
      <div main-title>My main title header</div>
    </app-toolbar>
  </app-header>
  <!-- Dialogs here don't work -->
</app-header-layout>
<!-- Dialogs here work -->

Browsers Affected

Only tested on Chrome 59.0.3071.104 (Official Build) (64-bit)

Related issues

#7

@simonfuhrmann
Copy link
Author

In #7, there was a workaround that globally moved all <paper-dialog> elements to a DOM position outside of the app layout (here, app-header-layout, back then paper-header-panel). The workaround is relying on querySelectorAll, and as far as I understand, this workaround doesn't work anymore with shadow dom.

@valdrinkoshi
Copy link
Member

Unfortunately we can't fix this problem with the current overlay system, here more details on why.

The best solution you have is to declare dialogs in the right stacking context.

If you can afford loosing style encapsulation, you can reparent the dialogs to document.body before you open them, e.g. http://jsbin.com/vukazer/2/edit?html,output

@simonfuhrmann
Copy link
Author

simonfuhrmann commented Jun 30, 2017

Are you saying it is not possible to have any dialogs with backdrop in an app layout?
This seems like a severe limitation, especially when the workaround only works without local styles. Since you marked this with wontfix, will there be no solution to this problem in Polymer 2.0 in the foreseeable future?

In fact, I do have local styles that I want to preserve. Are there other workarounds?

@web-padawan
Copy link

@simonfuhrmann you can use app-drawer and app-header and create wrapper aroun them, similar to app-drawer-layout but without setting z-index.

@simonfuhrmann
Copy link
Author

simonfuhrmann commented Jun 30, 2017

@web-padawan, thanks, in fact, not using app-drawer-layout seems to be the easiest solution for me.

@valdrinkoshi
Copy link
Member

@simonfuhrmann the issue is not with app-drawer-layout really, but a missing feature of the web platform - we cannot add elements on the Top Layer of the document. Today, any element that creates a new stacking context will "trap" its contents within it.
The concept of Blocking Elements stack is in the works, which will finally allow us to add arbitrary elements in the Top Layer.
I've been working on possible workarounds for this issue in iron-overlay-behavior (see this PR), and finally opted for a new overlay system, iron-overlay.

Unfortunately there's no silver bullet so far. The safest option is to declare the dialog in <body>.

@simonfuhrmann
Copy link
Author

simonfuhrmann commented Jun 30, 2017

@valdrinkoshi, I understand.

But I can't declare the dialog in the body, because of how the app is structured (i.e., the dialog is defined in a deeply nested element). I also cannot move the dialog using document.body.appendChild(this.$.dialog), as it relies on local styling.

Although this may not be an issue of add-drawer-layout, removing the element and re-implementing what I need is the easiest fix that actually works on my side.

@maria-le
Copy link

maria-le commented Sep 4, 2017

@simonfuhrmann @valdrinkoshi

I just became aware of a workaround released by IswPolymerElements called <isw-dialog>.

I have not used it yet, so I can not vouch for it. But the demo looks encouraging.

Edit: I tried <isw-dialog> and it did not perform as expected. But I did remove the modal attribute from <paper-dialog> and that workaround will be temporarily sufficient for my purposes for now until the full solution is implemented that will allow me to use the modal feature again.

@web-padawan
Copy link

web-padawan commented Sep 4, 2017

@maria-le any dialog using paper-dialog-behavior should behave the same.

The only exception known to me is <vaadin-dialog> which uses the same approach as prototyped in <iron-overlay>: connecting overlay as direct body child.

@rene-lindner-isw
Copy link

rene-lindner-isw commented Sep 8, 2017

@maria-le Can you provide some feedback about the problem you ran into?

Edit: The idea of the <isw-dialog> is to define the dialog somewhere in the right stacking context and control it from inside your view via <isw-dialog-remote>. I know it is uncomfortable, but at least this allows you to control the dialog in the context you use them, without the need for manual controller/mediate communication.

Remote and Dialog are communicating via events on document, like <iron-signals> does.

@jukbot
Copy link

jukbot commented Jan 21, 2018

any progress on this issue :(

@Pietfro
Copy link

Pietfro commented Mar 15, 2018

@jukbot , to workaround this you could try adding a function that will move dialog's backdrop before dialog itself, like so:

const overlay = document.querySelector('iron-overlay-backdrop');
this.parentNode.insertBefore(overlay, this);

This has to be fired on iron-overlay-opened. Additionally, you should remove the backdrop on iron-overlay-closed:

this.parentNode.removeChild(document.querySelector('iron-overlay-backdrop'))

this refers to element that contains paper-dialog

@ergo
Copy link

ergo commented Mar 16, 2018

The problem with this workaround is that when you fire events from your dialog they will end up in different place than someone might expect I believe?

@punkratz312
Copy link

+1

@ruckc
Copy link

ruckc commented Apr 12, 2018

+1, with the entire goal of webcomponents being isolated and relatively loosely coupled components, having to move the dialog outside of the app-header-layout kinda destroys the intent.

@craPkit
Copy link

craPkit commented May 25, 2018

I have just found a (rather complex) work-around that works with (my setup of) app-header-layout/app-header—as-is, without modifications—in most situations. (i.e. every state except scrolled + revealed).

In a nutshell: remove the z-index from app-header as long as it's not fully scrolled away, and re-apply once it is. Then work around non-clickable contents using CSS' pointer-events property.

The only way I've found for getting the visible height involves observing CSS transform, so the actual code gets rather convoluted:

<template>
  <style>
    app-header-layout {
      pointer-events: none;
    }

    app-header {
      z-index: 0;
      pointer-events: auto;
    }

    app-header.scrolled {
      z-index: 1;
    }

    #content {
      pointer-events: auto;
    }
  </style>
  
    <app-header-layout id="headerLayout" class="header">
        <app-header id="header" slot="header" reveals condenses effects="waterfall">
          ...
        </app-header>
        <div id="content">
           ...
        </div>
    </app-header-layout>
  </template>
  <script>
    Polymer({
    ...
    attached() {
        const observer = new MutationObserver(this._handleStyleMutation.bind(this));
        observer.observe(this.$.header, {
          attributes: true,
          attributeOldValue: true,
          attributeFilter: ['style']
        });
        // create scroll-eventListener, too, throttled by 100ms
     },
     _handleStyleMutation(mutation) {
        const firstMutation = mutation[0];
        const cssStyleDeclaration = firstMutation.target[firstMutation.attributeName];
        const transform = cssStyleDeclaration.transform;
        if (!transform) {
          return;
        }

        const matches = transform.match(/translate3d\((.+)px, (.+)px, (.+)px\)/);
        if (matches.length <3) {
          return;
        }
        this._headerTop = parseFloat(matches[2], 10);
      },
    _handlePageScrollThrottled(evt) {
        this.$.header.classList.toggle('scrolled', window.scrollY >= this.visibleHeaderHeight);
      },
    get visibleHeaderHeight() {
        const height = this.$.header.getBoundingClientRect().height;
        return height + (this._headerTop || 0);
      }
    ...
  </script>

@web-padawan
Copy link

web-padawan commented May 25, 2018

In case someone is interested, we recently published a Material theme preview for Vaadin components, so that <vaadin-dialog> can be now used with a Material Theme, see examples. This component gets teleported as I mentioned in the above comment, so it will bypass the app-layout limitations.

@ergo
Copy link

ergo commented May 25, 2018

How about overriding app layout elements to make them NOT use shadow dom? Would that solve the issue? Since polymer 2.x this is possible.

@valdrinkoshi valdrinkoshi removed their assignment Sep 5, 2018
@carlardron
Copy link

This behaviour seems to (almost) contravene the specifications for Material Design parent-child hierarchical transitions as described here: https://material.io/design/navigation/navigation-transitions.html#hierarchical-transitions. I say almost because I'm not finding anything specifically describing the navigation bar overlay, but I do see many examples of full screen dialog screens.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests