Bug 1463545 - Overlay the selected <rich-option> on top of the native <select>. r=sfoster
authorMatthew Noorenberghe <mozilla@noorenberghe.ca>
Thu, 20 Sep 2018 21:07:09 +0000
changeset 437548 913329feffe4
parent 437547 7106b3f8d717
child 437549 b6cc6d47b5be
push id34685
push userrgurzau@mozilla.com
push dateFri, 21 Sep 2018 04:12:55 +0000
treeherdermozilla-central@8d8dc3f35c3d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssfoster
bugs1463545
milestone64.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1463545 - Overlay the selected <rich-option> on top of the native <select>. r=sfoster * We get the shared colours (including hover) and dropmarker. * Makes it harder to regress the clickable area of the <rich-select> since the problem will be visible. * Hides the text for the closed state of the <select> so the <rich-option> text can be presented. Differential Revision: https://phabricator.services.mozilla.com/D6330
browser/components/payments/res/components/rich-select.css
browser/components/payments/res/components/rich-select.js
browser/components/payments/res/containers/rich-picker.css
browser/components/payments/test/mochitest/test_rich_select.html
--- a/browser/components/payments/res/components/rich-select.css
+++ b/browser/components/payments/res/components/rich-select.css
@@ -1,36 +1,57 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 rich-select {
-  border: 1px solid #0C0C0D33;
+  /* Include the padding in the max-width calculation so that we truncate rather
+     than grow wider than 100% of the parent. */
+  box-sizing: border-box;
   display: block;
   margin: 14px 0;
+  /* Padding for the dropmarker (copied from common.css) */
+  padding-inline-end: 24px;
   position: relative;
+  /* Don't allow the <rich-select> to grow wider than the container so that we
+     truncate with text-overflow for long options instead. */
+  max-width: 100%;
 }
 
 /* Focusing on the underlying select element outlines the outer
-rich-select wrapper making it appear like rich-select is focused. */
-rich-select:focus-within {
-  outline: 1px dotted;
+   rich-select wrapper making it appear like rich-select is focused. */
+rich-select:focus-within > select {
+  outline: 1px dotted var(--in-content-text-color);
 }
 
 /*
  * The HTML select element is hidden and placed on the rich-option
  * element to make it look like clicking on the rich-option element
  * in the closed state opens the HTML select dropdown. */
 rich-select > select {
-  opacity: 0;
+  /* Hide the text from the closed state so that the text/layout from
+     <rich-option> won't overlap it. */
+  color: transparent;
   position: absolute;
   top: 0;
   left: 0;
   width: 100%;
   height: 100%;
   margin: 0;
 }
 
+rich-select > select > option {
+  /* Reset the text color in the popup/open state */
+  color: var(--in-content-text-color);
+}
+
 .rich-option {
   display: grid;
-  background: #fff; /* TODO: system colors */
   padding: 8px;
 }
+
+.rich-select-selected-option {
+  /* Clicks on the selected rich option should go to the <select> below to open the popup */
+  pointer-events: none;
+  /* Use position:relative so this is positioned on top of the <select> which
+     also has position:relative. */
+  position: relative;
+}
--- a/browser/components/payments/res/components/rich-select.js
+++ b/browser/components/payments/res/components/rich-select.js
@@ -31,16 +31,22 @@ export default class RichSelect extends 
     this.appendChild(this.popupBox);
     this.render();
   }
 
   get selectedOption() {
     return this.getOptionByValue(this.value);
   }
 
+  get selectedRichOption() {
+    // XXX: Bug 1475684 - This can be removed once `selectedOption` returns a
+    // RichOption which extends HTMLOptionElement.
+    return this.querySelector(":scope > .rich-select-selected-option");
+  }
+
   get value() {
     return this.popupBox.value;
   }
 
   set value(guid) {
     this.popupBox.value = guid;
     this.render();
   }
@@ -68,27 +74,30 @@ export default class RichSelect extends 
 
     if (this.value) {
       let optionType = this.getAttribute("option-type");
       if (selectedRichOption.localName != optionType) {
         selectedRichOption = document.createElement(optionType);
       }
 
       let option = this.getOptionByValue(this.value);
-      let attributeNames = selectedRichOption.constructor.recordAttributes;
+      let attributeNames = selectedRichOption.constructor.observedAttributes;
       for (let attributeName of attributeNames) {
         let attributeValue = option.getAttribute(attributeName);
         if (attributeValue) {
           selectedRichOption.setAttribute(attributeName, attributeValue);
         } else {
           selectedRichOption.removeAttribute(attributeName);
         }
       }
     } else {
       selectedRichOption = new RichOption();
-      selectedRichOption.textContent = "(None selected)";
+      selectedRichOption.textContent = "(None selected)"; // XXX: bug 1473772
     }
     selectedRichOption.classList.add("rich-select-selected-option");
+    // Hide the rich-option from a11y tools since the native <select> will
+    // already provide the selected option label.
+    selectedRichOption.setAttribute("aria-hidden", "true");
     selectedRichOption = this.appendChild(selectedRichOption);
   }
 }
 
 customElements.define("rich-select", RichSelect);
--- a/browser/components/payments/res/containers/rich-picker.css
+++ b/browser/components/payments/res/containers/rich-picker.css
@@ -30,18 +30,18 @@
   grid-area: edit;
   border-right: 1px solid #0C0C0D33;
 }
 
 .rich-picker > rich-select {
   grid-area: dropdown;
 }
 
-.invalid-selected-option > rich-select {
-  outline: 1px solid #c70011;
+.invalid-selected-option > rich-select > select {
+  border: 1px solid #c70011;
 }
 
 .rich-picker > .invalid-label {
   grid-area: invalid;
   font-weight: normal;
   color: #c70011;
 }
 
--- a/browser/components/payments/test/mochitest/test_rich_select.html
+++ b/browser/components/payments/test/mochitest/test_rich_select.html
@@ -77,16 +77,24 @@ let option2 = options[1];
 function get_selected_clone() {
   return select1.querySelector(".rich-select-selected-option");
 }
 
 function is_visible(element, message) {
   ok(!isHidden(element), message);
 }
 
+add_task(async function test_clickable_area() {
+  ok(select1, "select1 exists");
+  isnot(document.activeElement, select1.popupBox, "<select> shouldn't have focus");
+  synthesizeMouseAtCenter(select1, {});
+  is(document.activeElement, select1.popupBox, "<select> should have focus when clicked");
+  document.activeElement.blur();
+});
+
 add_task(async function test_closed_state_on_selection() {
   ok(select1, "select1 exists");
   select1.popupBox.focus();
   synthesizeKey(option1.textContent, {});
   await asyncElementRendered();
   ok(option1.selected, "option 1 is now selected");
 
   let selectedClone = get_selected_clone();