mirror of
https://github.com/woocommerce/woocommerce-paypal-payments.git
synced 2025-09-05 08:59:14 +08:00
Merge pull request #1469 from woocommerce/PCP-1607-smart-buttons-not-greyed-out-removed-on-single-product-when-deselecting-product-variation
Smart buttons not greyed out/removed on single product when deselecting product variation (1607)
This commit is contained in:
commit
b4cc4dcfe9
7 changed files with 197 additions and 99 deletions
|
@ -121,10 +121,22 @@ const bootstrap = () => {
|
||||||
return actions.reject();
|
return actions.reject();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const onSmartButtonsInit = () => {
|
|
||||||
buttonsSpinner.unblock();
|
let smartButtonsOptions = {
|
||||||
|
onInit: null,
|
||||||
|
init: function (actions) {
|
||||||
|
this.actions = actions;
|
||||||
|
if (typeof this.onInit === 'function') {
|
||||||
|
this.onInit();
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
const renderer = new Renderer(creditCardRenderer, PayPalCommerceGateway, onSmartButtonClick, onSmartButtonsInit);
|
|
||||||
|
const onSmartButtonsInit = (data, actions) => {
|
||||||
|
buttonsSpinner.unblock();
|
||||||
|
smartButtonsOptions.init(actions);
|
||||||
|
};
|
||||||
|
const renderer = new Renderer(creditCardRenderer, PayPalCommerceGateway, onSmartButtonClick, onSmartButtonsInit, smartButtonsOptions);
|
||||||
const messageRenderer = new MessageRenderer(PayPalCommerceGateway.messages);
|
const messageRenderer = new MessageRenderer(PayPalCommerceGateway.messages);
|
||||||
const context = PayPalCommerceGateway.context;
|
const context = PayPalCommerceGateway.context;
|
||||||
if (context === 'mini-cart' || context === 'product') {
|
if (context === 'mini-cart' || context === 'product') {
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import ButtonsToggleListener from '../Helper/ButtonsToggleListener';
|
|
||||||
import Product from '../Entity/Product';
|
import Product from '../Entity/Product';
|
||||||
import onApprove from '../OnApproveHandler/onApproveForContinue';
|
import onApprove from '../OnApproveHandler/onApproveForContinue';
|
||||||
import {payerData} from "../Helper/PayerData";
|
import {payerData} from "../Helper/PayerData";
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import UpdateCart from "../Helper/UpdateCart";
|
import UpdateCart from "../Helper/UpdateCart";
|
||||||
import SingleProductActionHandler from "../ActionHandler/SingleProductActionHandler";
|
import SingleProductActionHandler from "../ActionHandler/SingleProductActionHandler";
|
||||||
import {hide, show, setVisible} from "../Helper/Hiding";
|
import {hide, show} from "../Helper/Hiding";
|
||||||
import ButtonsToggleListener from "../Helper/ButtonsToggleListener";
|
import {disable, enable} from "../Helper/ButtonDisabler";
|
||||||
|
|
||||||
class SingleProductBootstap {
|
class SingleProductBootstap {
|
||||||
constructor(gateway, renderer, messages, errorHandler) {
|
constructor(gateway, renderer, messages, errorHandler) {
|
||||||
|
@ -10,56 +10,93 @@ class SingleProductBootstap {
|
||||||
this.messages = messages;
|
this.messages = messages;
|
||||||
this.errorHandler = errorHandler;
|
this.errorHandler = errorHandler;
|
||||||
this.mutationObserver = new MutationObserver(this.handleChange.bind(this));
|
this.mutationObserver = new MutationObserver(this.handleChange.bind(this));
|
||||||
|
this.formSelector = 'form.cart';
|
||||||
|
|
||||||
|
if (this.renderer && this.renderer.smartButtonsOptions) {
|
||||||
|
this.renderer.smartButtonsOptions.onInit = () => {
|
||||||
|
this.handleChange();
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
form() {
|
||||||
|
return document.querySelector(this.formSelector);
|
||||||
|
}
|
||||||
|
|
||||||
handleChange() {
|
handleChange() {
|
||||||
const shouldRender = this.shouldRender();
|
|
||||||
setVisible(this.gateway.button.wrapper, shouldRender);
|
|
||||||
setVisible(this.gateway.messages.wrapper, shouldRender);
|
|
||||||
if (!shouldRender) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.render();
|
|
||||||
}
|
|
||||||
|
|
||||||
init() {
|
|
||||||
const form = document.querySelector('form.cart');
|
|
||||||
if (!form) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
form.addEventListener('change', this.handleChange.bind(this));
|
|
||||||
this.mutationObserver.observe(form, {childList: true, subtree: true});
|
|
||||||
|
|
||||||
const buttonObserver = new ButtonsToggleListener(
|
|
||||||
form.querySelector('.single_add_to_cart_button'),
|
|
||||||
() => {
|
|
||||||
show(this.gateway.button.wrapper);
|
|
||||||
show(this.gateway.messages.wrapper);
|
|
||||||
this.messages.renderWithAmount(this.priceAmount())
|
|
||||||
},
|
|
||||||
() => {
|
|
||||||
hide(this.gateway.button.wrapper);
|
|
||||||
hide(this.gateway.messages.wrapper);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
buttonObserver.init();
|
|
||||||
|
|
||||||
if (!this.shouldRender()) {
|
if (!this.shouldRender()) {
|
||||||
hide(this.gateway.button.wrapper);
|
this.renderer.disableSmartButtons();
|
||||||
|
hide(this.gateway.button.wrapper, this.formSelector);
|
||||||
hide(this.gateway.messages.wrapper);
|
hide(this.gateway.messages.wrapper);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.render();
|
this.render();
|
||||||
|
|
||||||
|
this.renderer.enableSmartButtons();
|
||||||
|
show(this.gateway.button.wrapper);
|
||||||
|
show(this.gateway.messages.wrapper);
|
||||||
|
|
||||||
|
this.handleButtonStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
handleButtonStatus() {
|
||||||
|
if (!this.shouldEnable()) {
|
||||||
|
this.renderer.disableSmartButtons();
|
||||||
|
disable(this.gateway.button.wrapper, this.formSelector);
|
||||||
|
disable(this.gateway.messages.wrapper);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.renderer.enableSmartButtons();
|
||||||
|
enable(this.gateway.button.wrapper);
|
||||||
|
enable(this.gateway.messages.wrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
const form = this.form();
|
||||||
|
|
||||||
|
if (!form) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
form.addEventListener('change', () => {
|
||||||
|
this.handleChange();
|
||||||
|
|
||||||
|
setTimeout(() => { // Wait for the DOM to be fully updated
|
||||||
|
// For the moment renderWithAmount should only be done here to prevent undesired side effects due to priceAmount()
|
||||||
|
// not being correctly formatted in some cases, can be moved to handleButtonStatus() once this issue is fixed
|
||||||
|
this.messages.renderWithAmount(this.priceAmount());
|
||||||
|
}, 100);
|
||||||
|
});
|
||||||
|
this.mutationObserver.observe(form, { childList: true, subtree: true });
|
||||||
|
|
||||||
|
const addToCartButton = form.querySelector('.single_add_to_cart_button');
|
||||||
|
|
||||||
|
if (addToCartButton) {
|
||||||
|
(new MutationObserver(this.handleButtonStatus.bind(this)))
|
||||||
|
.observe(addToCartButton, { attributes : true });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.shouldRender()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.render();
|
||||||
|
this.handleChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
shouldRender() {
|
shouldRender() {
|
||||||
return document.querySelector('form.cart') !== null
|
return this.form() !== null
|
||||||
|
&& !this.isWcsattSubscriptionMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
shouldEnable() {
|
||||||
|
const form = this.form();
|
||||||
|
const addToCartButton = form ? form.querySelector('.single_add_to_cart_button') : null;
|
||||||
|
|
||||||
|
return this.shouldRender()
|
||||||
&& !this.priceAmountIsZero()
|
&& !this.priceAmountIsZero()
|
||||||
&& !this.isSubscriptionMode();
|
&& ((null === addToCartButton) || !addToCartButton.classList.contains('disabled'));
|
||||||
}
|
}
|
||||||
|
|
||||||
priceAmount() {
|
priceAmount() {
|
||||||
|
@ -93,7 +130,7 @@ class SingleProductBootstap {
|
||||||
return !price || price === 0;
|
return !price || price === 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
isSubscriptionMode() {
|
isWcsattSubscriptionMode() {
|
||||||
// Check "All products for subscriptions" plugin.
|
// Check "All products for subscriptions" plugin.
|
||||||
return document.querySelector('.wcsatt-options-product:not(.wcsatt-options-product--hidden) .subscription-option input[type="radio"]:checked') !== null
|
return document.querySelector('.wcsatt-options-product:not(.wcsatt-options-product--hidden) .subscription-option input[type="radio"]:checked') !== null
|
||||||
|| document.querySelector('.wcsatt-options-prompt-label-subscription input[type="radio"]:checked') !== null; // grouped
|
|| document.querySelector('.wcsatt-options-prompt-label-subscription input[type="radio"]:checked') !== null; // grouped
|
||||||
|
@ -106,7 +143,7 @@ class SingleProductBootstap {
|
||||||
this.gateway.ajax.change_cart.endpoint,
|
this.gateway.ajax.change_cart.endpoint,
|
||||||
this.gateway.ajax.change_cart.nonce,
|
this.gateway.ajax.change_cart.nonce,
|
||||||
),
|
),
|
||||||
document.querySelector('form.cart'),
|
this.form(),
|
||||||
this.errorHandler,
|
this.errorHandler,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
/**
|
||||||
|
* @param selectorOrElement
|
||||||
|
* @returns {Element}
|
||||||
|
*/
|
||||||
|
const getElement = (selectorOrElement) => {
|
||||||
|
if (typeof selectorOrElement === 'string') {
|
||||||
|
return document.querySelector(selectorOrElement);
|
||||||
|
}
|
||||||
|
return selectorOrElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const setEnabled = (selectorOrElement, enable, form = null) => {
|
||||||
|
const element = getElement(selectorOrElement);
|
||||||
|
|
||||||
|
if (!element) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enable) {
|
||||||
|
jQuery(element).css({
|
||||||
|
'cursor': '',
|
||||||
|
'-webkit-filter': '',
|
||||||
|
'filter': '',
|
||||||
|
} )
|
||||||
|
.off('mouseup')
|
||||||
|
.find('> *')
|
||||||
|
.css('pointer-events', '');
|
||||||
|
} else {
|
||||||
|
jQuery(element).css({
|
||||||
|
'cursor': 'not-allowed',
|
||||||
|
'-webkit-filter': 'grayscale(100%)',
|
||||||
|
'filter': 'grayscale(100%)',
|
||||||
|
})
|
||||||
|
.on('mouseup', function(event) {
|
||||||
|
event.stopImmediatePropagation();
|
||||||
|
|
||||||
|
if (form) {
|
||||||
|
// Trigger form submit to show the error message
|
||||||
|
jQuery(form).find(':submit').trigger('click');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.find('> *')
|
||||||
|
.css('pointer-events', 'none');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const disable = (selectorOrElement, form = null) => {
|
||||||
|
setEnabled(selectorOrElement, false, form);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const enable = (selectorOrElement) => {
|
||||||
|
setEnabled(selectorOrElement, true);
|
||||||
|
};
|
|
@ -1,39 +0,0 @@
|
||||||
/**
|
|
||||||
* When you can't add something to the cart, the PayPal buttons should not show.
|
|
||||||
* Therefore we listen for changes on the add to cart button and show/hide the buttons accordingly.
|
|
||||||
*/
|
|
||||||
|
|
||||||
class ButtonsToggleListener {
|
|
||||||
constructor(element, showCallback, hideCallback)
|
|
||||||
{
|
|
||||||
this.element = element;
|
|
||||||
this.showCallback = showCallback;
|
|
||||||
this.hideCallback = hideCallback;
|
|
||||||
this.observer = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
init()
|
|
||||||
{
|
|
||||||
if (!this.element) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const config = { attributes : true };
|
|
||||||
const callback = () => {
|
|
||||||
if (this.element.classList.contains('disabled')) {
|
|
||||||
this.hideCallback();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.showCallback();
|
|
||||||
}
|
|
||||||
this.observer = new MutationObserver(callback);
|
|
||||||
this.observer.observe(this.element, config);
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
|
|
||||||
disconnect()
|
|
||||||
{
|
|
||||||
this.observer.disconnect();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ButtonsToggleListener;
|
|
|
@ -2,6 +2,7 @@ class MessageRenderer {
|
||||||
|
|
||||||
constructor(config) {
|
constructor(config) {
|
||||||
this.config = config;
|
this.config = config;
|
||||||
|
this.optionsFingerprint = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -9,38 +10,58 @@ class MessageRenderer {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
paypal.Messages({
|
const options = {
|
||||||
amount: this.config.amount,
|
amount: this.config.amount,
|
||||||
placement: this.config.placement,
|
placement: this.config.placement,
|
||||||
style: this.config.style
|
style: this.config.style
|
||||||
}).render(this.config.wrapper);
|
};
|
||||||
|
|
||||||
|
if (this.optionsEqual(options)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
paypal.Messages(options).render(this.config.wrapper);
|
||||||
|
|
||||||
jQuery(document.body).on('updated_cart_totals', () => {
|
jQuery(document.body).on('updated_cart_totals', () => {
|
||||||
paypal.Messages({
|
paypal.Messages(options).render(this.config.wrapper);
|
||||||
amount: this.config.amount,
|
|
||||||
placement: this.config.placement,
|
|
||||||
style: this.config.style
|
|
||||||
}).render(this.config.wrapper);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
renderWithAmount(amount) {
|
renderWithAmount(amount) {
|
||||||
|
|
||||||
if (! this.shouldRender()) {
|
if (! this.shouldRender()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
amount,
|
||||||
|
placement: this.config.placement,
|
||||||
|
style: this.config.style
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this.optionsEqual(options)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const newWrapper = document.createElement('div');
|
const newWrapper = document.createElement('div');
|
||||||
newWrapper.setAttribute('id', this.config.wrapper.replace('#', ''));
|
newWrapper.setAttribute('id', this.config.wrapper.replace('#', ''));
|
||||||
|
|
||||||
const sibling = document.querySelector(this.config.wrapper).nextSibling;
|
const oldWrapper = document.querySelector(this.config.wrapper);
|
||||||
document.querySelector(this.config.wrapper).parentElement.removeChild(document.querySelector(this.config.wrapper));
|
const sibling = oldWrapper.nextSibling;
|
||||||
|
oldWrapper.parentElement.removeChild(oldWrapper);
|
||||||
sibling.parentElement.insertBefore(newWrapper, sibling);
|
sibling.parentElement.insertBefore(newWrapper, sibling);
|
||||||
paypal.Messages({
|
|
||||||
amount,
|
paypal.Messages(options).render(this.config.wrapper);
|
||||||
placement: this.config.placement,
|
}
|
||||||
style: this.config.style
|
|
||||||
}).render(this.config.wrapper);
|
optionsEqual(options) {
|
||||||
|
const fingerprint = JSON.stringify(options);
|
||||||
|
|
||||||
|
if (this.optionsFingerprint === fingerprint) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.optionsFingerprint = fingerprint;
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
shouldRender() {
|
shouldRender() {
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
import merge from "deepmerge";
|
import merge from "deepmerge";
|
||||||
|
|
||||||
class Renderer {
|
class Renderer {
|
||||||
constructor(creditCardRenderer, defaultSettings, onSmartButtonClick, onSmartButtonsInit) {
|
constructor(creditCardRenderer, defaultSettings, onSmartButtonClick, onSmartButtonsInit, smartButtonsOptions) {
|
||||||
this.defaultSettings = defaultSettings;
|
this.defaultSettings = defaultSettings;
|
||||||
this.creditCardRenderer = creditCardRenderer;
|
this.creditCardRenderer = creditCardRenderer;
|
||||||
this.onSmartButtonClick = onSmartButtonClick;
|
this.onSmartButtonClick = onSmartButtonClick;
|
||||||
this.onSmartButtonsInit = onSmartButtonsInit;
|
this.onSmartButtonsInit = onSmartButtonsInit;
|
||||||
|
this.smartButtonsOptions = smartButtonsOptions;
|
||||||
|
|
||||||
this.renderedSources = new Set();
|
this.renderedSources = new Set();
|
||||||
}
|
}
|
||||||
|
@ -106,6 +107,20 @@ class Renderer {
|
||||||
enableCreditCardFields() {
|
enableCreditCardFields() {
|
||||||
this.creditCardRenderer.enableFields();
|
this.creditCardRenderer.enableFields();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
disableSmartButtons() {
|
||||||
|
if (!this.smartButtonsOptions || !this.smartButtonsOptions.actions) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.smartButtonsOptions.actions.disable();
|
||||||
|
}
|
||||||
|
|
||||||
|
enableSmartButtons() {
|
||||||
|
if (!this.smartButtonsOptions || !this.smartButtonsOptions.actions) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.smartButtonsOptions.actions.enable();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Renderer;
|
export default Renderer;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue