Make a native web component dialog appear on top of another native web component dialog

39 views Asked by At

In this code snippet I have a wc-notifier component to show errors. There is an existing error shown (Oh no existing error!).

Then a "Form Modal" (wc-modal-form) is shown, which would contain inputs for the user to fillout. Currently it incorrectly appears in front of the error messages (error message should be on top). If one clicks the "validate" button below, the additional error message are also incorrectly shown below the form modal (error message should be on top). I can't figure out how to make the error noitifier (dialog) appear on top of the form modal (dialog).

How does one alter the order of the element's in the browser's Top-layer ?

I have tried every trick I can, in terms of order of defining the components, order of creating the components, order of opening the dialogs.

class NotifierComponent extends HTMLElement {
  constructor() {
      super().attachShadow({mode:'open'});
      const template = document.getElementById("TEMPLATE_notifier");
      this.shadowRoot.appendChild(template.content.cloneNode(true));
  }
  error(msg) {
      const errorEl = document.createElement('div');
      errorEl.innerText = msg;
      this.appendChild(errorEl)
  }
}
window.customElements.define('wc-notifier', NotifierComponent);

document.getElementById("NOTIFIER").error("Oh no existing error!")

class FormModalComponent extends HTMLElement {
  constructor() {
      super().attachShadow({mode:'open'});
      const template = document.getElementById("TEMPLATE_modalform");
      this.shadowRoot.appendChild(template.content.cloneNode(true));
  }
  showModal() {
      this.shadowRoot.getElementById("MODAL").showModal();
  }
}
window.customElements.define('wc-modal-form', FormModalComponent);

const modalFormEl = document.createElement('wc-modal-form');
document.body.appendChild(modalFormEl)
modalFormEl.showModal();
<template id="TEMPLATE_notifier">
  <style>
      dialog {
         display: flex;
         gap: 16px;
         flex-direction: column;         
         border: none;
         background: none;
         top: 0;
         margin-left: auto;
         margin-right: 0;
      }
      ::slotted(*) {
         background: red;
         padding: 16px 8px;
      }
      </style>
  <dialog>
     <slot></slot>
  </dialog>
</template>

<wc-notifier id="NOTIFIER"></wc-notifier>

<template id="TEMPLATE_modalform">
  <style>
      dialog {
          background: cream;
          padding: 64px;
          width: 100%;
      }
  </style>
  <dialog id="MODAL">
      <div>This is the FORM MODAL. It should appear below the errors</div>
      <button id="VALIDATE" type="button" onclick="document.getElementById('NOTIFIER').error('Please fill out all the required form fields')">Validate Form</button> 
  </dialog>
</template>

1

There are 1 answers

0
Danny '365CSI' Engelman On

It is all about applying .showModal() at the right time.

Do read: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog

Here is a (not perfect) playground:

https://jsfiddle.net/WebComponents/gtn86p5c/

<template id="WC-NOTIFIER">
  <style>
    dialog { display:block;width:50vh;height:50vh;background:lightcoral }
    ::slotted(*) { /* doesn't work on TEXT nodes only */ background:red;height:100% }
  </style>
  <dialog><slot></slot></dialog>
</template>

<wc-notifier id="NOTIFIER">EXISTING ERROR</wc-notifier>

<template id="WC-MODAL-FORM">
  <style>
    dialog { width:100%; text-align:center }
  </style>
  <dialog>
    <div>This is the FORM MODAL. It should appear below the errors</div>
    <button id="VALIDATE" type="button" onclick="this.getRootNode().host.validate()">Validate Form</button>
  </dialog>
</template>

<script>
  class BaseClass extends HTMLElement {
    constructor() {
      super().attachShadow({mode:'open'})
             .append(document.getElementById(this.nodeName).content.cloneNode(true));
    }
    get dialog(){   return this.shadowRoot.querySelector("dialog") }
    showModal(){
        this.close(); /* can not re-open <dialog> as modal */
      this.dialog.showModal();    
    }
    close(){ this.dialog.close() }
    open() { this.dialog.open()  }
    connectedCallback() {
      this.showModal();
      console.log("connected", this.nodeName);
    }
  }
  customElements.define('wc-notifier', class extends BaseClass {
    set error(innerText) {
      this.innerHTML = "";
      this.append(Object.assign(document.createElement('div'), {
        innerText,
        onclick: evt => this.close()
      }));
      this.showModal();
    }
  });
  customElements.define('wc-modal-form', class extends BaseClass {
    validate() {
      // This is tight coupling!! Should do this with Events!!
      document.getElementById("NOTIFIER").error = "VERIFY INPUTS!";
    }
  });
  
  // executed BEFORE <wc-modal-form> !! so NOT stacked at the top!
  //document.getElementById("NOTIFIER").error = "NEW ERROR";
</script>

<wc-modal-form></wc-modal-form>