Add FieldDisplayManager.

Refactor admin field show/hide.
This commit is contained in:
Pedro Silva 2023-09-14 17:48:46 +01:00
parent d3a02e79c5
commit 0cddf83b9d
No known key found for this signature in database
GPG key ID: E2EE20C0669D24B3
13 changed files with 320 additions and 100 deletions

View file

@ -12,6 +12,7 @@ namespace WooCommerce\PayPalCommerce\Applepay;
use WooCommerce\PayPalCommerce\Applepay\Assets\PropertiesDictionary;
use WooCommerce\PayPalCommerce\Onboarding\State;
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
use WooCommerce\PayPalCommerce\WcGateway\Helper\FieldDisplayManager;
return array(
@ -24,6 +25,9 @@ return array(
return array_merge( array_slice( $array, 0, $pos ), $new, array_slice( $array, $pos ) );
};
$fields_manager = $container->get( 'wcgateway.field-display-manager' );
assert( $fields_manager instanceof FieldDisplayManager );
return $insert_after(
$fields,
'allow_card_button_gateway',
@ -45,15 +49,17 @@ return array(
'gateway' => 'paypal',
'requirements' => array(),
'custom_attributes' => array(
'data-ppcp-handlers' => wp_json_encode(
'data-ppcp-display' => wp_json_encode(
array(
array(
'handler' => 'SubElementsHandler',
'options' => array(
'values' => array( '1' ),
'elements' => array( '#field-applepay_sandbox_validation_file', '#field-applepay_live_validation_file', '#field-applepay_button_color', '#field-applepay_button_type', '#field-applepay_button_language' ),
),
),
$fields_manager
->rule()
->condition( 'applepay_button_enabled', 'equals', '1' )
->action( 'applepay_sandbox_validation_file', 'visible' )
->action( 'applepay_live_validation_file', 'visible' )
->action( 'applepay_button_color', 'visible' )
->action( 'applepay_button_type', 'visible' )
->action( 'applepay_button_language', 'visible' )
->to_array(),
)
),
),

View file

@ -12,6 +12,7 @@ namespace WooCommerce\PayPalCommerce\Googlepay;
use WooCommerce\PayPalCommerce\Googlepay\Helper\PropertiesDictionary;
use WooCommerce\PayPalCommerce\Onboarding\State;
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
use WooCommerce\PayPalCommerce\WcGateway\Helper\FieldDisplayManager;
return array(
@ -31,6 +32,9 @@ return array(
return array_merge( array_slice( $array, 0, $pos ), $new, array_slice( $array, $pos ) );
};
$fields_manager = $container->get( 'wcgateway.field-display-manager' );
assert( $fields_manager instanceof FieldDisplayManager );
return $insert_after(
$fields,
'allow_card_button_gateway',
@ -52,20 +56,16 @@ return array(
'gateway' => 'paypal',
'requirements' => array(),
'custom_attributes' => array(
'data-ppcp-handlers' => wp_json_encode(
'data-ppcp-display' => wp_json_encode(
array(
array(
'handler' => 'SubElementsHandler',
'options' => array(
'values' => array( '1' ),
'elements' => array(
'#field-googlepay_button_color',
'#field-googlepay_button_type',
'#field-googlepay_button_language',
'#field-googlepay_button_shipping_enabled',
),
),
),
$fields_manager
->rule()
->condition( 'googlepay_button_enabled', 'equals', '1' )
->action( 'googlepay_button_type', 'visible' )
->action( 'googlepay_button_color', 'visible' )
->action( 'googlepay_button_language', 'visible' )
->action( 'googlepay_button_shipping_enabled', 'visible' )
->to_array(),
)
),
),

View file

@ -0,0 +1,11 @@
.ppcp-field-hidden {
display: none !important;
}
.ppcp-field-disabled {
cursor: not-allowed;
-webkit-filter: grayscale(100%);
filter: grayscale(100%);
opacity: 0.5;
}

View file

@ -1,51 +0,0 @@
class SubElementsHandler {
constructor(element, options) {
const fieldSelector = 'input, select, textarea';
this.element = element;
this.values = options.values;
this.elements = options.elements;
this.elementsSelector = this.elements.join(',');
this.input = jQuery(this.element).is(fieldSelector)
? this.element
: jQuery(this.element).find(fieldSelector).get(0);
this.updateElementsVisibility();
jQuery(this.input).change(() => {
this.updateElementsVisibility();
});
}
updateElementsVisibility() {
const $elements = jQuery(this.elementsSelector);
let value = this.getValue(this.input);
value = (value !== null ? value.toString() : value);
if (this.values.indexOf(value) !== -1) {
$elements.show();
} else {
$elements.hide();
}
}
getValue(element) {
const $el = jQuery(element);
if ($el.is(':checkbox') || $el.is(':radio')) {
if ($el.is(':checked')) {
return $el.val();
} else {
return null;
}
} else {
return $el.val();
}
}
}
export default SubElementsHandler;

View file

@ -1,10 +1,24 @@
import FieldDisplayManager from "./common/FieldDisplayManager";
import moveWrappedElements from "./common/wrapped-elements";
document.addEventListener(
'DOMContentLoaded',
() => {
// Wait for current execution context to end.
setTimeout(function () {
moveWrappedElements();
}, 0);
// Initialize FieldDisplayManager.
const fieldDisplayManager = new FieldDisplayManager();
jQuery( '*[data-ppcp-display]' ).each( (index, el) => {
const rules = jQuery(el).data('ppcpDisplay');
for (const rule of rules) {
fieldDisplayManager.addRule(rule);
}
});
}
);

View file

@ -0,0 +1,74 @@
class FieldDisplayManager {
constructor() {
this.rules = [];
document.ppcpDisplayManagerLog = () => {
console.log('rules', this.rules);
}
}
addRule(rule) {
this.rules.push(rule);
for (const condition of rule.conditions) {
jQuery(document).on('change', condition.selector, () => {
this.updateElementsVisibility(condition, rule);
});
this.updateElementsVisibility(condition, rule);
}
}
updateElementsVisibility(condition, rule) {
let value = this.getValue(condition.selector);
value = (value !== null ? value.toString() : value);
if (condition.value === value) {
for (const action of rule.actions) {
if (action.action === 'visible') {
jQuery(action.selector).removeClass('ppcp-field-hidden');
}
if (action.action === 'enable') {
jQuery(action.selector).removeClass('ppcp-field-disabled')
.off('mouseup')
.find('> *')
.css('pointer-events', '');
}
}
} else {
for (const action of rule.actions) {
if (action.action === 'visible') {
jQuery(action.selector).addClass('ppcp-field-hidden');
}
if (action.action === 'enable') {
jQuery(action.selector).addClass('ppcp-field-disabled')
.on('mouseup', function(event) {
event.stopImmediatePropagation();
})
.find('> *')
.css('pointer-events', 'none');
}
}
}
}
getValue(element) {
const $el = jQuery(element);
if ($el.is(':checkbox') || $el.is(':radio')) {
if ($el.is(':checked')) {
return $el.val();
} else {
return null;
}
} else {
return $el.val();
}
}
}
export default FieldDisplayManager;

View file

@ -4,7 +4,6 @@ import Renderer from '../../../ppcp-button/resources/js/modules/Renderer/Rendere
import MessageRenderer from "../../../ppcp-button/resources/js/modules/Renderer/MessageRenderer";
import {setVisibleByClass, isVisible} from "../../../ppcp-button/resources/js/modules/Helper/Hiding";
import widgetBuilder from "../../../ppcp-button/resources/js/modules/Renderer/WidgetBuilder";
import SubElementsHandler from "./SettingsHandler/SubElementsHandler";
document.addEventListener(
'DOMContentLoaded',
@ -308,16 +307,5 @@ document.addEventListener(
createButtonPreview(() => getButtonDefaultSettings('#ppcpPayLaterButtonPreview'));
});
}
// Generic behaviours, can be moved to common.js once it's on trunk branch.
jQuery( '*[data-ppcp-handlers]' ).each( (index, el) => {
const handlers = jQuery(el).data('ppcpHandlers');
for (const handlerConfig of handlers) {
new {
SubElementsHandler: SubElementsHandler
}[handlerConfig.handler](el, handlerConfig.options)
}
});
}
);

View file

@ -11,21 +11,18 @@ declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\WcGateway;
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\BillingAgreementsEndpoint;
use WooCommerce\PayPalCommerce\Session\SessionHandler;
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
use WooCommerce\PayPalCommerce\ApiClient\Authentication\Bearer;
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PayUponInvoiceOrderEndpoint;
use WooCommerce\PayPalCommerce\ApiClient\Entity\ApplicationContext;
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
use WooCommerce\PayPalCommerce\ApiClient\Helper\Cache;
use WooCommerce\PayPalCommerce\ApiClient\Helper\DccApplies;
use WooCommerce\PayPalCommerce\Button\Helper\MessagesApply;
use WooCommerce\PayPalCommerce\Button\Helper\MessagesDisclaimers;
use WooCommerce\PayPalCommerce\Common\Pattern\SingletonDecorator;
use WooCommerce\PayPalCommerce\Onboarding\Environment;
use WooCommerce\PayPalCommerce\Onboarding\Render\OnboardingOptionsRenderer;
use WooCommerce\PayPalCommerce\Onboarding\State;
use WooCommerce\PayPalCommerce\Subscription\Helper\SubscriptionHelper;
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
use WooCommerce\PayPalCommerce\WcGateway\Admin\FeesRenderer;
use WooCommerce\PayPalCommerce\WcGateway\Admin\OrderTablePaymentStatusColumn;
use WooCommerce\PayPalCommerce\WcGateway\Admin\PaymentStatusOrderDetail;
@ -35,6 +32,9 @@ use WooCommerce\PayPalCommerce\WcGateway\Checkout\CheckoutPayPalAddressPreset;
use WooCommerce\PayPalCommerce\WcGateway\Checkout\DisableGateways;
use WooCommerce\PayPalCommerce\WcGateway\Cli\SettingsCommand;
use WooCommerce\PayPalCommerce\WcGateway\Endpoint\ReturnUrlEndpoint;
use WooCommerce\PayPalCommerce\WcGateway\FraudNet\FraudNet;
use WooCommerce\PayPalCommerce\WcGateway\FraudNet\FraudNetSessionId;
use WooCommerce\PayPalCommerce\WcGateway\FraudNet\FraudNetSourceWebsiteId;
use WooCommerce\PayPalCommerce\WcGateway\FundingSource\FundingSourceRenderer;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CardButtonGateway;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway;
@ -42,15 +42,13 @@ use WooCommerce\PayPalCommerce\WcGateway\Gateway\GatewayRepository;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\OXXO\OXXO;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\OXXO\OXXOGateway;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
use WooCommerce\PayPalCommerce\WcGateway\FraudNet\FraudNet;
use WooCommerce\PayPalCommerce\WcGateway\FraudNet\FraudNetSessionId;
use WooCommerce\PayPalCommerce\WcGateway\FraudNet\FraudNetSourceWebsiteId;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayUponInvoice\PaymentSourceFactory;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayUponInvoice\PayUponInvoice;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayUponInvoice\PayUponInvoiceGateway;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\TransactionUrlProvider;
use WooCommerce\PayPalCommerce\WcGateway\Helper\CheckoutHelper;
use WooCommerce\PayPalCommerce\WcGateway\Helper\DCCProductStatus;
use WooCommerce\PayPalCommerce\WcGateway\Helper\FieldDisplayManager;
use WooCommerce\PayPalCommerce\WcGateway\Helper\PayUponInvoiceHelper;
use WooCommerce\PayPalCommerce\WcGateway\Helper\PayUponInvoiceProductStatus;
use WooCommerce\PayPalCommerce\WcGateway\Helper\RefundFeesUpdater;
@ -1419,4 +1417,9 @@ return array(
$container->get( 'wcgateway.settings' )
);
},
'wcgateway.field-display-manager' => SingletonDecorator::make(
static function( ContainerInterface $container ): FieldDisplayManager {
return new FieldDisplayManager();
}
),
);

View file

@ -229,6 +229,13 @@ class SettingsPageAssets {
* Register assets for PayPal admin pages.
*/
private function register_admin_assets(): void {
wp_enqueue_style(
'ppcp-admin-common',
trailingslashit( $this->module_url ) . 'assets/css/common.css',
array(),
$this->version
);
wp_enqueue_script(
'ppcp-admin-common',
trailingslashit( $this->module_url ) . 'assets/js/common.js',

View file

@ -0,0 +1,41 @@
<?php
/**
* Helper to manage the field display behaviour.
*
* @package WooCommerce\PayPalCommerce\WcGateway\Helper;
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\WcGateway\Helper;
/**
* FieldsManager class.
*/
class FieldDisplayManager {
/**
* The rules.
*
* @var array
*/
protected $rules = array();
/**
* Creates and returns a rule.
*
* @param string|null $key The rule key.
* @return FieldDisplayRule
*/
public function rule( string $key = null ): FieldDisplayRule {
if ( null === $key ) {
$key = (string) count( $this->rules );
}
$rule = new FieldDisplayRule( $key );
$this->rules[ $key ] = $rule;
return $rule;
}
}

View file

@ -0,0 +1,124 @@
<?php
/**
* Element used by field display manager.
*
* @package WooCommerce\PayPalCommerce\WcGateway\Helper;
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\WcGateway\Helper;
/**
* FieldDisplayRule class.
*/
class FieldDisplayRule {
/**
* The element selector.
*
* @var string
*/
protected $key;
/**
* The conditions of this rule.
*
* @var array
*/
protected $conditions = array();
/**
* The actions of this rule.
*
* @var array
*/
protected $actions = array();
/**
* The actions of this rule.
*
* @var array
*/
protected $add_selector_prefixes = true;
/**
* FieldDisplayElement constructor.
*
* @param string $key The rule key.
*/
public function __construct( string $key ) {
$this->key = $key;
}
/**
* Adds a condition to the rule.
*
* @param string $selector The condition selector.
* @param string $operation The condition operation (ex: equals, differs, in, not_empty, empty).
* @param mixed $value The value to compare against.
* @return self
*/
public function condition( string $selector, string $operation, $value ): self {
if ( $this->add_selector_prefixes ) {
$selector = '#ppcp-' . $selector; // Refers to the input.
}
$this->conditions[] = array(
'selector' => $selector,
'operation' => $operation,
'value' => $value,
);
return $this;
}
/**
* Adds a condition to enable the element.
*
* @param string $selector The condition selector.
* @param string $action The action.
*/
public function action( string $selector, string $action ): self {
if ( $this->add_selector_prefixes ) {
$selector = '#field-' . $selector; // Refers to the whole field.
}
$this->actions[] = array(
'selector' => $selector,
'action' => $action,
);
return $this;
}
/**
* Set if selector prefixes like, "#ppcp-" or "#field-" should be added to condition or action selectors.
*
* @param bool $add_selector_prefixes If should add prefixes.
* @return self
*/
public function should_add_selector_prefixes( bool $add_selector_prefixes = true ): self {
$this->add_selector_prefixes = $add_selector_prefixes;
return $this;
}
/**
* Returns array representation.
*
* @return array
*/
public function to_array(): array {
return array(
'key' => $this->key,
'conditions' => $this->conditions,
'actions' => $this->actions,
);
}
/**
* Returns JSON representation.
*
* @return string
*/
public function json(): string {
return wp_json_encode( $this->to_array() ) ?: '';
}
}

View file

@ -17,6 +17,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Helper\DccApplies;
use WooCommerce\PayPalCommerce\Onboarding\Environment;
use WooCommerce\PayPalCommerce\Onboarding\Render\OnboardingOptionsRenderer;
use WooCommerce\PayPalCommerce\Onboarding\State;
use WooCommerce\PayPalCommerce\WcGateway\Helper\FieldDisplayManager;
return function ( ContainerInterface $container, array $fields ): array {
@ -39,6 +40,9 @@ return function ( ContainerInterface $container, array $fields ): array {
$module_url = $container->get( 'wcgateway.url' );
$fields_manager = $container->get( 'wcgateway.field-display-manager' );
assert( $fields_manager instanceof FieldDisplayManager );
$connection_fields = array(
'ppcp_onboarading_header' => array(
'type' => 'ppcp-text',
@ -504,15 +508,13 @@ return function ( ContainerInterface $container, array $fields ): array {
'requirements' => array(),
'gateway' => Settings::CONNECTION_TAB_ID,
'custom_attributes' => array(
'data-ppcp-handlers' => wp_json_encode(
'data-ppcp-display' => wp_json_encode(
array(
array(
'handler' => 'SubElementsHandler',
'options' => array(
'values' => array( PurchaseUnitSanitizer::MODE_EXTRA_LINE ),
'elements' => array( '#field-subtotal_mismatch_line_name' ),
),
),
$fields_manager
->rule()
->condition( 'subtotal_mismatch_behavior', 'equals', PurchaseUnitSanitizer::MODE_EXTRA_LINE )
->action( 'subtotal_mismatch_line_name', 'visible' )
->to_array(),
)
),
),

View file

@ -11,6 +11,7 @@ module.exports = {
'fraudnet': path.resolve('./resources/js/fraudnet.js'),
'oxxo': path.resolve('./resources/js/oxxo.js'),
'gateway-settings-style': path.resolve('./resources/css/gateway-settings.scss'),
'common-style': path.resolve('./resources/css/common.scss'),
},
output: {
path: path.resolve(__dirname, 'assets/'),