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:
Sam Foster 2018-09-28 16:53:37 +00:00
Родитель 45e7e08e6d
Коммит b6ecb8ed07
14 изменённых файлов: 110 добавлений и 42 удалений

Просмотреть файл

@ -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");