Bug 1440504 - Basic Payment Request Contact/Payer Add/Edit page (wip) draft
authorJared Wein <jwein@mozilla.com>
Wed, 04 Apr 2018 22:46:44 -0400
changeset 777649 0bff14995607c76c80a6f9a88a437266b95b3cf0
parent 776999 bae581c239662a9cb1f6c8088d89edb6809c3dce
push id105259
push userbmo:jaws@mozilla.com
push dateThu, 05 Apr 2018 02:47:14 +0000
bugs1440504
milestone61.0a1
Bug 1440504 - Basic Payment Request Contact/Payer Add/Edit page (wip) MozReview-Commit-ID: 8JK10dBimf2
toolkit/components/payments/res/containers/address-form.js
toolkit/components/payments/res/paymentRequest.xhtml
new file mode 100644
--- /dev/null
+++ b/toolkit/components/payments/res/containers/address-form.js
@@ -0,0 +1,162 @@
+/* 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-globals-from ../../../../../browser/extensions/formautofill/content/autofillEditForms.js*/
+/* import-globals-from ../mixins/PaymentStateSubscriberMixin.js */
+/* import-globals-from ../unprivileged-fallbacks.js */
+/* import-globals-from ../paymentRequest.js */
+
+"use strict";
+
+/**
+ * <address-form></address-form>
+ *
+ * XXX: Bug 1446164 - This form isn't localized when used via this custom element
+ * as it will be much easier to share the logic once we switch to Fluent.
+ */
+
+class AddressForm extends PaymentStateSubscriberMixin(HTMLElement) {
+  constructor() {
+    super();
+
+    this.genericErrorText = document.createElement("div");
+
+    this.backButton = document.createElement("button");
+    this.backButton.addEventListener("click", this);
+
+    this.saveButton = document.createElement("button");
+    this.saveButton.addEventListener("click", this);
+
+    // The markup is shared with form autofill preferences.
+    let url = "formautofill/editAddress.xhtml";
+    this.promiseReady = this._fetchMarkup(url).then(doc => {
+      this.form = doc.getElementById("form");
+      return this.form;
+    });
+  }
+
+  _fetchMarkup(url) {
+    return new Promise((resolve, reject) => {
+      let xhr = new XMLHttpRequest();
+      xhr.responseType = "document";
+      xhr.addEventListener("error", reject);
+      xhr.addEventListener("load", evt => {
+        resolve(xhr.response);
+      });
+      xhr.open("GET", url);
+      xhr.send();
+    });
+  }
+
+  connectedCallback() {
+    this.promiseReady.then(form => {
+      this.appendChild(form);
+
+      let record = {};
+      this.formHandler = new EditAddress({
+        form,
+      }, record, {
+        DEFAULT_REGION: ["US"],
+        supportedCountries: ["US", "CA"],
+      });
+
+      this.appendChild(this.genericErrorText);
+      this.appendChild(this.backButton);
+      this.appendChild(this.saveButton);
+      // Only call the connected super callback(s) once our markup is fully
+      // connected, including the shared form fetched asynchronously.
+      super.connectedCallback();
+    });
+  }
+
+  render(state) {
+    this.backButton.textContent = this.dataset.backButtonLabel;
+    this.saveButton.textContent = this.dataset.saveButtonLabel;
+
+    let record = {};
+    let {
+      page,
+      savedAddresses,
+    } = state;
+
+    this.genericErrorText.textContent = page.error;
+
+    let editing = !!page.guid;
+
+    // If a card is selected we want to edit it.
+    if (editing) {
+      record = savedAddresses[page.guid];
+      if (!record) {
+        throw new Error("Trying to edit a non-existing address: " + page.guid);
+      }
+    }
+
+    this.formHandler.loadRecord(record);
+  }
+
+  handleEvent(event) {
+    switch (event.type) {
+      case "click": {
+        this.onClick(event);
+        break;
+      }
+    }
+  }
+
+  onClick(evt) {
+    switch (evt.target) {
+      case this.backButton: {
+        this.requestStore.setState({
+          page: {
+            id: "payment-summary",
+          },
+        });
+        break;
+      }
+      case this.saveButton: {
+        this.saveRecord();
+        break;
+      }
+      default: {
+        throw new Error("Unexpected click target");
+      }
+    }
+  }
+
+  saveRecord() {
+    let record = this.formHandler.buildFormObject();
+    let {
+      page,
+    } = this.requestStore.getState();
+
+    
+    for (let editableFieldName of ["cc-name", "cc-exp-month", "cc-exp-year"]) {
+      record[editableFieldName] = record[editableFieldName] || "";
+    }
+
+    // Only save the card number if we're saving a new record, otherwise we'd
+    // overwrite the unmasked card number with the masked one.
+    if (!page.guid) {
+      record["cc-number"] = record["cc-number"] || "";
+    }
+
+    paymentRequest.updateAutofillRecord("creditCards", record, page.guid, {
+      errorStateChange: {
+        page: {
+          id: "basic-card-page",
+          error: this.dataset.errorGenericSave,
+        },
+      },
+      preserveOldProperties: true,
+      selectedStateKey: "selectedPaymentCard",
+      successStateChange: {
+        page: {
+          id: "payment-summary",
+        },
+      },
+    });
+  }
+}
+
+customElements.define("address-form", AddressForm);
--- a/toolkit/components/payments/res/paymentRequest.xhtml
+++ b/toolkit/components/payments/res/paymentRequest.xhtml
@@ -23,16 +23,19 @@
   <!ENTITY failPaymentButton.label       "Fail">
   <!ENTITY unknownPaymentButton.label    "Unknown">
   <!ENTITY orderDetailsLabel          "Order Details">
   <!ENTITY orderTotalLabel            "Total">
   <!ENTITY useSameAsShipping.label    "Use shipping address as payment address">
   <!ENTITY basicCardPage.error.genericSave    "There was an error saving the payment card.">
   <!ENTITY basicCardPage.backButton.label     "Back">
   <!ENTITY basicCardPage.saveButton.label     "Save">
+  <!ENTITY addressPage.error.genericSave      "There was an error saving the contact information.">
+  <!ENTITY addressPage.backButton.label       "Back">
+  <!ENTITY addressPage.saveButton.label       "Save">
 ]>
 <html xmlns="http://www.w3.org/1999/xhtml">
 <head>
   <title>&paymentSummaryTitle;</title>
 
   <!-- chrome: is needed for global.dtd -->
   <meta http-equiv="Content-Security-Policy" content="default-src 'self' chrome:"/>
 
@@ -62,16 +65,17 @@
   <script src="components/rich-option.js"></script>
   <script src="components/address-option.js"></script>
   <script src="components/shipping-option.js"></script>
   <script src="containers/address-picker.js"></script>
   <script src="components/basic-card-option.js"></script>
   <script src="containers/shipping-option-picker.js"></script>
   <script src="containers/payment-method-picker.js"></script>
   <script src="containers/basic-card-form.js"></script>
+  <script src="containers/address-form.js"></script>
   <script src="containers/payment-dialog.js"></script>
 
   <script src="paymentRequest.js"></script>
 
   <template id="payment-dialog-template">
     <header>
       <div id="total">
         <h2 class="label"></h2>
@@ -133,16 +137,23 @@
       </section>
 
       <basic-card-form id="basic-card-page"
                        class="page"
                        data-error-generic-save="&basicCardPage.error.genericSave;"
                        data-back-button-label="&basicCardPage.backButton.label;"
                        data-save-button-label="&basicCardPage.saveButton.label;"
                        hidden="hidden"></basic-card-form>
+
+      <address-form id="address-form"
+                    class="page"
+                    data-error-generic-save="&addressPage.error.genericSave;"
+                    data-back-button-label="&addressPage.backButton.label;"
+                    data-save-button-label="&addressPage.saveButton.label;"
+                    hidden="hidden"></address-form>
     </div>
 
     <div id="disabled-overlay" hidden="hidden">
       <!-- overlay to prevent changes while waiting for a response from the merchant -->
     </div>
   </template>
 
   <template id="order-details-template">