@automattic/newspack-blocks 4.29.0-alpha.1 → 4.29.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- <?php return array('dependencies' => array(), 'version' => '9022ef8f0db88e64270b');
1
+ <?php return array('dependencies' => array(), 'version' => 'a25d06a462dfabdbe12a');
package/dist/modal.js CHANGED
@@ -1 +1 @@
1
- (()=>{"use strict";function e(e,t=!1){const n=e.querySelectorAll('button, [href], input:not([type="hidden"]), select, textarea, [tabindex]:not([tabindex="-1"])');if(0===n.length)return!1;const o=n?.[0];let c;o.focus(),t?(document.addEventListener("keydown",function(e){if(("Tab"===e.key||9===e.keyCode)&&e.shiftKey&&document.activeElement===o){const n=t.contentWindow.document,o=n.getElementById("checkout-after-success");c=null!==o?o:n.getElementById("checkout_cancel"),c.focus(),e.preventDefault()}}),document.getElementById("newspack-a11y-last-element").addEventListener("focus",()=>{o.focus()})):(c=n[n.length-1],document.addEventListener("keydown",function(e){("Tab"===e.key||9===e.keyCode)&&(e.shiftKey?document.activeElement===o&&(c.focus(),e.preventDefault()):document.activeElement===c&&(o.focus(),e.preventDefault()))}))}const t=(e,t={})=>({...t,action:e}),n=["action","action_type","amount","currency","product_id","product_type","variation_id","variation_ids","is_variable","is_grouped","child_ids","price_summary","newspack_popup_id","prompt_title","gate_post_id","recurrence","referrer"],o=(e,t="np_modal_checkout_interaction")=>{if("function"==typeof window.gtag&&e){const o={};for(const t of n)e[t]&&("boolean"==typeof e[t]?e[t]=e[t]?"yes":"no":"true"===e[t]?e[t]="yes":"false"===e[t]&&(e[t]="no"),o[t]=e[t].toString());window.gtag("event",t,o)}},c=e=>{if("function"!=typeof window.gtag)return;let n="opened";const{amount:c="",is_variable:a="",is_grouped:r="",price:i="",variation_id:s=""}=e,l={...e};(c||i)&&(l.amount=c||i),a&&!s&&(n="opened_variations"),r&&(n="opened_grouped");const u=t(n,l);o(u)};function a(e,t=null){const n=document.createElement("input");return n.type="hidden",n.name=e,t&&(n.value=t),n}function r(e){e.requestSubmit(e.querySelector('button[type="submit"]'))}function i(e){const t="string"==typeof e?document.getElementById(e):e;if(!t)return console.warn("No container found for checkout data"),{};let n={};if("FORM"===t.tagName){const e=new FormData(t);n=Object.fromEntries(e)}const o=t.dataset.checkout;if(!o)return console.warn("No checkout data found"),n;try{n={...n,...JSON.parse(o)}}catch(e){console.warn("Error parsing checkout data")}const c=new URL(t.ownerDocument.defaultView.location.href);return c.searchParams.get("action_type")&&(n.action_type=c.searchParams.get("action_type")),n}const s=e=>{if("function"!=typeof window.gtag)return;e=e||i("newspack_modal_checkout");const n=t("dismissed",e);o(n)};function l(e){const t=e&&e.dataset?e.dataset.checkout:null;if(!t)return null;try{return JSON.parse(t)}catch(e){return null}}function u(e,t,n=null){const o=e.querySelectorAll(".wp-block-newspack-blocks-checkout-button"),c=null!=n&&""!==String(n);let a=null;return o.forEach(e=>{if(a)return;const o=e.querySelector("form"),r=l(o);r&&String(r.product_id)===String(t)&&(c&&String(r.variation_id)!==String(n)||(a=o))}),a}const d=["after_success_behavior","after_success_url","after_success_button_label","gate_post_id","newspack_popup_id","prompt_title"];const p={gate:"gate_post_id",prompt:"newspack_popup_id"};const m=newspackBlocksModal.newspack_class_prefix,_="newspack_modal_checkout_iframe",h="newspack_modal_checkout_container",w=`${m}__modal`,f="newspack-blocks__modal-variation",y=`${m}__processing-payment-text`,k=newspackBlocksModal.processing_payment_messages;let v=!1,g=newspackBlocksModal.labels.checkout_modal_title,b=null;const S=e=>{e.overlayId&&window.newspackReaderActivation?.overlays&&window.newspackReaderActivation?.overlays.remove(e.overlayId),e.setAttribute("data-state","closed"),document.body.style.overflow="auto"};window.onpageshow=e=>{e.persisted&&(document.querySelectorAll(".modal-processing").forEach(e=>e.classList.remove("modal-processing")),document.querySelectorAll(".non-modal-checkout-loading").forEach(e=>e.classList.remove("non-modal-checkout-loading")),document.querySelectorAll(`.${w}-container`).forEach(e=>S(e)))};const E=new CustomEvent("checkout-closed");var A;window.newspackRAS=window.newspackRAS||[],A=()=>{const t=document.querySelector("#newspack_modal_checkout");if(!t)return;const n=t.querySelector(`.${w}__content`),o=a("modal_checkout","1"),A=n.querySelector(`.${m}__spinner`);let q=document.querySelector(".newspack-reader__account-link")?.[0];const L="600px",R=document.createElement("iframe");R.name=_,R.style.height=L,R.style.visibility="hidden";const $=e=>{D.observe(e),"none"!==A.style.display&&(A.style.display="none"),"visible"!==R.style.visibility&&(R.style.visibility="visible"),R._ready=!0};!function(e,t,n){e._ready=!1,e._readyTimer&&clearTimeout(e._readyTimer);let o=!1,c="";function a(){o||(o=!0,clearTimeout(e._readyTimer),t.call(this))}function r(){"complete"===this.readyState&&a.call(this)}function i(){e._ready=!1,n&&n(),e._readyTimer&&clearTimeout(e._readyTimer);const t=e.contentDocument||e.contentWindow?.document;t&&(t.removeEventListener("DOMContentLoaded",a),t.removeEventListener("readystatechange",r))}function s(){if(e._ready)return void i();const t=e.contentDocument||e.contentWindow?.document;t&&0!==t.URL.indexOf("about:")?"complete"===t?.readyState?a.call(t):(t.addEventListener("DOMContentLoaded",a),t.addEventListener("readystatechange",r)):e._readyTimer=setTimeout(s,10)}e._observer||(e._observer=new MutationObserver(e=>{e.forEach(e=>{"attributes"===e.type&&"src"===e.attributeName&&(o=!1,i(),s())})}),e._observer.observe(e,{attributes:!0})),e._locationObserver||(e._locationObserver=setInterval(function(){const t=e.contentDocument||e.contentWindow?.document;t&&t.URL!==c&&(c=t.URL,o=!1,i(),s())},50)),s()}(R,function(){const n=R.contentWindow?.location;if(window.newspackReaderActivation&&n?.href?.includes("order-received")){const e=window.newspackReaderActivation,t=new Proxy(new URLSearchParams(n.search),{get:(e,t)=>e.get(t)});t.email&&(e.setReaderEmail(t.email),e.setAuthenticated(!0))}const o=R?.contentDocument?.querySelector(`#${h}`);if(!o)return;const c=i(o.querySelector("#modal-checkout-product-details")),a=document.createElement("p");a.classList.add(y);let r=[];const s=()=>{r.forEach(e=>clearTimeout(e)),r=[]},l=()=>{A.style.display="none",s(),A.querySelectorAll(`.${y}`).forEach(e=>e.remove())};!function(e,t){e.checkoutCancel?t():e.addEventListener("checkout-cancel",t)}(o,()=>{x()}),function(e,t){e.checkoutPlaceOrderStart?t():e.addEventListener("checkout-place-order-start",t)}(o,e=>{A.querySelectorAll(`.${y}`).forEach(e=>e.remove()),A.style.display="flex",s(),a.textContent=k[0]?.text??"",k.slice(1).forEach(({text:t,delay:n})=>{const o=setTimeout(()=>{e.target.dispatchEvent(new CustomEvent("checkout-place-order-processing")),a.textContent=t},n);r.push(o)}),A.appendChild(a)}),function(e,t){e.checkoutPlaceOrderError?t():e.addEventListener("checkout-place-order-error",t)}(o,l),function(e,t){e.checkoutReady?t():e.addEventListener("checkout-ready",t)}(o,()=>{if(t.initialErrors){const e=document.createElement("div");e.classList.add("woocommerce-error"),e.textContent=t.initialErrors,o.prepend(e),delete t.initialErrors}I(),N(g),R.contentWindow?.newspackBlocksModalCheckout?.checkout_nonce&&(t.checkout_nonce=R.contentWindow.newspackBlocksModalCheckout.checkout_nonce),$(o)}),function(e,t){e.checkoutComplete?t():e.addEventListener("checkout-complete",t)}(o,()=>{window.newspackRAS.push(["checkout_completed",c]),window?.newspackReaderActivation?.refreshNewslettersSignupModal&&window?.newspackReaderActivation?.getReader()?.email&&window.newspackReaderActivation.refreshNewslettersSignupModal(window.newspackReaderActivation.getReader().email),I("small"),N(newspackBlocksModal.labels.thankyou_modal_title),$(o),e(t.querySelector(`.${w}`)),l()}),function(e,t){e.checkoutPlaceOrderCriticalError?t():e.addEventListener("checkout-place-order-critical-error",t)}(o,()=>{var e;(e=b)&&(x(),A.style.display="none",t.initialErrors=newspackBlocksModal.labels.critical_error,e.requestSubmit(e.querySelector('button[type="submit"]')),l())})},()=>{A.style.display="flex"});const M=e=>new Promise((t,n)=>{const o=new URLSearchParams(e);o.append("action","modal_checkout_request"),fetch(newspackBlocksModal.ajax_url+"?"+o.toString()).then(e=>{e.ok||n(e),e.json().then(e=>{t(e.url)}).catch(n)}).catch(n)}),C=()=>"undefined"!=typeof newspack_ras_config&&!newspack_ras_config?.is_logged_in&&!window?.newspackReaderActivation?.getReader?.()?.authenticated&&newspackBlocksModal?.is_registration_required&&window?.newspackReaderActivation?.openAuthModal,B=t=>{const n=!newspackBlocksModal.has_unsupported_payment_gateway;n||t.preventDefault();const o=t.target;o.classList.add("modal-processing"),function(e){if(!e||"function"!=typeof e.querySelector)return!1;if(e.closest(".newspack-content-gate__gate"))return!1;const t=function(){let e,t;try{e=window.sessionStorage.getItem("newspack_cta_attribution")}catch(e){return null}if(!e)return null;try{t=JSON.parse(e)}catch(e){return null}return t&&1===t.v&&p[t.type]&&t.id?"number"!=typeof t.ts||Date.now()-t.ts>18e5?null:t:null}();if(!t)return!1;const n=p[t.type];if(e.querySelector(`input[name="${n}"]`))return!1;const o=document.createElement("input");o.type="hidden",o.name=n,o.value=t.id,e.prepend(o)}(o);const u=i(o);if(u.newspack_donate){const e=u.donation_frequency,t=[...o.querySelectorAll(`.donation-tier__${e}, .donation-frequency__${e}`)],n=u.donation_tier_index;let c,a;n?(c=t[n],a=u[`donation_value_${e}`]):(c=t[0],u[`donation_value_${e}_untiered`]?a=u[`donation_value_${e}_untiered`]:(a=u[`donation_value_${e}`],"other"===a&&(a=u[`donation_value_${e}_other`])));const r=i(c);for(const e in r)u[e]=r[e];u.amount=a,u.price_summary=u.summary_template.replace("{{PRICE}}",function(e,t="USD"){return parseFloat(e).toLocaleString(document.documentElement.lang,{style:"currency",currency:t,currencyDisplay:"narrowSymbol"})}(u.amount,u.currency))}u&&Object.keys(u).forEach(e=>{0===o.querySelectorAll('input[name="'+e+'"]').length&&o.prepend(a(e,u[e]))}),u.variation_id||(q=t.submitter);const d=document.querySelectorAll(`.${f}`);if(d.forEach(e=>{(C()||n)&&S(e)}),u.is_grouped||u.is_variable&&!u.variation_id){const n=[...d].find(e=>e.dataset.productId===u.product_id);if(n)return n.querySelectorAll(`form[target="${_}"]`).forEach(e=>{["after_success_behavior","after_success_url","after_success_button_label","gate_post_id","newspack_popup_id","prompt_title"].forEach(t=>{0===e.querySelectorAll('input[name="'+t+'"]').length&&e.prepend(a(t,u[t]))});const t=l(e);t&&Object.keys(t).forEach(n=>{0===e.querySelectorAll('input[name="'+n+'"]').length&&e.prepend(a(n,t[n]))})}),t.preventDefault(),o.classList.remove("modal-processing"),T(n),e(n,!1),v||c(u),void document.getElementById("newspack_modal_checkout").setAttribute("data-checkout",JSON.stringify(u))}if(n||C()){if(o.classList.remove("modal-processing"),v||c(u),v=!0,C()){t.preventDefault();const e=M(u);e.then(e=>{window.newspackReaderActivation?.setPendingCheckout?.(e)}),window.newspackReaderActivation.openAuthModal({title:newspackBlocksModal.labels.auth_modal_title,onSuccess:(t,o)=>{e.then(e=>{o?.registered&&n&&(e+=`&${newspackBlocksModal.checkout_registration_flag}=1`),n?r(P(e)):M(u).then(window.location.href=e)}).catch(e=>{console.warn("Unable to generate cart:",e),x()})},onError:()=>{x()},onDismiss:()=>{s(u),v=!1,document.getElementById("newspack_modal_checkout").removeAttribute("data-checkout")},skipSuccess:!0,skipNewslettersSignup:!0,labels:{signin:{title:newspackBlocksModal.labels.signin_modal_title},register:{title:newspackBlocksModal.labels.register_modal_title}},trigger:t.submitter,closeOnSuccess:n})}else O(),document.getElementById("newspack_modal_checkout").setAttribute("data-checkout",JSON.stringify(u));b=o}else M(u).then(e=>{window.location.href=e}),(!u.is_variable||u.variation_id)&&o.querySelectorAll("button[type=submit]:focus").forEach(e=>{e.classList.add("non-modal-checkout-loading");const t=e.innerHTML;e.innerHTML="<span>"+t+"</span>"})},P=e=>{const t=document.createElement("form");t.method="POST",t.action=e,t.target=_,t.style.display="none";const n=document.createElement("button");return n.setAttribute("type","submit"),t.appendChild(n),document.body.appendChild(t),t.addEventListener("submit",B),t},D=new ResizeObserver(e=>{if(!e||!e.length)return;if(!R.contentDocument)return;const o=e[0].contentRect;if(o){const e=.01*Math.max(document.documentElement.clientHeight,window.innerHeight||0)*90-(t.querySelector(`.${w}__header`)?.offsetHeight||0),c=o.top+o.bottom,a=Math.min(c,e);if(0===a)return void(R.style.visibility="hidden");n.style.height=a+"px",R.style.height=a+"px"}}),x=()=>{const e=R?.contentDocument?.querySelector(`#${h}`),o=e?.querySelector('input[name="after_success_url"]'),c=e?.querySelector('input[name="after_success_behavior"]'),a=document?.querySelector(".newspack-newsletters-signup-modal"),r=i(e?.querySelector("#modal-checkout-product-details"));e?.checkoutComplete||(async()=>{const e=new FormData;newspackBlocksModal.has_unsupported_payment_gateway||e.append("modal_checkout","1"),e.append("action","abandon_modal_checkout"),e.append("_wpnonce",t.checkout_nonce),t.checkout_nonce=null;try{await fetch(newspackBlocksModal.ajax_url,{method:"POST",body:e})}catch(e){console.warn("Unable to empty cart:",e)}})();const l=!(R.contentDocument&&o&&c&&e?.checkoutComplete);if((l||a)&&(A.style.display="flex",R&&n.contains(R)&&(R._ready=!1,R.src="about:blank",R.style.height=L,R.style.visibility="hidden",n.style.height=L,n.removeChild(R)),D&&D.disconnect(),document.querySelectorAll(`.${w}-container`).forEach(e=>S(e)),q&&q.focus(),document.dispatchEvent(E)),e?.checkoutComplete){const e=()=>{if(o&&c){const e=o.getAttribute("value"),t=c.getAttribute("value");if("custom"===t)window.location.href=e;else if("referrer"===t){const e=document.referrer;let t=!1;if(e)try{new URL(e).origin===window.location.origin&&(window.location.href=e,t=!0)}catch(e){}t||window.history.back()}}window?.newspackReaderActivation?.setPendingCheckout?.(),v=!1};"subscription_switch"!==r.action_type&&window?.newspackReaderActivation?.openNewslettersSignupModal?window.newspackReaderActivation.openNewslettersSignupModal({onSuccess:e,onError:e,closeOnSuccess:l}):e(),l&&(g=newspackBlocksModal.labels.checkout_modal_title,I(),N(g))}else window?.newspackReaderActivation?.setPendingCheckout?.(),s(),v=!1,document.getElementById("newspack_modal_checkout").removeAttribute("data-checkout");document.removeEventListener("keydown",U)},O=o=>{o&&(R.src=o),A.style.display="flex",T(t),n.appendChild(R),t.addEventListener("click",e=>{e.target===t&&x()}),e(t,R),document.addEventListener("keydown",U)},T=e=>{window.newspackReaderActivation?.overlays&&(e.overlayId=window.newspackReaderActivation?.overlays.add()),e.setAttribute("data-state","open"),document.body.style.overflow="hidden"},N=e=>{const n=t.querySelector(`.${w}__header h2`);n&&(n.innerText=e)},I=(e="default")=>{const n=t.querySelector(`.${w}`);n&&("small"===e?n.classList.add(`${w}--small`):n.classList.remove(`${w}--small`))};t.querySelectorAll(`.${w}__close`).forEach(e=>{e.addEventListener("click",e=>{e.preventDefault(),x()})}),document.querySelectorAll(".newspack-blocks__modal-variation").forEach(e=>{e.addEventListener("click",t=>{t.target===e&&x()}),e.querySelectorAll(`.${w}__close`).forEach(e=>{e.addEventListener("click",e=>{e.preventDefault(),x()})})});const U=e=>{"Escape"===e.key&&x()};document.querySelectorAll(".wpbnbd.wpbnbd--platform-wc, .wp-block-newspack-blocks-checkout-button, .newspack-blocks__modal-variation").forEach(e=>{e.querySelectorAll("form").forEach(e=>{newspackBlocksModal.has_unsupported_payment_gateway||e.prepend(o.cloneNode()),e.target=_,e.addEventListener("submit",B)})}),(()=>{const e=new URLSearchParams(window.location.search);if(!e.has("checkout"))return;let t=!0;const n=e.get("type");if("donate"===n){const t=e.get("layout"),n=e.get("frequency"),o=e.get("amount"),c=e.get("other");t&&n&&o&&((e,t,n,o=null)=>{let c;document.querySelectorAll(".wpbnbd.wpbnbd--platform-wc form").forEach(a=>{const r=a.querySelector(`input[name="donation_frequency"][value="${t}"]`);if(r)if("tiered"===e){const e=document.querySelector(`button[data-frequency-slug="${t}"]`);if(!e)return;e.click();const o=a.querySelector(`button[type="submit"][name="donation_value_${t}"][value="${n}"]`);if(!o)return;o.click()}else{const i="untiered"===e?a.querySelector(`input[name="donation_value_${t}_untiered"]`):a.querySelector(`input[name="donation_value_${t}"][value="${n}"]`);if(r&&i){if(r.checked=!0,"untiered"===e)i.value=n;else if("other"===n){i.click();const e=a.querySelector(`input[name="donation_value_${t}_other"]`);e&&o&&(e.value=o)}else i.checked=!0;c=a}}}),c&&r(c)})(t,n,o,c)}else if("checkout_button"===n){const n=e.get("product_id"),o=e.get("variation_id");n?t=((e,t=null)=>{const n=function(e,t,n,o={}){const{allowProductOnlyFallback:c=!1}=o;if(null==n||""===String(n))return u(e,t,null);const a=u(e,t,n);if(a)return a;const r=function(e,t,n,o={}){const{variationModalClassPrefix:c,iframeName:a}=o,r=[...e.querySelectorAll(`.${c}`)].find(e=>String(e.dataset.productId)===String(t));if(!r)return null;const i=r.querySelectorAll("form"),s=a?[...i].find(e=>e.getAttribute("target")===a):i[0];if(!s)return null;const l=[...s.querySelectorAll('input[type="radio"][name="product_id"]')].find(e=>String(e.value)===String(n));return l?(l.checked=!0,s):null}(e,t,n,o);return r?(function(e,t,n=d){if(!e||!t)return;const o=t.ownerDocument,c=new FormData(e);n.forEach(e=>{if(t.querySelector(`input[name="${e}"]`))return;const n=c.getAll(e).filter(e=>"string"==typeof e&&e);if(!n.length)return;const a=o.createElement("input");a.type="hidden",a.name=e,a.value=n[n.length-1],t.prepend(a)})}(u(e,t,null),r),r):c?u(e,t,null):null}(document,e,t,{variationModalClassPrefix:f,iframeName:_});if(n)return r(n),!0;const o=`Newspack modal checkout: no checkout form found for product_id "${e}"`+(t?` and variation_id "${t}"`:"")+". The checkout was not triggered.";return console.warn(o),!1})(n,o):(t=!1,console.warn("Newspack modal checkout: checkout_button trigger is missing product_id. The checkout was not triggered."))}else{const e=window.newspackReaderActivation?.getPendingCheckout?.();e&&r(P(e))}t&&window.history.replaceState(null,null,window.location.pathname)})(),window.newspackOpenModalCheckout=({url:e=null,title:t=null,actionType:n=null,afterSuccess:o={},onCheckoutComplete:c=null,onClose:a=null})=>{if(g=t||newspackBlocksModal.labels.checkout_modal_title,N(g),(e=new URL(e||newspackBlocksModal.checkout_url)).searchParams.has("my_account_checkout")&&e.searchParams.delete("my_account_checkout"),e.searchParams.has("modal_checkout")||e.searchParams.set("modal_checkout","1"),n&&e.searchParams.set("action_type",n),o?.url&&e.searchParams.set("after_success_url",o.url),(o?.behavior||o?.url)&&e.searchParams.set("after_success_behavior",o.behavior||"custom"),o?.buttonLabel&&e.searchParams.set("after_success_button_label",o.buttonLabel),c){const e=({detail:{action:e,data:t}})=>{"checkout_completed"===e&&c(t)};window.newspackRAS.push(t=>{t.on("activity",e);const n=()=>{t.off("activity",e),document.removeEventListener("checkout-closed",n)};document.addEventListener("checkout-closed",n)})}if(a){const e=()=>{a(),document.removeEventListener("checkout-closed",e)};document.addEventListener("checkout-closed",e)}O(e.toString())},window.newspackCloseModalCheckout=x},"undefined"!=typeof document&&("complete"!==document.readyState&&"interactive"!==document.readyState?document.addEventListener("DOMContentLoaded",A):A())})();
1
+ (()=>{"use strict";function e(e,t=!1){const n=e.querySelectorAll('button, [href], input:not([type="hidden"]), select, textarea, [tabindex]:not([tabindex="-1"])');if(0===n.length)return!1;const o=n?.[0];let c;o.focus(),t?(document.addEventListener("keydown",function(e){if(("Tab"===e.key||9===e.keyCode)&&e.shiftKey&&document.activeElement===o){const n=t.contentWindow.document,o=n.getElementById("checkout-after-success");c=null!==o?o:n.getElementById("checkout_cancel"),c.focus(),e.preventDefault()}}),document.getElementById("newspack-a11y-last-element").addEventListener("focus",()=>{o.focus()})):(c=n[n.length-1],document.addEventListener("keydown",function(e){("Tab"===e.key||9===e.keyCode)&&(e.shiftKey?document.activeElement===o&&(c.focus(),e.preventDefault()):document.activeElement===c&&(o.focus(),e.preventDefault()))}))}const t=(e,t={})=>({...t,action:e}),n=["action","action_type","amount","currency","product_id","product_type","variation_id","variation_ids","is_variable","is_grouped","child_ids","price_summary","newspack_popup_id","prompt_title","gate_post_id","recurrence","referrer"],o=(e,t="np_modal_checkout_interaction")=>{if("function"==typeof window.gtag&&e){const o={};for(const t of n)e[t]&&("boolean"==typeof e[t]?e[t]=e[t]?"yes":"no":"true"===e[t]?e[t]="yes":"false"===e[t]&&(e[t]="no"),o[t]=e[t].toString());window.gtag("event",t,o)}},c=e=>{if("function"!=typeof window.gtag)return;let n="opened";const{amount:c="",is_variable:a="",is_grouped:r="",price:i="",variation_id:s=""}=e,l={...e};(c||i)&&(l.amount=c||i),a&&!s&&(n="opened_variations"),r&&(n="opened_grouped");const u=t(n,l);o(u)};function a(e,t=null){const n=document.createElement("input");return n.type="hidden",n.name=e,t&&(n.value=t),n}function r(e){e.requestSubmit(e.querySelector('button[type="submit"]'))}function i(e){const t="string"==typeof e?document.getElementById(e):e;if(!t)return console.warn("No container found for checkout data"),{};let n={};if("FORM"===t.tagName){const e=new FormData(t);n=Object.fromEntries(e)}const o=t.dataset.checkout;if(!o)return console.warn("No checkout data found"),n;try{n={...n,...JSON.parse(o)}}catch(e){console.warn("Error parsing checkout data")}const c=new URL(t.ownerDocument.defaultView.location.href);return c.searchParams.get("action_type")&&(n.action_type=c.searchParams.get("action_type")),n}const s=e=>{if("function"!=typeof window.gtag)return;e=e||i("newspack_modal_checkout");const n=t("dismissed",e);o(n)};function l(e){const t=e&&e.dataset?e.dataset.checkout:null;if(!t)return null;try{return JSON.parse(t)}catch(e){return null}}function u(e,t,n=null){const o=e.querySelectorAll(".wp-block-newspack-blocks-checkout-button"),c=null!=n&&""!==String(n);let a=null;return o.forEach(e=>{if(a)return;const o=e.querySelector("form"),r=l(o);r&&String(r.product_id)===String(t)&&(c&&String(r.variation_id)!==String(n)||(a=o))}),a}const d=["after_success_behavior","after_success_url","after_success_button_label","gate_post_id","newspack_popup_id","prompt_title","coupon"];const p={gate:"gate_post_id",prompt:"newspack_popup_id"};const m=newspackBlocksModal.newspack_class_prefix,h="newspack_modal_checkout_iframe",_="newspack_modal_checkout_container",f=`${m}__modal`,w="newspack-blocks__modal-variation",y=`${m}__processing-payment-text`,k=newspackBlocksModal.processing_payment_messages;let v=!1,g=newspackBlocksModal.labels.checkout_modal_title,b=null;const S=e=>{e.overlayId&&window.newspackReaderActivation?.overlays&&window.newspackReaderActivation?.overlays.remove(e.overlayId),e.setAttribute("data-state","closed"),document.body.style.overflow="auto"};window.onpageshow=e=>{e.persisted&&(document.querySelectorAll(".modal-processing").forEach(e=>e.classList.remove("modal-processing")),document.querySelectorAll(".non-modal-checkout-loading").forEach(e=>e.classList.remove("non-modal-checkout-loading")),document.querySelectorAll(`.${f}-container`).forEach(e=>S(e)))};const E=new CustomEvent("checkout-closed");var q;window.newspackRAS=window.newspackRAS||[],q=()=>{const t=document.querySelector("#newspack_modal_checkout");if(!t)return;const n=t.querySelector(`.${f}__content`),o=a("modal_checkout","1"),q=n.querySelector(`.${m}__spinner`);let A=document.querySelector(".newspack-reader__account-link")?.[0];const L="600px",$=document.createElement("iframe");$.name=h,$.style.height=L,$.style.visibility="hidden";const R=e=>{P.observe(e),"none"!==q.style.display&&(q.style.display="none"),"visible"!==$.style.visibility&&($.style.visibility="visible"),$._ready=!0};!function(e,t,n){e._ready=!1,e._readyTimer&&clearTimeout(e._readyTimer);let o=!1,c="";function a(){o||(o=!0,clearTimeout(e._readyTimer),t.call(this))}function r(){"complete"===this.readyState&&a.call(this)}function i(){e._ready=!1,n&&n(),e._readyTimer&&clearTimeout(e._readyTimer);const t=e.contentDocument||e.contentWindow?.document;t&&(t.removeEventListener("DOMContentLoaded",a),t.removeEventListener("readystatechange",r))}function s(){if(e._ready)return void i();const t=e.contentDocument||e.contentWindow?.document;t&&0!==t.URL.indexOf("about:")?"complete"===t?.readyState?a.call(t):(t.addEventListener("DOMContentLoaded",a),t.addEventListener("readystatechange",r)):e._readyTimer=setTimeout(s,10)}e._observer||(e._observer=new MutationObserver(e=>{e.forEach(e=>{"attributes"===e.type&&"src"===e.attributeName&&(o=!1,i(),s())})}),e._observer.observe(e,{attributes:!0})),e._locationObserver||(e._locationObserver=setInterval(function(){const t=e.contentDocument||e.contentWindow?.document;t&&t.URL!==c&&(c=t.URL,o=!1,i(),s())},50)),s()}($,function(){const n=$.contentWindow?.location;if(window.newspackReaderActivation&&n?.href?.includes("order-received")){const e=window.newspackReaderActivation,t=new Proxy(new URLSearchParams(n.search),{get:(e,t)=>e.get(t)});t.email&&(e.setReaderEmail(t.email),e.setAuthenticated(!0))}const o=$?.contentDocument?.querySelector(`#${_}`);if(!o)return;const c=i(o.querySelector("#modal-checkout-product-details")),a=document.createElement("p");a.classList.add(y);let r=[];const s=()=>{r.forEach(e=>clearTimeout(e)),r=[]},l=()=>{q.style.display="none",s(),q.querySelectorAll(`.${y}`).forEach(e=>e.remove())};!function(e,t){e.checkoutCancel?t():e.addEventListener("checkout-cancel",t)}(o,()=>{x()}),function(e,t){e.checkoutPlaceOrderStart?t():e.addEventListener("checkout-place-order-start",t)}(o,e=>{q.querySelectorAll(`.${y}`).forEach(e=>e.remove()),q.style.display="flex",s(),a.textContent=k[0]?.text??"",k.slice(1).forEach(({text:t,delay:n})=>{const o=setTimeout(()=>{e.target.dispatchEvent(new CustomEvent("checkout-place-order-processing")),a.textContent=t},n);r.push(o)}),q.appendChild(a)}),function(e,t){e.checkoutPlaceOrderError?t():e.addEventListener("checkout-place-order-error",t)}(o,l),function(e,t){e.checkoutReady?t():e.addEventListener("checkout-ready",t)}(o,()=>{if(t.initialErrors){const e=document.createElement("div");e.classList.add("woocommerce-error"),e.textContent=t.initialErrors,o.prepend(e),delete t.initialErrors}I(),N(g),$.contentWindow?.newspackBlocksModalCheckout?.checkout_nonce&&(t.checkout_nonce=$.contentWindow.newspackBlocksModalCheckout.checkout_nonce),R(o)}),function(e,t){e.checkoutComplete?t():e.addEventListener("checkout-complete",t)}(o,()=>{window.newspackRAS.push(["checkout_completed",c]),window?.newspackReaderActivation?.refreshNewslettersSignupModal&&window?.newspackReaderActivation?.getReader()?.email&&window.newspackReaderActivation.refreshNewslettersSignupModal(window.newspackReaderActivation.getReader().email),I("small"),N(newspackBlocksModal.labels.thankyou_modal_title),R(o),e(t.querySelector(`.${f}`)),l()}),function(e,t){e.checkoutPlaceOrderCriticalError?t():e.addEventListener("checkout-place-order-critical-error",t)}(o,()=>{var e;(e=b)&&(x(),q.style.display="none",t.initialErrors=newspackBlocksModal.labels.critical_error,e.requestSubmit(e.querySelector('button[type="submit"]')),l())})},()=>{q.style.display="flex"});const M=e=>new Promise((t,n)=>{const o=new URLSearchParams(e);o.append("action","modal_checkout_request"),fetch(newspackBlocksModal.ajax_url+"?"+o.toString()).then(e=>{e.ok||n(e),e.json().then(e=>{t(e.url)}).catch(n)}).catch(n)}),C=()=>"undefined"!=typeof newspack_ras_config&&!newspack_ras_config?.is_logged_in&&!window?.newspackReaderActivation?.getReader?.()?.authenticated&&newspackBlocksModal?.is_registration_required&&window?.newspackReaderActivation?.openAuthModal,B=t=>{const n=!newspackBlocksModal.has_unsupported_payment_gateway;n||t.preventDefault();const o=t.target;o.classList.add("modal-processing"),function(e){if(!e||"function"!=typeof e.querySelector)return!1;if(e.closest(".newspack-content-gate__gate"))return!1;const t=function(){let e,t;try{e=window.sessionStorage.getItem("newspack_cta_attribution")}catch(e){return null}if(!e)return null;try{t=JSON.parse(e)}catch(e){return null}return t&&1===t.v&&p[t.type]&&t.id?"number"!=typeof t.ts||Date.now()-t.ts>18e5?null:t:null}();if(!t)return!1;const n=p[t.type];if(e.querySelector(`input[name="${n}"]`))return!1;const o=document.createElement("input");o.type="hidden",o.name=n,o.value=t.id,e.prepend(o)}(o);const u=i(o);if(u.newspack_donate){const e=u.donation_frequency,t=[...o.querySelectorAll(`.donation-tier__${e}, .donation-frequency__${e}`)],n=u.donation_tier_index;let c,a;n?(c=t[n],a=u[`donation_value_${e}`]):(c=t[0],u[`donation_value_${e}_untiered`]?a=u[`donation_value_${e}_untiered`]:(a=u[`donation_value_${e}`],"other"===a&&(a=u[`donation_value_${e}_other`])));const r=i(c);for(const e in r)u[e]=r[e];u.amount=a,u.price_summary=u.summary_template.replace("{{PRICE}}",function(e,t="USD"){return parseFloat(e).toLocaleString(document.documentElement.lang,{style:"currency",currency:t,currencyDisplay:"narrowSymbol"})}(u.amount,u.currency))}u&&Object.keys(u).forEach(e=>{0===o.querySelectorAll('input[name="'+e+'"]').length&&o.prepend(a(e,u[e]))}),u.variation_id||(A=t.submitter);const m=document.querySelectorAll(`.${w}`);if(m.forEach(e=>{(C()||n)&&S(e)}),u.is_grouped||u.is_variable&&!u.variation_id){const n=[...m].find(e=>e.dataset.productId===u.product_id);if(n)return n.querySelectorAll(`form[target="${h}"]`).forEach(e=>{!function(e,t,n=d){if(!e||!t)return;const o=e.ownerDocument;n.forEach(n=>{e.querySelectorAll(`input[name="${n}"]`).forEach(e=>e.remove());const c=t[n],a=null==c?"":String(c);if(!a)return;const r=o.createElement("input");r.type="hidden",r.name=n,r.value=a,e.prepend(r)})}(e,u);const t=l(e);t&&Object.keys(t).forEach(n=>{0===e.querySelectorAll('input[name="'+n+'"]').length&&e.prepend(a(n,t[n]))})}),t.preventDefault(),o.classList.remove("modal-processing"),T(n),e(n,!1),v||c(u),void document.getElementById("newspack_modal_checkout").setAttribute("data-checkout",JSON.stringify(u))}if(n||C()){if(o.classList.remove("modal-processing"),v||c(u),v=!0,C()){t.preventDefault();const e=M(u);e.then(e=>{window.newspackReaderActivation?.setPendingCheckout?.(e)}),window.newspackReaderActivation.openAuthModal({title:newspackBlocksModal.labels.auth_modal_title,onSuccess:(t,o)=>{e.then(e=>{o?.registered&&n&&(e+=`&${newspackBlocksModal.checkout_registration_flag}=1`),n?r(D(e)):M(u).then(window.location.href=e)}).catch(e=>{console.warn("Unable to generate cart:",e),x()})},onError:()=>{x()},onDismiss:()=>{s(u),v=!1,document.getElementById("newspack_modal_checkout").removeAttribute("data-checkout")},skipSuccess:!0,skipNewslettersSignup:!0,labels:{signin:{title:newspackBlocksModal.labels.signin_modal_title},register:{title:newspackBlocksModal.labels.register_modal_title}},trigger:t.submitter,closeOnSuccess:n})}else O(),document.getElementById("newspack_modal_checkout").setAttribute("data-checkout",JSON.stringify(u));b=o}else M(u).then(e=>{window.location.href=e}),(!u.is_variable||u.variation_id)&&o.querySelectorAll("button[type=submit]:focus").forEach(e=>{e.classList.add("non-modal-checkout-loading");const t=e.innerHTML;e.innerHTML="<span>"+t+"</span>"})},D=e=>{const t=document.createElement("form");t.method="POST",t.action=e,t.target=h,t.style.display="none";const n=document.createElement("button");return n.setAttribute("type","submit"),t.appendChild(n),document.body.appendChild(t),t.addEventListener("submit",B),t},P=new ResizeObserver(e=>{if(!e||!e.length)return;if(!$.contentDocument)return;const o=e[0].contentRect;if(o){const e=.01*Math.max(document.documentElement.clientHeight,window.innerHeight||0)*90-(t.querySelector(`.${f}__header`)?.offsetHeight||0),c=o.top+o.bottom,a=Math.min(c,e);if(0===a)return void($.style.visibility="hidden");n.style.height=a+"px",$.style.height=a+"px"}}),x=()=>{const e=$?.contentDocument?.querySelector(`#${_}`),o=e?.querySelector('input[name="after_success_url"]'),c=e?.querySelector('input[name="after_success_behavior"]'),a=document?.querySelector(".newspack-newsletters-signup-modal"),r=i(e?.querySelector("#modal-checkout-product-details"));e?.checkoutComplete||(async()=>{const e=new FormData;newspackBlocksModal.has_unsupported_payment_gateway||e.append("modal_checkout","1"),e.append("action","abandon_modal_checkout"),e.append("_wpnonce",t.checkout_nonce),t.checkout_nonce=null;try{await fetch(newspackBlocksModal.ajax_url,{method:"POST",body:e})}catch(e){console.warn("Unable to empty cart:",e)}})();const l=!($.contentDocument&&o&&c&&e?.checkoutComplete);if((l||a)&&(q.style.display="flex",$&&n.contains($)&&($._ready=!1,$.src="about:blank",$.style.height=L,$.style.visibility="hidden",n.style.height=L,n.removeChild($)),P&&P.disconnect(),document.querySelectorAll(`.${f}-container`).forEach(e=>S(e)),A&&A.focus(),document.dispatchEvent(E)),e?.checkoutComplete){const e=()=>{if(o&&c){const e=o.getAttribute("value"),t=c.getAttribute("value");if("custom"===t)window.location.href=e;else if("referrer"===t){const e=document.referrer;let t=!1;if(e)try{new URL(e).origin===window.location.origin&&(window.location.href=e,t=!0)}catch(e){}t||window.history.back()}}window?.newspackReaderActivation?.setPendingCheckout?.(),v=!1};"subscription_switch"!==r.action_type&&window?.newspackReaderActivation?.openNewslettersSignupModal?window.newspackReaderActivation.openNewslettersSignupModal({onSuccess:e,onError:e,closeOnSuccess:l}):e(),l&&(g=newspackBlocksModal.labels.checkout_modal_title,I(),N(g))}else window?.newspackReaderActivation?.setPendingCheckout?.(),s(),v=!1,document.getElementById("newspack_modal_checkout").removeAttribute("data-checkout");document.removeEventListener("keydown",U)},O=o=>{o&&($.src=o),q.style.display="flex",T(t),n.appendChild($),t.addEventListener("click",e=>{e.target===t&&x()}),e(t,$),document.addEventListener("keydown",U)},T=e=>{window.newspackReaderActivation?.overlays&&(e.overlayId=window.newspackReaderActivation?.overlays.add()),e.setAttribute("data-state","open"),document.body.style.overflow="hidden"},N=e=>{const n=t.querySelector(`.${f}__header h2`);n&&(n.innerText=e)},I=(e="default")=>{const n=t.querySelector(`.${f}`);n&&("small"===e?n.classList.add(`${f}--small`):n.classList.remove(`${f}--small`))};t.querySelectorAll(`.${f}__close`).forEach(e=>{e.addEventListener("click",e=>{e.preventDefault(),x()})}),document.querySelectorAll(".newspack-blocks__modal-variation").forEach(e=>{e.addEventListener("click",t=>{t.target===e&&x()}),e.querySelectorAll(`.${f}__close`).forEach(e=>{e.addEventListener("click",e=>{e.preventDefault(),x()})})});const U=e=>{"Escape"===e.key&&x()};document.querySelectorAll(".wpbnbd.wpbnbd--platform-wc, .wp-block-newspack-blocks-checkout-button, .newspack-blocks__modal-variation").forEach(e=>{e.querySelectorAll("form").forEach(e=>{newspackBlocksModal.has_unsupported_payment_gateway||e.prepend(o.cloneNode()),e.target=h,e.addEventListener("submit",B)})}),(()=>{const e=new URLSearchParams(window.location.search);if(!e.has("checkout"))return;let t=!0;const n=e.get("type");if("donate"===n){const t=e.get("layout"),n=e.get("frequency"),o=e.get("amount"),c=e.get("other");t&&n&&o&&((e,t,n,o=null)=>{let c;document.querySelectorAll(".wpbnbd.wpbnbd--platform-wc form").forEach(a=>{const r=a.querySelector(`input[name="donation_frequency"][value="${t}"]`);if(r)if("tiered"===e){const e=document.querySelector(`button[data-frequency-slug="${t}"]`);if(!e)return;e.click();const o=a.querySelector(`button[type="submit"][name="donation_value_${t}"][value="${n}"]`);if(!o)return;o.click()}else{const i="untiered"===e?a.querySelector(`input[name="donation_value_${t}_untiered"]`):a.querySelector(`input[name="donation_value_${t}"][value="${n}"]`);if(r&&i){if(r.checked=!0,"untiered"===e)i.value=n;else if("other"===n){i.click();const e=a.querySelector(`input[name="donation_value_${t}_other"]`);e&&o&&(e.value=o)}else i.checked=!0;c=a}}}),c&&r(c)})(t,n,o,c)}else if("checkout_button"===n){const n=e.get("product_id"),o=e.get("variation_id");n?t=((e,t=null)=>{const n=function(e,t,n,o={}){const{allowProductOnlyFallback:c=!1}=o;if(null==n||""===String(n))return u(e,t,null);const a=u(e,t,n);if(a)return a;const r=function(e,t,n,o={}){const{variationModalClassPrefix:c,iframeName:a}=o,r=[...e.querySelectorAll(`.${c}`)].find(e=>String(e.dataset.productId)===String(t));if(!r)return null;const i=r.querySelectorAll("form"),s=a?[...i].find(e=>e.getAttribute("target")===a):i[0];if(!s)return null;const l=[...s.querySelectorAll('input[type="radio"][name="product_id"]')].find(e=>String(e.value)===String(n));return l?(l.checked=!0,s):null}(e,t,n,o);return r?(function(e,t,n=d){if(!e||!t)return;const o=t.ownerDocument,c=new FormData(e);n.forEach(e=>{if(t.querySelector(`input[name="${e}"]`))return;const n=c.getAll(e).filter(e=>"string"==typeof e&&e);if(!n.length)return;const a=o.createElement("input");a.type="hidden",a.name=e,a.value=n[n.length-1],t.prepend(a)})}(function(e,t){const n=e.querySelectorAll(".wp-block-newspack-blocks-checkout-button");let o=null,c=null;return n.forEach(e=>{if(c)return;const n=e.querySelector("form"),a=l(n);a&&String(a.product_id)===String(t)&&(a.variation_id?o=o||n:c=n)}),c||o}(e,t),r),r):c?u(e,t,null):null}(document,e,t,{variationModalClassPrefix:w,iframeName:h});if(n)return r(n),!0;const o=`Newspack modal checkout: no checkout form found for product_id "${e}"`+(t?` and variation_id "${t}"`:"")+". The checkout was not triggered.";return console.warn(o),!1})(n,o):(t=!1,console.warn("Newspack modal checkout: checkout_button trigger is missing product_id. The checkout was not triggered."))}else{const e=window.newspackReaderActivation?.getPendingCheckout?.();e&&r(D(e))}t&&window.history.replaceState(null,null,window.location.pathname)})(),window.newspackOpenModalCheckout=({url:e=null,title:t=null,actionType:n=null,afterSuccess:o={},onCheckoutComplete:c=null,onClose:a=null})=>{if(g=t||newspackBlocksModal.labels.checkout_modal_title,N(g),(e=new URL(e||newspackBlocksModal.checkout_url)).searchParams.has("my_account_checkout")&&e.searchParams.delete("my_account_checkout"),e.searchParams.has("modal_checkout")||e.searchParams.set("modal_checkout","1"),n&&e.searchParams.set("action_type",n),o?.url&&e.searchParams.set("after_success_url",o.url),(o?.behavior||o?.url)&&e.searchParams.set("after_success_behavior",o.behavior||"custom"),o?.buttonLabel&&e.searchParams.set("after_success_button_label",o.buttonLabel),c){const e=({detail:{action:e,data:t}})=>{"checkout_completed"===e&&c(t)};window.newspackRAS.push(t=>{t.on("activity",e);const n=()=>{t.off("activity",e),document.removeEventListener("checkout-closed",n)};document.addEventListener("checkout-closed",n)})}if(a){const e=()=>{a(),document.removeEventListener("checkout-closed",e)};document.addEventListener("checkout-closed",e)}O(e.toString())},window.newspackCloseModalCheckout=x},"undefined"!=typeof document&&("complete"!==document.readyState&&"interactive"!==document.readyState?document.addEventListener("DOMContentLoaded",q):q())})();
@@ -7,7 +7,7 @@
7
7
  * Author URI: https://newspack.com/
8
8
  * Text Domain: newspack-blocks
9
9
  * Domain Path: /languages
10
- * Version: 4.29.0-alpha.1
10
+ * Version: 4.29.0
11
11
  *
12
12
  * @package Newspack_Blocks
13
13
  */
@@ -15,7 +15,7 @@
15
15
  define( 'NEWSPACK_BLOCKS__PLUGIN_FILE', __FILE__ );
16
16
  define( 'NEWSPACK_BLOCKS__BLOCKS_DIRECTORY', 'dist/' );
17
17
  define( 'NEWSPACK_BLOCKS__PLUGIN_DIR', plugin_dir_path( NEWSPACK_BLOCKS__PLUGIN_FILE ) );
18
- define( 'NEWSPACK_BLOCKS__VERSION', '4.29.0-alpha.1' );
18
+ define( 'NEWSPACK_BLOCKS__VERSION', '4.29.0' );
19
19
 
20
20
  require_once NEWSPACK_BLOCKS__PLUGIN_DIR . 'includes/class-newspack-blocks.php';
21
21
  require_once NEWSPACK_BLOCKS__PLUGIN_DIR . 'includes/class-newspack-blocks-api.php';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automattic/newspack-blocks",
3
- "version": "4.29.0-alpha.1",
3
+ "version": "4.29.0",
4
4
  "author": "Automattic",
5
5
  "description": "=== Newspack Blocks === Contributors: (this should be a list of wordpress.org userid's) Donate link: https://example.com/ Tags: comments, spam Requires at least: 4.5 Tested up to: 5.1.1 Stable tag: 0.1.0 License: GPLv2 or later License URI: https://www.gnu.org/licenses/gpl-2.0.html",
6
6
  "repository": {
@@ -46,8 +46,8 @@
46
46
  "dependencies": {
47
47
  "classnames": "^2.5.1",
48
48
  "lodash": "^4.17.21",
49
- "newspack-colors": "1.1.1-alpha.1",
50
- "newspack-icons": "1.1.0-alpha.1",
49
+ "newspack-colors": "1.1.1",
50
+ "newspack-icons": "1.1.0",
51
51
  "redux": "^5.0.0",
52
52
  "redux-saga": "^1.5.0",
53
53
  "regenerator-runtime": "^0.14.1",
@@ -55,8 +55,8 @@
55
55
  },
56
56
  "devDependencies": {
57
57
  "@types/lodash": "^4.17.23",
58
- "newspack-components": "4.6.0-alpha.1",
59
- "newspack-scripts": "5.10.0-alpha.1"
58
+ "newspack-components": "4.6.0",
59
+ "newspack-scripts": "5.10.0"
60
60
  },
61
61
  "overrides": {
62
62
  "path-to-regexp": "1.7.0"
@@ -20,9 +20,11 @@ import apiFetch from '@wordpress/api-fetch';
20
20
  * @param {Object} props
21
21
  * @param {string} props.value Currently selected coupon code.
22
22
  * @param {Function} props.onChange Called with the selected coupon code (or '').
23
+ * @param {boolean} props.disabled Whether a coupon can be set. A stored code stays
24
+ * visible and removable so it can't get stranded.
23
25
  * @return {JSX.Element} The control.
24
26
  */
25
- export default function CouponControl( { value, onChange } ) {
27
+ export default function CouponControl( { value, onChange, disabled = false } ) {
26
28
  const [ inFlight, setInFlight ] = useState( false );
27
29
  const [ suggestions, setSuggestions ] = useState( [] );
28
30
  const [ selected, setSelected ] = useState( false );
@@ -100,9 +102,13 @@ export default function CouponControl( { value, onChange } ) {
100
102
  <div className="newspack-checkout-button__coupon-field">
101
103
  <BaseControl label={ __( 'Coupon', 'newspack-blocks' ) } id="newspack-checkout-button-coupon">
102
104
  <TextControl value={ selected } __next40pxDefaultSize disabled />
103
- <Button variant="link" onClick={ () => setIsChanging( true ) }>
104
- { __( 'Edit', 'newspack-blocks' ) }
105
- </Button>{ ' ' }
105
+ { ! disabled && (
106
+ <>
107
+ <Button variant="link" onClick={ () => setIsChanging( true ) }>
108
+ { __( 'Edit', 'newspack-blocks' ) }
109
+ </Button>{ ' ' }
110
+ </>
111
+ ) }
106
112
  <Button
107
113
  variant="link"
108
114
  isDestructive
@@ -121,13 +127,16 @@ export default function CouponControl( { value, onChange } ) {
121
127
  return (
122
128
  <div className="newspack-checkout-button__coupon-field">
123
129
  <FormTokenField
124
- placeholder={ __( 'Type to search for a coupon…', 'newspack-blocks' ) }
130
+ placeholder={
131
+ disabled ? __( 'Coupons are not available', 'newspack-blocks' ) : __( 'Type to search for a coupon…', 'newspack-blocks' )
132
+ }
125
133
  label={ __( 'Coupon', 'newspack-blocks' ) }
126
134
  maxLength={ 1 }
127
135
  value={ [] }
128
136
  onChange={ onTokenChange }
129
137
  onInputChange={ handleInputChange }
130
- suggestions={ suggestions }
138
+ suggestions={ disabled ? [] : suggestions }
139
+ disabled={ disabled }
131
140
  __next40pxDefaultSize
132
141
  />
133
142
  { inFlight && <Spinner /> }
@@ -27,6 +27,7 @@ import {
27
27
  SelectControl,
28
28
  FormTokenField,
29
29
  Button,
30
+ Notice,
30
31
  Spinner,
31
32
  // eslint-disable-next-line @wordpress/no-unsafe-wp-apis
32
33
  __experimentalToggleGroupControl as ToggleGroupControl,
@@ -138,6 +139,10 @@ function ProductControl( props ) {
138
139
  fetchSaved();
139
140
  } else {
140
141
  setSelected( false );
142
+ // Clear the parent's copy too, otherwise product-derived state (e.g.
143
+ // whether this is a donation) keeps describing the product that was
144
+ // just removed.
145
+ props.onProduct( {} );
141
146
  }
142
147
  }, [ props.value ] );
143
148
  function onChange( tokens ) {
@@ -208,6 +213,11 @@ function CheckoutButtonEdit( props ) {
208
213
  const [ variations, setVariations ] = useState( [] );
209
214
  const [ nyp, setNYP ] = useState( false );
210
215
 
216
+ // Resolved server-side, since a donation is not always identifiable from the
217
+ // product meta. Variations inherit it from their parent, which is the product
218
+ // fetched here, so the selected variation needs no separate check.
219
+ const isDonation = !! productData?.newspack_is_donation;
220
+
211
221
  function handleProduct( data ) {
212
222
  setProductData( data );
213
223
  // Handle product variation data.
@@ -339,7 +349,26 @@ function CheckoutButtonEdit( props ) {
339
349
  ) }
340
350
  </ProductControl>
341
351
  { newspack_blocks_data?.coupons_enabled && (
342
- <CouponControl value={ coupon } onChange={ value => setAttributes( { coupon: value } ) } />
352
+ <>
353
+ { /*
354
+ * Coupons are not supported on donation products. The control is
355
+ * disabled rather than hidden so a code stored before the product
356
+ * was swapped stays visible — and removable, since the block emits
357
+ * it regardless of donation status.
358
+ */ }
359
+ { isDonation && (
360
+ <Notice status={ coupon ? 'warning' : 'info' } isDismissible={ false }>
361
+ { coupon
362
+ ? sprintf(
363
+ // translators: %s: the coupon code stored on the block.
364
+ __( 'Donation products do not support coupons. "%s" will not be applied.', 'newspack-blocks' ),
365
+ coupon
366
+ )
367
+ : __( 'Donation products do not support coupons.', 'newspack-blocks' ) }
368
+ </Notice>
369
+ ) }
370
+ <CouponControl value={ coupon } onChange={ value => setAttributes( { coupon: value } ) } disabled={ isDonation } />
371
+ </>
343
372
  ) }
344
373
  <WidthControl selectedWidth={ width } setAttributes={ setAttributes } />
345
374
  </PanelBody>
@@ -24,6 +24,38 @@ function register_block() {
24
24
  }
25
25
  add_action( 'init', __NAMESPACE__ . '\\register_block' );
26
26
 
27
+ /**
28
+ * Expose whether a product is a donation to the products REST response.
29
+ *
30
+ * The block editor needs this to tell the publisher that an attached coupon
31
+ * will not be applied: Newspack disables coupons for any cart containing a
32
+ * donation (see Newspack\Donations::disable_coupons()). It cannot be derived
33
+ * from the product meta alone, because the legacy donation product and its
34
+ * children are matched by ID rather than by the donation flag, so the answer
35
+ * is delegated to Donations::is_donation_product().
36
+ */
37
+ function register_donation_rest_field() {
38
+ if ( ! class_exists( '\Newspack\Donations' ) || ! method_exists( '\Newspack\Donations', 'is_donation_product' ) ) {
39
+ return;
40
+ }
41
+ register_rest_field(
42
+ 'product',
43
+ 'newspack_is_donation',
44
+ [
45
+ 'get_callback' => function ( $product ) {
46
+ return (bool) \Newspack\Donations::is_donation_product( $product['id'] );
47
+ },
48
+ 'schema' => [
49
+ 'description' => __( 'Whether the product is treated as a donation by Newspack.', 'newspack-blocks' ),
50
+ 'type' => 'boolean',
51
+ 'context' => [ 'view', 'edit' ],
52
+ 'readonly' => true,
53
+ ],
54
+ ]
55
+ );
56
+ }
57
+ add_action( 'rest_api_init', __NAMESPACE__ . '\\register_donation_rest_field' );
58
+
27
59
  /**
28
60
  * Render the block.
29
61
  *
@@ -58,6 +58,41 @@ export function findCheckoutButtonForm( root, productId, variationId = null ) {
58
58
  return match;
59
59
  }
60
60
 
61
+ /**
62
+ * Find the button whose context a picker submission should inherit.
63
+ *
64
+ * Prefers a button that is not locked to a single variation: it stands for the
65
+ * whole product, so its context — including any attached coupon — applies to
66
+ * whichever variation the reader picks. A locked button was configured for one
67
+ * specific variation and is only used when nothing better exists.
68
+ *
69
+ * @param {Document|HTMLElement} root The DOM root to search.
70
+ * @param {string} productId The requested product ID.
71
+ *
72
+ * @return {HTMLFormElement|null} The donor form, or null.
73
+ */
74
+ function findContextDonorForm( root, productId ) {
75
+ const buttons = root.querySelectorAll( '.wp-block-newspack-blocks-checkout-button' );
76
+ let fallback = null;
77
+ let unlocked = null;
78
+ buttons.forEach( button => {
79
+ if ( unlocked ) {
80
+ return;
81
+ }
82
+ const form = button.querySelector( 'form' );
83
+ const data = readCheckoutData( form );
84
+ if ( ! data || String( data.product_id ) !== String( productId ) ) {
85
+ return;
86
+ }
87
+ if ( ! data.variation_id ) {
88
+ unlocked = form;
89
+ return;
90
+ }
91
+ fallback = fallback || form;
92
+ } );
93
+ return unlocked || fallback;
94
+ }
95
+
61
96
  /**
62
97
  * Select the requested variation in a product picker.
63
98
  * Picker forms use the selected radio value instead of `data-checkout`.
@@ -98,6 +133,12 @@ export function selectPickerForm( root, productId, variationId, options = {} ) {
98
133
  /**
99
134
  * Hidden fields copied from a source checkout button to a picker submission.
100
135
  *
136
+ * The picker form is rendered once per variable product in the footer and is
137
+ * submitted instead of the button's own form, so anything the block attached to
138
+ * that button — after-success behavior, attribution, the auto-applied coupon —
139
+ * is lost unless it is listed here. Shared with modal.js so the click path and
140
+ * the URL-trigger path carry the same context.
141
+ *
101
142
  * @type {string[]}
102
143
  */
103
144
  export const PICKER_CONTEXT_FIELDS = [
@@ -107,12 +148,54 @@ export const PICKER_CONTEXT_FIELDS = [
107
148
  'gate_post_id',
108
149
  'newspack_popup_id',
109
150
  'prompt_title',
151
+ 'coupon',
110
152
  ];
111
153
 
154
+ /**
155
+ * Stamp context fields onto a picker form from the button that opened it.
156
+ *
157
+ * Authoritative, unlike copyContextFields(): the picker is rendered once per
158
+ * parent product and shared by every button targeting it, and nothing clears it
159
+ * when the modal closes, so each open must overwrite whatever the previous one
160
+ * left behind. An absent value removes the field rather than preserving a stale
161
+ * one — for `coupon` that is the difference between the reader's discount and
162
+ * a discount attached to a different button.
163
+ *
164
+ * @param {HTMLFormElement|null} targetForm Picker form to stamp.
165
+ * @param {Object|null} data Checkout data read from the clicked button's form.
166
+ * @param {string[]} fields Field names to stamp.
167
+ *
168
+ * @return {void}
169
+ */
170
+ export function applyContextFields( targetForm, data, fields = PICKER_CONTEXT_FIELDS ) {
171
+ if ( ! targetForm || ! data ) {
172
+ return;
173
+ }
174
+ const doc = targetForm.ownerDocument;
175
+ fields.forEach( name => {
176
+ // Drop whatever a previous open left behind before writing this one's value.
177
+ targetForm.querySelectorAll( `input[name="${ name }"]` ).forEach( input => input.remove() );
178
+ const raw = data[ name ];
179
+ const value = raw === undefined || raw === null ? '' : String( raw );
180
+ if ( ! value ) {
181
+ return;
182
+ }
183
+ const input = doc.createElement( 'input' );
184
+ input.type = 'hidden';
185
+ input.name = name;
186
+ input.value = value;
187
+ targetForm.prepend( input );
188
+ } );
189
+ }
190
+
112
191
  /**
113
192
  * Copy context fields. Target values are preserved, empty source values are
114
193
  * skipped, and null forms are ignored.
115
194
  *
195
+ * Used by the URL-trigger path, which runs once on load before any click. The
196
+ * click path uses applyContextFields() instead and overwrites anything left
197
+ * here, so preserving existing values cannot strand a stale coupon.
198
+ *
116
199
  * @param {HTMLFormElement|null} sourceForm Checkout button form to read from.
117
200
  * @param {HTMLFormElement|null} targetForm Picker form to copy into.
118
201
  * @param {string[]} fields Field names to copy.
@@ -173,12 +256,14 @@ export function resolveCheckoutButtonForm( root, productId, variationId, options
173
256
 
174
257
  const picker = selectPickerForm( root, productId, variationId, options );
175
258
  if ( picker ) {
176
- // The source button may be locked to another variation. Use it only
177
- // for block context, then submit the picker. The picker is only reached
178
- // because no button matches the requested variation, so when several
179
- // buttons share this parent product there is no single correct one to
180
- // prefer: the first in DOM order supplies the context.
181
- copyContextFields( findCheckoutButtonForm( root, productId, null ), picker );
259
+ // The picker is only reached because no button matches the requested
260
+ // variation, so the context has to come from some other button for this
261
+ // product. Since PICKER_CONTEXT_FIELDS now carries `coupon`, that choice
262
+ // decides a discount: a button locked to a variation was configured for
263
+ // that variation, so its coupon should not follow the reader to a
264
+ // different one. Prefer an unlocked button, which stands for the whole
265
+ // product, and fall back to DOM order only when there isn't one.
266
+ copyContextFields( findContextDonorForm( root, productId ), picker );
182
267
  return picker;
183
268
  }
184
269
 
@@ -2,7 +2,15 @@
2
2
  * Tests for the checkout-button URL trigger resolution helpers.
3
3
  */
4
4
 
5
- import { readCheckoutData, findCheckoutButtonForm, selectPickerForm, resolveCheckoutButtonForm, copyContextFields } from './checkout-button-trigger';
5
+ import {
6
+ readCheckoutData,
7
+ findCheckoutButtonForm,
8
+ selectPickerForm,
9
+ resolveCheckoutButtonForm,
10
+ copyContextFields,
11
+ applyContextFields,
12
+ PICKER_CONTEXT_FIELDS,
13
+ } from './checkout-button-trigger';
6
14
 
7
15
  const VARIATION_MODAL_CLASS_PREFIX = 'newspack-blocks__modal-variation';
8
16
  const IFRAME_NAME = 'newspack_modal_checkout_iframe';
@@ -220,6 +228,46 @@ describe( 'resolveCheckoutButtonForm', () => {
220
228
  expect( pickerForm.querySelector( 'input[name="after_success_button_label"]' ).value ).toBe( 'Thanks!' );
221
229
  expect( pickerForm.querySelector( 'input[name="after_success_url"]' ).value ).toBe( '/welcome/' );
222
230
  } );
231
+
232
+ // A button locked to one variation was configured for that variation, so its
233
+ // coupon should not follow the reader to a different one when an unlocked
234
+ // button for the same product is available to supply the context instead.
235
+ it( 'prefers an unlocked button over a variation-locked one for the picker context', () => {
236
+ const locked = `<div class="wp-block-newspack-blocks-checkout-button"><form data-checkout='${ JSON.stringify( {
237
+ product_id: '1406',
238
+ variation_id: '1408',
239
+ is_variable: true,
240
+ } ) }'><input type="hidden" name="coupon" value="LOCKED20"><button type="submit">Annual</button></form></div>`;
241
+ const unlocked = `<div class="wp-block-newspack-blocks-checkout-button"><form data-checkout='${ JSON.stringify( {
242
+ product_id: '1406',
243
+ is_variable: true,
244
+ } ) }'><input type="hidden" name="coupon" value="ANYTIER5"><button type="submit">Subscribe</button></form></div>`;
245
+ const root = render( locked + unlocked + variationPicker( '1406', [ '1407', '1408', '1409' ] ) );
246
+ const pickerForm = root.querySelector( `.${ VARIATION_MODAL_CLASS_PREFIX } form` );
247
+
248
+ const result = resolveCheckoutButtonForm( root, '1406', '1407', PICKER_OPTIONS );
249
+
250
+ expect( result ).toBe( pickerForm );
251
+ expect( pickerForm.querySelector( 'input[name="coupon"]' ).value ).toBe( 'ANYTIER5' );
252
+ } );
253
+
254
+ // With nothing but locked buttons there is no correct donor, so the first in
255
+ // DOM order supplies the context — the other half of findContextDonorForm.
256
+ it( 'falls back to the first locked button when no unlocked one exists', () => {
257
+ const locked = ( variationId, coupon ) =>
258
+ `<div class="wp-block-newspack-blocks-checkout-button"><form data-checkout='${ JSON.stringify( {
259
+ product_id: '1406',
260
+ variation_id: variationId,
261
+ is_variable: true,
262
+ } ) }'><input type="hidden" name="coupon" value="${ coupon }"><button type="submit">Buy</button></form></div>`;
263
+ const root = render( locked( '1408', 'FIRST20' ) + locked( '1409', 'SECOND10' ) + variationPicker( '1406', [ '1407', '1408', '1409' ] ) );
264
+ const pickerForm = root.querySelector( `.${ VARIATION_MODAL_CLASS_PREFIX } form` );
265
+
266
+ const result = resolveCheckoutButtonForm( root, '1406', '1407', PICKER_OPTIONS );
267
+
268
+ expect( result ).toBe( pickerForm );
269
+ expect( pickerForm.querySelector( 'input[name="coupon"]' ).value ).toBe( 'FIRST20' );
270
+ } );
223
271
  } );
224
272
 
225
273
  describe( 'copyContextFields', () => {
@@ -263,4 +311,98 @@ describe( 'copyContextFields', () => {
263
311
  expect( () => copyContextFields( null, target ) ).not.toThrow();
264
312
  expect( () => copyContextFields( target, null ) ).not.toThrow();
265
313
  } );
314
+
315
+ // The picker form replaces the button's own form, so a coupon attached to the
316
+ // Checkout Button block is only auto-applied if it is carried across.
317
+ it( 'copies the auto-applied coupon to the picker form', () => {
318
+ const root = render( '<form id="src"><input type="hidden" name="coupon" value="MEMBER10"></form><form id="dst"></form>' );
319
+
320
+ copyContextFields( root.querySelector( '#src' ), root.querySelector( '#dst' ) );
321
+
322
+ expect( root.querySelector( '#dst input[name="coupon"]' ).value ).toBe( 'MEMBER10' );
323
+ } );
324
+ } );
325
+
326
+ describe( 'applyContextFields', () => {
327
+ // The picker is rendered once per parent product and shared by every button
328
+ // targeting it, and nothing clears it when the modal closes. These two cases
329
+ // are what the click path gets wrong if the stamp is not authoritative.
330
+ const picker = () => render( '<form id="picker"></form>' ).querySelector( '#picker' );
331
+ const couponValue = form => {
332
+ const input = form.querySelector( 'input[name="coupon"]' );
333
+ return input ? input.value : null;
334
+ };
335
+
336
+ it( 'replaces a coupon left behind by a previously clicked button', () => {
337
+ const form = picker();
338
+
339
+ applyContextFields( form, { coupon: 'SAVE20' } );
340
+ applyContextFields( form, { coupon: 'MEMBER10' } );
341
+
342
+ expect( couponValue( form ) ).toBe( 'MEMBER10' );
343
+ expect( form.querySelectorAll( 'input[name="coupon"]' ) ).toHaveLength( 1 );
344
+ } );
345
+
346
+ it( 'clears the coupon when the clicked button has none', () => {
347
+ const form = picker();
348
+
349
+ applyContextFields( form, { coupon: 'SAVE20' } );
350
+ applyContextFields( form, {} );
351
+
352
+ expect( couponValue( form ) ).toBeNull();
353
+ } );
354
+
355
+ it( 'does not leave an empty coupon input that would block a later button', () => {
356
+ const form = picker();
357
+
358
+ applyContextFields( form, {} );
359
+ applyContextFields( form, { coupon: 'SAVE20' } );
360
+
361
+ expect( couponValue( form ) ).toBe( 'SAVE20' );
362
+ } );
363
+
364
+ it( 'stamps the after-success and attribution context', () => {
365
+ const form = picker();
366
+
367
+ applyContextFields( form, { after_success_behavior: 'custom', gate_post_id: 12, prompt_title: '' } );
368
+
369
+ expect( form.querySelector( 'input[name="after_success_behavior"]' ).value ).toBe( 'custom' );
370
+ expect( form.querySelector( 'input[name="gate_post_id"]' ).value ).toBe( '12' );
371
+ expect( form.querySelector( 'input[name="prompt_title"]' ) ).toBeNull();
372
+ } );
373
+
374
+ it( 'leaves the picker’s own fields alone', () => {
375
+ const form = render(
376
+ '<form id="picker"><input type="hidden" name="newspack_checkout" value="1"><input type="radio" name="product_id" value="9"></form>'
377
+ ).querySelector( '#picker' );
378
+
379
+ applyContextFields( form, { coupon: 'SAVE20' } );
380
+
381
+ expect( form.querySelector( 'input[name="newspack_checkout"]' ).value ).toBe( '1' );
382
+ expect( form.querySelector( 'input[name="product_id"]' ).value ).toBe( '9' );
383
+ } );
384
+
385
+ it( 'does not throw on a null form or missing data', () => {
386
+ const form = picker();
387
+ expect( () => applyContextFields( null, { coupon: 'X' } ) ).not.toThrow();
388
+ expect( () => applyContextFields( form, null ) ).not.toThrow();
389
+ } );
390
+ } );
391
+
392
+ describe( 'PICKER_CONTEXT_FIELDS', () => {
393
+ // modal.js reads this same list for the click path, so a field missing here is
394
+ // dropped by both the URL trigger and the variation picker.
395
+ it( 'carries the coupon alongside the after-success and attribution context', () => {
396
+ expect( PICKER_CONTEXT_FIELDS ).toEqual(
397
+ expect.arrayContaining( [
398
+ 'after_success_behavior',
399
+ 'after_success_url',
400
+ 'after_success_button_label',
401
+ 'gate_post_id',
402
+ 'newspack_popup_id',
403
+ 'prompt_title',
404
+ 'coupon',
405
+ ] )
406
+ );
407
+ } );
266
408
  } );
@@ -24,7 +24,7 @@ import {
24
24
  getCheckoutData,
25
25
  getFormattedAmount,
26
26
  } from './utils';
27
- import { resolveCheckoutButtonForm, readCheckoutData } from './checkout-button-trigger';
27
+ import { resolveCheckoutButtonForm, readCheckoutData, applyContextFields } from './checkout-button-trigger';
28
28
  import { applyCtaAttribution } from '../shared/js/cta-attribution';
29
29
 
30
30
  const CLASS_PREFIX = newspackBlocksModal.newspack_class_prefix;
@@ -363,20 +363,10 @@ domReady( () => {
363
363
  const variationModal = [ ...variationModals ].find( modal => modal.dataset.productId === checkoutData.product_id );
364
364
  if ( variationModal ) {
365
365
  variationModal.querySelectorAll( `form[target="${ IFRAME_NAME }"]` ).forEach( singleVariationForm => {
366
- // Fill in the hidden params in the variation modal.
367
- [
368
- 'after_success_behavior',
369
- 'after_success_url',
370
- 'after_success_button_label',
371
- 'gate_post_id',
372
- 'newspack_popup_id',
373
- 'prompt_title',
374
- ].forEach( hiddenParam => {
375
- const existingInputs = singleVariationForm.querySelectorAll( 'input[name="' + hiddenParam + '"]' );
376
- if ( 0 === existingInputs.length ) {
377
- singleVariationForm.prepend( createHiddenInput( hiddenParam, checkoutData[ hiddenParam ] ) );
378
- }
379
- } );
366
+ // Fill in the hidden params in the variation modal. The picker is
367
+ // shared by every button for this product and is never reset, so
368
+ // this overwrites the previous open's context rather than adding to it.
369
+ applyContextFields( singleVariationForm, checkoutData );
380
370
 
381
371
  // Append the product data hidden inputs.
382
372
  const data = readCheckoutData( singleVariationForm );