summaryrefslogtreecommitdiffstats
path: root/browser/components/payments/res/containers/rich-picker.js
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/payments/res/containers/rich-picker.js')
-rw-r--r--browser/components/payments/res/containers/rich-picker.js114
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));
+ }
+}