mirror of
https://github.com/woocommerce/woocommerce-paypal-payments.git
synced 2025-09-05 08:59:14 +08:00
🔀 Merge branch 'trunk'
This commit is contained in:
commit
964b9ac606
20 changed files with 483 additions and 122 deletions
|
@ -17,6 +17,7 @@ import {
|
|||
import {setVisibleByClass} from "./modules/Helper/Hiding";
|
||||
import {isChangePaymentPage} from "./modules/Helper/Subscriptions";
|
||||
import FreeTrialHandler from "./modules/ActionHandler/FreeTrialHandler";
|
||||
import MultistepCheckoutHelper from "./modules/Helper/MultistepCheckoutHelper";
|
||||
import FormSaver from './modules/Helper/FormSaver';
|
||||
import FormValidator from "./modules/Helper/FormValidator";
|
||||
import {loadPaypalScript} from "./modules/Helper/ScriptLoading";
|
||||
|
@ -53,6 +54,8 @@ const bootstrap = () => {
|
|||
|
||||
const freeTrialHandler = new FreeTrialHandler(PayPalCommerceGateway, checkoutFormSelector, formSaver, formValidator, spinner, errorHandler);
|
||||
|
||||
new MultistepCheckoutHelper(checkoutFormSelector);
|
||||
|
||||
jQuery('form.woocommerce-checkout input').on('keydown', e => {
|
||||
if (e.key === 'Enter' && [
|
||||
PaymentMethods.PAYPAL,
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
import { debounce } from '../../../../../ppcp-blocks/resources/js/Helper/debounce';
|
||||
|
||||
const REFRESH_BUTTON_EVENT = 'ppcp_refresh_payment_buttons';
|
||||
|
||||
/**
|
||||
* Triggers a refresh of the payment buttons.
|
||||
* This function dispatches a custom event that the button components listen for.
|
||||
*
|
||||
* Use this function on the front-end to update payment buttons after the checkout form was updated.
|
||||
*/
|
||||
export function refreshButtons() {
|
||||
document.dispatchEvent(new Event(REFRESH_BUTTON_EVENT));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up event listeners for various cart and checkout update events.
|
||||
* When these events occur, it triggers a refresh of the payment buttons.
|
||||
*
|
||||
* @param {Function} refresh - Callback responsible to re-render the payment button.
|
||||
*/
|
||||
export function setupButtonEvents(refresh) {
|
||||
const miniCartInitDelay = 1000;
|
||||
const debouncedRefresh = debounce(refresh, 50);
|
||||
|
||||
// Listen for our custom refresh event.
|
||||
document.addEventListener(REFRESH_BUTTON_EVENT, debouncedRefresh);
|
||||
|
||||
// Listen for cart and checkout update events.
|
||||
document.body.addEventListener('updated_cart_totals', debouncedRefresh);
|
||||
document.body.addEventListener('updated_checkout', debouncedRefresh);
|
||||
|
||||
// Use setTimeout for fragment events to avoid unnecessary refresh on initial render.
|
||||
setTimeout(() => {
|
||||
document.body.addEventListener('wc_fragments_loaded', debouncedRefresh);
|
||||
document.body.addEventListener('wc_fragments_refreshed', debouncedRefresh);
|
||||
}, miniCartInitDelay);
|
||||
}
|
|
@ -0,0 +1,120 @@
|
|||
import { refreshButtons } from './ButtonRefreshHelper';
|
||||
|
||||
const DEFAULT_TRIGGER_ELEMENT_SELECTOR = '.woocommerce-checkout-payment';
|
||||
|
||||
/**
|
||||
* The MultistepCheckoutHelper class ensures the initialization of payment buttons
|
||||
* on websites using a multistep checkout plugin. These plugins usually hide the
|
||||
* payment button on page load up and reveal it later using JS. During the
|
||||
* invisibility period of wrappers, some payment buttons fail to initialize,
|
||||
* so we wait for the payment element to be visible.
|
||||
*
|
||||
* @property {HTMLElement} form - Checkout form element.
|
||||
* @property {HTMLElement} triggerElement - Element, which visibility we need to detect.
|
||||
* @property {boolean} isVisible - Whether the triggerElement is visible.
|
||||
*/
|
||||
class MultistepCheckoutHelper {
|
||||
|
||||
/**
|
||||
* Selector that defines the HTML element we are waiting to become visible.
|
||||
* @type {string}
|
||||
*/
|
||||
#triggerElementSelector;
|
||||
|
||||
/**
|
||||
* Interval (in milliseconds) in which the visibility of the trigger element is checked.
|
||||
* @type {number}
|
||||
*/
|
||||
#intervalTime = 150;
|
||||
|
||||
/**
|
||||
* The interval ID returned by the setInterval() method.
|
||||
* @type {number|false}
|
||||
*/
|
||||
#intervalId;
|
||||
|
||||
/**
|
||||
* Selector passed to the constructor that identifies the checkout form
|
||||
* @type {string}
|
||||
*/
|
||||
#formSelector;
|
||||
|
||||
/**
|
||||
* @param {string} formSelector - Selector of the checkout form
|
||||
* @param {string} triggerElementSelector - Optional. Selector of the dependant element.
|
||||
*/
|
||||
constructor(formSelector, triggerElementSelector = '') {
|
||||
this.#formSelector = formSelector;
|
||||
this.#triggerElementSelector = triggerElementSelector || DEFAULT_TRIGGER_ELEMENT_SELECTOR;
|
||||
this.#intervalId = false;
|
||||
|
||||
/*
|
||||
Start the visibility checker after a brief delay. This allows eventual multistep plugins to
|
||||
dynamically prepare the checkout page, so we can decide whether this helper is needed.
|
||||
*/
|
||||
setTimeout(() => {
|
||||
if (this.form && !this.isVisible) {
|
||||
this.start();
|
||||
}
|
||||
}, 250);
|
||||
}
|
||||
|
||||
/**
|
||||
* The checkout form element.
|
||||
* @returns {Element|null} - Form element or null.
|
||||
*/
|
||||
get form() {
|
||||
return document.querySelector(this.#formSelector);
|
||||
}
|
||||
|
||||
/**
|
||||
* The element which must be visible before payment buttons should be initialized.
|
||||
* @returns {Element|null} - Trigger element or null.
|
||||
*/
|
||||
get triggerElement() {
|
||||
return this.form?.querySelector(this.#triggerElementSelector);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the visibility of the payment button wrapper.
|
||||
* @returns {boolean} - returns boolean value on the basis of visibility of element.
|
||||
*/
|
||||
get isVisible() {
|
||||
const box = this.triggerElement?.getBoundingClientRect();
|
||||
|
||||
return !!(box && box.width && box.height);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the observation of the DOM, initiates monitoring the checkout form.
|
||||
* To ensure multiple calls to start don't create multiple intervals, we first call stop.
|
||||
*/
|
||||
start() {
|
||||
this.stop();
|
||||
this.#intervalId = setInterval(() => this.checkElement(), this.#intervalTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the observation of the checkout form.
|
||||
* Multiple calls to stop are safe as clearInterval doesn't throw if provided ID doesn't exist.
|
||||
*/
|
||||
stop() {
|
||||
if (this.#intervalId) {
|
||||
clearInterval(this.#intervalId);
|
||||
this.#intervalId = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the trigger element is visible.
|
||||
* If visible, it initialises the payment buttons and stops the observation.
|
||||
*/
|
||||
checkElement() {
|
||||
if (this.isVisible) {
|
||||
refreshButtons();
|
||||
this.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default MultistepCheckoutHelper;
|
Loading…
Add table
Add a link
Reference in a new issue