-
- { __(
- 'Shown at the top of the subscribe popup that appears when a visitor clicks a Subscribe block.',
- 'jetpack-newsletter'
- ) }
-
-
-
-
-
-
-
-
-
-
- );
-}
diff --git a/projects/packages/newsletter/src/settings/sections/subscriptions-section.tsx b/projects/packages/newsletter/src/settings/sections/subscriptions-section.tsx
index d3e2e0b08897..5cae3aa89e68 100644
--- a/projects/packages/newsletter/src/settings/sections/subscriptions-section.tsx
+++ b/projects/packages/newsletter/src/settings/sections/subscriptions-section.tsx
@@ -36,6 +36,10 @@ const PLACEMENT_SLUG_BY_KEY: Record< string, string > = {
jetpack_subscribe_floating_button_enabled: 'floating_button',
};
+// The "Subscribe button pop-up" card below has no underlying setting to
+// toggle (see its usage), so its `onChange` is inert by design.
+const noop = () => {};
+
interface SubscriptionsSectionProps {
data: NewsletterSettings;
/**
@@ -263,6 +267,31 @@ export function SubscriptionsSection( {
/>
);
} ) }
+ { /* Not a real placement toggle — the pop-up shown when a
+ visitor clicks a "Button only" style Subscribe block is
+ inherent to using that block style, with no separate
+ enable/disable setting. Always checked and disabled to
+ reflect that, with the same "Preview and edit" link as
+ its siblings. */ }
+ }
+ previewUrl={
+ canShowBlockThemeEditorLinks
+ ? addQueryArgs( getAdminUrl( 'site-editor.php' ), {
+ postType: 'wp_template_part',
+ postId: `${ newsletterScriptData.themeStylesheet }//jetpack-subscribe-modal-button`,
+ canvas: 'edit',
+ } )
+ : undefined
+ }
+ checked
+ disabled
+ onChange={ noop }
+ onPreviewClick={ handlePlacementPreviewClick }
+ />
diff --git a/projects/plugins/jetpack/changelog/add-subscribe-button-only-local-popup b/projects/plugins/jetpack/changelog/add-subscribe-button-only-local-popup
new file mode 100644
index 000000000000..f9077909c9ec
--- /dev/null
+++ b/projects/plugins/jetpack/changelog/add-subscribe-button-only-local-popup
@@ -0,0 +1,4 @@
+Significance: minor
+Type: enhancement
+
+Subscribe Block: show a local, editable pop-up for the "Button only" style before opening the subscribe checkout, instead of opening checkout with no message to show. The floating subscribe button, which uses the same style internally, benefits from this too.
diff --git a/projects/plugins/jetpack/extensions/blocks/subscriptions/view.js b/projects/plugins/jetpack/extensions/blocks/subscriptions/view.js
index fa8a273f5708..cc82687bfd00 100644
--- a/projects/plugins/jetpack/extensions/blocks/subscriptions/view.js
+++ b/projects/plugins/jetpack/extensions/blocks/subscriptions/view.js
@@ -78,6 +78,28 @@ domReady( function () {
if ( action === 'subscribe' ) {
event.preventDefault();
+ // "Button only" style renders no visible email input, so email
+ // is empty here. Rather than opening the checkout iframe with no
+ // email (which then has to ask for it inside the iframe itself),
+ // open a local pop-up with its own embedded Subscribe block first.
+ // That pop-up's own form submit re-enters this same handler with
+ // email populated and is-style-button absent, and flows through
+ // normally below.
+ const isButtonOnlyStyle = form
+ .closest( '.wp-block-jetpack-subscriptions' )
+ ?.classList.contains( 'is-style-button' );
+ if ( ! email && isButtonOnlyStyle ) {
+ const modal = document.querySelector( '.jetpack-subscribe-modal-button' );
+ if ( modal ) {
+ modal.classList.add( 'open' );
+ document.body.classList.add( 'jetpack-subscribe-modal-button-open' );
+ modal.querySelector( 'input[type="email"]' )?.focus();
+ }
+ button.classList.remove( 'is-loading' );
+ button.setAttribute( 'aria-busy', 'false' );
+ return;
+ }
+
const post_id = form.querySelector( 'input[name=post_id]' )?.value ?? '';
const tier_id = form.querySelector( 'input[name=tier_id]' )?.value ?? '';
const app_source = form.querySelector( 'input[name=app_source]' )?.value ?? '';
diff --git a/projects/plugins/jetpack/modules/subscriptions.php b/projects/plugins/jetpack/modules/subscriptions.php
index 557d2f08ccf0..145f9cb343d8 100644
--- a/projects/plugins/jetpack/modules/subscriptions.php
+++ b/projects/plugins/jetpack/modules/subscriptions.php
@@ -1146,6 +1146,7 @@ public function track_newsletter_category_creation() {
require __DIR__ . '/subscriptions/subscribe-modal/class-jetpack-subscribe-modal.php';
require __DIR__ . '/subscriptions/subscribe-overlay/class-jetpack-subscribe-overlay.php';
require __DIR__ . '/subscriptions/subscribe-floating-button/class-jetpack-subscribe-floating-button.php';
+require __DIR__ . '/subscriptions/subscribe-modal-button/class-jetpack-subscribe-modal-button.php';
require __DIR__ . '/subscriptions/newsletter-widget/class-jetpack-newsletter-dashboard-widget.php';
require_once __DIR__ . '/subscriptions/abilities/class-newsletter-abilities.php';
diff --git a/projects/plugins/jetpack/modules/subscriptions/subscribe-modal-button/class-jetpack-subscribe-modal-button.php b/projects/plugins/jetpack/modules/subscriptions/subscribe-modal-button/class-jetpack-subscribe-modal-button.php
new file mode 100644
index 000000000000..80f8b0503f20
--- /dev/null
+++ b/projects/plugins/jetpack/modules/subscriptions/subscribe-modal-button/class-jetpack-subscribe-modal-button.php
@@ -0,0 +1,168 @@
+
+
+
+
+
+
+ get_template();
+ }
+ }
+
+ return $block_template;
+ }
+
+ /**
+ * Returns a custom template for the pop-up.
+ *
+ * @return WP_Block_Template
+ */
+ public function get_template() {
+ $template = new WP_Block_Template();
+ $template->theme = get_stylesheet();
+ $template->slug = self::BLOCK_TEMPLATE_PART_SLUG;
+ $template->id = self::get_block_template_part_id();
+ $template->area = 'uncategorized';
+ $template->content = $this->get_subscribe_modal_button_template_content();
+ $template->source = 'plugin';
+ $template->type = 'wp_template_part';
+ $template->title = __( 'Jetpack Subscribe button modal', 'jetpack' );
+ $template->status = 'publish';
+ $template->has_theme_file = false;
+ $template->is_custom = true;
+ $template->description = __( 'The pop-up shown when a visitor clicks a "Button only" style Subscribe block.', 'jetpack' );
+
+ return $template;
+ }
+
+ /**
+ * Returns the initial content of the pop-up template.
+ * This can then be edited by the user.
+ *
+ * Seeds the heading from the legacy `subscribe_modal_heading` site
+ * setting when present, so a site that already customized it via the
+ * Newsletter settings page doesn't silently lose that text the first
+ * time this ships. Once the owner edits this template part directly, the
+ * template part becomes the sole source of truth for the heading.
+ *
+ * @return string
+ */
+ public function get_subscribe_modal_button_template_content() {
+ $subscription_options = (array) get_option( 'subscription_options', array() );
+ $legacy_heading = isset( $subscription_options['subscribe_modal_heading'] )
+ ? trim( (string) $subscription_options['subscribe_modal_heading'] )
+ : '';
+ $heading_text = '' !== $legacy_heading
+ ? $legacy_heading
+ : __( 'Subscribe now to stay ahead and never miss a beat!', 'jetpack' );
+
+ $group_block_name = esc_attr__( 'Subscribe button pop-up container', 'jetpack' );
+
+ return <<
+
+
+
+
$heading_text
+
+
+
+
+
+HTML;
+ }
+}
+
+Jetpack_Subscribe_Modal_Button::init();
+
+add_action(
+ 'rest_api_switched_to_blog',
+ function () {
+ Jetpack_Subscribe_Modal_Button::init();
+ }
+);
diff --git a/projects/plugins/jetpack/modules/subscriptions/subscribe-modal-button/subscribe-modal-button.css b/projects/plugins/jetpack/modules/subscriptions/subscribe-modal-button/subscribe-modal-button.css
new file mode 100644
index 000000000000..8b84462d8d32
--- /dev/null
+++ b/projects/plugins/jetpack/modules/subscriptions/subscribe-modal-button/subscribe-modal-button.css
@@ -0,0 +1,58 @@
+body.jetpack-subscribe-modal-button-open {
+ overflow: hidden;
+}
+
+.jetpack-subscribe-modal-button {
+ visibility: hidden;
+ position: fixed;
+ z-index: 50000; /* Same as WP.com Action bar */
+ left: 0;
+ top: 0;
+ width: 100%;
+ height: 100%;
+ overflow: auto;
+ background-color: transparent;
+ transition: all 0.4s;
+}
+
+.jetpack-subscribe-modal-button.open {
+ background-color: rgba(0, 0, 0, 0.3);
+ visibility: visible;
+}
+
+.jetpack-subscribe-modal-button__modal-content {
+ position: relative;
+ visibility: hidden;
+ overflow: hidden;
+ top: 100%;
+ background-color: #fefefe;
+ margin: 15% auto;
+ width: 100%;
+ max-width: 600px;
+ border-radius: 10px;
+ box-sizing: border-box;
+ transition: all 0.4s;
+ text-wrap: balance;
+}
+
+.jetpack-subscribe-modal-button.open .jetpack-subscribe-modal-button__modal-content {
+ top: 0;
+ visibility: visible;
+}
+
+/*
+ * These text-wrap properties still have limited browser
+ * support, but based on feedback still adding them for when
+ * they are supported.
+ */
+.jetpack-subscribe-modal-button__modal-content p {
+ text-wrap: balance;
+ text-wrap: pretty;
+}
+
+@media screen and (max-width: 640px) {
+
+ .jetpack-subscribe-modal-button__modal-content {
+ width: 94%;
+ }
+}
diff --git a/projects/plugins/jetpack/modules/subscriptions/subscribe-modal-button/subscribe-modal-button.js b/projects/plugins/jetpack/modules/subscriptions/subscribe-modal-button/subscribe-modal-button.js
new file mode 100644
index 000000000000..ebaf6d6f00cf
--- /dev/null
+++ b/projects/plugins/jetpack/modules/subscriptions/subscribe-modal-button/subscribe-modal-button.js
@@ -0,0 +1,35 @@
+const { domReady } = wp;
+domReady( () => {
+ const modal = document.querySelector( '.jetpack-subscribe-modal-button' );
+ if ( ! modal ) {
+ return;
+ }
+
+ function closeModal() {
+ modal.classList.remove( 'open' );
+ document.body.classList.remove( 'jetpack-subscribe-modal-button-open' );
+ }
+
+ // Opening is triggered from the "Button only" Subscribe block's own
+ // submit handler (extensions/blocks/subscriptions/view.js), not here —
+ // this file only wires up how to close it again once open.
+ window.addEventListener( 'click', event => {
+ if ( event.target === modal ) {
+ closeModal();
+ }
+ } );
+
+ window.addEventListener( 'keydown', event => {
+ if ( event.key === 'Escape' && modal.classList.contains( 'open' ) ) {
+ closeModal();
+ }
+ } );
+
+ // When the pop-up's own embedded Subscribe block form is submitted, the
+ // checkout iframe takes over — hide this pop-up so it's not left open
+ // underneath it.
+ const form = modal.querySelector( 'form' );
+ if ( form ) {
+ form.addEventListener( 'subscription-modal-loaded', closeModal );
+ }
+} );