Bug 1427950 - Change rich-select to default to an indeterminate state when no options are selected. r=jaws draft
authorMatthew Noorenberghe <mozilla@noorenberghe.ca>
Mon, 29 Jan 2018 19:54:59 -0800
changeset 749034 49a69af9a49107ea63c308ce9bd945b4868d7485
parent 749033 45613b70daa2757f83de5c2fdeaacc596d6ce012
child 749035 537cd80bbe17fddc5966ca65d491450e7104af99
push id97299
push usermozilla@noorenberghe.ca
push dateTue, 30 Jan 2018 21:24:25 +0000
reviewersjaws
bugs1427950
milestone60.0a1
Bug 1427950 - Change rich-select to default to an indeterminate state when no options are selected. r=jaws This avoids the magic of the select automatically making the first option selected without the application state having a good way to know about this. MozReview-Commit-ID: 1OEsjh2KW1h
toolkit/components/payments/res/components/rich-option.js
toolkit/components/payments/res/components/rich-select.js
--- a/toolkit/components/payments/res/components/rich-option.js
+++ b/toolkit/components/payments/res/components/rich-option.js
@@ -15,26 +15,24 @@ class RichOption extends ObservedPropert
   static get observedAttributes() {
     return [
       "selected",
       "value",
     ];
   }
 
   connectedCallback() {
+    this.classList.add("rich-option");
     this.render();
-    let richSelect = this.closest("rich-select");
-    if (richSelect && richSelect.render) {
-      richSelect.render();
-    }
-
     this.addEventListener("click", this);
     this.addEventListener("keydown", this);
   }
 
+  render() {}
+
   handleEvent(event) {
     switch (event.type) {
       case "click": {
         this.onClick(event);
         break;
       }
       case "keydown": {
         this.onKeyDown(event);
@@ -64,8 +62,10 @@ class RichOption extends ObservedPropert
 
   static _createElement(fragment, className) {
     let element = document.createElement("span");
     element.classList.add(className);
     fragment.appendChild(element);
     return element;
   }
 }
+
+customElements.define("rich-option", RichOption);
--- a/toolkit/components/payments/res/components/rich-select.js
+++ b/toolkit/components/payments/res/components/rich-select.js
@@ -22,29 +22,37 @@ class RichSelect extends ObservedPropert
   }
 
   constructor() {
     super();
 
     this.addEventListener("blur", this);
     this.addEventListener("click", this);
     this.addEventListener("keydown", this);
+
+    this._mutationObserver = new MutationObserver((mutations) => {
+      for (let mutation of mutations) {
+        for (let addedNode of mutation.addedNodes) {
+          if (addedNode.nodeType != Node.ELEMENT_NODE ||
+              !addedNode.matches(".rich-option:not(.rich-select-selected-clone)")) {
+            continue;
+          }
+          // Move the added rich option to the popup.
+          this.popupBox.appendChild(addedNode);
+        }
+      }
+    });
+    this._mutationObserver.observe(this, {
+      childList: true,
+    });
   }
 
   connectedCallback() {
     this.setAttribute("tabindex", "0");
     this.render();
-
-    this._mutationObserver = new MutationObserver(() => {
-      this.render();
-    });
-    this._mutationObserver.observe(this, {
-      childList: true,
-      subtree: true,
-    });
   }
 
   get popupBox() {
     return this.querySelector(":scope > .rich-select-popup-box");
   }
 
   get selectedOption() {
     return this.popupBox.querySelector(":scope > [selected]");
@@ -147,46 +155,42 @@ class RichSelect extends ObservedPropert
   render() {
     let popupBox = this.popupBox;
     if (!popupBox) {
       popupBox = document.createElement("div");
       popupBox.classList.add("rich-select-popup-box");
       this.appendChild(popupBox);
     }
 
-    /* eslint-disable max-len */
-    let options =
-      this.querySelectorAll(":scope > :not(.rich-select-popup-box):not(.rich-select-selected-clone)");
-    /* eslint-enable max-len */
+    let options = this.querySelectorAll(":scope > .rich-option:not(.rich-select-selected-clone)");
     for (let option of options) {
       popupBox.appendChild(option);
     }
 
     let selectedChild;
     for (let child of popupBox.children) {
       if (child.selected) {
         selectedChild = child;
         break;
       }
     }
-    if (!selectedChild && popupBox.children.length) {
-      selectedChild = popupBox.children[0];
-      selectedChild.selected = true;
-    }
 
     let selectedClone = this.querySelector(":scope > .rich-select-selected-clone");
     if (!this._optionsAreEquivalent(selectedClone, selectedChild)) {
       if (selectedClone) {
         selectedClone.remove();
       }
 
       if (selectedChild) {
         selectedClone = selectedChild.cloneNode(false);
         selectedClone.removeAttribute("id");
         selectedClone.removeAttribute("selected");
-        selectedClone.classList.add("rich-select-selected-clone");
-        selectedClone = this.appendChild(selectedClone);
+      } else {
+        selectedClone = document.createElement("rich-option");
+        selectedClone.textContent = "(None selected)";
       }
+      selectedClone.classList.add("rich-select-selected-clone");
+      selectedClone = this.appendChild(selectedClone);
     }
   }
 }
 
 customElements.define("rich-select", RichSelect);