Merge pull request #1858 from woocommerce/PCP-2273-block-buttons

Render block buttons separately and add block style settings
This commit is contained in:
Niklas Gutberlet 2023-11-24 14:22:24 +01:00 committed by GitHub
commit 9f5d62e2f2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 506 additions and 137 deletions

View file

@ -32,7 +32,7 @@
.wp-block-woocommerce-checkout {
#applepay-container, .ppcp-button-applepay {
--apple-pay-button-margin: 0;
--apple-pay-button-height: 40px;
--apple-pay-button-height: 48px;
&.ppcp-button-pill {
--apple-pay-button-border-radius: 50px;
}
@ -42,7 +42,7 @@
.wp-block-woocommerce-cart {
#applepay-container, .ppcp-button-applepay {
--apple-pay-button-margin: 0;
--apple-pay-button-height: 40px;
--apple-pay-button-height: 48px;
}
/* Workaround for blocks grid */
.wc-block-components-express-payment__event-buttons {

View file

@ -107,7 +107,7 @@ class ApplepayButton {
let config = {
wrapper: this.buttonConfig.button.wrapper,
ppcpStyle: this.ppcpConfig.button.style,
//buttonStyle: this.buttonConfig.button.style,
buttonStyle: this.buttonConfig.button.style,
ppcpButtonWrapper: this.ppcpConfig.button.wrapper
}
@ -119,7 +119,7 @@ class ApplepayButton {
}
if (['cart-block', 'checkout-block'].indexOf(this.context) !== -1) {
config.ppcpButtonWrapper = '#express-payment-method-ppcp-gateway';
config.ppcpButtonWrapper = '#express-payment-method-ppcp-gateway-paypal';
}
return config;
@ -167,14 +167,8 @@ class ApplepayButton {
addButton() {
this.log('addButton', this.context);
const wrapper =
(this.context === 'mini-cart')
? this.buttonConfig.button.mini_cart_wrapper
: this.buttonConfig.button.wrapper;
const shape =
(this.context === 'mini-cart')
? this.ppcpConfig.button.mini_cart_style.shape
: this.ppcpConfig.button.style.shape;
const { wrapper, ppcpStyle } = this.contextConfig();
const appleContainer = document.getElementById(wrapper);
const type = this.buttonConfig.button.type;
const language = this.buttonConfig.button.lang;
@ -185,7 +179,12 @@ class ApplepayButton {
appleContainer.innerHTML = `<apple-pay-button id="${id}" buttonstyle="${color}" type="${type}" locale="${language}">`;
}
jQuery('#' + wrapper).addClass('ppcp-button-' + shape);
jQuery('#' + wrapper).addClass('ppcp-button-' + ppcpStyle.shape);
if (ppcpStyle.height) {
jQuery('#' + wrapper).css('--apple-pay-button-height', `${ppcpStyle.height}px`)
}
jQuery(wrapper).append(appleContainer);
}

View file

@ -55,9 +55,8 @@ const ApplePayComponent = () => {
}
const features = ['products'];
let registerMethod = registerExpressPaymentMethod;
registerMethod({
registerExpressPaymentMethod({
name: buttonData.id,
label: <div dangerouslySetInnerHTML={{__html: buttonData.title}}/>,
content: <ApplePayComponent isEditing={false}/>,

View file

@ -1,13 +1,20 @@
import {useEffect, useState} from '@wordpress/element';
import {registerExpressPaymentMethod, registerPaymentMethod} from '@woocommerce/blocks-registry';
import {mergeWcAddress, paypalAddressToWc, paypalOrderToWcAddresses} from "./Helper/Address";
import {loadPaypalScript} from '../../../ppcp-button/resources/js/modules/Helper/ScriptLoading'
import {
loadPaypalScriptPromise
} from '../../../ppcp-button/resources/js/modules/Helper/ScriptLoading'
import {
normalizeStyleForFundingSource
} from '../../../ppcp-button/resources/js/modules/Helper/Style'
import buttonModuleWatcher from "../../../ppcp-button/resources/js/modules/ButtonModuleWatcher";
const config = wc.wcSettings.getSetting('ppcp-gateway_data');
window.ppcpFundingSource = config.fundingSource;
let registeredContext = false;
const PayPalComponent = ({
onClick,
onClose,
@ -18,12 +25,15 @@ const PayPalComponent = ({
activePaymentMethod,
shippingData,
isEditing,
fundingSource,
}) => {
const {onPaymentSetup, onCheckoutFail, onCheckoutValidation} = eventRegistration;
const {responseTypes} = emitResponse;
const [paypalOrder, setPaypalOrder] = useState(null);
const methodId = fundingSource ? `${config.id}-${fundingSource}` : config.id;
useEffect(() => {
// fill the form if in continuation (for product or mini-cart buttons)
if (!config.scriptData.continuation || !config.scriptData.continuation.order || window.ppcpContinuationFilled) {
@ -40,24 +50,6 @@ const PayPalComponent = ({
window.ppcpContinuationFilled = true;
}, [])
const [loaded, setLoaded] = useState(false);
useEffect(() => {
if (!loaded && !config.scriptData.continuation) {
loadPaypalScript(config.scriptData, () => {
setLoaded(true);
buttonModuleWatcher.registerContextBootstrap(config.scriptData.context, {
createOrder: () => {
return createOrder();
},
onApprove: (data, actions) => {
return handleApprove(data, actions);
},
});
});
}
}, [loaded]);
const createOrder = async () => {
try {
const res = await fetch(config.scriptData.ajax.create_order.endpoint, {
@ -233,7 +225,7 @@ const PayPalComponent = ({
}
useEffect(() => {
if (activePaymentMethod !== config.id) {
if (activePaymentMethod !== methodId) {
return;
}
@ -269,7 +261,7 @@ const PayPalComponent = ({
}, [onPaymentSetup, paypalOrder, activePaymentMethod]);
useEffect(() => {
if (activePaymentMethod !== config.id) {
if (activePaymentMethod !== methodId) {
return;
}
const unsubscribe = onCheckoutFail(({ processingResponse }) => {
@ -296,15 +288,26 @@ const PayPalComponent = ({
)
}
if (!loaded) {
return null;
if (!registeredContext) {
buttonModuleWatcher.registerContextBootstrap(config.scriptData.context, {
createOrder: () => {
return createOrder();
},
onApprove: (data, actions) => {
return handleApprove(data, actions);
},
});
registeredContext = true;
}
const style = normalizeStyleForFundingSource(config.scriptData.button.style, fundingSource);
const PayPalButton = window.paypal.Buttons.driver("react", { React, ReactDOM });
return (
<PayPalButton
style={config.scriptData.button.style}
fundingSource={fundingSource}
style={style}
onClick={handleClick}
onCancel={onClose}
onError={onClose}
@ -317,7 +320,7 @@ const PayPalComponent = ({
const features = ['products'];
if (config.usePlaceOrder && !config.scriptData.continuation) {
if ((config.addPlaceOrderMethod || config.usePlaceOrder) && !config.scriptData.continuation) {
registerPaymentMethod({
name: config.id,
label: <div dangerouslySetInnerHTML={{__html: config.title}}/>,
@ -330,22 +333,39 @@ if (config.usePlaceOrder && !config.scriptData.continuation) {
features: features,
},
});
} else {
let registerMethod = registerExpressPaymentMethod;
if (config.scriptData.continuation) {
features.push('ppcp_continuation');
registerMethod = registerPaymentMethod;
}
}
registerMethod({
if (config.scriptData.continuation) {
registerPaymentMethod({
name: config.id,
label: <div dangerouslySetInnerHTML={{__html: config.title}}/>,
content: <PayPalComponent isEditing={false}/>,
edit: <PayPalComponent isEditing={true}/>,
ariaLabel: config.title,
canMakePayment: () => config.enabled,
canMakePayment: () => true,
supports: {
features: features,
features: [...features, 'ppcp_continuation'],
},
});
} else if (!config.usePlaceOrder) {
const paypalScriptPromise = loadPaypalScriptPromise(config.scriptData);
for (const fundingSource of ['paypal', ...config.enabledFundingSources]) {
registerExpressPaymentMethod({
name: `${config.id}-${fundingSource}`,
paymentMethodId: config.id,
label: <div dangerouslySetInnerHTML={{__html: config.title}}/>,
content: <PayPalComponent isEditing={false} fundingSource={fundingSource}/>,
edit: <PayPalComponent isEditing={true} fundingSource={fundingSource}/>,
ariaLabel: config.title,
canMakePayment: async () => {
await paypalScriptPromise;
return paypal.Buttons({fundingSource}).isEligible();
},
supports: {
features: features,
},
});
}
}

View file

@ -35,8 +35,10 @@ return array(
$container->get( 'blocks.settings.final_review_enabled' ),
$container->get( 'session.cancellation.view' ),
$container->get( 'session.handler' ),
$container->get( 'blocks.add-place-order-method' ),
$container->get( 'wcgateway.use-place-order-button' ),
$container->get( 'wcgateway.place-order-button-text' )
$container->get( 'wcgateway.place-order-button-text' ),
$container->get( 'wcgateway.all-funding-sources' )
);
},
'blocks.settings.final_review_enabled' => static function ( ContainerInterface $container ): bool {
@ -56,4 +58,14 @@ return array(
$container->get( 'woocommerce.logger.woocommerce' )
);
},
'blocks.add-place-order-method' => function ( ContainerInterface $container ) : bool {
/**
* Whether to create a non-express method with the standard "Place order" button redirecting to PayPal.
*/
return apply_filters(
'woocommerce_paypal_payments_blocks_add_place_order_method',
true
);
},
);

View file

@ -88,7 +88,14 @@ class PayPalPaymentMethod extends AbstractPaymentMethodType {
private $session_handler;
/**
* Whether to use the standard "Place order" button.
* Whether to create a non-express method with the standard "Place order" button.
*
* @var bool
*/
protected $add_place_order_method;
/**
* Whether to use the standard "Place order" button instead of PayPal buttons.
*
* @var bool
*/
@ -101,6 +108,13 @@ class PayPalPaymentMethod extends AbstractPaymentMethodType {
*/
protected $place_order_button_text;
/**
* All existing funding sources for PayPal buttons.
*
* @var array
*/
private $all_funding_sources;
/**
* Assets constructor.
*
@ -113,8 +127,10 @@ class PayPalPaymentMethod extends AbstractPaymentMethodType {
* @param bool $final_review_enabled Whether the final review is enabled.
* @param CancelView $cancellation_view The cancellation view.
* @param SessionHandler $session_handler The Session handler.
* @param bool $use_place_order Whether to use the standard "Place order" button.
* @param bool $add_place_order_method Whether to create a non-express method with the standard "Place order" button.
* @param bool $use_place_order Whether to use the standard "Place order" button instead of PayPal buttons.
* @param string $place_order_button_text The text for the standard "Place order" button.
* @param array $all_funding_sources All existing funding sources for PayPal buttons.
*/
public function __construct(
string $module_url,
@ -126,8 +142,10 @@ class PayPalPaymentMethod extends AbstractPaymentMethodType {
bool $final_review_enabled,
CancelView $cancellation_view,
SessionHandler $session_handler,
bool $add_place_order_method,
bool $use_place_order,
string $place_order_button_text
string $place_order_button_text,
array $all_funding_sources
) {
$this->name = PayPalGateway::ID;
$this->module_url = $module_url;
@ -139,8 +157,10 @@ class PayPalPaymentMethod extends AbstractPaymentMethodType {
$this->final_review_enabled = $final_review_enabled;
$this->cancellation_view = $cancellation_view;
$this->session_handler = $session_handler;
$this->add_place_order_method = $add_place_order_method;
$this->use_place_order = $use_place_order;
$this->place_order_button_text = $place_order_button_text;
$this->all_funding_sources = $all_funding_sources;
}
/**
@ -194,22 +214,32 @@ class PayPalPaymentMethod extends AbstractPaymentMethodType {
}
}
$disabled_funding_sources = explode( ',', $script_data['url_params']['disable-funding'] ) ?: array();
$funding_sources = array_values(
array_diff(
array_keys( $this->all_funding_sources ),
$disabled_funding_sources
)
);
return array(
'id' => $this->gateway->id,
'title' => $this->gateway->title,
'description' => $this->gateway->description,
'enabled' => $this->settings_status->is_smart_button_enabled_for_location( $script_data['context'] ),
'fundingSource' => $this->session_handler->funding_source(),
'finalReviewEnabled' => $this->final_review_enabled,
'usePlaceOrder' => $this->use_place_order,
'placeOrderButtonText' => $this->place_order_button_text,
'ajax' => array(
'id' => $this->gateway->id,
'title' => $this->gateway->title,
'description' => $this->gateway->description,
'enabled' => $this->settings_status->is_smart_button_enabled_for_location( $script_data['context'] ),
'fundingSource' => $this->session_handler->funding_source(),
'finalReviewEnabled' => $this->final_review_enabled,
'addPlaceOrderMethod' => $this->add_place_order_method,
'usePlaceOrder' => $this->use_place_order,
'placeOrderButtonText' => $this->place_order_button_text,
'enabledFundingSources' => $funding_sources,
'ajax' => array(
'update_shipping' => array(
'endpoint' => WC_AJAX::get_endpoint( UpdateShippingEndpoint::ENDPOINT ),
'nonce' => wp_create_nonce( UpdateShippingEndpoint::nonce() ),
),
),
'scriptData' => $script_data,
'scriptData' => $script_data,
);
}
}

View file

@ -279,11 +279,12 @@ document.addEventListener(
});
let bootstrapped = false;
let failed = false;
hideOrderButtonIfPpcpGateway();
jQuery(document.body).on('updated_checkout payment_method_selected', () => {
if (bootstrapped) {
if (bootstrapped || failed) {
return;
}
@ -294,6 +295,12 @@ document.addEventListener(
bootstrapped = true;
bootstrap();
}, () => {
failed = true;
setVisibleByClass(ORDER_BUTTON_SELECTOR, true, 'ppcp-hidden');
buttonsSpinner.unblock();
cardsSpinner.unblock();
});
},
);

View file

@ -26,7 +26,7 @@ const storeToken = (token) => {
sessionStorage.setItem(storageKey, JSON.stringify(token));
}
const dataClientIdAttributeHandler = (scriptOptions, config, callback) => {
const dataClientIdAttributeHandler = (scriptOptions, config, callback, errorCallback = null) => {
fetch(config.endpoint, {
method: 'POST',
headers: {
@ -51,6 +51,10 @@ const dataClientIdAttributeHandler = (scriptOptions, config, callback) => {
if (typeof callback === 'function') {
callback(paypal);
}
}).catch(err => {
if (typeof errorCallback === 'function') {
errorCallback(err);
}
});
});
}

View file

@ -7,10 +7,11 @@ import {keysToCamelCase} from "./Utils";
// This component may be used by multiple modules. This assures that options are shared between all instances.
let options = window.ppcpWidgetBuilder = window.ppcpWidgetBuilder || {
isLoading: false,
onLoadedCallbacks: []
onLoadedCallbacks: [],
onErrorCallbacks: [],
};
export const loadPaypalScript = (config, onLoaded) => {
export const loadPaypalScript = (config, onLoaded, onError = null) => {
// If PayPal is already loaded call the onLoaded callback and return.
if (typeof paypal !== 'undefined') {
onLoaded();
@ -19,6 +20,9 @@ export const loadPaypalScript = (config, onLoaded) => {
// Add the onLoaded callback to the onLoadedCallbacks stack.
options.onLoadedCallbacks.push(onLoaded);
if (onError) {
options.onErrorCallbacks.push(onError);
}
// Return if it's still loading.
if (options.isLoading) {
@ -26,6 +30,12 @@ export const loadPaypalScript = (config, onLoaded) => {
}
options.isLoading = true;
const resetState = () => {
options.isLoading = false;
options.onLoadedCallbacks = [];
options.onErrorCallbacks = [];
}
// Callback to be called once the PayPal script is loaded.
const callback = (paypal) => {
widgetBuilder.setPaypal(paypal);
@ -34,8 +44,14 @@ export const loadPaypalScript = (config, onLoaded) => {
onLoadedCallback();
}
options.isLoading = false;
options.onLoadedCallbacks = [];
resetState();
}
const errorCallback = (err) => {
for (const onErrorCallback of options.onErrorCallbacks) {
onErrorCallback(err);
}
resetState();
}
// Build the PayPal script options.
@ -44,12 +60,20 @@ export const loadPaypalScript = (config, onLoaded) => {
// Load PayPal script for special case with data-client-token
if (config.data_client_id.set_attribute) {
dataClientIdAttributeHandler(scriptOptions, config.data_client_id, callback);
dataClientIdAttributeHandler(scriptOptions, config.data_client_id, callback, errorCallback);
return;
}
// Load PayPal script
loadScript(scriptOptions).then(callback);
loadScript(scriptOptions)
.then(callback)
.catch(errorCallback);
}
export const loadPaypalScriptPromise = (config) => {
return new Promise((resolve, reject) => {
loadPaypalScript(config, resolve, reject)
});
}
export const loadPaypalJsScript = (options, buttons, container) => {

View file

@ -0,0 +1,20 @@
export const normalizeStyleForFundingSource = (style, fundingSource) => {
const commonProps = {};
['shape', 'height'].forEach(prop => {
if (style[prop]) {
commonProps[prop] = style[prop];
}
});
switch (fundingSource) {
case 'paypal':
return style;
case 'paylater':
return {
color: style.color,
...commonProps
};
default:
return commonProps;
}
}

View file

@ -2,6 +2,7 @@ import merge from "deepmerge";
import {loadScript} from "@paypal/paypal-js";
import {keysToCamelCase} from "../Helper/Utils";
import widgetBuilder from "./WidgetBuilder";
import {normalizeStyleForFundingSource} from "../Helper/Style";
class Renderer {
constructor(creditCardRenderer, defaultSettings, onSmartButtonClick, onSmartButtonsInit) {
@ -36,16 +37,7 @@ class Renderer {
} else {
// render each button separately
for (const fundingSource of paypal.getFundingSources().filter(s => !(s in enabledSeparateGateways))) {
let style = settings.button.style;
if (fundingSource !== 'paypal') {
style = {
shape: style.shape,
color: style.color,
};
if (fundingSource !== 'paylater') {
delete style.color;
}
}
const style = normalizeStyleForFundingSource(settings.button.style, fundingSource);
this.renderButtons(
settings.button.wrapper,

View file

@ -1049,30 +1049,39 @@ document.querySelector("#payment").before(document.querySelector("#ppcp-messages
'mini_cart_wrapper' => '#ppc-button-minicart',
'is_mini_cart_disabled' => $this->is_button_disabled( 'mini-cart' ),
'cancel_wrapper' => '#ppcp-cancel',
'mini_cart_style' => array(
'layout' => $this->style_for_context( 'layout', 'mini-cart' ),
'color' => $this->style_for_context( 'color', 'mini-cart' ),
'shape' => $this->style_for_context( 'shape', 'mini-cart' ),
'label' => $this->style_for_context( 'label', 'mini-cart' ),
'tagline' => $this->style_for_context( 'tagline', 'mini-cart' ),
'height' => $this->settings->has( 'button_mini-cart_height' ) && $this->settings->get( 'button_mini-cart_height' ) ? $this->normalize_height( (int) $this->settings->get( 'button_mini-cart_height' ) ) : 35,
'mini_cart_style' => $this->normalize_style(
array(
'layout' => $this->style_for_context( 'layout', 'mini-cart' ),
'color' => $this->style_for_context( 'color', 'mini-cart' ),
'shape' => $this->style_for_context( 'shape', 'mini-cart' ),
'label' => $this->style_for_context( 'label', 'mini-cart' ),
'tagline' => $this->style_for_context( 'tagline', 'mini-cart' ),
'height' => $this->normalize_height( $this->style_for_context( 'height', 'mini-cart', 35 ), 25, 55 ),
)
),
'style' => array(
'layout' => $this->style_for_context( 'layout', $this->context() ),
'color' => $this->style_for_context( 'color', $this->context() ),
'shape' => $this->style_for_context( 'shape', $this->context() ),
'label' => $this->style_for_context( 'label', $this->context() ),
'tagline' => $this->style_for_context( 'tagline', $this->context() ),
'style' => $this->normalize_style(
array(
'layout' => $this->style_for_context( 'layout', $this->context() ),
'color' => $this->style_for_context( 'color', $this->context() ),
'shape' => $this->style_for_context( 'shape', $this->context() ),
'label' => $this->style_for_context( 'label', $this->context() ),
'tagline' => $this->style_for_context( 'tagline', $this->context() ),
'height' => in_array( $this->context(), array( 'cart-block', 'checkout-block' ), true )
? $this->normalize_height( $this->style_for_context( 'height', $this->context(), 48 ), 40, 55 )
: null,
)
),
),
'separate_buttons' => array(
'card' => array(
'id' => CardButtonGateway::ID,
'wrapper' => '#ppc-button-' . CardButtonGateway::ID,
'style' => array(
'shape' => $this->style_for_apm( 'shape', 'card' ),
'color' => $this->style_for_apm( 'color', 'card', 'black' ),
'layout' => $this->style_for_apm( 'poweredby_tagline', 'card', false ) === $this->normalize_style_value( true ) ? 'vertical' : 'horizontal',
'style' => $this->normalize_style(
array(
'shape' => $this->style_for_apm( 'shape', 'card' ),
'color' => $this->style_for_apm( 'color', 'card', 'black' ),
'layout' => $this->style_for_apm( 'poweredby_tagline', 'card', false ) === $this->normalize_style_value( true ) ? 'vertical' : 'horizontal',
)
),
),
),
@ -1143,13 +1152,6 @@ document.querySelector("#payment").before(document.querySelector("#ppcp-messages
$localize['pay_now'] = $this->pay_now_script_data();
}
if ( $this->style_for_context( 'layout', 'mini-cart' ) !== 'horizontal' ) {
$localize['button']['mini_cart_style']['tagline'] = false;
}
if ( $this->style_for_context( 'layout', $this->context() ) !== 'horizontal' ) {
$localize['button']['style']['tagline'] = false;
}
if ( $this->is_paypal_continuation() ) {
$order = $this->session_handler->order();
assert( $order !== null );
@ -1410,12 +1412,14 @@ document.querySelector("#payment").before(document.querySelector("#ppcp-messages
*
* @param string $style The name of the style property.
* @param string $context The context.
* @param ?mixed $default The default value.
*
* @return string
* @return string|int
*/
private function style_for_context( string $style, string $context ): string {
// Use the cart/checkout styles for blocks.
$context = str_replace( '-block', '', $context );
private function style_for_context( string $style, string $context, $default = null ) {
if ( $context === 'checkout-block' ) {
$context = 'checkout-block-express';
}
$defaults = array(
'layout' => 'vertical',
@ -1433,6 +1437,7 @@ document.querySelector("#payment").before(document.querySelector("#ppcp-messages
return $this->get_style_value( "button_{$context}_${style}" )
?? $this->get_style_value( "button_${style}" )
?? ( $default ? $this->normalize_style_value( $default ) : null )
?? $this->normalize_style_value( $defaults[ $style ] ?? '' );
}
@ -1443,9 +1448,9 @@ document.querySelector("#payment").before(document.querySelector("#ppcp-messages
* @param string $apm The APM name, such as 'card'.
* @param ?mixed $default The default value.
*
* @return string
* @return string|int
*/
private function style_for_apm( string $style, string $apm, $default = null ): string {
private function style_for_apm( string $style, string $apm, $default = null ) {
return $this->get_style_value( "${apm}_button_${style}" )
?? ( $default ? $this->normalize_style_value( $default ) : null )
?? $this->style_for_context( $style, 'checkout' );
@ -1455,9 +1460,9 @@ document.querySelector("#payment").before(document.querySelector("#ppcp-messages
* Returns the style property value or null.
*
* @param string $key The style property key in the settings.
* @return string|null
* @return string|int|null
*/
private function get_style_value( string $key ): ?string {
private function get_style_value( string $key ) {
if ( ! $this->settings->has( $key ) ) {
return null;
}
@ -1468,27 +1473,49 @@ document.querySelector("#payment").before(document.querySelector("#ppcp-messages
* Converts the style property value to string.
*
* @param mixed $value The style property value.
* @return string
* @return string|int
*/
private function normalize_style_value( $value ): string {
private function normalize_style_value( $value ) {
if ( is_bool( $value ) ) {
$value = $value ? 'true' : 'false';
}
if ( is_int( $value ) ) {
return $value;
}
return (string) $value;
}
/**
* Returns a value between 25 and 55.
* Fixes the style.
*
* @param int $height The input value.
* @param array $style The style properties.
* @return array
*/
private function normalize_style( array $style ): array {
if ( array_key_exists( 'tagline', $style ) && ( ! array_key_exists( 'layout', $style ) || $style['layout'] !== 'horizontal' ) ) {
$style['tagline'] = false;
}
if ( array_key_exists( 'height', $style ) && ! $style['height'] ) {
unset( $style['height'] );
}
return $style;
}
/**
* Returns a number between min and max.
*
* @param mixed $height The input value.
* @param int $min The minimum value.
* @param int $max The maximum value.
* @return int The normalized output value.
*/
private function normalize_height( int $height ): int {
if ( $height < 25 ) {
return 25;
private function normalize_height( $height, int $min, int $max ): int {
$height = (int) $height;
if ( $height < $min ) {
return $min;
}
if ( $height > 55 ) {
return 55;
if ( $height > $max ) {
return $max;
}
return $height;

View file

@ -25,14 +25,14 @@
.wp-block-woocommerce-checkout {
.ppcp-button-googlepay {
margin: 0;
height: 40px;
height: 48px;
}
}
.wp-block-woocommerce-cart {
.ppcp-button-googlepay {
margin: 0;
height: 40px;
height: 48px;
}
/* Workaround for blocks grid */
.wc-block-components-express-payment__event-buttons {

View file

@ -111,7 +111,7 @@ class GooglepayButton {
}
if (['cart-block', 'checkout-block'].indexOf(this.context) !== -1) {
config.ppcpButtonWrapper = '#express-payment-method-ppcp-gateway';
config.ppcpButtonWrapper = '#express-payment-method-ppcp-gateway-paypal';
}
return config;
@ -168,6 +168,10 @@ class GooglepayButton {
this.waitForWrapper(wrapper, () => {
jQuery(wrapper).addClass('ppcp-button-' + ppcpStyle.shape);
if (ppcpStyle.height) {
jQuery(wrapper).css('height', `${ppcpStyle.height}px`)
}
const button =
this.paymentsClient.createButton({
onClick: this.onButtonClick.bind(this),

View file

@ -56,9 +56,8 @@ const GooglePayComponent = () => {
}
const features = ['products'];
let registerMethod = registerExpressPaymentMethod;
registerMethod({
registerExpressPaymentMethod({
name: buttonData.id,
label: <div dangerouslySetInnerHTML={{__html: buttonData.title}}/>,
content: <GooglePayComponent isEditing={false}/>,

View file

@ -9,7 +9,7 @@ document.addEventListener(
const smartButtonLocationsSelector = '#field-smart_button_locations';
const smartButtonLocationsSelect = smartButtonLocationsSelector + ' select';
const smartButtonSelectableLocations = ['product', 'cart', 'checkout', 'mini-cart'];
const smartButtonSelectableLocations = ['product', 'cart', 'checkout', 'mini-cart', 'cart-block', 'checkout-block-express'];
const groupToggle = (selector, group) => {
const toggleElement = document.querySelector(selector);
@ -317,6 +317,7 @@ document.addEventListener(
'#field-button' + locationPrefix + '_label',
'#field-button' + locationPrefix + '_color',
'#field-button' + locationPrefix + '_shape',
'#field-button' + locationPrefix + '_height',
'#field-button' + locationPrefix + '_preview',
]
@ -324,11 +325,7 @@ document.addEventListener(
inputSelectors.push('#field-button_' + location + '_heading');
}
if (location === 'mini-cart') {
inputSelectors.push('#field-button' + locationPrefix + '_height');
}
return inputSelectors
return inputSelectors.filter(selector => document.querySelector(selector));
}
const allPayLaterMessaginginputSelectors = () => {

View file

@ -310,11 +310,11 @@ document.addEventListener(
loadPaypalScript(oldScriptSettings, () => {
const payLaterMessagingLocations = ['product', 'cart', 'checkout', 'shop', 'home', 'general'];
const paypalButtonLocations = ['product', 'cart', 'checkout', 'mini-cart', 'general'];
const paypalButtonLocations = ['product', 'cart', 'checkout', 'mini-cart', 'cart-block', 'checkout-block-express', 'general'];
paypalButtonLocations.forEach((location) => {
const inputNamePrefix = location === 'checkout' ? '#ppcp-button' : '#ppcp-button_' + location;
let wrapperName = location.charAt(0).toUpperCase() + location.slice(1);
const wrapperName = location.split('-').map(s => s.charAt(0).toUpperCase() + s.slice(1)).join('');
const fields = {
'color': inputNamePrefix + '_color',
'shape': inputNamePrefix + '_shape',
@ -323,9 +323,8 @@ document.addEventListener(
'layout': inputNamePrefix + '_layout',
}
if (location === 'mini-cart') {
if (document.querySelector(inputNamePrefix + '_height')) {
fields['height'] = inputNamePrefix + '_height';
wrapperName = 'MiniCart';
}
createButtonPreview(() => getButtonSettings('#ppcp' + wrapperName + 'ButtonPreview', fields));

View file

@ -802,6 +802,242 @@ return function ( ContainerInterface $container, array $fields ): array {
'requirements' => array(),
'gateway' => 'paypal',
),
// Block express checkout settings.
'button_checkout-block-express_heading' => array(
'heading' => __( 'Block Express Checkout Buttons', 'woocommerce-paypal-payments' ),
'description' => sprintf(
// translators: %1$s and %2$s are the opening and closing of HTML <a> tag.
__(
'Customize the appearance of the PayPal smart buttons on the %1$sBlock Express Checkout%2$s.',
'woocommerce-paypal-payments'
),
'<a href="https://woocommerce.com/document/woocommerce-paypal-payments/#button-on-block-express-checkout" target="_blank">',
'</a>'
),
'type' => 'ppcp-heading',
'screens' => array( State::STATE_START, State::STATE_ONBOARDED ),
'requirements' => array(),
'gateway' => 'paypal',
),
'button_checkout-block-express_label' => array(
'title' => __( 'Button Label', 'woocommerce-paypal-payments' ),
'type' => 'select',
'class' => array(),
'input_class' => array( 'wc-enhanced-select' ),
/**
* Returns default label ID of the PayPal button in block express checkout.
*/
'default' => apply_filters( 'woocommerce_paypal_payments_button_checkout_block_express_label_default', 'paypal' ),
'desc_tip' => true,
'description' => __(
'This controls the label on the primary button.',
'woocommerce-paypal-payments'
),
'options' => array(
'paypal' => __( 'PayPal', 'woocommerce-paypal-payments' ),
'checkout' => __( 'Checkout', 'woocommerce-paypal-payments' ),
'buynow' => __( 'PayPal Buy Now', 'woocommerce-paypal-payments' ),
'pay' => __( 'Pay with PayPal', 'woocommerce-paypal-payments' ),
),
'screens' => array(
State::STATE_START,
State::STATE_ONBOARDED,
),
'requirements' => array(),
'gateway' => 'paypal',
),
'button_checkout-block-express_color' => array(
'title' => __( 'Color', 'woocommerce-paypal-payments' ),
'type' => 'select',
'class' => array(),
'input_class' => array( 'wc-enhanced-select' ),
'default' => 'gold',
'desc_tip' => true,
'description' => __(
'Controls the background color of the primary button. Use "Gold" to leverage PayPal\'s recognition and preference, or change it to match your site design or aesthetic.',
'woocommerce-paypal-payments'
),
'options' => array(
'gold' => __( 'Gold (Recommended)', 'woocommerce-paypal-payments' ),
'blue' => __( 'Blue', 'woocommerce-paypal-payments' ),
'silver' => __( 'Silver', 'woocommerce-paypal-payments' ),
'black' => __( 'Black', 'woocommerce-paypal-payments' ),
'white' => __( 'White', 'woocommerce-paypal-payments' ),
),
'screens' => array(
State::STATE_START,
State::STATE_ONBOARDED,
),
'requirements' => array(),
'gateway' => 'paypal',
),
'button_checkout-block-express_shape' => array(
'title' => __( 'Shape', 'woocommerce-paypal-payments' ),
'type' => 'select',
'class' => array(),
'input_class' => array( 'wc-enhanced-select' ),
'default' => 'rect',
'desc_tip' => true,
'description' => __(
'The pill-shaped button\'s unique and powerful shape signifies PayPal in people\'s minds. Use the rectangular button as an alternative when pill-shaped buttons might pose design challenges.',
'woocommerce-paypal-payments'
),
'options' => array(
'pill' => __( 'Pill', 'woocommerce-paypal-payments' ),
'rect' => __( 'Rectangle', 'woocommerce-paypal-payments' ),
),
'screens' => array(
State::STATE_START,
State::STATE_ONBOARDED,
),
'requirements' => array(),
'gateway' => 'paypal',
),
'button_checkout-block-express_height' => array(
'title' => __( 'Button Height', 'woocommerce-paypal-payments' ),
'type' => 'number',
'default' => '48',
'custom_attributes' => array(
'min' => 40,
'max' => 55,
),
'desc_tip' => true,
'description' => __( 'Set a value from 40 to 55.', 'woocommerce-paypal-payments' ),
'screens' => array(
State::STATE_START,
State::STATE_ONBOARDED,
),
'requirements' => array(),
'gateway' => 'paypal',
),
'button_checkout-block-express_preview' => array(
'type' => 'ppcp-text',
'text' => $render_preview_element( 'ppcpCheckoutBlockExpressButtonPreview' ),
'screens' => array(
State::STATE_ONBOARDED,
),
'requirements' => array(),
'gateway' => 'paypal',
),
// Block cart settings.
'button_cart-block_heading' => array(
'heading' => __( 'Block Cart Buttons', 'woocommerce-paypal-payments' ),
'description' => sprintf(
// translators: %1$s and %2$s are the opening and closing of HTML <a> tag.
__(
'Customize the appearance of the PayPal smart buttons on the %1$sBlock Cart%2$s.',
'woocommerce-paypal-payments'
),
'<a href="https://woocommerce.com/document/woocommerce-paypal-payments/#button-on-cart-block" target="_blank">',
'</a>'
),
'type' => 'ppcp-heading',
'screens' => array( State::STATE_START, State::STATE_ONBOARDED ),
'requirements' => array(),
'gateway' => 'paypal',
),
'button_cart-block_label' => array(
'title' => __( 'Button Label', 'woocommerce-paypal-payments' ),
'type' => 'select',
'class' => array(),
'input_class' => array( 'wc-enhanced-select' ),
/**
* Returns default label ID of the PayPal button in block cart.
*/
'default' => apply_filters( 'woocommerce_paypal_payments_button_cart_block_label_default', 'paypal' ),
'desc_tip' => true,
'description' => __(
'This controls the label on the primary button.',
'woocommerce-paypal-payments'
),
'options' => array(
'paypal' => __( 'PayPal', 'woocommerce-paypal-payments' ),
'checkout' => __( 'Checkout', 'woocommerce-paypal-payments' ),
'buynow' => __( 'PayPal Buy Now', 'woocommerce-paypal-payments' ),
'pay' => __( 'Pay with PayPal', 'woocommerce-paypal-payments' ),
),
'screens' => array(
State::STATE_START,
State::STATE_ONBOARDED,
),
'requirements' => array(),
'gateway' => 'paypal',
),
'button_cart-block_color' => array(
'title' => __( 'Color', 'woocommerce-paypal-payments' ),
'type' => 'select',
'class' => array(),
'input_class' => array( 'wc-enhanced-select' ),
'default' => 'gold',
'desc_tip' => true,
'description' => __(
'Controls the background color of the primary button. Use "Gold" to leverage PayPal\'s recognition and preference, or change it to match your site design or aesthetic.',
'woocommerce-paypal-payments'
),
'options' => array(
'gold' => __( 'Gold (Recommended)', 'woocommerce-paypal-payments' ),
'blue' => __( 'Blue', 'woocommerce-paypal-payments' ),
'silver' => __( 'Silver', 'woocommerce-paypal-payments' ),
'black' => __( 'Black', 'woocommerce-paypal-payments' ),
'white' => __( 'White', 'woocommerce-paypal-payments' ),
),
'screens' => array(
State::STATE_START,
State::STATE_ONBOARDED,
),
'requirements' => array(),
'gateway' => 'paypal',
),
'button_cart-block_shape' => array(
'title' => __( 'Shape', 'woocommerce-paypal-payments' ),
'type' => 'select',
'class' => array(),
'input_class' => array( 'wc-enhanced-select' ),
'default' => 'rect',
'desc_tip' => true,
'description' => __(
'The pill-shaped button\'s unique and powerful shape signifies PayPal in people\'s minds. Use the rectangular button as an alternative when pill-shaped buttons might pose design challenges.',
'woocommerce-paypal-payments'
),
'options' => array(
'pill' => __( 'Pill', 'woocommerce-paypal-payments' ),
'rect' => __( 'Rectangle', 'woocommerce-paypal-payments' ),
),
'screens' => array(
State::STATE_START,
State::STATE_ONBOARDED,
),
'requirements' => array(),
'gateway' => 'paypal',
),
'button_cart-block_height' => array(
'title' => __( 'Button Height', 'woocommerce-paypal-payments' ),
'type' => 'number',
'default' => '48',
'custom_attributes' => array(
'min' => 40,
'max' => 55,
),
'desc_tip' => true,
'description' => __( 'Set a value from 40 to 55.', 'woocommerce-paypal-payments' ),
'screens' => array(
State::STATE_START,
State::STATE_ONBOARDED,
),
'requirements' => array(),
'gateway' => 'paypal',
),
'button_cart-block_preview' => array(
'type' => 'ppcp-text',
'text' => $render_preview_element( 'ppcpCartBlockButtonPreview' ),
'screens' => array(
State::STATE_ONBOARDED,
),
'requirements' => array(),
'gateway' => 'paypal',
),
);
return array_merge( $fields, $smart_button_fields );