Bug 1469464 - Consistent PaymentRequest footer positioning with <payment-request-page>. r=sfoster
MozReview-Commit-ID: Oq06q6xF0e
--- a/browser/components/payments/docs/index.rst
+++ b/browser/components/payments/docs/index.rst
@@ -9,17 +9,17 @@ JSDoc style comments are used within the
.. toctree::
:maxdepth: 5
Debugging/Development
=====================
Must Have Electrolysis
--------
+----------------------
Web Payments `does not work without e10s <https://bugzilla.mozilla.org/show_bug.cgi?id=1365964>`_!
Logging
-------
Set the pref ``dom.payments.loglevel`` to "Debug" to increase the verbosity of console messages.
@@ -64,8 +64,24 @@ In order to communicate across the proce
This is because the unprivileged document cannot access message managers.
Instead, all communication across the privileged/unprivileged boundary is done via custom DOM events:
* A ``paymentContentToChrome`` event is dispatched when the dialog contents want to communicate with the privileged dialog wrapper.
* A ``paymentChromeToContent`` event is dispatched on the ``window`` with the ``detail`` property populated when the privileged dialog wrapper communicates with the unprivileged dialog.
These events are converted to/from message manager messages of the same name to communicate to the other process.
The purpose of `paymentDialogFrameScript.js` is to simply convert unprivileged DOM events to/from messages from the other process.
+
+Custom Elements
+---------------
+
+The Payment Request UI uses Custom Elements for the UI components.
+
+Some guidelines:
+* If you're overriding a lifecycle callback, don't forget to call that method on
+ ``super`` from the implementation to ensure that mixins and ancestor classes
+ work properly.
+* From within a custom element, don't use ``document.getElementById`` or
+ ``document.querySelector*`` because they can return elements that are outside
+ of the component, thus breaking the modularization. It can also cause problems
+ if the elements you're looking for aren't attached to the document yet. Use
+ ``querySelector*`` on ``this`` (the custom element) or one of its descendants
+ instead.
new file mode 100644
--- /dev/null
+++ b/browser/components/payments/res/components/payment-request-page.js
@@ -0,0 +1,33 @@
+/* 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/. */
+
+/**
+ * <payment-request-page></payment-request-page>
+ */
+
+export default class PaymentRequestPage extends HTMLElement {
+ constructor() {
+ super();
+
+ this.classList.add("page");
+
+ this.pageTitleHeading = document.createElement("h2");
+
+ // The body and footer may be pre-defined in the template so re-use them if they exist.
+ this.body = this.querySelector(":scope > .page-body") || document.createElement("div");
+ this.body.classList.add("page-body");
+
+ this.footer = this.querySelector(":scope > footer") || document.createElement("footer");
+ }
+
+ connectedCallback() {
+ // The heading goes inside the body so it scrolls.
+ this.body.prepend(this.pageTitleHeading);
+ this.appendChild(this.body);
+
+ this.appendChild(this.footer);
+ }
+}
+
+customElements.define("payment-request-page", PaymentRequestPage);
--- a/browser/components/payments/res/containers/address-form.js
+++ b/browser/components/payments/res/containers/address-form.js
@@ -1,30 +1,34 @@
/* 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 LabelledCheckbox from "../components/labelled-checkbox.js";
+import PaymentRequestPage from "../components/payment-request-page.js";
import PaymentStateSubscriberMixin from "../mixins/PaymentStateSubscriberMixin.js";
import paymentRequest from "../paymentRequest.js";
/* import-globals-from ../unprivileged-fallbacks.js */
/**
* <address-form></address-form>
*
+ * Don't use document.getElementById or document.querySelector* to access form
+ * elements, use querySelector on `this` or `this.form` instead so that elements
+ * can be found before the element is connected.
+ *
* 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.
*/
-export default class AddressForm extends PaymentStateSubscriberMixin(HTMLElement) {
+export default class AddressForm extends PaymentStateSubscriberMixin(PaymentRequestPage) {
constructor() {
super();
- this.pageTitle = document.createElement("h2");
this.genericErrorText = document.createElement("div");
this.cancelButton = document.createElement("button");
this.cancelButton.className = "cancel-button";
this.cancelButton.addEventListener("click", this);
this.backButton = document.createElement("button");
this.backButton.className = "back-button";
@@ -68,33 +72,33 @@ export default class AddressForm extends
});
xhr.open("GET", url);
xhr.send();
});
}
connectedCallback() {
this.promiseReady.then(form => {
- this.appendChild(this.pageTitle);
- this.appendChild(form);
+ this.body.appendChild(form);
let record = {};
this.formHandler = new EditAddress({
form,
}, record, {
DEFAULT_REGION: PaymentDialogUtils.DEFAULT_REGION,
getFormFormat: PaymentDialogUtils.getFormFormat,
supportedCountries: PaymentDialogUtils.supportedCountries,
});
- this.appendChild(this.persistCheckbox);
- this.appendChild(this.genericErrorText);
- this.appendChild(this.cancelButton);
- this.appendChild(this.backButton);
- this.appendChild(this.saveButton);
+ this.body.appendChild(this.persistCheckbox);
+ this.body.appendChild(this.genericErrorText);
+
+ this.footer.appendChild(this.cancelButton);
+ this.footer.appendChild(this.backButton);
+ this.footer.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) {
let record = {};
@@ -118,17 +122,17 @@ export default class AddressForm extends
this.cancelButton.hidden = !page.onboardingWizard;
if (addressPage.addressFields) {
this.setAttribute("address-fields", addressPage.addressFields);
} else {
this.removeAttribute("address-fields");
}
- this.pageTitle.textContent = addressPage.title;
+ this.pageTitleHeading.textContent = addressPage.title;
this.genericErrorText.textContent = page.error;
let editing = !!addressPage.guid;
let addresses = paymentRequest.getAddresses(state);
// If an address is selected we want to edit it.
if (editing) {
record = addresses[addressPage.guid];
@@ -156,18 +160,18 @@ export default class AddressForm extends
container.setAttribute("required", "true");
} else {
container.removeAttribute("required");
}
}
let shippingAddressErrors = request.paymentDetails.shippingAddressErrors;
for (let [errorName, errorSelector] of Object.entries(this._errorFieldMap)) {
- let container = document.querySelector(errorSelector + "-container");
- let field = document.querySelector(errorSelector);
+ let container = this.form.querySelector(errorSelector + "-container");
+ let field = this.form.querySelector(errorSelector);
let errorText = (shippingAddressErrors && shippingAddressErrors[errorName]) || "";
container.classList.toggle("error", !!errorText);
field.setCustomValidity(errorText);
let span = container.querySelector(".error-text");
if (!span) {
span = document.createElement("span");
span.className = "error-text";
container.appendChild(span);
--- a/browser/components/payments/res/containers/basic-card-form.js
+++ b/browser/components/payments/res/containers/basic-card-form.js
@@ -1,56 +1,59 @@
/* 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 LabelledCheckbox from "../components/labelled-checkbox.js";
+import PaymentRequestPage from "../components/payment-request-page.js";
import PaymentStateSubscriberMixin from "../mixins/PaymentStateSubscriberMixin.js";
import paymentRequest from "../paymentRequest.js";
/* import-globals-from ../unprivileged-fallbacks.js */
/**
* <basic-card-form></basic-card-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.
*/
-export default class BasicCardForm extends PaymentStateSubscriberMixin(HTMLElement) {
+export default class BasicCardForm extends PaymentStateSubscriberMixin(PaymentRequestPage) {
constructor() {
super();
- this.pageTitle = document.createElement("h2");
this.genericErrorText = document.createElement("div");
- this.cancelButton = document.createElement("button");
- this.cancelButton.className = "cancel-button";
- this.cancelButton.addEventListener("click", this);
-
this.addressAddLink = document.createElement("a");
this.addressAddLink.className = "add-link";
this.addressAddLink.href = "javascript:void(0)";
this.addressAddLink.addEventListener("click", this);
this.addressEditLink = document.createElement("a");
this.addressEditLink.className = "edit-link";
this.addressEditLink.href = "javascript:void(0)";
this.addressEditLink.addEventListener("click", this);
+ this.persistCheckbox = new LabelledCheckbox();
+ this.persistCheckbox.className = "persist-checkbox";
+
+ // page footer
+ this.cancelButton = document.createElement("button");
+ this.cancelButton.className = "cancel-button";
+ this.cancelButton.addEventListener("click", this);
+
this.backButton = document.createElement("button");
this.backButton.className = "back-button";
this.backButton.addEventListener("click", this);
this.saveButton = document.createElement("button");
this.saveButton.className = "save-button primary";
this.saveButton.addEventListener("click", this);
- this.persistCheckbox = new LabelledCheckbox();
- this.persistCheckbox.className = "persist-checkbox";
+ this.footer.append(this.cancelButton, this.backButton, this.saveButton);
// The markup is shared with form autofill preferences.
let url = "formautofill/editCreditCard.xhtml";
this.promiseReady = this._fetchMarkup(url).then(doc => {
this.form = doc.getElementById("form");
return this.form;
});
}
@@ -65,18 +68,17 @@ export default class BasicCardForm exten
});
xhr.open("GET", url);
xhr.send();
});
}
connectedCallback() {
this.promiseReady.then(form => {
- this.appendChild(this.pageTitle);
- this.appendChild(form);
+ this.body.appendChild(form);
let record = {};
let addresses = [];
this.formHandler = new EditCreditCard({
form,
}, record, addresses, {
isCCNumber: PaymentDialogUtils.isCCNumber,
getAddressLabel: PaymentDialogUtils.getAddressLabel,
@@ -84,21 +86,18 @@ export default class BasicCardForm exten
let fragment = document.createDocumentFragment();
fragment.append(this.addressAddLink);
fragment.append(" ");
fragment.append(this.addressEditLink);
let billingAddressRow = this.form.querySelector(".billingAddressRow");
billingAddressRow.appendChild(fragment);
- this.appendChild(this.persistCheckbox);
- this.appendChild(this.genericErrorText);
- this.appendChild(this.cancelButton);
- this.appendChild(this.backButton);
- this.appendChild(this.saveButton);
+ this.body.appendChild(this.persistCheckbox);
+ this.body.appendChild(this.genericErrorText);
// Only call the connected super callback(s) once our markup is fully
// connected, including the shared form fetched asynchronously.
super.connectedCallback();
});
}
render(state) {
let {
@@ -130,25 +129,25 @@ export default class BasicCardForm exten
this.genericErrorText.textContent = page.error;
let editing = !!basicCardPage.guid;
this.form.querySelector("#cc-number").disabled = editing;
// If a card is selected we want to edit it.
if (editing) {
- this.pageTitle.textContent = this.dataset.editBasicCardTitle;
+ this.pageTitleHeading.textContent = this.dataset.editBasicCardTitle;
record = basicCards[basicCardPage.guid];
if (!record) {
throw new Error("Trying to edit a non-existing card: " + basicCardPage.guid);
}
// When editing an existing record, prevent changes to persistence
this.persistCheckbox.hidden = true;
} else {
- this.pageTitle.textContent = this.dataset.addBasicCardTitle;
+ this.pageTitleHeading.textContent = this.dataset.addBasicCardTitle;
// Use a currently selected shipping address as the default billing address
record.billingAddressGUID = basicCardPage.billingAddressGUID;
if (!record.billingAddressGUID && selectedShippingAddress) {
record.billingAddressGUID = selectedShippingAddress;
}
// Adding a new record: default persistence to checked when in a not-private session
this.persistCheckbox.hidden = false;
this.persistCheckbox.checked = !state.isPrivate;
--- a/browser/components/payments/res/containers/payment-dialog.js
+++ b/browser/components/payments/res/containers/payment-dialog.js
@@ -3,16 +3,17 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import "../vendor/custom-elements.min.js";
import PaymentStateSubscriberMixin from "../mixins/PaymentStateSubscriberMixin.js";
import paymentRequest from "../paymentRequest.js";
import "../components/currency-amount.js";
+import "../components/payment-request-page.js";
import "./address-picker.js";
import "./address-form.js";
import "./basic-card-form.js";
import "./order-details.js";
import "./payment-method-picker.js";
import "./shipping-option-picker.js";
/* import-globals-from ../unprivileged-fallbacks.js */
--- a/browser/components/payments/res/debugging.css
+++ b/browser/components/payments/res/debugging.css
@@ -1,16 +1,13 @@
/* 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/. */
html {
- /* Based on global.css styles for top-level XUL windows */
- -moz-appearance: dialog;
- background-color: -moz-Dialog;
color: -moz-DialogText;
font: message-box;
/* Make sure the background ends to the bottom if there is unused space */
height: 100%;
}
h1 {
font-size: 1em;
--- a/browser/components/payments/res/paymentRequest.css
+++ b/browser/components/payments/res/paymentRequest.css
@@ -1,96 +1,93 @@
/* 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/. */
html {
- /* Based on global.css styles for top-level XUL windows */
- color: -moz-DialogText;
- font: message-box;
height: 100%;
}
body {
- /* Override font-size from in-content/common.css which is too large */
- font-size: inherit;
-}
-
-#order-details-overlay,
-html {
- /* Based on global.css styles for top-level XUL windows */
- -moz-appearance: dialog;
- background-color: -moz-Dialog;
-}
-
-body {
height: 100%;
margin: 0;
- overflow: hidden;
+ /* Override font-size from in-content/common.css which is too large */
+ font-size: inherit;
}
[hidden] {
display: none !important;
}
#debugging-console {
/* include the default borders in the max-height */
box-sizing: border-box;
float: right;
- /* avoid causing the body to scroll */
- max-height: 100vh;
+ height: 100vh;
/* Float above the other overlays */
position: relative;
z-index: 99;
}
payment-dialog {
box-sizing: border-box;
display: grid;
- grid-template-rows: fit-content(10%) auto;
+ grid-template: "header" auto
+ "main" 1fr
+ "disabled-overlay" auto;
height: 100%;
margin: 0 10%;
padding: 1em;
}
payment-dialog > header {
display: flex;
}
#main-container {
display: flex;
+ grid-area: main;
position: relative;
+ max-height: 100%;
}
-#payment-summary {
- display: grid;
- flex: 1 1 auto;
- grid-template-rows: fit-content(10%) auto fit-content(10%);
+.page {
+ display: flex;
+ flex-direction: column;
+ height: 100%;
position: relative;
+ width: 100%;
+}
+
+.page > .page-body {
+ /* The area above the footer should scroll, if necessary. */
+ overflow: auto;
+}
+
+.page > footer {
+ align-items: end;
+ display: flex;
+ flex-grow: 1;
}
#error-text {
text-align: center;
}
#order-details-overlay {
+ background-color: var(--in-content-page-background);
overflow: auto;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 1;
}
-payment-dialog > footer {
- align-items: baseline;
- display: flex;
-}
-
#total {
flex: 1 1 auto;
margin: 5px;
}
#total > currency-amount > .currency-code {
color: GrayText;
}
@@ -110,16 +107,17 @@ payment-dialog[changes-prevented][comple
payment-dialog[changes-prevented][completion-state="success"] #pay {
/* Show the pay button above #disabled-overlay */
position: relative;
z-index: 1;
}
#disabled-overlay {
background: white;
+ grid-area: disabled-overlay;
opacity: 0.6;
width: 100%;
height: 100%;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
--- a/browser/components/payments/res/paymentRequest.xhtml
+++ b/browser/components/payments/res/paymentRequest.xhtml
@@ -87,18 +87,18 @@
<div>&header.payTo; <span id="host-name"></span></div>
</div>
<div id="top-buttons" hidden="hidden">
<button id="view-all" class="closed">&viewAllItems;</button>
</div>
</header>
<div id="main-container">
- <section id="payment-summary" class="page">
- <section>
+ <payment-request-page id="payment-summary">
+ <div class="page-body">
<div id="error-text"></div>
<div class="shipping-related"
id="shipping-type-label"
data-shipping-address-label="&shippingAddressLabel;"
data-delivery-address-label="&deliveryAddressLabel;"
data-persist-checkbox-label="&addressPage.persistCheckbox.label;"
data-pickup-address-label="&pickupAddressLabel;"><label></label></div>
@@ -117,51 +117,49 @@
</payment-method-picker>
<div class="payer-related"><label>&payerLabel;</label></div>
<address-picker class="payer-related"
data-add-link-label="&payer.addLink.label;"
data-edit-link-label="&payer.editLink.label;"
selected-state-key="selectedPayerAddress"></address-picker>
<div id="error-text"></div>
- </section>
+ </div>
- <footer id="controls-container">
+ <footer>
<button id="cancel">&cancelPaymentButton.label;</button>
<button id="pay"
class="primary"
data-initial-label="&approvePaymentButton.label;"
data-processing-label="&processingPaymentButton.label;"
data-fail-label="&failPaymentButton.label;"
data-unknown-label="&unknownPaymentButton.label;"
data-success-label="&successPaymentButton.label;"></button>
</footer>
- </section>
+ </payment-request-page>
<section id="order-details-overlay" hidden="hidden">
<h2>&orderDetailsLabel;</h2>
<order-details></order-details>
</section>
<basic-card-form id="basic-card-page"
- class="page"
data-add-basic-card-title="&basicCard.addPage.title;"
data-edit-basic-card-title="&basicCard.editPage.title;"
data-error-generic-save="&basicCardPage.error.genericSave;"
data-address-add-link-label="&basicCardPage.addressAddLink.label;"
data-address-edit-link-label="&basicCardPage.addressEditLink.label;"
data-billing-address-title-add="&billingAddress.addPage.title;"
data-billing-address-title-edit="&billingAddress.editPage.title;"
data-back-button-label="&basicCardPage.backButton.label;"
data-save-button-label="&basicCardPage.saveButton.label;"
data-cancel-button-label="&cancelPaymentButton.label;"
data-persist-checkbox-label="&basicCardPage.persistCheckbox.label;"
hidden="hidden"></basic-card-form>
<address-form id="address-page"
- class="page"
data-error-generic-save="&addressPage.error.genericSave;"
data-cancel-button-label="&addressPage.cancelButton.label;"
data-back-button-label="&addressPage.backButton.label;"
data-save-button-label="&addressPage.saveButton.label;"
data-persist-checkbox-label="&addressPage.persistCheckbox.label;"
hidden="hidden"></address-form>
</div>
@@ -177,18 +175,18 @@
<div class="details-total">
<h2 class="label">&orderTotalLabel;</h2>
<currency-amount></currency-amount>
</div>
</template>
</head>
<body dir="&locale.dir;">
<iframe id="debugging-console"
- hidden="hidden"
- height="400"></iframe>
+ hidden="hidden">
+ </iframe>
<payment-dialog data-shipping-address-title-add="&shippingAddress.addPage.title;"
data-shipping-address-title-edit="&shippingAddress.editPage.title;"
data-delivery-address-title-add="&deliveryAddress.addPage.title;"
data-delivery-address-title-edit="&deliveryAddress.editPage.title;"
data-pickup-address-title-add="&pickupAddress.addPage.title;"
data-pickup-address-title-edit="&pickupAddress.editPage.title;"
data-billing-address-title-add="&billingAddress.addPage.title;"
data-payer-title-add="&payer.addPage.title;"
--- a/browser/components/payments/test/mochitest/test_address_form.html
+++ b/browser/components/payments/test/mochitest/test_address_form.html
@@ -89,17 +89,17 @@ add_task(async function test_backButton(
title: "Sample page title",
},
});
await form.promiseReady;
display.appendChild(form);
await asyncElementRendered();
let stateChangePromise = promiseStateChange(form.requestStore);
- is(form.pageTitle.textContent, "Sample page title", "Check label");
+ is(form.pageTitleHeading.textContent, "Sample page title", "Check label");
is(form.backButton.textContent, "Back", "Check label");
form.backButton.scrollIntoView();
synthesizeMouseAtCenter(form.backButton, {});
let {page} = await stateChangePromise;
is(page.id, "payment-summary", "Check initial page after appending");
--- a/browser/components/payments/test/mochitest/test_basic_card_form.html
+++ b/browser/components/payments/test/mochitest/test_basic_card_form.html
@@ -73,17 +73,17 @@ add_task(async function test_backButton(
"basic-card-page": {
},
});
await form.promiseReady;
display.appendChild(form);
await asyncElementRendered();
let stateChangePromise = promiseStateChange(form.requestStore);
- is(form.pageTitle.textContent, "Sample page title 2", "Check title");
+ is(form.pageTitleHeading.textContent, "Sample page title 2", "Check title");
is(form.backButton.textContent, "Back", "Check label");
synthesizeMouseAtCenter(form.backButton, {});
let {page} = await stateChangePromise;
is(page.id, "payment-summary", "Check initial page after appending");
form.remove();
});
--- a/browser/components/payments/test/mochitest/test_payment_dialog.html
+++ b/browser/components/payments/test/mochitest/test_payment_dialog.html
@@ -89,17 +89,17 @@ add_task(async function test_initialStat
is(initialState.page.id, "payment-summary", "Check initial page");
});
add_task(async function test_viewAllButtonVisibility() {
await setup();
let button = el1._viewAllButton;
ok(button.hidden, "Button is initially hidden when there are no items to show");
- ok(isHidden(button), "Button should be visibly hidden since bug 1469464")
+ ok(isHidden(button), "Button should be visibly hidden since bug 1469464");
// Add a display item.
let request = deepClone(el1.requestStore.getState().request);
request.paymentDetails.displayItems = [
{
"label": "Triangle",
"amount": {
"currency": "CAD",
@@ -177,16 +177,29 @@ add_task(async function test_completionS
ok(payButton.disabled, "Button is disabled");
let rect = payButton.getBoundingClientRect();
let visibleElement =
document.elementFromPoint(rect.x + rect.width / 2, rect.y + rect.height / 2);
ok(payButton === visibleElement, "Pay button is on top of the overlay");
}
});
+add_task(async function test_scrollPaymentRequestPage() {
+ await setup();
+ info("making the payment-dialog container small to require scrolling");
+ el1.parentElement.style.height = "100px";
+ let summaryPageBody = document.querySelector("#payment-summary .page-body");
+ is(summaryPageBody.scrollTop, 0, "Page body not scrolled initially");
+ let securityCodeInput = summaryPageBody.querySelector("payment-method-picker input");
+ securityCodeInput.focus();
+ await new Promise(resolve => SimpleTest.executeSoon(resolve));
+ ok(summaryPageBody.scrollTop > 0, "Page body scrolled after focusing the CVV field");
+ el1.parentElement.style.height = "";
+});
+
add_task(async function test_disconnect() {
await setup();
el1.remove();
await el1.requestStore.setState({orderDetailsShowing: true});
await asyncElementRendered();
ok(el1.stateChangeCallback.notCalled, "stateChangeCallback not called");
ok(el1.render.notCalled, "render not called");
--- a/browser/extensions/formautofill/content/autofillEditForms.js
+++ b/browser/extensions/formautofill/content/autofillEditForms.js
@@ -134,42 +134,42 @@ class EditAddress extends EditAutofillFo
"street-address",
"address-level2",
"address-level1",
"postal-code",
];
let inputs = [];
for (let i = 0; i < fieldsOrder.length; i++) {
let {fieldId, newLine} = fieldsOrder[i];
- let container = document.getElementById(`${fieldId}-container`);
+ let container = this._elements.form.querySelector(`#${fieldId}-container`);
let containerInputs = [...container.querySelectorAll("input, textarea, select")];
containerInputs.forEach(function(input) { input.disabled = false; });
inputs.push(...containerInputs);
container.style.display = "flex";
container.style.order = i;
container.style.pageBreakAfter = newLine ? "always" : "auto";
// Remove the field from the list of fields
fields.splice(fields.indexOf(fieldId), 1);
}
for (let i = 0; i < inputs.length; i++) {
// Assign tabIndex starting from 1
inputs[i].tabIndex = i + 1;
}
// Hide the remaining fields
for (let field of fields) {
- let container = document.getElementById(`${field}-container`);
+ let container = this._elements.form.querySelector(`#${field}-container`);
container.style.display = "none";
for (let input of [...container.querySelectorAll("input, textarea, select")]) {
input.disabled = true;
}
}
}
updatePostalCodeValidation(postalCodePattern) {
- let postalCodeInput = document.getElementById("postal-code");
+ let postalCodeInput = this._elements.form.querySelector("#postal-code");
if (postalCodePattern && postalCodeInput.style.display != "none") {
postalCodeInput.setAttribute("pattern", postalCodePattern);
} else {
postalCodeInput.removeAttribute("pattern");
}
}
populateCountries() {