зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1477102 - Toggle accepted cards list on add/edit cards. Add stub for using AppConstants.MOZILLA_OFFICIAL to fallback for non-branded builds. r=MattN
Differential Revision: https://phabricator.services.mozilla.com/D7025 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
45e7e08e6d
Коммит
b6ecb8ed07
|
@ -103,6 +103,11 @@ let PaymentFrameScript = {
|
|||
}, waivedContent);
|
||||
return Cu.cloneInto(prefValues, waivedContent);
|
||||
},
|
||||
|
||||
isOfficialBranding() {
|
||||
// XXX: stub, will reflect AppConstants.MOZILLA_OFFICIAL when we have real logos
|
||||
return false;
|
||||
},
|
||||
};
|
||||
waivedContent.PaymentDialogUtils = Cu.cloneInto(PaymentDialogUtils, waivedContent, {
|
||||
cloneFunctions: true,
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
accepted-cards {
|
||||
margin: 1em 0;
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
align-items: first baseline;
|
||||
}
|
||||
|
||||
.accepted-cards-label {
|
||||
display: inline-block;
|
||||
margin-inline-end: 1em;
|
||||
color: GrayText;
|
||||
font-size: smaller;
|
||||
flex: 0 2 content;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.accepted-cards-list {
|
||||
|
@ -14,31 +17,37 @@ accepted-cards {
|
|||
list-style-type: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
flex: 2 1 auto;
|
||||
}
|
||||
|
||||
.accepted-cards-list > .accepted-cards-item {
|
||||
display: inline-block;
|
||||
width: 50px;
|
||||
height: 22px;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
padding: 0;
|
||||
text-align: center;
|
||||
margin-inline-end: 8px;
|
||||
margin: 5px 0;
|
||||
margin-inline-start: 10px;
|
||||
vertical-align: middle;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
vertical-align: middle;
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
/* placeholders for specific card icons we don't yet have assets for */
|
||||
.accepted-cards-item[data-network-id] {
|
||||
accepted-cards:not(.branded) .accepted-cards-item[data-network-id] {
|
||||
width: 48px;
|
||||
text-align: center;
|
||||
background-image: url("./card-icon.svg");
|
||||
-moz-context-properties: fill-opacity;
|
||||
fill-opacity: 0.5;
|
||||
}
|
||||
.accepted-cards-item[data-network-id]::after {
|
||||
accepted-cards:not(.branded) .accepted-cards-item[data-network-id]::after {
|
||||
box-sizing: border-box;
|
||||
content: attr(data-network-id);
|
||||
padding: 4px;
|
||||
padding: 8px 4px 0 4px;
|
||||
text-align: center;
|
||||
font-size: 0.7rem;
|
||||
display: block;
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
import PaymentStateSubscriberMixin from "../mixins/PaymentStateSubscriberMixin.js";
|
||||
import paymentRequest from "../paymentRequest.js";
|
||||
/* import-globals-from ../unprivileged-fallbacks.js */
|
||||
|
||||
/**
|
||||
|
@ -30,8 +29,12 @@ export default class AcceptedCards extends PaymentStateSubscriberMixin(HTMLEleme
|
|||
let item = document.createElement("li");
|
||||
item.classList.add("accepted-cards-item");
|
||||
item.dataset.networkId = network;
|
||||
item.setAttribute("aria-role", "image");
|
||||
item.setAttribute("aria-label", network);
|
||||
this._listEl.appendChild(item);
|
||||
}
|
||||
let isBranded = PaymentDialogUtils.isOfficialBranding();
|
||||
this.classList.toggle("branded", isBranded);
|
||||
this.appendChild(this._listEl);
|
||||
// Only call the connected super callback(s) once our markup is fully
|
||||
// connected
|
||||
|
@ -39,10 +42,19 @@ export default class AcceptedCards extends PaymentStateSubscriberMixin(HTMLEleme
|
|||
}
|
||||
|
||||
render(state) {
|
||||
let acceptedNetworks = paymentRequest.getAcceptedNetworks(state.request);
|
||||
for (let item of this._listEl.children) {
|
||||
let network = item.dataset.networkId;
|
||||
item.hidden = !(network && acceptedNetworks.includes(network));
|
||||
let basicCardMethod = state.request.paymentMethods
|
||||
.find(method => method.supportedMethods == "basic-card");
|
||||
let merchantNetworks = basicCardMethod && basicCardMethod.data &&
|
||||
basicCardMethod.data.supportedNetworks;
|
||||
if (merchantNetworks && merchantNetworks.length) {
|
||||
for (let item of this._listEl.children) {
|
||||
let network = item.dataset.networkId;
|
||||
item.hidden = !(network && merchantNetworks.includes(network));
|
||||
}
|
||||
this.hidden = false;
|
||||
} else {
|
||||
// hide the whole list if the merchant didn't specify a preference
|
||||
this.hidden = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 47 22">
|
||||
<rect x="0" y="0" width="47" height="22" rx="4" ry="4" fill="#000" fill-opacity="0.2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 32">
|
||||
<rect x="0" y="0" width="48" height="32" rx="4" ry="4" fill="#000" fill-opacity="context-fill-opacity">
|
||||
</rect>
|
||||
<rect x="0" y="5" width="47" height="12" fill="#fff" fill-opacity="1">
|
||||
<rect x="0" y="6" width="48" height="20" fill="#fff" fill-opacity="1">
|
||||
</rect>
|
||||
</svg>
|
||||
|
|
До Ширина: | Высота: | Размер: 250 B После Ширина: | Высота: | Размер: 267 B |
|
@ -154,7 +154,6 @@ export default class BasicCardForm extends PaymentStateSubscriberMixin(PaymentRe
|
|||
this.addressAddLink.textContent = this.dataset.addressAddLinkLabel;
|
||||
this.addressEditLink.textContent = this.dataset.addressEditLinkLabel;
|
||||
this.acceptedCardsList.label = this.dataset.acceptedCardsLabel;
|
||||
this.acceptedCardsList.hidden = editing;
|
||||
|
||||
// The next line needs an onboarding check since we don't set previousId
|
||||
// when navigating to add/edit directly from the summary page.
|
||||
|
|
|
@ -374,13 +374,6 @@ export default class PaymentDialog extends PaymentStateSubscriberMixin(HTMLEleme
|
|||
|
||||
this._renderPayerFields(state);
|
||||
|
||||
// hide the accepted cards list if the merchant didn't specify a preference
|
||||
let basicCardMethod = request.paymentMethods
|
||||
.find(method => method.supportedMethods == "basic-card");
|
||||
let merchantNetworks = basicCardMethod && basicCardMethod.data &&
|
||||
basicCardMethod.data.supportedNetworks;
|
||||
this._acceptedCardsList.hidden = !(merchantNetworks && merchantNetworks.length);
|
||||
|
||||
let isMac = /mac/i.test(navigator.platform);
|
||||
for (let manageTextEl of this._manageText.children) {
|
||||
manageTextEl.hidden = manageTextEl.dataset.os == "mac" ? !isMac : isMac;
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
import BasicCardOption from "../components/basic-card-option.js";
|
||||
import RichPicker from "./rich-picker.js";
|
||||
import paymentRequest from "../paymentRequest.js";
|
||||
/* import-globals-from ../unprivileged-fallbacks.js */
|
||||
|
||||
/**
|
||||
* <payment-method-picker></payment-method-picker>
|
||||
|
@ -93,7 +94,11 @@ export default class PaymentMethodPicker extends RichPicker {
|
|||
return true;
|
||||
}
|
||||
|
||||
let acceptedNetworks = paymentRequest.getAcceptedNetworks(state.request);
|
||||
let basicCardMethod = state.request.paymentMethods
|
||||
.find(method => method.supportedMethods == "basic-card");
|
||||
let merchantNetworks = basicCardMethod && basicCardMethod.data &&
|
||||
basicCardMethod.data.supportedNetworks;
|
||||
let acceptedNetworks = merchantNetworks || PaymentDialogUtils.getCreditCardNetworks();
|
||||
let selectedCard = paymentRequest.getBasicCards(state)[selectedOption.value];
|
||||
let isSupported = selectedCard["cc-type"] &&
|
||||
acceptedNetworks.includes(selectedCard["cc-type"]);
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
<button id="logState">Log state</button>
|
||||
<button id="debugFrame" hidden>Debug frame</button>
|
||||
<button id="toggleDirectionality">Toggle :dir</button>
|
||||
<button id="toggleBranding">Toggle branding</button>
|
||||
</section>
|
||||
<section class="group">
|
||||
<h1>Requests</h1>
|
||||
|
|
|
@ -519,6 +519,12 @@ let buttonActions = {
|
|||
let body = paymentDialog.ownerDocument.body;
|
||||
body.dir = body.dir == "rtl" ? "ltr" : "rtl";
|
||||
},
|
||||
|
||||
toggleBranding() {
|
||||
for (let container of paymentDialog.querySelectorAll("accepted-cards")) {
|
||||
container.classList.toggle("branded");
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
window.addEventListener("click", function onButtonClick(evt) {
|
||||
|
|
|
@ -281,18 +281,6 @@ var paymentRequest = {
|
|||
let cards = Object.assign({}, state.savedBasicCards, state.tempBasicCards);
|
||||
return cards;
|
||||
},
|
||||
|
||||
getAcceptedNetworks(request) {
|
||||
let basicCardMethod = request.paymentMethods
|
||||
.find(method => method.supportedMethods == "basic-card");
|
||||
let merchantNetworks = basicCardMethod && basicCardMethod.data &&
|
||||
basicCardMethod.data.supportedNetworks;
|
||||
if (merchantNetworks && merchantNetworks.length) {
|
||||
return merchantNetworks;
|
||||
}
|
||||
// fallback to the complete list if the merchant didn't specify
|
||||
return PaymentDialogUtils.getCreditCardNetworks();
|
||||
},
|
||||
};
|
||||
|
||||
paymentRequest.init();
|
||||
|
|
|
@ -103,4 +103,7 @@ var PaymentDialogUtils = {
|
|||
};
|
||||
return prefValues;
|
||||
},
|
||||
isOfficialBranding() {
|
||||
return false;
|
||||
},
|
||||
};
|
||||
|
|
|
@ -111,9 +111,10 @@ SpecialPowers.registerConsoleListener(function onConsoleMessage(msg) {
|
|||
// Ignore unknown CSP error.
|
||||
return;
|
||||
}
|
||||
if (msg.message.includes("Security Error: Content at http://mochi.test:8888")) {
|
||||
if (msg.message && msg.message.includes("Security Error: Content at http://mochi.test:8888")) {
|
||||
// Check for same-origin policy violations and ignore specific errors
|
||||
if (msg.message.includes("icon-credit-card-generic.svg") ||
|
||||
msg.message.includes("accepted-cards.css") ||
|
||||
msg.message.includes("editDialog-shared.css") ||
|
||||
msg.message.includes("editAddress.css") ||
|
||||
msg.message.includes("editDialog.css") ||
|
||||
|
|
|
@ -30,6 +30,8 @@ Test the accepted-cards element
|
|||
<script type="module">
|
||||
/** Test the accepted-cards component **/
|
||||
|
||||
/* global sinon, PaymentDialogUtils */
|
||||
|
||||
import "../../res/components/accepted-cards.js";
|
||||
import {requestStore} from "../../res/mixins/PaymentStateSubscriberMixin.js";
|
||||
let emptyState = requestStore.getState();
|
||||
|
@ -76,6 +78,35 @@ add_task(async function test_someAccepted() {
|
|||
}
|
||||
});
|
||||
|
||||
add_task(async function test_officialBranding() {
|
||||
// verify we get the expected result when isOfficialBranding returns true
|
||||
sinon.stub(PaymentDialogUtils, "isOfficialBranding").callsFake(() => { return true; });
|
||||
|
||||
let container = acceptedElem.parentNode;
|
||||
let removed = container.removeChild(acceptedElem);
|
||||
container.appendChild(removed);
|
||||
|
||||
ok(PaymentDialogUtils.isOfficialBranding.calledOnce,
|
||||
"isOfficialBranding was called");
|
||||
ok(acceptedElem.classList.contains("branded"),
|
||||
"The branded class is added when isOfficialBranding returns true");
|
||||
PaymentDialogUtils.isOfficialBranding.restore();
|
||||
|
||||
// verify we get the expected result when isOfficialBranding returns false
|
||||
sinon.stub(PaymentDialogUtils, "isOfficialBranding").callsFake(() => { return false; });
|
||||
|
||||
// the branded class is toggled in the 'connectedCallback',
|
||||
// so remove and re-add the element to re-evaluate branded-ness
|
||||
removed = container.removeChild(acceptedElem);
|
||||
container.appendChild(removed);
|
||||
|
||||
ok(PaymentDialogUtils.isOfficialBranding.calledOnce,
|
||||
"isOfficialBranding was called");
|
||||
ok(!acceptedElem.classList.contains("branded"),
|
||||
"The branded class is removed when isOfficialBranding returns false");
|
||||
PaymentDialogUtils.isOfficialBranding.restore();
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
|
|
@ -37,6 +37,13 @@ Test the basic-card-form element
|
|||
import BasicCardForm from "../../res/containers/basic-card-form.js";
|
||||
|
||||
let display = document.getElementById("display");
|
||||
let supportedNetworks = ["discover", "amex"];
|
||||
let paymentMethods = [{
|
||||
supportedMethods: "basic-card",
|
||||
data: {
|
||||
supportedNetworks,
|
||||
},
|
||||
}];
|
||||
|
||||
function checkCCForm(customEl, expectedCard) {
|
||||
const CC_PROPERTY_NAMES = [
|
||||
|
@ -117,7 +124,11 @@ add_task(async function test_saveButton() {
|
|||
let address2 = deepClone(PTU.Addresses.TimBL2);
|
||||
address2.guid = "TimBL2GUID";
|
||||
|
||||
|
||||
await form.requestStore.setState({
|
||||
request: {
|
||||
paymentMethods,
|
||||
},
|
||||
savedAddresses: {
|
||||
[address1.guid]: deepClone(address1),
|
||||
[address2.guid]: deepClone(address2),
|
||||
|
@ -126,6 +137,7 @@ add_task(async function test_saveButton() {
|
|||
|
||||
await asyncElementRendered();
|
||||
|
||||
// when merchant provides supportedNetworks, the accepted card list should be visible
|
||||
ok(!form.acceptedCardsList.hidden, "Accepted card list should be visible when adding a card");
|
||||
|
||||
ok(form.saveButton.disabled, "Save button should initially be disabled");
|
||||
|
@ -349,6 +361,9 @@ add_task(async function test_edit() {
|
|||
card1.billingAddressGUID = address1.guid;
|
||||
|
||||
await form.requestStore.setState({
|
||||
request: {
|
||||
paymentMethods,
|
||||
},
|
||||
page: {
|
||||
id: "basic-card-page",
|
||||
},
|
||||
|
@ -368,7 +383,7 @@ add_task(async function test_edit() {
|
|||
"Check no fields are visibly invalid on an 'edit' form with a complete card");
|
||||
checkCCForm(form, card1);
|
||||
ok(!form.saveButton.disabled, "Save button should be enabled upon edit for a valid card");
|
||||
ok(form.acceptedCardsList.hidden, "Accepted card list should be hidden when editing a card");
|
||||
ok(!form.acceptedCardsList.hidden, "Accepted card list should be visible when editing a card");
|
||||
|
||||
let requiredElements = [...form.form.elements].filter(e => e.required && !e.disabled);
|
||||
ok(requiredElements.length, "There should be at least one required element");
|
||||
|
|
Загрузка…
Ссылка в новой задаче