From f41fa4f951a58288dfee7d9e08ec3641bb4c1af1 Mon Sep 17 00:00:00 2001
From: Philipp Stracker
Date: Tue, 8 Oct 2024 18:47:32 +0200
Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Streamline=20button=20configuraton?=
=?UTF-8?q?=20validation=20logic?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../resources/js/ApplepayButton.js | 44 ++++++------
.../js/modules/Renderer/PaymentButton.js | 72 ++++++++++++++++++-
.../resources/js/GooglepayButton.js | 56 ++++++---------
3 files changed, 113 insertions(+), 59 deletions(-)
diff --git a/modules/ppcp-applepay/resources/js/ApplepayButton.js b/modules/ppcp-applepay/resources/js/ApplepayButton.js
index 3a7c8cdd7..89634747a 100644
--- a/modules/ppcp-applepay/resources/js/ApplepayButton.js
+++ b/modules/ppcp-applepay/resources/js/ApplepayButton.js
@@ -217,33 +217,31 @@ class ApplePayButton extends PaymentButton {
/**
* @inheritDoc
*/
- validateConfiguration( silent = false ) {
- const validEnvs = [ 'PRODUCTION', 'TEST' ];
+ registerValidationRules( invalidIf, validIf ) {
+ invalidIf(
+ () =>
+ [ 'TEST', 'PRODUCTION' ].includes(
+ this.buttonConfig.environment
+ ),
+ `Invalid environment: ${ this.buttonConfig.environment }`
+ );
- const isInvalid = ( ...args ) => {
- if ( ! silent ) {
- this.error( ...args );
- }
- return false;
- };
+ validIf( () => this.isPreview );
- if ( ! validEnvs.includes( this.buttonConfig.environment ) ) {
- return isInvalid(
- 'Invalid environment:',
- this.buttonConfig.environment
- );
- }
+ invalidIf(
+ () => ! this.#applePayConfig,
+ 'No API configuration - missing configure() call?'
+ );
- // Preview buttons only need a valid environment.
- if ( this.isPreview ) {
- return true;
- }
+ invalidIf(
+ () => ! this.#transactionInfo,
+ 'No transactionInfo - missing configure() call?'
+ );
- if ( ! typeof this.contextHandler?.validateContext() ) {
- return isInvalid( 'Invalid context handler.', this.contextHandler );
- }
-
- return true;
+ invalidIf(
+ () => ! this.contextHandler?.validateContext(),
+ `Invalid context handler.`
+ );
}
/**
diff --git a/modules/ppcp-button/resources/js/modules/Renderer/PaymentButton.js b/modules/ppcp-button/resources/js/modules/Renderer/PaymentButton.js
index 3ce35c9b5..35d938b34 100644
--- a/modules/ppcp-button/resources/js/modules/Renderer/PaymentButton.js
+++ b/modules/ppcp-button/resources/js/modules/Renderer/PaymentButton.js
@@ -192,6 +192,13 @@ export default class PaymentButton {
*/
#button = null;
+ /**
+ * List of checks to perform to verify the PaymentButton has is configured correctly.
+ *
+ * @type {{check, errorMessage, shouldPass}[]}
+ */
+ #validationChecks = [];
+
/**
* Factory method to create a new PaymentButton while limiting a single instance per context.
*
@@ -305,6 +312,11 @@ export default class PaymentButton {
);
this.applyButtonStyles( this.#buttonConfig );
+ this.registerValidationRules(
+ this.#assertIsInvalid.bind( this ),
+ this.#assertIsValid.bind( this )
+ );
+
apmButtonsInit( this.#ppcpConfig );
this.initEventListeners();
}
@@ -634,16 +646,74 @@ export default class PaymentButton {
this.#logger.group( label );
}
+ /**
+ * Register a validation check that marks the configuration as invalid when passed.
+ *
+ * @param {Function} check - A function that returns a truthy value if the check passes.
+ * @param {string} errorMessage - The error message to display if the check fails.
+ */
+ #assertIsInvalid( check, errorMessage ) {
+ this.#validationChecks.push( {
+ check,
+ errorMessage,
+ shouldPass: false,
+ } );
+ }
+
+ /**
+ * Register a validation check that instantly marks the configuration as valid when passed.
+ *
+ * @param {Function} check - A function that returns a truthy value if the check passes.
+ */
+ #assertIsValid( check ) {
+ this.#validationChecks.push( { check, shouldPass: true } );
+ }
+
+ /**
+ * Defines a series of validation steps to ensure the payment button is configured correctly.
+ *
+ * Each validation step is executed in the order they are defined within this method.
+ *
+ * If a validation step using `invalidIf` returns true, the configuration is immediately considered
+ * invalid, and an error message is logged. Conversely, if a validation step using `validIf`
+ * returns true, the configuration is immediately considered valid.
+ *
+ * If no validation step returns true, the configuration is assumed to be valid by default.
+ *
+ * @param {(condition: () => boolean, errorMessage: string) => void} invalidIf - Registers a validation step that fails if the condition returns true.
+ * @param {(condition: () => boolean) => void} validIf - Registers a validation step that passes if the condition returns true.
+ */
+ registerValidationRules( invalidIf, validIf ) {}
+
/**
* Determines if the current button instance has valid and complete configuration details.
* Used during initialization to decide if the button can be initialized or should be skipped.
*
- * Can be implemented by the derived class.
+ * All required validation steps must be registered in the constructor of the derived class
+ * using `this.addValidationFailure()` or `this.addValidationSuccess()`.
*
* @param {boolean} [silent=false] - Set to true to suppress console errors.
* @return {boolean} True indicates the config is valid and initialization can continue.
*/
validateConfiguration( silent = false ) {
+ for ( const step of this.#validationChecks ) {
+ const result = step.check();
+
+ if ( step.shouldPass && result ) {
+ // If a success check passes, mark as valid immediately.
+ return true;
+ }
+
+ if ( ! step.shouldPass && result ) {
+ // If a failure check passes, mark as invalid.
+ if ( ! silent && step.errorMessage ) {
+ this.error( step.errorMessage );
+ }
+
+ return false;
+ }
+ }
+
return true;
}
diff --git a/modules/ppcp-googlepay/resources/js/GooglepayButton.js b/modules/ppcp-googlepay/resources/js/GooglepayButton.js
index 97bfa6d2c..0cd2bab70 100644
--- a/modules/ppcp-googlepay/resources/js/GooglepayButton.js
+++ b/modules/ppcp-googlepay/resources/js/GooglepayButton.js
@@ -215,45 +215,31 @@ class GooglepayButton extends PaymentButton {
/**
* @inheritDoc
*/
- validateConfiguration( silent = false ) {
- const validEnvs = [ 'PRODUCTION', 'TEST' ];
+ registerValidationRules( invalidIf, validIf ) {
+ invalidIf(
+ () =>
+ [ 'TEST', 'PRODUCTION' ].includes(
+ this.buttonConfig.environment
+ ),
+ `Invalid environment: ${ this.buttonConfig.environment }`
+ );
- const isInvalid = ( ...args ) => {
- if ( ! silent ) {
- this.error( ...args );
- }
- return false;
- };
+ validIf( () => this.isPreview );
- if ( ! validEnvs.includes( this.buttonConfig.environment ) ) {
- return isInvalid(
- 'Invalid environment:',
- this.buttonConfig.environment
- );
- }
+ invalidIf(
+ () => ! this.googlePayConfig,
+ 'No API configuration - missing configure() call?'
+ );
- // Preview buttons only need a valid environment.
- if ( this.isPreview ) {
- return true;
- }
+ invalidIf(
+ () => ! this.transactionInfo,
+ 'No transactionInfo - missing configure() call?'
+ );
- if ( ! this.googlePayConfig ) {
- return isInvalid(
- 'No API configuration - missing configure() call?'
- );
- }
-
- if ( ! this.transactionInfo ) {
- return isInvalid(
- 'No transactionInfo - missing configure() call?'
- );
- }
-
- if ( ! typeof this.contextHandler?.validateContext() ) {
- return isInvalid( 'Invalid context handler.', this.contextHandler );
- }
-
- return true;
+ invalidIf(
+ () => ! this.contextHandler?.validateContext(),
+ `Invalid context handler.`
+ );
}
/**