diff options
Diffstat (limited to 'browser/components/payments/res/containers/rich-picker.js')
-rw-r--r-- | browser/components/payments/res/containers/rich-picker.js | 114 |
1 files changed, 114 insertions, 0 deletions
diff --git a/browser/components/payments/res/containers/rich-picker.js b/browser/components/payments/res/containers/rich-picker.js new file mode 100644 index 0000000000..3752f67942 --- /dev/null +++ b/browser/components/payments/res/containers/rich-picker.js @@ -0,0 +1,114 @@ +/* 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/. */ + +import PaymentStateSubscriberMixin from "../mixins/PaymentStateSubscriberMixin.js"; +import RichSelect from "../components/rich-select.js"; + +export default class RichPicker extends PaymentStateSubscriberMixin( + HTMLElement +) { + static get observedAttributes() { + return ["label"]; + } + + constructor() { + super(); + this.classList.add("rich-picker"); + + this.dropdown = new RichSelect(); + this.dropdown.addEventListener("change", this); + + this.labelElement = document.createElement("label"); + + this.addLink = document.createElement("a"); + this.addLink.className = "add-link"; + this.addLink.href = "javascript:void(0)"; + this.addLink.addEventListener("click", this); + + this.editLink = document.createElement("a"); + this.editLink.className = "edit-link"; + this.editLink.href = "javascript:void(0)"; + this.editLink.addEventListener("click", this); + + this.invalidLabel = document.createElement("label"); + this.invalidLabel.className = "invalid-label"; + } + + connectedCallback() { + if (!this.dropdown.popupBox.id) { + this.dropdown.popupBox.id = + "select-" + Math.floor(Math.random() * 1000000); + } + this.labelElement.setAttribute("for", this.dropdown.popupBox.id); + this.invalidLabel.setAttribute("for", this.dropdown.popupBox.id); + + // The document order, by default, controls tab order so keep that in mind if changing this. + this.appendChild(this.labelElement); + this.appendChild(this.dropdown); + this.appendChild(this.editLink); + this.appendChild(this.addLink); + this.appendChild(this.invalidLabel); + super.connectedCallback(); + } + + attributeChangedCallback(name, oldValue, newValue) { + if (name == "label") { + this.labelElement.textContent = newValue; + } + } + + render(state) { + this.editLink.hidden = !this.dropdown.value; + + let errorText = this.errorForSelectedOption(state); + this.classList.toggle("invalid-selected-option", !!errorText); + this.invalidLabel.textContent = errorText; + this.addLink.textContent = this.dataset.addLinkLabel; + this.editLink.textContent = this.dataset.editLinkLabel; + } + + get selectedOption() { + return this.dropdown.selectedOption; + } + + get selectedRichOption() { + return this.dropdown.selectedRichOption; + } + + get requiredFields() { + return this.selectedOption ? this.selectedOption.requiredFields || [] : []; + } + + get fieldNames() { + return []; + } + + /** + * @param {object} state Application state + * @returns {string} Containing an error message for the picker or "" for no error. + */ + errorForSelectedOption(state) { + if (!this.selectedOption) { + return ""; + } + if (!this.dataset.invalidLabel) { + throw new Error("data-invalid-label is required"); + } + return this.missingFieldsOfSelectedOption().length + ? this.dataset.invalidLabel + : ""; + } + + missingFieldsOfSelectedOption() { + let selectedOption = this.selectedOption; + if (!selectedOption) { + return []; + } + + let fieldNames = this.selectedRichOption.requiredFields || []; + + // Return all field names that are empty or missing from the option. + return fieldNames.filter(name => !selectedOption.getAttribute(name)); + } +} |