mirror of
https://github.com/woocommerce/woocommerce-paypal-payments.git
synced 2025-09-06 13:44:42 +08:00
♻️ Simplify PaymentButton creation code
This commit is contained in:
parent
8c811d9f8e
commit
95c7d4f7bc
2 changed files with 237 additions and 108 deletions
|
@ -26,22 +26,53 @@ import {
|
||||||
* @property {string} SmartButton - Wrapper for smart button container.
|
* @property {string} SmartButton - Wrapper for smart button container.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the provided PaymentButton instance to a global payment-button collection.
|
||||||
|
*
|
||||||
|
* This is debugging logic that should not be used on a production site.
|
||||||
|
*
|
||||||
|
* @param {string} methodName - Used to group the buttons.
|
||||||
|
* @param {PaymentButton} button - Appended to the button collection.
|
||||||
|
*/
|
||||||
|
const addToDebuggingCollection = ( methodName, button ) => {
|
||||||
|
window.ppcpPaymentButtonList = window.ppcpPaymentButtonList || {};
|
||||||
|
|
||||||
|
const collection = window.ppcpPaymentButtonList;
|
||||||
|
|
||||||
|
collection[ methodName ] = collection[ methodName ] || [];
|
||||||
|
collection[ methodName ].push( button );
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class for APM payment buttons, like GooglePay and ApplePay.
|
* Base class for APM payment buttons, like GooglePay and ApplePay.
|
||||||
*
|
*
|
||||||
* This class is not intended for the PayPal button.
|
* This class is not intended for the PayPal button.
|
||||||
*/
|
*/
|
||||||
export default class PaymentButton {
|
export default class PaymentButton {
|
||||||
|
/**
|
||||||
|
* Defines the implemented payment method.
|
||||||
|
*
|
||||||
|
* Used to identify and address the button internally.
|
||||||
|
* Overwrite this in the derived class.
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
static methodId = 'generic';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CSS class that is added to the payment button wrapper.
|
||||||
|
*
|
||||||
|
* Overwrite this in the derived class.
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
static cssClass = '';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {ConsoleLogger}
|
* @type {ConsoleLogger}
|
||||||
*/
|
*/
|
||||||
#logger;
|
#logger;
|
||||||
|
|
||||||
/**
|
|
||||||
* @type {string}
|
|
||||||
*/
|
|
||||||
#methodId;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether the payment button is initialized.
|
* Whether the payment button is initialized.
|
||||||
*
|
*
|
||||||
|
@ -70,7 +101,7 @@ export default class PaymentButton {
|
||||||
#styles;
|
#styles;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* APM relevant configuration; e.g., configuration of the GooglePay button
|
* APM relevant configuration; e.g., configuration of the GooglePay button.
|
||||||
*/
|
*/
|
||||||
#buttonConfig;
|
#buttonConfig;
|
||||||
|
|
||||||
|
@ -101,38 +132,72 @@ export default class PaymentButton {
|
||||||
*/
|
*/
|
||||||
#button = null;
|
#button = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list with all wrapper IDs for the implemented payment method, categorized by context.
|
||||||
|
*
|
||||||
|
* @abstract
|
||||||
|
* @param {Object} buttonConfig - Payment method specific configuration.
|
||||||
|
* @param {Object} ppcpConfig - Global plugin configuration.
|
||||||
|
* @return {{MiniCart, Gateway, Block, SmartButton, Default}} The wrapper ID collection.
|
||||||
|
*/
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
static getWrappers( buttonConfig, ppcpConfig ) {
|
||||||
|
throw new Error( 'Must be implemented in the child class' );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of all button styles for the implemented payment method, categorized by context.
|
||||||
|
*
|
||||||
|
* @abstract
|
||||||
|
* @param {Object} buttonConfig - Payment method specific configuration.
|
||||||
|
* @param {Object} ppcpConfig - Global plugin configuration.
|
||||||
|
* @return {{MiniCart: (*), Default: (*)}} Combined styles, separated by context.
|
||||||
|
*/
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
static getStyles( buttonConfig, ppcpConfig ) {
|
||||||
|
throw new Error( 'Must be implemented in the child class' );
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the payment button instance.
|
* Initialize the payment button instance.
|
||||||
*
|
*
|
||||||
* @param {string} methodId - Payment method ID (slug, e.g., "ppcp-googlepay").
|
* Do not create new button instances directly; use the `createButton` method instead
|
||||||
|
* to avoid multiple button instances handling the same context.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
* @param {string} context - Button context name.
|
* @param {string} context - Button context name.
|
||||||
* @param {WrapperCollection} wrappers - Button wrapper IDs, by context.
|
|
||||||
* @param {StylesCollection} styles - Button styles, by context.
|
|
||||||
* @param {Object} buttonConfig - Payment button specific configuration.
|
* @param {Object} buttonConfig - Payment button specific configuration.
|
||||||
* @param {Object} ppcpConfig - Plugin wide configuration object.
|
* @param {Object} ppcpConfig - Plugin wide configuration object.
|
||||||
*/
|
*/
|
||||||
constructor(
|
constructor( context, buttonConfig, ppcpConfig ) {
|
||||||
methodId,
|
if ( this.methodId === PaymentButton.methodId ) {
|
||||||
context,
|
throw new Error( 'Cannot initialize the PaymentButton base class' );
|
||||||
wrappers,
|
}
|
||||||
styles,
|
|
||||||
buttonConfig,
|
|
||||||
ppcpConfig
|
|
||||||
) {
|
|
||||||
const methodName = methodId.replace( /^ppcp?-/, '' );
|
|
||||||
|
|
||||||
this.#methodId = methodId;
|
const isDebugging = !! buttonConfig?.is_debug;
|
||||||
|
const methodName = this.methodId.replace( /^ppcp?-/, '' );
|
||||||
this.#logger = new ConsoleLogger( methodName, context );
|
|
||||||
this.#logger.enabled = !! buttonConfig?.is_debug;
|
|
||||||
|
|
||||||
this.#context = context;
|
this.#context = context;
|
||||||
this.#wrappers = wrappers;
|
|
||||||
this.#styles = styles;
|
|
||||||
this.#buttonConfig = buttonConfig;
|
this.#buttonConfig = buttonConfig;
|
||||||
this.#ppcpConfig = ppcpConfig;
|
this.#ppcpConfig = ppcpConfig;
|
||||||
|
|
||||||
apmButtonsInit( ppcpConfig );
|
this.#wrappers = this.constructor.getWrappers(
|
||||||
|
this.#buttonConfig,
|
||||||
|
this.#ppcpConfig
|
||||||
|
);
|
||||||
|
this.#styles = this.constructor.getStyles(
|
||||||
|
this.#buttonConfig,
|
||||||
|
this.#ppcpConfig
|
||||||
|
);
|
||||||
|
|
||||||
|
this.#logger = new ConsoleLogger( methodName, context );
|
||||||
|
|
||||||
|
if ( isDebugging ) {
|
||||||
|
this.#logger.enabled = true;
|
||||||
|
addToDebuggingCollection( methodName, this );
|
||||||
|
}
|
||||||
|
|
||||||
|
apmButtonsInit( this.#ppcpConfig );
|
||||||
this.initEventListeners();
|
this.initEventListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,10 +205,20 @@ export default class PaymentButton {
|
||||||
* Internal ID of the payment gateway.
|
* Internal ID of the payment gateway.
|
||||||
*
|
*
|
||||||
* @readonly
|
* @readonly
|
||||||
* @return {string} The internal gateway ID.
|
* @return {string} The internal gateway ID, defined in the derived class.
|
||||||
*/
|
*/
|
||||||
get methodId() {
|
get methodId() {
|
||||||
return this.#methodId;
|
return this.constructor.methodId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CSS class that is added to the button wrapper.
|
||||||
|
*
|
||||||
|
* @readonly
|
||||||
|
* @return {string} CSS class, defined in the derived class.
|
||||||
|
*/
|
||||||
|
get cssClass() {
|
||||||
|
return this.constructor.cssClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -168,6 +243,24 @@ export default class PaymentButton {
|
||||||
return this.#context;
|
return this.#context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration, specific for the implemented payment button.
|
||||||
|
*
|
||||||
|
* @return {Object} Configuration object.
|
||||||
|
*/
|
||||||
|
get buttonConfig() {
|
||||||
|
return this.#buttonConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plugin-wide configuration; i.e., PayPal client ID, shop currency, etc.
|
||||||
|
*
|
||||||
|
* @return {Object} Configuration object.
|
||||||
|
*/
|
||||||
|
get ppcpConfig() {
|
||||||
|
return this.#ppcpConfig;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Button wrapper details.
|
* Button wrapper details.
|
||||||
*
|
*
|
||||||
|
@ -380,6 +473,10 @@ export default class PaymentButton {
|
||||||
}
|
}
|
||||||
|
|
||||||
triggerRedraw() {
|
triggerRedraw() {
|
||||||
|
if ( this.isEligible && this.isSeparateGateway ) {
|
||||||
|
this.showPaymentGateway();
|
||||||
|
}
|
||||||
|
|
||||||
dispatchButtonEvent( {
|
dispatchButtonEvent( {
|
||||||
event: ButtonEvents.REDRAW,
|
event: ButtonEvents.REDRAW,
|
||||||
paymentMethod: this.methodId,
|
paymentMethod: this.methodId,
|
||||||
|
@ -418,46 +515,75 @@ export default class PaymentButton {
|
||||||
* Refreshes the payment button on the page.
|
* Refreshes the payment button on the page.
|
||||||
*/
|
*/
|
||||||
refresh() {
|
refresh() {
|
||||||
const showButtonWrapper = () => {
|
if ( ! this.isPresent ) {
|
||||||
this.log( 'Show' );
|
return;
|
||||||
|
|
||||||
const styleSelectors = `style[data-hide-gateway="${ this.methodId }"]`;
|
|
||||||
|
|
||||||
document
|
|
||||||
.querySelectorAll( styleSelectors )
|
|
||||||
.forEach( ( el ) => el.remove() );
|
|
||||||
|
|
||||||
this.allElements.forEach( ( element ) => {
|
|
||||||
element.style.display = 'block';
|
|
||||||
} );
|
|
||||||
};
|
|
||||||
|
|
||||||
const hideButtonWrapper = () => {
|
|
||||||
this.log( 'Hide' );
|
|
||||||
|
|
||||||
this.allElements.forEach( ( element ) => {
|
|
||||||
element.style.display = 'none';
|
|
||||||
} );
|
|
||||||
};
|
|
||||||
|
|
||||||
// Refresh or hide the actual payment button.
|
|
||||||
if ( this.isVisible ) {
|
|
||||||
this.addButton();
|
|
||||||
} else {
|
|
||||||
this.removeButton();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show the wrapper or gateway entry, i.e. add space for the button.
|
this.applyWrapperStyles();
|
||||||
if ( this.isEligible && this.isPresent ) {
|
|
||||||
showButtonWrapper();
|
// Refresh or hide the actual payment button.
|
||||||
|
if ( this.isEligible && this.isPresent && this.isVisible ) {
|
||||||
|
this.addButton();
|
||||||
} else {
|
} else {
|
||||||
hideButtonWrapper();
|
// this.removeButton();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes the custom payment gateway visible by removing initial inline styles from the DOM.
|
||||||
|
*
|
||||||
|
* Only relevant on the checkout page, i.e., when `this.isSeparateGateway` is `true`
|
||||||
|
*/
|
||||||
|
showPaymentGateway() {
|
||||||
|
const styleSelectors = `style[data-hide-gateway="${ this.methodId }"]`;
|
||||||
|
|
||||||
|
const styles = document.querySelectorAll( styleSelectors );
|
||||||
|
|
||||||
|
if ( ! styles.length ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.log( 'Show gateway' );
|
||||||
|
|
||||||
|
styles.forEach( ( el ) => el.remove() );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies CSS classes and inline styling to the payment button wrapper.
|
||||||
|
*/
|
||||||
|
applyWrapperStyles() {
|
||||||
|
const wrapper = this.wrapperElement;
|
||||||
|
const { shape, height } = this.style;
|
||||||
|
|
||||||
|
wrapper.classList.add(
|
||||||
|
`ppcp-button-${ shape }`,
|
||||||
|
'ppcp-button-apm',
|
||||||
|
this.cssClass
|
||||||
|
);
|
||||||
|
|
||||||
|
if ( height ) {
|
||||||
|
wrapper.style.height = `${ height }px`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply the wrapper visibility.
|
||||||
|
wrapper.style.display = this.isVisible ? 'block' : 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new payment button (HTMLElement) and must call `this.insertButton()` to display
|
||||||
|
* that button in the correct wrapper.
|
||||||
|
*
|
||||||
|
* @abstract
|
||||||
|
*/
|
||||||
|
addButton() {
|
||||||
|
throw new Error( 'Must be implemented by the child class' );
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prepares the button wrapper element and inserts the provided payment button into the DOM.
|
* Prepares the button wrapper element and inserts the provided payment button into the DOM.
|
||||||
*
|
*
|
||||||
|
* If a payment button was previously inserted to the wrapper, calling this method again will
|
||||||
|
* first remove the previous button.
|
||||||
|
*
|
||||||
* @param {HTMLElement} button - The button element to inject.
|
* @param {HTMLElement} button - The button element to inject.
|
||||||
*/
|
*/
|
||||||
insertButton( button ) {
|
insertButton( button ) {
|
||||||
|
@ -465,28 +591,16 @@ export default class PaymentButton {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.log( 'addButton', button );
|
||||||
|
const wrapper = this.wrapperElement;
|
||||||
|
|
||||||
if ( this.#button ) {
|
if ( this.#button ) {
|
||||||
this.#button.remove();
|
this.log( 'addButton.removePrevious', this.#button );
|
||||||
|
wrapper.removeChild( this.#button );
|
||||||
}
|
}
|
||||||
|
|
||||||
this.#button = button;
|
this.#button = button;
|
||||||
this.log( 'addButton', button );
|
wrapper.appendChild( this.#button );
|
||||||
|
|
||||||
const wrapper = this.wrapperElement;
|
|
||||||
const { shape, height } = this.style;
|
|
||||||
const methodSlug = this.methodId.replace( /^ppcp?-/, '' );
|
|
||||||
|
|
||||||
wrapper.classList.add(
|
|
||||||
`ppcp-button-${ shape }`,
|
|
||||||
'ppcp-button-apm',
|
|
||||||
`ppcp-button-${ methodSlug }`
|
|
||||||
);
|
|
||||||
|
|
||||||
if ( height ) {
|
|
||||||
wrapper.style.height = `${ height }px`;
|
|
||||||
}
|
|
||||||
|
|
||||||
wrapper.appendChild( button );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -500,8 +614,10 @@ export default class PaymentButton {
|
||||||
this.log( 'removeButton' );
|
this.log( 'removeButton' );
|
||||||
|
|
||||||
if ( this.#button ) {
|
if ( this.#button ) {
|
||||||
this.#button.remove();
|
const wrapper = this.wrapperElement;
|
||||||
}
|
wrapper.removeChild( this.#button );
|
||||||
|
|
||||||
this.#button = null;
|
this.#button = null;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
/* global google */
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
combineStyles,
|
combineStyles,
|
||||||
combineWrapperIds,
|
combineWrapperIds,
|
||||||
|
@ -49,11 +47,47 @@ import { PaymentMethods } from '../../../ppcp-button/resources/js/modules/Helper
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class GooglepayButton extends PaymentButton {
|
class GooglepayButton extends PaymentButton {
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
static methodId = PaymentMethods.GOOGLEPAY;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
static cssClass = 'google-pay';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Client reference, provided by the Google Pay JS SDK.
|
* Client reference, provided by the Google Pay JS SDK.
|
||||||
*/
|
*/
|
||||||
#paymentsClient = null;
|
#paymentsClient = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
static getWrappers( buttonConfig, ppcpConfig ) {
|
||||||
|
return combineWrapperIds(
|
||||||
|
buttonConfig.button.wrapper,
|
||||||
|
buttonConfig.button.mini_cart_wrapper,
|
||||||
|
ppcpConfig.button.wrapper,
|
||||||
|
'express-payment-method-ppcp-googlepay',
|
||||||
|
'ppc-button-ppcp-googlepay'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
static getStyles( buttonConfig, ppcpConfig ) {
|
||||||
|
const styles = combineStyles( ppcpConfig.button, buttonConfig.button );
|
||||||
|
|
||||||
|
if ( 'buy' === styles.MiniCart.type ) {
|
||||||
|
styles.MiniCart.type = 'pay';
|
||||||
|
}
|
||||||
|
|
||||||
|
return styles;
|
||||||
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
context,
|
context,
|
||||||
externalHandler,
|
externalHandler,
|
||||||
|
@ -61,30 +95,8 @@ class GooglepayButton extends PaymentButton {
|
||||||
ppcpConfig,
|
ppcpConfig,
|
||||||
contextHandler
|
contextHandler
|
||||||
) {
|
) {
|
||||||
const wrappers = combineWrapperIds(
|
super( context, buttonConfig, ppcpConfig );
|
||||||
buttonConfig.button.wrapper,
|
|
||||||
buttonConfig.button.mini_cart_wrapper,
|
|
||||||
ppcpConfig.button.wrapper,
|
|
||||||
'express-payment-method-ppcp-googlepay',
|
|
||||||
'ppc-button-ppcp-googlepay'
|
|
||||||
);
|
|
||||||
|
|
||||||
const styles = combineStyles( ppcpConfig.button, buttonConfig.button );
|
|
||||||
|
|
||||||
if ( 'buy' === styles.MiniCart.type ) {
|
|
||||||
styles.MiniCart.type = 'pay';
|
|
||||||
}
|
|
||||||
|
|
||||||
super(
|
|
||||||
PaymentMethods.GOOGLEPAY,
|
|
||||||
context,
|
|
||||||
wrappers,
|
|
||||||
styles,
|
|
||||||
buttonConfig,
|
|
||||||
ppcpConfig
|
|
||||||
);
|
|
||||||
|
|
||||||
this.buttonConfig = buttonConfig;
|
|
||||||
this.contextHandler = contextHandler;
|
this.contextHandler = contextHandler;
|
||||||
|
|
||||||
this.log( 'Create instance' );
|
this.log( 'Create instance' );
|
||||||
|
@ -226,10 +238,11 @@ class GooglepayButton extends PaymentButton {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a Google Pay purchase button.
|
* Creates the payment button and calls `this.insertButton()` to make the button visible in the
|
||||||
|
* correct wrapper.
|
||||||
*/
|
*/
|
||||||
addButton() {
|
addButton() {
|
||||||
if ( ! this.isInitialized ) {
|
if ( ! this.isInitialized || ! this.paymentsClient ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue