Merge branch 'trunk' into PCP-1486-paylater-block

This commit is contained in:
Alex P 2023-12-20 09:00:57 +02:00
commit c83975f293
No known key found for this signature in database
GPG key ID: 54487A734A204D71
54 changed files with 2315 additions and 1600 deletions

View file

@ -22,6 +22,7 @@ import FormValidator from "./modules/Helper/FormValidator";
import {loadPaypalScript} from "./modules/Helper/ScriptLoading";
import buttonModuleWatcher from "./modules/ButtonModuleWatcher";
import MessagesBootstrap from "./modules/ContextBootstrap/MessagesBootstap";
import {apmButtonsInit} from "./modules/Helper/ApmButtons";
// TODO: could be a good idea to have a separate spinner for each gateway,
// but I think we care mainly about the script loading, so one spinner should be enough.
@ -145,6 +146,7 @@ const bootstrap = () => {
};
const onSmartButtonsInit = () => {
jQuery(document).trigger('ppcp-smart-buttons-init', this);
buttonsSpinner.unblock();
};
const renderer = new Renderer(creditCardRenderer, PayPalCommerceGateway, onSmartButtonClick, onSmartButtonsInit);
@ -217,6 +219,8 @@ const bootstrap = () => {
messageRenderer,
);
messagesBootstrap.init();
apmButtonsInit(PayPalCommerceGateway);
};
document.addEventListener(

View file

@ -2,6 +2,7 @@ import 'formdata-polyfill';
import onApprove from '../OnApproveHandler/onApproveForPayNow.js';
import {payerData} from "../Helper/PayerData";
import {getCurrentPaymentMethod} from "../Helper/CheckoutMethodState";
import validateCheckoutForm from "../Helper/CheckoutFormValidation";
class CheckoutActionHandler {
@ -13,7 +14,13 @@ class CheckoutActionHandler {
subscriptionsConfiguration() {
return {
createSubscription: (data, actions) => {
createSubscription: async (data, actions) => {
try {
await validateCheckoutForm(this.config);
} catch (error) {
throw {type: 'form-validation-error'};
}
return actions.subscription.create({
'plan_id': this.config.subscription_plan_id
});

View file

@ -0,0 +1,114 @@
export const apmButtonsInit = (config, selector = '.ppcp-button-apm') => {
let selectorInContainer = selector;
if (window.ppcpApmButtons) {
return;
}
if (config && config.button) {
// If it's separate gateways, modify wrapper to account for the individual buttons as individual APMs.
const wrapper = config.button.wrapper;
const isSeparateGateways = jQuery(wrapper).children('div[class^="item-"]').length > 0;
if (isSeparateGateways) {
selector += `, ${wrapper} div[class^="item-"]`;
selectorInContainer += `, div[class^="item-"]`;
}
}
window.ppcpApmButtons = new ApmButtons(selector, selectorInContainer);
}
export class ApmButtons {
constructor(selector, selectorInContainer) {
this.selector = selector;
this.selectorInContainer = selectorInContainer;
this.containers = [];
// Reloads button containers.
this.reloadContainers();
// Refresh button layout.
jQuery(window).resize(() => {
this.refresh();
}).resize();
jQuery(document).on('ppcp-smart-buttons-init', () => {
this.refresh();
});
// Observes for new buttons.
(new MutationObserver(this.observeElementsCallback.bind(this)))
.observe(document.body, { childList: true, subtree: true });
}
observeElementsCallback(mutationsList, observer) {
const observeSelector = this.selector + ', .widget_shopping_cart, .widget_shopping_cart_content';
let shouldReload = false;
for (let mutation of mutationsList) {
if (mutation.type === 'childList') {
mutation.addedNodes.forEach(node => {
if (node.matches && node.matches(observeSelector)) {
shouldReload = true;
}
});
}
}
if (shouldReload) {
this.reloadContainers();
this.refresh();
}
};
reloadContainers() {
jQuery(this.selector).each((index, el) => {
const parent = jQuery(el).parent();
if (!this.containers.some($el => $el.is(parent))) {
this.containers.push(parent);
}
});
console.log('this.containers', this.containers);
}
refresh() {
for (const container of this.containers) {
const $container = jQuery(container);
// Check width and add classes
const width = $container.width();
$container.removeClass('ppcp-width-500 ppcp-width-300 ppcp-width-min');
if (width >= 500) {
$container.addClass('ppcp-width-500');
} else if (width >= 300) {
$container.addClass('ppcp-width-300');
} else {
$container.addClass('ppcp-width-min');
}
// Check first apm button
const $firstElement = $container.children(':visible').first();
// Assign margins to buttons
$container.find(this.selectorInContainer).each((index, el) => {
const $el = jQuery(el);
if ($el.is($firstElement)) {
$el.css('margin-top', `0px`);
return true;
}
const height = $el.height();
$el.css('margin-top', `${Math.round(height * 0.3)}px`);
});
}
}
}

View file

@ -0,0 +1,50 @@
export const cardFieldStyles = (field) => {
const allowedProperties = [
'appearance',
'color',
'direction',
'font',
'font-family',
'font-size',
'font-size-adjust',
'font-stretch',
'font-style',
'font-variant',
'font-variant-alternates',
'font-variant-caps',
'font-variant-east-asian',
'font-variant-ligatures',
'font-variant-numeric',
'font-weight',
'letter-spacing',
'line-height',
'opacity',
'outline',
'padding',
'padding-bottom',
'padding-left',
'padding-right',
'padding-top',
'text-shadow',
'transition',
'-moz-appearance',
'-moz-osx-font-smoothing',
'-moz-tap-highlight-color',
'-moz-transition',
'-webkit-appearance',
'-webkit-osx-font-smoothing',
'-webkit-tap-highlight-color',
'-webkit-transition',
];
const stylesRaw = window.getComputedStyle(field);
const styles = {};
Object.values(stylesRaw).forEach((prop) => {
if (!stylesRaw[prop] || !allowedProperties.includes(prop)) {
return;
}
styles[prop] = '' + stylesRaw[prop];
});
return styles;
}

View file

@ -0,0 +1,48 @@
import Spinner from "./Spinner";
import FormValidator from "./FormValidator";
import ErrorHandler from "../ErrorHandler";
const validateCheckoutForm = function (config) {
return new Promise(async (resolve, reject) => {
try {
const spinner = new Spinner();
const errorHandler = new ErrorHandler(
config.labels.error.generic,
document.querySelector('.woocommerce-notices-wrapper')
);
const formSelector = config.context === 'checkout' ? 'form.checkout' : 'form#order_review';
const formValidator = config.early_checkout_validation_enabled ?
new FormValidator(
config.ajax.validate_checkout.endpoint,
config.ajax.validate_checkout.nonce,
) : null;
if (!formValidator) {
resolve();
return;
}
formValidator.validate(document.querySelector(formSelector)).then((errors) => {
if (errors.length > 0) {
spinner.unblock();
errorHandler.clear();
errorHandler.messages(errors);
// fire WC event for other plugins
jQuery( document.body ).trigger( 'checkout_error' , [ errorHandler.currentHtml() ] );
reject();
} else {
resolve();
}
});
} catch (error) {
console.error(error);
reject();
}
});
}
export default validateCheckoutForm;

View file

@ -89,3 +89,11 @@ export const loadPaypalJsScript = (options, buttons, container) => {
paypal.Buttons(buttons).render(container);
});
}
export const loadPaypalJsScriptPromise = (options) => {
return new Promise((resolve, reject) => {
loadScript(options)
.then(resolve)
.catch(reject);
});
}

View file

@ -1,4 +1,5 @@
import {show} from "../Helper/Hiding";
import {cardFieldStyles} from "../Helper/CardFieldsHelper";
class CardFieldsRenderer {
@ -53,28 +54,28 @@ class CardFieldsRenderer {
if (cardField.isEligible()) {
const nameField = document.getElementById('ppcp-credit-card-gateway-card-name');
if (nameField) {
let styles = this.cardFieldStyles(nameField);
let styles = cardFieldStyles(nameField);
cardField.NameField({style: {'input': styles}}).render(nameField.parentNode);
nameField.remove();
}
const numberField = document.getElementById('ppcp-credit-card-gateway-card-number');
if (numberField) {
let styles = this.cardFieldStyles(numberField);
let styles = cardFieldStyles(numberField);
cardField.NumberField({style: {'input': styles}}).render(numberField.parentNode);
numberField.remove();
}
const expiryField = document.getElementById('ppcp-credit-card-gateway-card-expiry');
if (expiryField) {
let styles = this.cardFieldStyles(expiryField);
let styles = cardFieldStyles(expiryField);
cardField.ExpiryField({style: {'input': styles}}).render(expiryField.parentNode);
expiryField.remove();
}
const cvvField = document.getElementById('ppcp-credit-card-gateway-card-cvc');
if (cvvField) {
let styles = this.cardFieldStyles(cvvField);
let styles = cardFieldStyles(cvvField);
cardField.CVVField({style: {'input': styles}}).render(cvvField.parentNode);
cvvField.remove();
}
@ -91,8 +92,8 @@ class CardFieldsRenderer {
this.spinner.block();
this.errorHandler.clear();
const paymentToken = document.querySelector('input[name="wc-ppcp-credit-card-gateway-payment-token"]:checked').value
if(paymentToken !== 'new') {
const paymentToken = document.querySelector('input[name="wc-ppcp-credit-card-gateway-payment-token"]:checked')?.value
if(paymentToken && paymentToken !== 'new') {
fetch(this.defaultConfig.ajax.capture_card_payment.endpoint, {
method: 'POST',
credentials: 'same-origin',
@ -118,57 +119,6 @@ class CardFieldsRenderer {
});
}
cardFieldStyles(field) {
const allowedProperties = [
'appearance',
'color',
'direction',
'font',
'font-family',
'font-size',
'font-size-adjust',
'font-stretch',
'font-style',
'font-variant',
'font-variant-alternates',
'font-variant-caps',
'font-variant-east-asian',
'font-variant-ligatures',
'font-variant-numeric',
'font-weight',
'letter-spacing',
'line-height',
'opacity',
'outline',
'padding',
'padding-bottom',
'padding-left',
'padding-right',
'padding-top',
'text-shadow',
'transition',
'-moz-appearance',
'-moz-osx-font-smoothing',
'-moz-tap-highlight-color',
'-moz-transition',
'-webkit-appearance',
'-webkit-osx-font-smoothing',
'-webkit-tap-highlight-color',
'-webkit-transition',
];
const stylesRaw = window.getComputedStyle(field);
const styles = {};
Object.values(stylesRaw).forEach((prop) => {
if (!stylesRaw[prop] || !allowedProperties.includes(prop)) {
return;
}
styles[prop] = '' + stylesRaw[prop];
});
return styles;
}
disableFields() {}
enableFields() {}
}