From aad1fa7cb9d241f15ac5f9bef040bdb615100ad4 Mon Sep 17 00:00:00 2001 From: Dean Sas Date: Thu, 27 Nov 2025 17:10:05 +0000 Subject: [PATCH 1/6] Jetpack Newsletter: First take This is the initial, very rough work-in-progress of bringing the newsletter settings UI into wp-admin using DataForms. It's mostly untested, needs styling etc. --- pnpm-lock.yaml | 12 + projects/packages/newsletter/package.json | 4 + .../newsletter/src/class-settings.php | 63 ++ .../src/settings/components/header.scss | 17 + .../src/settings/components/header.tsx | 34 + .../newsletter/src/settings/index.tsx | 677 +++++++++++++++++- .../newsletter/src/settings/style.scss | 307 +++++++- 7 files changed, 1104 insertions(+), 10 deletions(-) create mode 100644 projects/packages/newsletter/src/settings/components/header.scss create mode 100644 projects/packages/newsletter/src/settings/components/header.tsx diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index af276f02b013a..38c5137102deb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -3370,6 +3370,15 @@ importers: projects/packages/newsletter: dependencies: + '@automattic/jetpack-api': + specifier: workspace:* + version: link:../../js-packages/api + '@automattic/jetpack-components': + specifier: workspace:* + version: link:../../js-packages/components + '@wordpress/components': + specifier: 30.8.0 + version: 30.8.0(patch_hash=2659f08edd4c0250f15fb428f013852a17e84da9c745e6dae6307de837e4d30b)(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@wordpress/dataviews': specifier: 10.2.0 version: 10.2.0(patch_hash=2659f08edd4c0250f15fb428f013852a17e84da9c745e6dae6307de837e4d30b)(@types/react@18.3.26)(react@18.3.1) @@ -3379,6 +3388,9 @@ importers: '@wordpress/i18n': specifier: 6.8.0 version: 6.8.0(patch_hash=0c63a888feb97f2f1d416ca013ad85c31b6360b41cc0b6e2b0ae28f778fbdc5b) + '@wordpress/notices': + specifier: 5.35.0 + version: 5.35.0(patch_hash=0c63a888feb97f2f1d416ca013ad85c31b6360b41cc0b6e2b0ae28f778fbdc5b)(react@18.3.1) debug: specifier: 4.4.1 version: 4.4.1 diff --git a/projects/packages/newsletter/package.json b/projects/packages/newsletter/package.json index 1b66077ab1ced..6d2ca4b6129af 100644 --- a/projects/packages/newsletter/package.json +++ b/projects/packages/newsletter/package.json @@ -32,9 +32,13 @@ "extends @wordpress/browserslist-config" ], "dependencies": { + "@automattic/jetpack-api": "workspace:*", + "@automattic/jetpack-components": "workspace:*", + "@wordpress/components": "30.8.0", "@wordpress/dataviews": "10.2.0", "@wordpress/element": "6.35.0", "@wordpress/i18n": "6.8.0", + "@wordpress/notices": "5.35.0", "debug": "4.4.1" }, "devDependencies": { diff --git a/projects/packages/newsletter/src/class-settings.php b/projects/packages/newsletter/src/class-settings.php index 34a7d3155564c..010545745ae6f 100644 --- a/projects/packages/newsletter/src/class-settings.php +++ b/projects/packages/newsletter/src/class-settings.php @@ -123,6 +123,69 @@ public function load_admin_scripts() { 'enqueue' => true, ) ); + + wp_add_inline_script( + 'jetpack-newsletter', + 'window.jetpackNewsletterSettings = ' . wp_json_encode( $this->get_settings_data() ) . ';', + 'before' + ); + } + + /** + * Get the data to be passed to the newsletter settings page. + * + * @return array + */ + private function get_settings_data() { + $current_user = wp_get_current_user(); + $theme = wp_get_theme(); + + // Get blog ID if available (for WordPress.com sites). + $blog_id = defined( 'Jetpack_Options' ) && class_exists( 'Jetpack_Options' ) + ? \Jetpack_Options::get_option( 'id', 0 ) + : 0; + + // Get site URL without protocol. + $site_url = get_site_url(); + $site_raw_url = preg_replace( '(^https?://)', '', $site_url ); + + return array( + 'isBlockTheme' => wp_is_block_theme(), + 'siteAdminUrl' => admin_url(), + 'themeStylesheet' => $theme->get_stylesheet(), + 'blogID' => $blog_id, + 'siteRawUrl' => $site_raw_url, + 'email' => $current_user->user_email, + 'gravatar' => get_avatar_url( $current_user->ID ), + 'displayName' => $current_user->display_name, + 'wpAdminSubscriberManagementEnabled' => apply_filters( 'jetpack_wpcom_subscriber_management_enabled', false ), + 'isSubscriptionSiteEditSupported' => wp_is_block_theme(), + 'setupPaymentPlansUrl' => $this->get_jetpack_cloud_url( 'monetize/payments' ), + 'isSitePublic' => (int) get_option( 'blog_public' ) === 1, + ); + } + + /** + * Get a Jetpack Cloud URL. + * + * @param string $path The path to append to the Jetpack Cloud URL. + * @return string + */ + private function get_jetpack_cloud_url( $path = '' ) { + $site_suffix = ''; + if ( defined( 'Jetpack_Options' ) && class_exists( 'Jetpack_Options' ) ) { + $blog_id = \Jetpack_Options::get_option( 'id', 0 ); + if ( $blog_id ) { + $site_suffix = $blog_id; + } + } + + if ( ! $site_suffix ) { + $site_url = get_site_url(); + $site_suffix = preg_replace( '(^https?://)', '', $site_url ); + } + + return 'https://cloud.jetpack.com/' . ltrim( $path, '/' ) . '/' . $site_suffix; } /** diff --git a/projects/packages/newsletter/src/settings/components/header.scss b/projects/packages/newsletter/src/settings/components/header.scss new file mode 100644 index 0000000000000..8ddaa744ceef2 --- /dev/null +++ b/projects/packages/newsletter/src/settings/components/header.scss @@ -0,0 +1,17 @@ +.newsletter-settings__header { + margin-bottom: 2em; + + &-top { + display: flex; + align-items: center; + gap: 1em; + } +} + +.newsletter-settings__title { + margin: 0; +} + +.newsletter-settings__tagline { + margin: 8px 0 0; +} diff --git a/projects/packages/newsletter/src/settings/components/header.tsx b/projects/packages/newsletter/src/settings/components/header.tsx new file mode 100644 index 0000000000000..ca4587f272e0a --- /dev/null +++ b/projects/packages/newsletter/src/settings/components/header.tsx @@ -0,0 +1,34 @@ +/** + * External dependencies + */ +import { JetpackLogo } from '@automattic/jetpack-components'; +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import './header.scss'; + +/** + * Newsletter Settings Header Component + * + * @return {JSX.Element} The header component. + */ +export function Header(): JSX.Element { + return ( +
+
+ +

+ { __( 'Newsletter Settings', 'jetpack-newsletter' ) } +

+
+

+ { __( + 'Transform your blog posts into newsletters to easily reach your subscribers.', + 'jetpack-newsletter' + ) } +

+
+ ); +} diff --git a/projects/packages/newsletter/src/settings/index.tsx b/projects/packages/newsletter/src/settings/index.tsx index d54aa3e77dcfb..e7a269d6c7d6b 100644 --- a/projects/packages/newsletter/src/settings/index.tsx +++ b/projects/packages/newsletter/src/settings/index.tsx @@ -1,23 +1,688 @@ /** * External dependencies */ -import { createRoot } from '@wordpress/element'; - +import restApi from '@automattic/jetpack-api'; +import { Button, Notice, ExternalLink, Snackbar } from '@wordpress/components'; +import { DataForm } from '@wordpress/dataviews'; +import { createRoot, useCallback, useEffect, useState } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; /** * Internal dependencies */ +import { Header } from './components/header'; import './style.scss'; +/** + * Type definitions for newsletter settings data + */ +interface NewsletterSettings { + subscriptions: boolean; + stb_enabled: boolean; + stc_enabled: boolean; + sm_enabled: boolean; + jetpack_subscribe_overlay_enabled: boolean; + jetpack_subscribe_floating_button_enabled: boolean; + jetpack_subscriptions_subscribe_post_end_enabled: boolean; + jetpack_subscriptions_login_navigation_enabled: boolean; + jetpack_subscriptions_subscribe_navigation_enabled: boolean; + wpcom_featured_image_in_email: boolean; + wpcom_subscription_emails_use_excerpt: boolean; + jetpack_gravatar_in_email: boolean; + jetpack_author_in_email: boolean; + jetpack_post_date_in_email: boolean; + jetpack_subscriptions_reply_to: 'comment' | 'author' | 'no-reply'; + jetpack_subscriptions_from_name: string; + wpcom_newsletter_categories_enabled: boolean; + wpcom_newsletter_categories: number[]; + subscription_options?: { + welcome: string; + }; +} + +/** + * Type definitions for Jetpack Newsletter settings passed from PHP + */ +interface JetpackNewsletterSettings { + isBlockTheme: boolean; + siteAdminUrl: string; + themeStylesheet: string; + blogID: number; + siteRawUrl: string; + email: string; + gravatar: string; + displayName: string; + wpAdminSubscriberManagementEnabled: boolean; + isSubscriptionSiteEditSupported: boolean; + setupPaymentPlansUrl: string; + isSitePublic: boolean; +} + /** * Newsletter Settings App * - * @return {Element} The newsletter settings component. + * @return {JSX.Element | null} The newsletter settings component or null. */ -function NewsletterSettingsApp() { +function NewsletterSettingsApp(): JSX.Element | null { + const [ data, setData ] = useState< NewsletterSettings | null >( null ); + const [ isLoading, setIsLoading ] = useState( true ); + const [ error, setError ] = useState< string | null >( null ); + + // Subscription settings state (for manual save) + const [ subscriptionChanges, setSubscriptionChanges ] = useState< Partial< NewsletterSettings > >( + {} + ); + const [ isSavingSubscriptions, setIsSavingSubscriptions ] = useState( false ); + + // Sender name state (for manual save) + const [ senderName, setSenderName ] = useState( '' ); + const [ isSavingSenderName, setIsSavingSenderName ] = useState( false ); + + // Snackbar notification state + const [ snackbarMessage, setSnackbarMessage ] = useState< string | null >( null ); + + // Get settings from PHP + const jetpackSettings = ( + window as Window & { jetpackNewsletterSettings?: JetpackNewsletterSettings } + ).jetpackNewsletterSettings; + + // Callback to clear error + const clearError = useCallback( () => setError( null ), [] ); + + // Callback to clear snackbar + const clearSnackbar = useCallback( () => setSnackbarMessage( null ), [] ); + + // Callback for sender name input change + const handleSenderNameChange = useCallback( + ( e: React.ChangeEvent< HTMLInputElement > ) => setSenderName( e.target.value ), + [] + ); + + // Load settings on mount + useEffect( () => { + // Initialize the REST API with WordPress settings + const wpApiSettings = ( window as Window & { wpApiSettings?: { root: string; nonce: string } } ) + .wpApiSettings; + if ( wpApiSettings ) { + restApi.setApiRoot( wpApiSettings.root ); + restApi.setApiNonce( wpApiSettings.nonce ); + } + + restApi + .fetchSettings() + .then( ( settings: NewsletterSettings ) => { + setData( settings ); + setSenderName( settings.jetpack_subscriptions_from_name || '' ); + setIsLoading( false ); + } ) + .catch( ( err: Error ) => { + setError( err.message || 'Failed to load settings' ); + setIsLoading( false ); + } ); + }, [] ); + + // Handle auto-save for newsletter toggle and email settings + const handleAutoSave = useCallback( + ( updates: Partial< NewsletterSettings > ) => { + if ( ! data ) { + return; + } + + // Update local state optimistically + setData( { ...data, ...updates } ); + + // Save to backend + restApi.updateSettings( updates ).catch( ( err: Error ) => { + setError( err.message || 'Failed to save settings' ); + // Revert optimistic update on error + setData( data ); + } ); + }, + [ data ] + ); + + // Handle subscription settings changes (staged, not auto-saved) + const handleSubscriptionChange = useCallback( + ( updates: Partial< NewsletterSettings > ) => { + if ( ! data ) { + return; + } + + // Update local state + setData( { ...data, ...updates } ); + + // Track changes for save button + setSubscriptionChanges( { ...subscriptionChanges, ...updates } ); + }, + [ data, subscriptionChanges ] + ); + + // Save subscription settings + const saveSubscriptionSettings = useCallback( () => { + setIsSavingSubscriptions( true ); + setError( null ); + + restApi + .updateSettings( subscriptionChanges ) + .then( () => { + setSubscriptionChanges( {} ); + setSnackbarMessage( __( 'Settings saved', 'jetpack-newsletter' ) ); + } ) + .catch( ( err: Error ) => { + setError( err.message || 'Failed to save subscription settings' ); + } ) + .finally( () => { + setIsSavingSubscriptions( false ); + } ); + }, [ subscriptionChanges ] ); + + // Save sender name + const saveSenderName = useCallback( () => { + setIsSavingSenderName( true ); + setError( null ); + + restApi + .updateSettings( { jetpack_subscriptions_from_name: senderName } ) + .then( () => { + if ( data ) { + setData( { ...data, jetpack_subscriptions_from_name: senderName } ); + } + setSnackbarMessage( __( 'Sender name saved', 'jetpack-newsletter' ) ); + } ) + .catch( ( err: Error ) => { + setError( err.message || 'Failed to save sender name' ); + } ) + .finally( () => { + setIsSavingSenderName( false ); + } ); + }, [ senderName, data ] ); + + // Define form fields + const fields = [ + { + id: 'subscriptions', + label: __( + 'Let visitors subscribe to this site and receive emails when you publish a post', + 'jetpack-newsletter' + ), + type: 'boolean' as const, + Edit: 'toggle' as const, + enableSorting: false, + }, + { + id: 'jetpack_subscriptions_subscribe_post_end_enabled', + label: __( 'Subscribe block at post end', 'jetpack-newsletter' ), + type: 'boolean' as const, + Edit: 'toggle' as const, + enableSorting: false, + }, + { + id: 'sm_enabled', + label: __( 'Subscription pop-up when scrolling', 'jetpack-newsletter' ), + type: 'boolean' as const, + Edit: 'toggle' as const, + enableSorting: false, + }, + { + id: 'jetpack_subscribe_overlay_enabled', + label: __( 'Subscription overlay on homepage', 'jetpack-newsletter' ), + type: 'boolean' as const, + Edit: 'toggle' as const, + enableSorting: false, + }, + { + id: 'jetpack_subscribe_floating_button_enabled', + label: __( 'Floating subscribe button', 'jetpack-newsletter' ), + type: 'boolean' as const, + Edit: 'toggle' as const, + enableSorting: false, + }, + { + id: 'jetpack_subscriptions_subscribe_navigation_enabled', + label: __( 'Subscribe block in navigation', 'jetpack-newsletter' ), + type: 'boolean' as const, + Edit: 'toggle' as const, + enableSorting: false, + }, + { + id: 'jetpack_subscriptions_login_navigation_enabled', + label: __( 'Subscriber login block in navigation', 'jetpack-newsletter' ), + type: 'boolean' as const, + Edit: 'toggle' as const, + enableSorting: false, + }, + { + id: 'stb_enabled', + label: __( '"Subscribe to site" on comment form', 'jetpack-newsletter' ), + type: 'boolean' as const, + Edit: 'toggle' as const, + enableSorting: false, + }, + { + id: 'stc_enabled', + label: __( '"Subscribe to comments" on comment form', 'jetpack-newsletter' ), + type: 'boolean' as const, + Edit: 'toggle' as const, + enableSorting: false, + }, + { + id: 'wpcom_featured_image_in_email', + label: __( 'Featured image in emails', 'jetpack-newsletter' ), + type: 'boolean' as const, + Edit: 'toggle' as const, + enableSorting: false, + description: __( + "Includes your post's featured image in the email sent out to your readers.", + 'jetpack-newsletter' + ), + }, + { + id: 'wpcom_subscription_emails_use_excerpt', + label: __( 'Email content', 'jetpack-newsletter' ), + type: 'integer' as const, + Edit: 'radio' as const, + elements: [ + { + value: 0, + label: __( 'Full text', 'jetpack-newsletter' ), + }, + { + value: 1, + label: __( 'Excerpt', 'jetpack-newsletter' ), + }, + ], + enableSorting: false, + description: __( + 'Sets whether email subscribers can read full posts in emails or just an excerpt and link to the full version.', + 'jetpack-newsletter' + ), + }, + { + id: 'jetpack_gravatar_in_email', + label: __( 'Show author avatar', 'jetpack-newsletter' ), + type: 'boolean' as const, + Edit: 'toggle' as const, + enableSorting: false, + description: __( + 'We use Gravatar, a service that associates an avatar image with your primary email address.', + 'jetpack-newsletter' + ), + }, + { + id: 'jetpack_author_in_email', + label: __( 'Show author display name', 'jetpack-newsletter' ), + type: 'boolean' as const, + Edit: 'toggle' as const, + enableSorting: false, + }, + { + id: 'jetpack_post_date_in_email', + label: __( 'Add post date', 'jetpack-newsletter' ), + type: 'boolean' as const, + Edit: 'toggle' as const, + enableSorting: false, + description: __( + "You can customize the date format in your site's general settings.", + 'jetpack-newsletter' + ), + }, + { + id: 'jetpack_subscriptions_reply_to', + label: __( 'Reply-to settings', 'jetpack-newsletter' ), + type: 'text' as const, + Edit: 'radio' as const, + elements: [ + { + value: 'comment', + label: __( 'Replies will be a public comment on the post', 'jetpack-newsletter' ), + }, + { + value: 'author', + label: __( "Replies will be sent to the post author's email", 'jetpack-newsletter' ), + }, + { value: 'no-reply', label: __( 'Replies are not allowed', 'jetpack-newsletter' ) }, + ], + enableSorting: false, + description: __( + "Sets the reply to email address for your newsletter emails. It's the email where subscribers send their replies.", + 'jetpack-newsletter' + ), + }, + { + id: 'wpcom_newsletter_categories_enabled', + label: __( 'Enable newsletter categories', 'jetpack-newsletter' ), + type: 'boolean' as const, + Edit: 'toggle' as const, + enableSorting: false, + description: __( + "Newsletter categories let you select the content that's emailed to subscribers. When enabled, only posts in the selected categories will be sent as newsletters.", + 'jetpack-newsletter' + ), + }, + ]; + + if ( isLoading ) { + return ( +
+

{ __( 'Loading newsletter settings…', 'jetpack-newsletter' ) }

+
+ ); + } + + if ( error ) { + return ( +
+ + { error } + +
+ ); + } + + if ( ! data ) { + return null; + } + + const hasSubscriptionChanges = Object.keys( subscriptionChanges ).length > 0; + const hasSenderNameChanged = senderName !== ( data.jetpack_subscriptions_from_name || '' ); + + // Helper function to get "Manage all subscribers" URL + const getManageSubscribersUrl = () => { + if ( ! jetpackSettings ) { + return '#'; + } + + if ( jetpackSettings.wpAdminSubscriberManagementEnabled ) { + return `${ jetpackSettings.siteAdminUrl }admin.php?page=subscribers`; + } + + // Fallback to WordPress.com URL + const site = jetpackSettings.blogID || jetpackSettings.siteRawUrl; + return `https://wordpress.com/subscribers/${ site }`; + }; + return (
-

Newsletter Settings

-

This is a proof of concept, I am rendered via React.

+
+ + { error && ( + + { error } + + ) } + + { /* Newsletter Section */ } +
+

+ { __( 'Newsletter', 'jetpack-newsletter' ) } +

+
+ f.id === 'subscriptions' ) } + form={ { + layout: { + type: 'regular', + labelPosition: 'top', + }, + fields: [ 'subscriptions' ], + } } + onChange={ handleAutoSave } + /> + { data.subscriptions && ( +
+ + { __( 'Manage all subscribers', 'jetpack-newsletter' ) } + +
+ ) } +
+
+ + { /* Subscriptions Section */ } +
+

+ { __( 'Subscriptions', 'jetpack-newsletter' ) } +

+

+ { __( + 'Automatically add subscription forms to your site and turn visitors into subscribers.', + 'jetpack-newsletter' + ) } +

+
+ + [ + 'jetpack_subscriptions_subscribe_post_end_enabled', + 'sm_enabled', + 'jetpack_subscribe_overlay_enabled', + 'jetpack_subscribe_floating_button_enabled', + 'jetpack_subscriptions_subscribe_navigation_enabled', + 'jetpack_subscriptions_login_navigation_enabled', + 'stb_enabled', + 'stc_enabled', + ].includes( f.id ) + ) } + form={ { + layout: { + type: 'regular', + labelPosition: 'top', + }, + fields: [ + { + id: 'homepage_and_posts', + label: __( 'Homepage and posts', 'jetpack-newsletter' ), + children: [ + 'jetpack_subscriptions_subscribe_post_end_enabled', + 'sm_enabled', + 'jetpack_subscribe_overlay_enabled', + 'jetpack_subscribe_floating_button_enabled', + ], + }, + { + id: 'navigation', + label: __( 'Navigation', 'jetpack-newsletter' ), + children: [ + 'jetpack_subscriptions_subscribe_navigation_enabled', + 'jetpack_subscriptions_login_navigation_enabled', + ], + }, + { + id: 'comments', + label: __( 'Comments', 'jetpack-newsletter' ), + children: [ 'stb_enabled', 'stc_enabled' ], + }, + ], + } } + onChange={ handleSubscriptionChange } + /> + +
+ +
+
+
+ + { /* Paid Newsletter Section */ } + { jetpackSettings?.setupPaymentPlansUrl && ( +
+

+ { __( 'Paid Newsletter', 'jetpack-newsletter' ) } +

+
+

+ { __( + 'Earn money through your Newsletter. Reward your most loyal subscribers with exclusive content or add a paywall to monetize content.', + 'jetpack-newsletter' + ) } +

+ +
+
+ ) } + + { /* Email Configuration Section */ } +
+

+ { __( 'Email configuration', 'jetpack-newsletter' ) } +

+
+ + [ + 'wpcom_featured_image_in_email', + 'wpcom_subscription_emails_use_excerpt', + 'jetpack_gravatar_in_email', + 'jetpack_author_in_email', + 'jetpack_post_date_in_email', + 'jetpack_subscriptions_reply_to', + ].includes( f.id ) + ) } + form={ { + layout: { + type: 'regular', + labelPosition: 'top', + }, + fields: [ + 'wpcom_featured_image_in_email', + { + id: 'email_content_settings', + label: __( 'For each new post email, include', 'jetpack-newsletter' ), + children: [ 'wpcom_subscription_emails_use_excerpt' ], + }, + { + id: 'email_byline', + label: __( 'Email byline', 'jetpack-newsletter' ), + children: [ + 'jetpack_gravatar_in_email', + 'jetpack_author_in_email', + 'jetpack_post_date_in_email', + ], + }, + { + id: 'reply_to_settings', + children: [ 'jetpack_subscriptions_reply_to' ], + }, + ], + } } + onChange={ handleAutoSave } + /> + + { /* Featured image learn more link */ } +
+ + { __( 'Learn more about featured images', 'jetpack-newsletter' ) } + +
+ + { /* Gravatar link */ } + { data.jetpack_gravatar_in_email && jetpackSettings?.email && ( +
+ + { __( 'Update my Gravatar', 'jetpack-newsletter' ) } + +
+ ) } + + { /* Reply-to learn more link */ } +
+ + { __( 'Learn more about subscriptions and newsletters', 'jetpack-newsletter' ) } + +
+ + { /* Sender name field with inline save */ } +
+ +
+ + { hasSenderNameChanged && ( + + ) } +
+

+ { __( 'Preview:', 'jetpack-newsletter' ) }{ ' ' } + + { senderName || + jetpackSettings?.displayName || + __( 'Your Name', 'jetpack-newsletter' ) } + { ' ' } + <comment-reply@wordpress.com> +

+

+ { __( + "This is the name that appears in subscribers' inboxes. It's usually the name of your newsletter or the author.", + 'jetpack-newsletter' + ) } +

+
+
+
+ + { /* Newsletter Categories Section */ } +
+

+ { __( 'Newsletter categories', 'jetpack-newsletter' ) } +

+

+ { __( + "Newsletter categories let you select the content that's emailed to subscribers. When enabled, only posts in the selected categories will be sent as newsletters.", + 'jetpack-newsletter' + ) } +

+
+ f.id === 'wpcom_newsletter_categories_enabled' ) } + form={ { + layout: { + type: 'regular', + labelPosition: 'top', + }, + fields: [ 'wpcom_newsletter_categories_enabled' ], + } } + onChange={ handleAutoSave } + /> +
+
+ + { /* Snackbar for success notifications */ } + { snackbarMessage && { snackbarMessage } }
); } diff --git a/projects/packages/newsletter/src/settings/style.scss b/projects/packages/newsletter/src/settings/style.scss index 91745bc2630fd..23fead13f1960 100644 --- a/projects/packages/newsletter/src/settings/style.scss +++ b/projects/packages/newsletter/src/settings/style.scss @@ -1,8 +1,307 @@ .newsletter-settings { - padding: 20px; + max-width: 1200px; + margin: 0 auto; + padding: 0; - h1 { - font-size: 24px; - margin-bottom: 16px; + // Card-based sections + &__section { + background: #fff; + border: 1px solid #c3c4c7; + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04); + margin-bottom: 20px; + padding: 0; + } + + &__section-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 16px 20px; + border-bottom: 1px solid #c3c4c7; + gap: 16px; + } + + &__section-title { + font-size: 18px; + font-weight: 600; + line-height: 1.4; + margin: 0; + padding: 16px 20px; + border-bottom: 1px solid #c3c4c7; + color: #1e1e1e; + + // When title is in header with button + .newsletter-settings__section-header & { + padding: 0; + border: none; + } + } + + &__section-description { + font-size: 14px; + line-height: 1.6; + margin: 0; + padding: 16px 20px; + color: #646970; + background: #f6f7f7; + border-bottom: 1px solid #dcdcde; + } + + &__section-content { + padding: 20px; + } + + &__section-actions { + margin-top: 24px; + display: flex; + justify-content: flex-start; + } + + // Links styling + &__link { + margin-top: 12px; + padding-top: 12px; + + a { + font-size: 14px; + text-decoration: none; + + &:hover { + text-decoration: underline; + } + } + } + + // Help text with links + &__help-text { + margin-top: 12px; + font-size: 13px; + + a { + font-size: 13px; + text-decoration: none; + + &:hover { + text-decoration: underline; + } + } + } + + // Sender name field with inline save button + &__sender-name { + margin-top: 24px; + } + + &__field-label { + display: block; + margin-bottom: 8px; + font-size: 11px; + font-weight: 600; + color: #1e1e1e; + } + + &__sender-name-controls { + display: flex; + gap: 8px; + align-items: center; + margin-bottom: 8px; + } + + &__text-input { + flex: 1; + max-width: 400px; + padding: 8px 12px; + border: 1px solid #8c8f94; + border-radius: 4px; + font-size: 14px; + line-height: 1.5; + + &:focus { + border-color: #2271b1; + box-shadow: 0 0 0 1px #2271b1; + outline: 2px solid transparent; + } + } + + &__field-description { + font-size: 13px; + line-height: 1.5; + margin: 8px 0 0; + color: #646970; + } + + &--error { + padding: 20px; + + h2 { + color: #d63638; + font-size: 18px; + margin-bottom: 12px; + } + + p { + color: #646970; + } + } + + // DataForm wrapper styling + .dataviews-view-form { + padding: 0; + } + + .dataforms-layouts-panel__field, + .dataforms-layouts-regular__field { + margin-bottom: 20px; + + &:last-child { + margin-bottom: 0; + } + } + + .dataforms-layouts-panel__field-label, + .dataforms-layouts-regular__field-label { + display: flex; + align-items: flex-start; + margin-bottom: 8px; + font-size: 11px; + font-weight: 600; + color: #1e1e1e; + line-height: 1.6; + } + + .dataforms-field-description { + display: block; + margin-top: 8px; + font-size: 13px; + color: #646970; + font-style: normal; + line-height: 1.5; + } + + // Group/section labels + .dataforms-layouts-panel__group-label { + display: block; + margin: 24px 0 16px; + font-size: 13px; + font-weight: 600; + color: #1e1e1e; + + &:first-child { + margin-top: 0; + } + } + + .components-toggle-control { + + .components-base-control__field { + display: flex; + align-items: flex-start; + margin-bottom: 0; + } + + .components-form-toggle { + margin: 2px 12px 0 0; + flex-shrink: 0; + } + + .components-toggle-control__label { + font-weight: 400; + font-size: 14px; + line-height: 1.6; + flex: 1; + } + } + + .components-radio-control { + + .components-base-control__field { + margin-top: 8px; + } + + .components-radio-control__option { + display: flex; + align-items: center; + margin-bottom: 8px; + padding: 8px 0; + + &:last-child { + margin-bottom: 0; + } + + input[type="radio"] { + margin: 0 8px 0 0; + flex-shrink: 0; + } + + label { + margin: 0; + font-weight: 400; + font-size: 14px; + line-height: 1.5; + } + } + } + + .components-text-control__input, + .components-textarea-control__input { + width: 100%; + max-width: 25rem; + padding: 8px 12px; + border: 1px solid #8c8f94; + border-radius: 4px; + font-size: 14px; + line-height: 1.5; + + &:focus { + border-color: #2271b1; + box-shadow: 0 0 0 1px #2271b1; + outline: 2px solid transparent; + } + } + + .components-textarea-control__input { + min-height: 100px; + resize: vertical; + } + + .components-button { + font-size: 13px; + height: auto; + padding: 6px 12px; + + &.is-primary { + background-color: #2271b1; + border-color: #2271b1; + color: #fff; + + &:hover:not(:disabled) { + background-color: #135e96; + border-color: #135e96; + } + + &:focus:not(:disabled) { + background-color: #135e96; + border-color: #135e96; + box-shadow: 0 0 0 1px #fff, 0 0 0 3px #2271b1; + } + } + } + + // Notice component styling + .components-notice { + margin: 0 0 20px; + } +} + +@keyframes fadeIn { + + from { + opacity: 0; + transform: translateY(-10px); + } + + to { + opacity: 1; + transform: translateY(0); } } From 71a6644de5c89a5604d51bc121845c5835dceb70 Mon Sep 17 00:00:00 2001 From: Dean Sas Date: Fri, 28 Nov 2025 17:03:28 +0000 Subject: [PATCH 2/6] Add changelog --- .../changelog/dotcom-15286-build-out-the-settings-screen | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 projects/packages/newsletter/changelog/dotcom-15286-build-out-the-settings-screen diff --git a/projects/packages/newsletter/changelog/dotcom-15286-build-out-the-settings-screen b/projects/packages/newsletter/changelog/dotcom-15286-build-out-the-settings-screen new file mode 100644 index 0000000000000..13c823a23de41 --- /dev/null +++ b/projects/packages/newsletter/changelog/dotcom-15286-build-out-the-settings-screen @@ -0,0 +1,4 @@ +Significance: minor +Type: added + +Implement comprehensive newsletter settings UI with DataForm-based interface, toggle controls, and card-based design From cd7439e6545824cfb81df9930360b2dd7724cccd Mon Sep 17 00:00:00 2001 From: Dean Sas Date: Fri, 28 Nov 2025 17:25:51 +0000 Subject: [PATCH 3/6] Remove needless enableSorting prop --- .../packages/newsletter/src/settings/index.tsx | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/projects/packages/newsletter/src/settings/index.tsx b/projects/packages/newsletter/src/settings/index.tsx index e7a269d6c7d6b..44b56f796f2e2 100644 --- a/projects/packages/newsletter/src/settings/index.tsx +++ b/projects/packages/newsletter/src/settings/index.tsx @@ -206,70 +206,60 @@ function NewsletterSettingsApp(): JSX.Element | null { ), type: 'boolean' as const, Edit: 'toggle' as const, - enableSorting: false, }, { id: 'jetpack_subscriptions_subscribe_post_end_enabled', label: __( 'Subscribe block at post end', 'jetpack-newsletter' ), type: 'boolean' as const, Edit: 'toggle' as const, - enableSorting: false, }, { id: 'sm_enabled', label: __( 'Subscription pop-up when scrolling', 'jetpack-newsletter' ), type: 'boolean' as const, Edit: 'toggle' as const, - enableSorting: false, }, { id: 'jetpack_subscribe_overlay_enabled', label: __( 'Subscription overlay on homepage', 'jetpack-newsletter' ), type: 'boolean' as const, Edit: 'toggle' as const, - enableSorting: false, }, { id: 'jetpack_subscribe_floating_button_enabled', label: __( 'Floating subscribe button', 'jetpack-newsletter' ), type: 'boolean' as const, Edit: 'toggle' as const, - enableSorting: false, }, { id: 'jetpack_subscriptions_subscribe_navigation_enabled', label: __( 'Subscribe block in navigation', 'jetpack-newsletter' ), type: 'boolean' as const, Edit: 'toggle' as const, - enableSorting: false, }, { id: 'jetpack_subscriptions_login_navigation_enabled', label: __( 'Subscriber login block in navigation', 'jetpack-newsletter' ), type: 'boolean' as const, Edit: 'toggle' as const, - enableSorting: false, }, { id: 'stb_enabled', label: __( '"Subscribe to site" on comment form', 'jetpack-newsletter' ), type: 'boolean' as const, Edit: 'toggle' as const, - enableSorting: false, }, { id: 'stc_enabled', label: __( '"Subscribe to comments" on comment form', 'jetpack-newsletter' ), type: 'boolean' as const, Edit: 'toggle' as const, - enableSorting: false, }, { id: 'wpcom_featured_image_in_email', label: __( 'Featured image in emails', 'jetpack-newsletter' ), type: 'boolean' as const, Edit: 'toggle' as const, - enableSorting: false, description: __( "Includes your post's featured image in the email sent out to your readers.", 'jetpack-newsletter' @@ -290,7 +280,6 @@ function NewsletterSettingsApp(): JSX.Element | null { label: __( 'Excerpt', 'jetpack-newsletter' ), }, ], - enableSorting: false, description: __( 'Sets whether email subscribers can read full posts in emails or just an excerpt and link to the full version.', 'jetpack-newsletter' @@ -301,7 +290,6 @@ function NewsletterSettingsApp(): JSX.Element | null { label: __( 'Show author avatar', 'jetpack-newsletter' ), type: 'boolean' as const, Edit: 'toggle' as const, - enableSorting: false, description: __( 'We use Gravatar, a service that associates an avatar image with your primary email address.', 'jetpack-newsletter' @@ -312,14 +300,12 @@ function NewsletterSettingsApp(): JSX.Element | null { label: __( 'Show author display name', 'jetpack-newsletter' ), type: 'boolean' as const, Edit: 'toggle' as const, - enableSorting: false, }, { id: 'jetpack_post_date_in_email', label: __( 'Add post date', 'jetpack-newsletter' ), type: 'boolean' as const, Edit: 'toggle' as const, - enableSorting: false, description: __( "You can customize the date format in your site's general settings.", 'jetpack-newsletter' @@ -341,7 +327,6 @@ function NewsletterSettingsApp(): JSX.Element | null { }, { value: 'no-reply', label: __( 'Replies are not allowed', 'jetpack-newsletter' ) }, ], - enableSorting: false, description: __( "Sets the reply to email address for your newsletter emails. It's the email where subscribers send their replies.", 'jetpack-newsletter' @@ -352,7 +337,6 @@ function NewsletterSettingsApp(): JSX.Element | null { label: __( 'Enable newsletter categories', 'jetpack-newsletter' ), type: 'boolean' as const, Edit: 'toggle' as const, - enableSorting: false, description: __( "Newsletter categories let you select the content that's emailed to subscribers. When enabled, only posts in the selected categories will be sent as newsletters.", 'jetpack-newsletter' From 581aa790a24a10c50cbb230101ffdabef28c044a Mon Sep 17 00:00:00 2001 From: Dean Sas Date: Fri, 28 Nov 2025 22:33:19 +0000 Subject: [PATCH 4/6] Add preview and template edit links --- pnpm-lock.yaml | 3 + projects/packages/newsletter/package.json | 1 + .../settings/components/toggle-with-link.scss | 5 + .../settings/components/toggle-with-link.tsx | 106 +++++++++++++ .../newsletter/src/settings/index.tsx | 148 +++++++++++++++++- .../newsletter/src/settings/style.scss | 1 - 6 files changed, 256 insertions(+), 8 deletions(-) create mode 100644 projects/packages/newsletter/src/settings/components/toggle-with-link.scss create mode 100644 projects/packages/newsletter/src/settings/components/toggle-with-link.tsx diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 38c5137102deb..34fb05f32f5fb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -3391,6 +3391,9 @@ importers: '@wordpress/notices': specifier: 5.35.0 version: 5.35.0(patch_hash=0c63a888feb97f2f1d416ca013ad85c31b6360b41cc0b6e2b0ae28f778fbdc5b)(react@18.3.1) + '@wordpress/url': + specifier: 4.35.0 + version: 4.35.0(patch_hash=2659f08edd4c0250f15fb428f013852a17e84da9c745e6dae6307de837e4d30b) debug: specifier: 4.4.1 version: 4.4.1 diff --git a/projects/packages/newsletter/package.json b/projects/packages/newsletter/package.json index 6d2ca4b6129af..5a0d30972a104 100644 --- a/projects/packages/newsletter/package.json +++ b/projects/packages/newsletter/package.json @@ -39,6 +39,7 @@ "@wordpress/element": "6.35.0", "@wordpress/i18n": "6.8.0", "@wordpress/notices": "5.35.0", + "@wordpress/url": "4.35.0", "debug": "4.4.1" }, "devDependencies": { diff --git a/projects/packages/newsletter/src/settings/components/toggle-with-link.scss b/projects/packages/newsletter/src/settings/components/toggle-with-link.scss new file mode 100644 index 0000000000000..b1368ae369854 --- /dev/null +++ b/projects/packages/newsletter/src/settings/components/toggle-with-link.scss @@ -0,0 +1,5 @@ +.toggle-with-link__label { + display: inline-flex; + align-items: center; + gap: 0.5em; +} diff --git a/projects/packages/newsletter/src/settings/components/toggle-with-link.tsx b/projects/packages/newsletter/src/settings/components/toggle-with-link.tsx new file mode 100644 index 0000000000000..d3ef161c9f6b3 --- /dev/null +++ b/projects/packages/newsletter/src/settings/components/toggle-with-link.tsx @@ -0,0 +1,106 @@ +/** + * External dependencies + */ +import { ExternalLink, ToggleControl } from '@wordpress/components'; +import { type Field } from '@wordpress/dataviews'; +import { useCallback } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; +import { addQueryArgs } from '@wordpress/url'; + +/** + * Internal dependencies + */ +import './toggle-with-link.scss'; + +interface ToggleWithLinkProps { + data: Record< string, unknown >; + field: Field< Record< string, unknown > >; + onChange: ( updates: Record< string, unknown > ) => void; + url: string; + linkText: string; +} + +/** + * Generic toggle control with an external link in the label + * + * @param {object} props - Component props + * @param {object} props.data - The data object + * @param {object} props.field - The field definition + * @param {Function} props.onChange - Change handler + * @param {string} props.url - URL for the external link + * @param {string} props.linkText - Text for the link + * @return {JSX.Element} The toggle control with link + */ +function ToggleWithLink( { + data, + field, + onChange, + url, + linkText, +}: ToggleWithLinkProps ): JSX.Element { + const handleChange = useCallback( () => { + onChange( { [ field.id ]: ! data[ field.id ] } ); + }, [ data, field.id, onChange ] ); + + return ( + + { field.label } + { linkText } + + } + /> + ); +} + +interface ToggleWithEditorLinkProps { + data: Record< string, unknown >; + field: Field< Record< string, unknown > >; + onChange: ( updates: Record< string, unknown > ) => void; + siteAdminUrl: string; + themeStylesheet: string; + postType: 'wp_template' | 'wp_template_part'; + templateId: string; +} + +/** + * Toggle control with a "Preview and edit" link to the site editor + * + * @param {object} props - Component props + * @param {object} props.data - The data object + * @param {object} props.field - The field definition + * @param {Function} props.onChange - Change handler + * @param {string} props.siteAdminUrl - Site admin URL + * @param {string} props.themeStylesheet - Theme stylesheet name + * @param {string} props.postType - Post type (wp_template or wp_template_part) + * @param {string} props.templateId - Template ID + * @return {JSX.Element} The toggle control with editor link + */ +export function ToggleWithEditorLink( { + data, + field, + onChange, + siteAdminUrl, + themeStylesheet, + postType, + templateId, +}: ToggleWithEditorLinkProps ): JSX.Element { + const url = addQueryArgs( `${ siteAdminUrl }site-editor.php`, { + postType, + postId: `${ themeStylesheet }//${ templateId }`, + canvas: 'edit', + } ); + + return ( + + ); +} diff --git a/projects/packages/newsletter/src/settings/index.tsx b/projects/packages/newsletter/src/settings/index.tsx index 44b56f796f2e2..a73d4771b7739 100644 --- a/projects/packages/newsletter/src/settings/index.tsx +++ b/projects/packages/newsletter/src/settings/index.tsx @@ -3,13 +3,14 @@ */ import restApi from '@automattic/jetpack-api'; import { Button, Notice, ExternalLink, Snackbar } from '@wordpress/components'; -import { DataForm } from '@wordpress/dataviews'; +import { DataForm, type Field } from '@wordpress/dataviews'; import { createRoot, useCallback, useEffect, useState } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; /** * Internal dependencies */ import { Header } from './components/header'; +import { ToggleWithEditorLink } from './components/toggle-with-link'; import './style.scss'; /** @@ -37,6 +38,7 @@ interface NewsletterSettings { subscription_options?: { welcome: string; }; + [ key: string ]: unknown; } /** @@ -196,6 +198,18 @@ function NewsletterSettingsApp(): JSX.Element | null { } ); }, [ senderName, data ] ); + // Helper to check if we can show editor links for block theme features + const canShowBlockThemeEditorLinks = + jetpackSettings?.isBlockTheme && + jetpackSettings?.siteAdminUrl && + jetpackSettings?.themeStylesheet; + + // Helper to check if we can show editor links for subscription site edit features + const canShowSubscriptionEditorLinks = + jetpackSettings?.isSubscriptionSiteEditSupported && + jetpackSettings?.siteAdminUrl && + jetpackSettings?.themeStylesheet; + // Define form fields const fields = [ { @@ -211,37 +225,157 @@ function NewsletterSettingsApp(): JSX.Element | null { id: 'jetpack_subscriptions_subscribe_post_end_enabled', label: __( 'Subscribe block at post end', 'jetpack-newsletter' ), type: 'boolean' as const, - Edit: 'toggle' as const, + Edit: canShowSubscriptionEditorLinks + ? ( { + data: formData, + field, + onChange, + }: { + data: NewsletterSettings; + field: Field< Record< string, unknown > >; + onChange: ( updates: Partial< NewsletterSettings > ) => void; + } ) => ( + + ) + : ( 'toggle' as const ), }, { id: 'sm_enabled', label: __( 'Subscription pop-up when scrolling', 'jetpack-newsletter' ), type: 'boolean' as const, - Edit: 'toggle' as const, + Edit: canShowBlockThemeEditorLinks + ? ( { + data: formData, + field, + onChange, + }: { + data: NewsletterSettings; + field: Field< Record< string, unknown > >; + onChange: ( updates: Partial< NewsletterSettings > ) => void; + } ) => ( + + ) + : ( 'toggle' as const ), }, { id: 'jetpack_subscribe_overlay_enabled', label: __( 'Subscription overlay on homepage', 'jetpack-newsletter' ), type: 'boolean' as const, - Edit: 'toggle' as const, + Edit: canShowBlockThemeEditorLinks + ? ( { + data: formData, + field, + onChange, + }: { + data: NewsletterSettings; + field: Field< Record< string, unknown > >; + onChange: ( updates: Partial< NewsletterSettings > ) => void; + } ) => ( + + ) + : ( 'toggle' as const ), }, { id: 'jetpack_subscribe_floating_button_enabled', label: __( 'Floating subscribe button', 'jetpack-newsletter' ), type: 'boolean' as const, - Edit: 'toggle' as const, + Edit: canShowBlockThemeEditorLinks + ? ( { + data: formData, + field, + onChange, + }: { + data: NewsletterSettings; + field: Field< Record< string, unknown > >; + onChange: ( updates: Partial< NewsletterSettings > ) => void; + } ) => ( + + ) + : ( 'toggle' as const ), }, { id: 'jetpack_subscriptions_subscribe_navigation_enabled', label: __( 'Subscribe block in navigation', 'jetpack-newsletter' ), type: 'boolean' as const, - Edit: 'toggle' as const, + Edit: canShowSubscriptionEditorLinks + ? ( { + data: formData, + field, + onChange, + }: { + data: NewsletterSettings; + field: Field< Record< string, unknown > >; + onChange: ( updates: Partial< NewsletterSettings > ) => void; + } ) => ( + + ) + : ( 'toggle' as const ), }, { id: 'jetpack_subscriptions_login_navigation_enabled', label: __( 'Subscriber login block in navigation', 'jetpack-newsletter' ), type: 'boolean' as const, - Edit: 'toggle' as const, + Edit: canShowSubscriptionEditorLinks + ? ( { + data: formData, + field, + onChange, + }: { + data: NewsletterSettings; + field: Field< Record< string, unknown > >; + onChange: ( updates: Partial< NewsletterSettings > ) => void; + } ) => ( + + ) + : ( 'toggle' as const ), }, { id: 'stb_enabled', diff --git a/projects/packages/newsletter/src/settings/style.scss b/projects/packages/newsletter/src/settings/style.scss index 23fead13f1960..c9d2fc7c9b40a 100644 --- a/projects/packages/newsletter/src/settings/style.scss +++ b/projects/packages/newsletter/src/settings/style.scss @@ -200,7 +200,6 @@ } .components-form-toggle { - margin: 2px 12px 0 0; flex-shrink: 0; } From d1d8810e8304e5ae40c232ca7fef1fb444e41760 Mon Sep 17 00:00:00 2001 From: Dean Sas Date: Fri, 28 Nov 2025 23:20:06 +0000 Subject: [PATCH 5/6] Update various strings --- .../newsletter/src/settings/index.tsx | 86 +++++++++---------- 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/projects/packages/newsletter/src/settings/index.tsx b/projects/packages/newsletter/src/settings/index.tsx index a73d4771b7739..f7576ead8105e 100644 --- a/projects/packages/newsletter/src/settings/index.tsx +++ b/projects/packages/newsletter/src/settings/index.tsx @@ -223,7 +223,7 @@ function NewsletterSettingsApp(): JSX.Element | null { }, { id: 'jetpack_subscriptions_subscribe_post_end_enabled', - label: __( 'Subscribe block at post end', 'jetpack-newsletter' ), + label: __( 'Add the Subscribe Block at the end of each post.', 'jetpack-newsletter' ), type: 'boolean' as const, Edit: canShowSubscriptionEditorLinks ? ( { @@ -249,7 +249,7 @@ function NewsletterSettingsApp(): JSX.Element | null { }, { id: 'sm_enabled', - label: __( 'Subscription pop-up when scrolling', 'jetpack-newsletter' ), + label: __( 'Show subscription pop-up when scrolling a post.', 'jetpack-newsletter' ), type: 'boolean' as const, Edit: canShowBlockThemeEditorLinks ? ( { @@ -301,7 +301,7 @@ function NewsletterSettingsApp(): JSX.Element | null { }, { id: 'jetpack_subscribe_floating_button_enabled', - label: __( 'Floating subscribe button', 'jetpack-newsletter' ), + label: __( "Floating subscribe button on site's bottom corner", 'jetpack-newsletter' ), type: 'boolean' as const, Edit: canShowBlockThemeEditorLinks ? ( { @@ -327,7 +327,7 @@ function NewsletterSettingsApp(): JSX.Element | null { }, { id: 'jetpack_subscriptions_subscribe_navigation_enabled', - label: __( 'Subscribe block in navigation', 'jetpack-newsletter' ), + label: __( 'Add the Subscribe Block to the navigation', 'jetpack-newsletter' ), type: 'boolean' as const, Edit: canShowSubscriptionEditorLinks ? ( { @@ -353,7 +353,7 @@ function NewsletterSettingsApp(): JSX.Element | null { }, { id: 'jetpack_subscriptions_login_navigation_enabled', - label: __( 'Subscriber login block in navigation', 'jetpack-newsletter' ), + label: __( 'Add the Subscriber Login Block to the navigation', 'jetpack-newsletter' ), type: 'boolean' as const, Edit: canShowSubscriptionEditorLinks ? ( { @@ -379,13 +379,19 @@ function NewsletterSettingsApp(): JSX.Element | null { }, { id: 'stb_enabled', - label: __( '"Subscribe to site" on comment form', 'jetpack-newsletter' ), + label: __( + 'Enable the "Subscribe to site" option on your comment form', + 'jetpack-newsletter' + ), type: 'boolean' as const, Edit: 'toggle' as const, }, { id: 'stc_enabled', - label: __( '"Subscribe to comments" on comment form', 'jetpack-newsletter' ), + label: __( + 'Enable the "Subscribe to comments" option on yourcomment form', + 'jetpack-newsletter' + ), type: 'boolean' as const, Edit: 'toggle' as const, }, @@ -421,7 +427,7 @@ function NewsletterSettingsApp(): JSX.Element | null { }, { id: 'jetpack_gravatar_in_email', - label: __( 'Show author avatar', 'jetpack-newsletter' ), + label: __( 'Show author avatar on your emails', 'jetpack-newsletter' ), type: 'boolean' as const, Edit: 'toggle' as const, description: __( @@ -437,7 +443,7 @@ function NewsletterSettingsApp(): JSX.Element | null { }, { id: 'jetpack_post_date_in_email', - label: __( 'Add post date', 'jetpack-newsletter' ), + label: __( 'Add the post date', 'jetpack-newsletter' ), type: 'boolean' as const, Edit: 'toggle' as const, description: __( @@ -462,7 +468,7 @@ function NewsletterSettingsApp(): JSX.Element | null { { value: 'no-reply', label: __( 'Replies are not allowed', 'jetpack-newsletter' ) }, ], description: __( - "Sets the reply to email address for your newsletter emails. It's the email where subscribers send their replies.", + 'Chooses who receives emails when subscribers reply to your newsletter.', 'jetpack-newsletter' ), }, @@ -471,10 +477,6 @@ function NewsletterSettingsApp(): JSX.Element | null { label: __( 'Enable newsletter categories', 'jetpack-newsletter' ), type: 'boolean' as const, Edit: 'toggle' as const, - description: __( - "Newsletter categories let you select the content that's emailed to subscribers. When enabled, only posts in the selected categories will be sent as newsletters.", - 'jetpack-newsletter' - ), }, ]; @@ -656,6 +658,33 @@ function NewsletterSettingsApp(): JSX.Element | null { ) } + { /* Newsletter Categories Section */ } +
+

+ { __( 'Newsletter categories', 'jetpack-newsletter' ) } +

+

+ { __( + "Newsletter categories let you select the content that's emailed to subscribers. When enabled, only posts in the selected categories will be sent as newsletters. By default, subscribers can choose from your selected categories, or you can pre-select categories using the subscribe block.", + 'jetpack-newsletter' + ) } +

+
+ f.id === 'wpcom_newsletter_categories_enabled' ) } + form={ { + layout: { + type: 'regular', + labelPosition: 'top', + }, + fields: [ 'wpcom_newsletter_categories_enabled' ], + } } + onChange={ handleAutoSave } + /> +
+
+ { /* Email Configuration Section */ }

@@ -772,40 +801,11 @@ function NewsletterSettingsApp(): JSX.Element | null {

- { /* Newsletter Categories Section */ } -
-

- { __( 'Newsletter categories', 'jetpack-newsletter' ) } -

-

- { __( - "Newsletter categories let you select the content that's emailed to subscribers. When enabled, only posts in the selected categories will be sent as newsletters.", - 'jetpack-newsletter' - ) } -

-
- f.id === 'wpcom_newsletter_categories_enabled' ) } - form={ { - layout: { - type: 'regular', - labelPosition: 'top', - }, - fields: [ 'wpcom_newsletter_categories_enabled' ], - } } - onChange={ handleAutoSave } - /> -
-
- - { /* Snackbar for success notifications */ } { snackbarMessage && { snackbarMessage } } ); } -// Initialize the app when DOM is ready const container = document.getElementById( 'newsletter-settings-root' ); if ( container ) { const root = createRoot( container ); From 2af1c9080193e2da039bac5342c634ddb37fcf68 Mon Sep 17 00:00:00 2001 From: Dean Sas Date: Sat, 29 Nov 2025 00:29:54 +0000 Subject: [PATCH 6/6] Remove extraneous css --- .../newsletter/src/settings/style.scss | 212 +----------------- 1 file changed, 6 insertions(+), 206 deletions(-) diff --git a/projects/packages/newsletter/src/settings/style.scss b/projects/packages/newsletter/src/settings/style.scss index c9d2fc7c9b40a..639f321c604b6 100644 --- a/projects/packages/newsletter/src/settings/style.scss +++ b/projects/packages/newsletter/src/settings/style.scss @@ -9,82 +9,38 @@ border: 1px solid #c3c4c7; box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04); margin-bottom: 20px; - padding: 0; - } - - &__section-header { - display: flex; - justify-content: space-between; - align-items: center; - padding: 16px 20px; - border-bottom: 1px solid #c3c4c7; - gap: 16px; + padding: 1.5em; } &__section-title { - font-size: 18px; - font-weight: 600; - line-height: 1.4; margin: 0; - padding: 16px 20px; - border-bottom: 1px solid #c3c4c7; - color: #1e1e1e; - - // When title is in header with button - .newsletter-settings__section-header & { - padding: 0; - border: none; - } + padding: 0; } &__section-description { - font-size: 14px; - line-height: 1.6; margin: 0; - padding: 16px 20px; - color: #646970; - background: #f6f7f7; - border-bottom: 1px solid #dcdcde; + padding: 0; } &__section-content { - padding: 20px; + padding-top: 1em; } &__section-actions { - margin-top: 24px; + margin-top: 2em; display: flex; justify-content: flex-start; } // Links styling &__link { - margin-top: 12px; - padding-top: 12px; - - a { - font-size: 14px; - text-decoration: none; - - &:hover { - text-decoration: underline; - } - } + margin-top: 2em; } // Help text with links &__help-text { margin-top: 12px; font-size: 13px; - - a { - font-size: 13px; - text-decoration: none; - - &:hover { - text-decoration: underline; - } - } } // Sender name field with inline save button @@ -92,14 +48,6 @@ margin-top: 24px; } - &__field-label { - display: block; - margin-bottom: 8px; - font-size: 11px; - font-weight: 600; - color: #1e1e1e; - } - &__sender-name-controls { display: flex; gap: 8px; @@ -123,13 +71,6 @@ } } - &__field-description { - font-size: 13px; - line-height: 1.5; - margin: 8px 0 0; - color: #646970; - } - &--error { padding: 20px; @@ -144,147 +85,6 @@ } } - // DataForm wrapper styling - .dataviews-view-form { - padding: 0; - } - - .dataforms-layouts-panel__field, - .dataforms-layouts-regular__field { - margin-bottom: 20px; - - &:last-child { - margin-bottom: 0; - } - } - - .dataforms-layouts-panel__field-label, - .dataforms-layouts-regular__field-label { - display: flex; - align-items: flex-start; - margin-bottom: 8px; - font-size: 11px; - font-weight: 600; - color: #1e1e1e; - line-height: 1.6; - } - - .dataforms-field-description { - display: block; - margin-top: 8px; - font-size: 13px; - color: #646970; - font-style: normal; - line-height: 1.5; - } - - // Group/section labels - .dataforms-layouts-panel__group-label { - display: block; - margin: 24px 0 16px; - font-size: 13px; - font-weight: 600; - color: #1e1e1e; - - &:first-child { - margin-top: 0; - } - } - - .components-toggle-control { - - .components-base-control__field { - display: flex; - align-items: flex-start; - margin-bottom: 0; - } - - .components-form-toggle { - flex-shrink: 0; - } - - .components-toggle-control__label { - font-weight: 400; - font-size: 14px; - line-height: 1.6; - flex: 1; - } - } - - .components-radio-control { - - .components-base-control__field { - margin-top: 8px; - } - - .components-radio-control__option { - display: flex; - align-items: center; - margin-bottom: 8px; - padding: 8px 0; - - &:last-child { - margin-bottom: 0; - } - - input[type="radio"] { - margin: 0 8px 0 0; - flex-shrink: 0; - } - - label { - margin: 0; - font-weight: 400; - font-size: 14px; - line-height: 1.5; - } - } - } - - .components-text-control__input, - .components-textarea-control__input { - width: 100%; - max-width: 25rem; - padding: 8px 12px; - border: 1px solid #8c8f94; - border-radius: 4px; - font-size: 14px; - line-height: 1.5; - - &:focus { - border-color: #2271b1; - box-shadow: 0 0 0 1px #2271b1; - outline: 2px solid transparent; - } - } - - .components-textarea-control__input { - min-height: 100px; - resize: vertical; - } - - .components-button { - font-size: 13px; - height: auto; - padding: 6px 12px; - - &.is-primary { - background-color: #2271b1; - border-color: #2271b1; - color: #fff; - - &:hover:not(:disabled) { - background-color: #135e96; - border-color: #135e96; - } - - &:focus:not(:disabled) { - background-color: #135e96; - border-color: #135e96; - box-shadow: 0 0 0 1px #fff, 0 0 0 3px #2271b1; - } - } - } // Notice component styling .components-notice {