From 68a33956b39999981dd8698ce6368d8c6265e281 Mon Sep 17 00:00:00 2001 From: Angela Blake Date: Tue, 5 May 2026 11:59:21 -0500 Subject: [PATCH 01/11] Donations Block: add modal display mode with trigger button and icon picker MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds `displayMode` (inline/modal), `triggerButtonText`, and `triggerIcon` attributes. In modal mode the block renders as a styled trigger button that opens the full donations form in an accessible overlay modal (focus trap, ESC to close, backdrop click to close, scroll lock, return focus on close). Includes a 3×3 curated icon picker (9 icons + None) in the editor sidebar and a trigger button preview in the editor canvas. Co-Authored-By: Claude Sonnet 4.6 --- .../extensions/blocks/donations/block.json | 12 ++ .../extensions/blocks/donations/controls.js | 76 ++++++++++++ .../extensions/blocks/donations/donations.php | 115 +++++++++++++++++- .../extensions/blocks/donations/edit.js | 37 +++++- .../extensions/blocks/donations/editor.scss | 80 ++++++++++++ .../extensions/blocks/donations/icons.js | 77 ++++++++++++ .../extensions/blocks/donations/view.js | 73 ++++++++++- .../extensions/blocks/donations/view.scss | 78 ++++++++++++ 8 files changed, 538 insertions(+), 10 deletions(-) create mode 100644 projects/plugins/jetpack/extensions/blocks/donations/icons.js diff --git a/projects/plugins/jetpack/extensions/blocks/donations/block.json b/projects/plugins/jetpack/extensions/blocks/donations/block.json index 2ac0c242e4d..c1041bcf4fd 100644 --- a/projects/plugins/jetpack/extensions/blocks/donations/block.json +++ b/projects/plugins/jetpack/extensions/blocks/donations/block.json @@ -180,6 +180,18 @@ }, "maximumAmount": { "type": "number" + }, + "displayMode": { + "type": "string", + "enum": [ "inline", "modal" ], + "default": "inline" + }, + "triggerButtonText": { + "type": "string" + }, + "triggerIcon": { + "type": "string", + "default": "coffee" } }, "example": {} diff --git a/projects/plugins/jetpack/extensions/blocks/donations/controls.js b/projects/plugins/jetpack/extensions/blocks/donations/controls.js index 37215cfdd52..c1c707885c6 100644 --- a/projects/plugins/jetpack/extensions/blocks/donations/controls.js +++ b/projects/plugins/jetpack/extensions/blocks/donations/controls.js @@ -2,21 +2,26 @@ import formatCurrency, { CURRENCIES } from '@automattic/format-currency'; import { getSiteFragment } from '@automattic/jetpack-shared-extension-utils'; import { AlignmentControl, BlockControls, InspectorControls } from '@wordpress/block-editor'; import { + Button, Dashicon, Dropdown, ExternalLink, Flex, FlexBlock, FlexItem, + Icon, MenuGroup, MenuItem, PanelBody, SelectControl, TextControl, ToggleControl, + __experimentalToggleGroupControl as ToggleGroupControl, // eslint-disable-line @wordpress/no-unsafe-wp-apis + __experimentalToggleGroupControlOption as ToggleGroupControlOption, // eslint-disable-line @wordpress/no-unsafe-wp-apis ToolbarGroup, ToolbarItem, ToolbarButton, + Tooltip, } from '@wordpress/components'; import { useCallback } from '@wordpress/element'; import { __, sprintf } from '@wordpress/i18n'; @@ -26,6 +31,7 @@ import { minimumTransactionAmountForCurrency, SUPPORTED_CURRENCIES, } from '../../shared/currencies'; +import { TRIGGER_ICONS } from './icons'; import { firstShownInterval } from './utils'; const INTERVAL_TO_ATTRIBUTE = { @@ -47,6 +53,9 @@ const Controls = props => { customAmountPlaceholder, minimumAmount, maximumAmount, + displayMode, + triggerButtonText, + triggerIcon, } = attributes; const stripeMin = minimumTransactionAmountForCurrency( currency ); @@ -216,6 +225,73 @@ const Controls = props => { + + setAttributes( { displayMode: value } ) } + isBlock + __nextHasNoMarginBottom={ true } + > + + + + { displayMode === 'modal' && ( + <> + setAttributes( { triggerButtonText: value || undefined } ) } + style={ { marginTop: 16 } } + __nextHasNoMarginBottom={ true } + /> +

+ { __( 'Button icon', 'jetpack' ) } +

+
+ + + + { TRIGGER_ICONS.map( ( { key, label, icon } ) => ( + + + + ) ) } +
+ + ) } +
' . $choose_amount_html . '

' : ''; + $display_mode = $attr['displayMode'] ?? 'inline'; + + if ( 'modal' === $display_mode ) { + $trigger_text = $attr['triggerButtonText'] ?? $default_texts['triggerButtonText']; + $trigger_icon_key = $attr['triggerIcon'] ?? 'coffee'; + $trigger_svg = get_trigger_icon_svg( $trigger_icon_key ); + $modal_id = $instance_id . '-modal'; + + return sprintf( + ' +
%9$s + + +
+', + $wrapper_attrs, + $nav, + $headings, + $choose_amount_block, + $amounts, + $custom_amount, + $extra_text, + $buttons, + $custom_styles ? '' : '', + esc_html( $trigger_text ), + esc_attr( $modal_id ), + $trigger_svg, + esc_attr__( 'Close', 'jetpack' ), + esc_attr( $tab_content_class ) + ); + } + return sprintf( '
%9$s @@ -569,24 +633,63 @@ function sanitize_css_value( $value ) { */ function get_default_texts() { return array( - 'chooseAmountText' => __( 'Choose an amount', 'jetpack' ), - 'customAmountText' => __( 'Or enter a custom amount', 'jetpack' ), - 'extraText' => __( 'Your contribution is appreciated.', 'jetpack' ), - 'oneTimeDonation' => array( + 'chooseAmountText' => __( 'Choose an amount', 'jetpack' ), + 'customAmountText' => __( 'Or enter a custom amount', 'jetpack' ), + 'extraText' => __( 'Your contribution is appreciated.', 'jetpack' ), + 'triggerButtonText' => __( 'Support me', 'jetpack' ), + 'oneTimeDonation' => array( 'heading' => __( 'Make a one-time donation', 'jetpack' ), 'buttonText' => __( 'Donate', 'jetpack' ), ), - 'monthlyDonation' => array( + 'monthlyDonation' => array( 'heading' => __( 'Make a monthly donation', 'jetpack' ), 'buttonText' => __( 'Donate monthly', 'jetpack' ), ), - 'annualDonation' => array( + 'annualDonation' => array( 'heading' => __( 'Make a yearly donation', 'jetpack' ), 'buttonText' => __( 'Donate yearly', 'jetpack' ), ), ); } +/** + * Return inline SVG markup for a trigger button icon. + * + * Path data sourced from icons.js ICON_SVG_PATHS — keep in sync. + * + * @param string $icon_key Icon key (e.g. 'coffee', 'heart', 'gift'). + * @return string SVG HTML string, or '' when key is 'none' or unknown. + */ +function get_trigger_icon_svg( $icon_key ) { + $paths = array( + 'heart' => array( 'd' => 'M16.5 4.5c2.206 0 4 1.794 4 4 0 4.67-5.543 8.94-8.5 11.023C9.043 17.44 3.5 13.17 3.5 8.5c0-2.206 1.794-4 4-4 1.298 0 2.522.638 3.273 1.706L12 7.953l1.227-1.746c.75-1.07 1.975-1.707 3.273-1.707m0-1.5c-1.862 0-3.505.928-4.5 2.344C11.005 3.928 9.362 3 7.5 3 4.462 3 2 5.462 2 8.5c0 5.72 6.5 10.438 10 12.85 3.5-2.412 10-7.13 10-12.85C22 5.462 19.538 3 16.5 3z' ), + 'gift' => array( 'd' => 'M15.333 4C16.6677 4 17.75 5.0823 17.75 6.41699V6.75C17.75 7.20058 17.6394 7.62468 17.4473 8H18.5C19.2767 8 19.9154 8.59028 19.9922 9.34668L20 9.5V18.5C20 19.3284 19.3284 20 18.5 20H5.5C4.72334 20 4.08461 19.4097 4.00781 18.6533L4 18.5V9.5L4.00781 9.34668C4.07949 8.64069 4.64069 8.07949 5.34668 8.00781L5.5 8H6.55273C6.36065 7.62468 6.25 7.20058 6.25 6.75V6.41699C6.25 5.0823 7.3323 4 8.66699 4C10.0436 4.00011 11.2604 4.68183 12 5.72559C12.7396 4.68183 13.9564 4.00011 15.333 4ZM5.5 18.5H11.25V9.5H5.5V18.5ZM12.75 18.5H18.5V9.5H12.75V18.5ZM8.66699 5.5C8.16073 5.5 7.75 5.91073 7.75 6.41699V6.75C7.75 7.44036 8.30964 8 9 8H11.2461C11.2021 6.61198 10.0657 5.50017 8.66699 5.5ZM15.333 5.5C13.9343 5.50017 12.7979 6.61198 12.7539 8H15C15.6904 8 16.25 7.44036 16.25 6.75V6.41699C16.25 5.91073 15.8393 5.5 15.333 5.5Z' ), + 'star' => array( 'd' => 'M11.776 4.454a.25.25 0 01.448 0l2.069 4.192a.25.25 0 00.188.137l4.626.672a.25.25 0 01.139.426l-3.348 3.263a.25.25 0 00-.072.222l.79 4.607a.25.25 0 01-.362.263l-4.138-2.175a.25.25 0 00-.232 0l-4.138 2.175a.25.25 0 01-.363-.263l.79-4.607a.25.25 0 00-.071-.222L4.754 9.881a.25.25 0 01.139-.426l4.626-.672a.25.25 0 00.188-.137l2.069-4.192z' ), + 'thumbs-up' => array( 'd' => 'm3 12 1 8h1.5l-1-8H3Zm15.8-2h-4.4l.8-3.6c.3-1.3-.7-2.4-1.9-2.4h-.2c-.6 0-1.2.3-1.6.8l-5 6.6c-.3.4-.4.8-.4 1.2v.2l.7 5.4v.2c.2.9 1 1.5 1.9 1.5h8.2c.9 0 1.7-.6 1.9-1.4l1.8-6c.4-1.3-.6-2.6-1.9-2.6Zm.5 2.1-1.8 6c0 .2-.3.4-.5.4H8.8c-.3 0-.5-.2-.5-.4l-.7-5.4v-.4l5-6.6c0-.1.2-.2.4-.2h.2c.3 0 .6.3.5.6l-.8 3.6c-.1.4 0 .9.3 1.3s.7.6 1.2.6h4.4c.3 0 .6.3.5.6Z' ), + 'smiley' => array( 'd' => 'M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm3.5-9c.83 0 1.5-.67 1.5-1.5S16.33 8 15.5 8 14 8.67 14 9.5 14.67 11 15.5 11zm-7 0c.83 0 1.5-.67 1.5-1.5S9.33 8 8.5 8 7 8.67 7 9.5 7.67 11 8.5 11zm3.5 6.5c2.33 0 4.31-1.46 5.11-3.5H6.89c.8 2.04 2.78 3.5 5.11 3.5z' ), + 'coffee' => array( 'd' => 'M20 3H4v10c0 2.21 1.79 4 4 4h6c2.21 0 4-1.79 4-4v-3h2c1.11 0 2-.89 2-2V5c0-1.11-.89-2-2-2zm0 5h-2V5h2v3zM4 19h16v2H4zM9 1v2M12 1v2M15 1v2' ), + 'tip-jar' => array( 'd' => 'M9 3h6c0-1.1-.9-2-2-2H11C9.9 1 9 1.9 9 3zm7 0H8c-.55 0-1 .45-1 1v1.5c0 .55.45 1 1 1h8c.55 0 1-.45 1-1V4c0-.55-.45-1-1-1zm-1 3.5H9V19c0 1.1.9 2 2 2h2c1.1 0 2-.9 2-2V6.5zm-3 1.5h2v1.5l-1 1.5-1-1.5V8z' ), + 'hand-heart' => array( 'd' => 'M15.5 2.1c-1.1 0-2 .6-2.5 1.4-.5-.9-1.4-1.4-2.5-1.4C8.8 2.1 7.5 3.4 7.5 5c0 2.5 4.5 5.9 5.5 6.6 1-.7 5.5-4.1 5.5-6.6 0-1.6-1.3-2.9-3-2.9zM9 14H7l-2 7h14l-2-7h-2l-1 3H10l-1-3z' ), + 'people' => array( + 'd' => 'M15.5 9.5a1 1 0 100-2 1 1 0 000 2zm0 1.5a2.5 2.5 0 100-5 2.5 2.5 0 000 5zm-2.25 6v-2a2.75 2.75 0 00-2.75-2.75h-4A2.75 2.75 0 003.75 15v2h1.5v-2c0-.69.56-1.25 1.25-1.25h4c.69 0 1.25.56 1.25 1.25v2h1.5zm7-2v2h-1.5v-2c0-.69-.56-1.25-1.25-1.25H15v-1.5h2.5A2.75 2.75 0 0120.25 15zM9.5 8.5a1 1 0 11-2 0 1 1 0 012 0zm1.5 0a2.5 2.5 0 11-5 0 2.5 2.5 0 015 0z', + 'fill-rule' => 'evenodd', + ), + ); + + if ( ! isset( $paths[ $icon_key ] ) || 'none' === $icon_key ) { + return ''; + } + + $icon = $paths[ $icon_key ]; + $extra_attrs = isset( $icon['fill-rule'] ) ? ' fill-rule="' . esc_attr( $icon['fill-rule'] ) . '"' : ''; + + return sprintf( + '', + esc_attr( $icon['d'] ), + $extra_attrs + ); +} + /** * Make default texts available to the editor. */ diff --git a/projects/plugins/jetpack/extensions/blocks/donations/edit.js b/projects/plugins/jetpack/extensions/blocks/donations/edit.js index 49af60a41f6..b1435f1e78b 100644 --- a/projects/plugins/jetpack/extensions/blocks/donations/edit.js +++ b/projects/plugins/jetpack/extensions/blocks/donations/edit.js @@ -1,5 +1,5 @@ import { useBlockProps } from '@wordpress/block-editor'; -import { Spinner } from '@wordpress/components'; +import { Icon, Spinner } from '@wordpress/components'; import { useInstanceId } from '@wordpress/compose'; import { useDispatch, useSelect } from '@wordpress/data'; import { useCallback, useState, useEffect } from '@wordpress/element'; @@ -15,6 +15,7 @@ import buildCustomStyles from './build-custom-styles'; import fetchDefaultProducts from './fetch-default-products'; import fetchStatus from './fetch-status'; import FirstTimeModal from './first-time-modal'; +import { TRIGGER_ICONS } from './icons'; import './first-time-modal.scss'; import LoadingError from './loading-error'; import StyleControls from './style-controls'; @@ -22,7 +23,8 @@ import Tabs from './tabs'; const Edit = props => { const { attributes, setAttributes } = props; - const { currency, tabsAppearance, className } = attributes; + const { currency, tabsAppearance, className, displayMode, triggerButtonText, triggerIcon } = + attributes; // Migrate legacy blocks that used the block-style variation // (`is-style-buttons` saved into `className`) over to the new @@ -207,7 +209,36 @@ const Edit = props => { // Memberships settings are still loading content = ; } else { - content = ; + const triggerIconEntry = TRIGGER_ICONS.find( ( { key } ) => key === triggerIcon ); + const triggerLabel = triggerButtonText || __( 'Support me', 'jetpack' ); + content = ( + <> + { displayMode === 'modal' && ( +
+ +

+ { __( 'Modal preview — form appears inside a modal when clicked', 'jetpack' ) } +

+
+ ) } +
+ +
+ + ); } // When the first time modal is closed, update the user meta to mark the donation warning as dismissed diff --git a/projects/plugins/jetpack/extensions/blocks/donations/editor.scss b/projects/plugins/jetpack/extensions/blocks/donations/editor.scss index f498191b0b7..eca5c051259 100644 --- a/projects/plugins/jetpack/extensions/blocks/donations/editor.scss +++ b/projects/plugins/jetpack/extensions/blocks/donations/editor.scss @@ -44,6 +44,51 @@ .jetpack-block-nudge { max-width: none; } + + // Trigger button preview (modal display mode). + .donations__trigger-preview { + margin-block-end: 16px; + } + + .donations__trigger-button { + display: inline-flex; + align-items: center; + gap: 8px; + cursor: default; + pointer-events: none; + } + + .donations__trigger-icon { + flex-shrink: 0; + fill: currentColor; + } + + .donations__trigger-hint { + font-size: 12px; + color: #757575; + margin: 6px 0 0; + font-style: italic; + } + + .donations__modal-form-preview { + border: 1px dashed #b0b0b0; + border-radius: 4px; + padding: 12px; + position: relative; + + &::before { + content: "Modal contents"; + position: absolute; + inset-block-start: -10px; + inset-inline-start: 12px; + background: #fff; + padding: 0 4px; + font-size: 11px; + color: #757575; + text-transform: uppercase; + letter-spacing: 0.05em; + } + } } .jetpack-donations__currency-toggle { @@ -73,6 +118,41 @@ border-bottom: 0; } +// Icon picker grid in the Display panel. +.jetpack-donations__icon-picker { + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 4px; + margin-block-start: 4px; + + .jetpack-donations__icon-option { + display: flex; + align-items: center; + justify-content: center; + width: 100%; + min-height: 36px; + padding: 4px; + border: 1px solid #ddd; + border-radius: 4px; + background: transparent; + cursor: pointer; + + &:hover { + border-color: #1e1e1e; + } + + &.is-selected { + border-color: var(--wp-admin-theme-color, #007cba); + box-shadow: 0 0 0 1px var(--wp-admin-theme-color, #007cba); + background: rgba(0, 124, 186, 0.08); + } + + svg { + display: block; + } + } +} + // Make labels inside our custom style panels inherit text color from the // inspector sidebar (`.interface-complementary-area`) instead of the // component default grey, matching the auto-rendered supports panels. diff --git a/projects/plugins/jetpack/extensions/blocks/donations/icons.js b/projects/plugins/jetpack/extensions/blocks/donations/icons.js new file mode 100644 index 00000000000..d13a3a3b081 --- /dev/null +++ b/projects/plugins/jetpack/extensions/blocks/donations/icons.js @@ -0,0 +1,77 @@ +import { __ } from '@wordpress/i18n'; +import { gift, people, starFilled, thumbsUp } from '@wordpress/icons'; +import { SVG, Path } from '@wordpress/primitives'; + +// Heart — reuses the Donations block's own block.json icon path. +const heart = ( + + + +); + +// Smiley face. +const smiley = ( + + + +); + +// Coffee cup (hot beverage mug with handle and steam). +const coffee = ( + + + +); + +// Tip jar — wide-mouth jar with a coin slot in the lid. +const tipJar = ( + + + +); + +// Hand with heart — open hand offering a heart. +const handWithHeart = ( + + + +); + +// SVG path strings used by PHP (donations.php) for server-side render. +// Keep in sync with the React icon definitions above and @wordpress/icons source. +export const ICON_SVG_PATHS = { + heart: + 'M16.5 4.5c2.206 0 4 1.794 4 4 0 4.67-5.543 8.94-8.5 11.023C9.043 17.44 3.5 13.17 3.5 8.5c0-2.206 1.794-4 4-4 1.298 0 2.522.638 3.273 1.706L12 7.953l1.227-1.746c.75-1.07 1.975-1.707 3.273-1.707m0-1.5c-1.862 0-3.505.928-4.5 2.344C11.005 3.928 9.362 3 7.5 3 4.462 3 2 5.462 2 8.5c0 5.72 6.5 10.438 10 12.85 3.5-2.412 10-7.13 10-12.85C22 5.462 19.538 3 16.5 3z', + gift: 'M15.333 4C16.6677 4 17.75 5.0823 17.75 6.41699V6.75C17.75 7.20058 17.6394 7.62468 17.4473 8H18.5C19.2767 8 19.9154 8.59028 19.9922 9.34668L20 9.5V18.5C20 19.3284 19.3284 20 18.5 20H5.5C4.72334 20 4.08461 19.4097 4.00781 18.6533L4 18.5V9.5L4.00781 9.34668C4.07949 8.64069 4.64069 8.07949 5.34668 8.00781L5.5 8H6.55273C6.36065 7.62468 6.25 7.20058 6.25 6.75V6.41699C6.25 5.0823 7.3323 4 8.66699 4C10.0436 4.00011 11.2604 4.68183 12 5.72559C12.7396 4.68183 13.9564 4.00011 15.333 4ZM5.5 18.5H11.25V9.5H5.5V18.5ZM12.75 18.5H18.5V9.5H12.75V18.5ZM8.66699 5.5C8.16073 5.5 7.75 5.91073 7.75 6.41699V6.75C7.75 7.44036 8.30964 8 9 8H11.2461C11.2021 6.61198 10.0657 5.50017 8.66699 5.5ZM15.333 5.5C13.9343 5.50017 12.7979 6.61198 12.7539 8H15C15.6904 8 16.25 7.44036 16.25 6.75V6.41699C16.25 5.91073 15.8393 5.5 15.333 5.5Z', + star: 'M11.776 4.454a.25.25 0 01.448 0l2.069 4.192a.25.25 0 00.188.137l4.626.672a.25.25 0 01.139.426l-3.348 3.263a.25.25 0 00-.072.222l.79 4.607a.25.25 0 01-.362.263l-4.138-2.175a.25.25 0 00-.232 0l-4.138 2.175a.25.25 0 01-.363-.263l.79-4.607a.25.25 0 00-.071-.222L4.754 9.881a.25.25 0 01.139-.426l4.626-.672a.25.25 0 00.188-.137l2.069-4.192z', + 'thumbs-up': + 'm3 12 1 8h1.5l-1-8H3Zm15.8-2h-4.4l.8-3.6c.3-1.3-.7-2.4-1.9-2.4h-.2c-.6 0-1.2.3-1.6.8l-5 6.6c-.3.4-.4.8-.4 1.2v.2l.7 5.4v.2c.2.9 1 1.5 1.9 1.5h8.2c.9 0 1.7-.6 1.9-1.4l1.8-6c.4-1.3-.6-2.6-1.9-2.6Zm.5 2.1-1.8 6c0 .2-.3.4-.5.4H8.8c-.3 0-.5-.2-.5-.4l-.7-5.4v-.4l5-6.6c0-.1.2-.2.4-.2h.2c.3 0 .6.3.5.6l-.8 3.6c-.1.4 0 .9.3 1.3s.7.6 1.2.6h4.4c.3 0 .6.3.5.6Z', + smiley: + 'M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm3.5-9c.83 0 1.5-.67 1.5-1.5S16.33 8 15.5 8 14 8.67 14 9.5 14.67 11 15.5 11zm-7 0c.83 0 1.5-.67 1.5-1.5S9.33 8 8.5 8 7 8.67 7 9.5 7.67 11 8.5 11zm3.5 6.5c2.33 0 4.31-1.46 5.11-3.5H6.89c.8 2.04 2.78 3.5 5.11 3.5z', + coffee: + 'M20 3H4v10c0 2.21 1.79 4 4 4h6c2.21 0 4-1.79 4-4v-3h2c1.11 0 2-.89 2-2V5c0-1.11-.89-2-2-2zm0 5h-2V5h2v3zM4 19h16v2H4zM9 1v2M12 1v2M15 1v2', + 'tip-jar': + 'M9 3h6c0-1.1-.9-2-2-2H11C9.9 1 9 1.9 9 3zm7 0H8c-.55 0-1 .45-1 1v1.5c0 .55.45 1 1 1h8c.55 0 1-.45 1-1V4c0-.55-.45-1-1-1zm-1 3.5H9V19c0 1.1.9 2 2 2h2c1.1 0 2-.9 2-2V6.5zm-3 1.5h2v1.5l-1 1.5-1-1.5V8z', + 'hand-heart': + 'M15.5 2.1c-1.1 0-2 .6-2.5 1.4-.5-.9-1.4-1.4-2.5-1.4C8.8 2.1 7.5 3.4 7.5 5c0 2.5 4.5 5.9 5.5 6.6 1-.7 5.5-4.1 5.5-6.6 0-1.6-1.3-2.9-3-2.9zM9 14H7l-2 7h14l-2-7h-2l-1 3H10l-1-3z', + people: + 'M15.5 9.5a1 1 0 100-2 1 1 0 000 2zm0 1.5a2.5 2.5 0 100-5 2.5 2.5 0 000 5zm-2.25 6v-2a2.75 2.75 0 00-2.75-2.75h-4A2.75 2.75 0 003.75 15v2h1.5v-2c0-.69.56-1.25 1.25-1.25h4c.69 0 1.25.56 1.25 1.25v2h1.5zm7-2v2h-1.5v-2c0-.69-.56-1.25-1.25-1.25H15v-1.5h2.5A2.75 2.75 0 0120.25 15zM9.5 8.5a1 1 0 11-2 0 1 1 0 012 0zm1.5 0a2.5 2.5 0 11-5 0 2.5 2.5 0 015 0z', +}; + +// Ordered list used to build the icon picker grid (3×3). +// Keys match ICON_SVG_PATHS and the triggerIcon block attribute. +export const TRIGGER_ICONS = [ + { key: 'heart', label: __( 'Heart', 'jetpack' ), icon: heart }, + { key: 'gift', label: __( 'Gift', 'jetpack' ), icon: gift }, + { key: 'star', label: __( 'Star', 'jetpack' ), icon: starFilled }, + { key: 'thumbs-up', label: __( 'Thumbs up', 'jetpack' ), icon: thumbsUp }, + { key: 'smiley', label: __( 'Smiley face', 'jetpack' ), icon: smiley }, + { key: 'coffee', label: __( 'Coffee cup', 'jetpack' ), icon: coffee }, + { key: 'tip-jar', label: __( 'Tip jar', 'jetpack' ), icon: tipJar }, + { key: 'hand-heart', label: __( 'Hand with heart', 'jetpack' ), icon: handWithHeart }, + { key: 'people', label: __( 'People', 'jetpack' ), icon: people }, +]; + +// @wordpress/icons exports needed by PHP path map (gift, starFilled, thumbsUp, people +// are React elements — PHP uses its own SVG strings for those). +export { gift, people, starFilled, thumbsUp }; diff --git a/projects/plugins/jetpack/extensions/blocks/donations/view.js b/projects/plugins/jetpack/extensions/blocks/donations/view.js index 7a5d7ca205d..44e9fe2d311 100644 --- a/projects/plugins/jetpack/extensions/blocks/donations/view.js +++ b/projects/plugins/jetpack/extensions/blocks/donations/view.js @@ -323,8 +323,79 @@ class JetpackDonations { } } +class JetpackDonationsModal { + constructor( block ) { + this.block = block; + this.trigger = block.querySelector( '.donations__trigger-button' ); + this.overlay = block.querySelector( '.donations__modal-overlay' ); + this.closeBtn = block.querySelector( '.donations__modal-close' ); + + if ( ! this.trigger || ! this.overlay ) { + return; + } + + this.trigger.addEventListener( 'click', () => this.open() ); + this.closeBtn?.addEventListener( 'click', () => this.close() ); + this.overlay.addEventListener( 'click', event => { + if ( event.target === this.overlay ) { + this.close(); + } + } ); + document.addEventListener( 'keydown', event => { + if ( event.key === 'Escape' && ! this.overlay.hidden ) { + this.close(); + } + } ); + } + + open() { + this.overlay.hidden = false; + this.overlay.ownerDocument.body.classList.add( 'donations-modal-open' ); + this._previousFocus = this.overlay.ownerDocument.activeElement; + const firstFocusable = this.overlay.querySelector( + 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])' + ); + firstFocusable?.focus(); + this.overlay.addEventListener( 'keydown', this._trapFocus ); + } + + close() { + this.overlay.hidden = true; + this.overlay.ownerDocument.body.classList.remove( 'donations-modal-open' ); + this.overlay.removeEventListener( 'keydown', this._trapFocus ); + this._previousFocus?.focus(); + } + + _trapFocus = event => { + if ( event.key !== 'Tab' ) { + return; + } + const focusable = Array.from( + this.overlay.querySelectorAll( + 'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])' + ) + ).filter( el => ! el.closest( '[hidden]' ) ); + if ( ! focusable.length ) { + return; + } + const first = focusable[ 0 ]; + const last = focusable[ focusable.length - 1 ]; + if ( event.shiftKey && this.overlay.ownerDocument.activeElement === first ) { + event.preventDefault(); + last.focus(); + } else if ( ! event.shiftKey && this.overlay.ownerDocument.activeElement === last ) { + event.preventDefault(); + first.focus(); + } + }; +} + domReady( () => { const blocks = document.querySelectorAll( '.wp-block-jetpack-donations' ); - blocks.forEach( block => new JetpackDonations( block ) ); + blocks.forEach( block => + block.querySelector( '.donations__modal-overlay' ) + ? [ new JetpackDonationsModal( block ), new JetpackDonations( block ) ] + : new JetpackDonations( block ) + ); initializeMembershipButtons( '.donations__donate-button' ); } ); diff --git a/projects/plugins/jetpack/extensions/blocks/donations/view.scss b/projects/plugins/jetpack/extensions/blocks/donations/view.scss index e537c1d3b7e..8bf05e14496 100644 --- a/projects/plugins/jetpack/extensions/blocks/donations/view.scss +++ b/projects/plugins/jetpack/extensions/blocks/donations/view.scss @@ -122,4 +122,82 @@ pointer-events: none; opacity: 0.2; } + + // Trigger button: icon + text inline. + .donations__trigger-button { + display: inline-flex; + align-items: center; + gap: 8px; + } + + .donations__trigger-icon { + flex-shrink: 0; + fill: currentColor; + } + + // Modal overlay: covers viewport, blurred backdrop. + .donations__modal-overlay { + position: fixed; + inset: 0; + z-index: 100000; + display: flex; + align-items: center; + justify-content: center; + background: rgba(0, 0, 0, 0.45); + backdrop-filter: blur(5px); + + &[hidden] { + display: none; + } + } + + // Modal dialog container. + .donations__modal-dialog { + position: relative; + width: 90%; + max-width: 640px; + max-height: 90vh; + overflow-y: auto; + background: var(--wp--style--color--background, #fff); + border-radius: inherit; + box-shadow: 0 8px 40px rgba(0, 0, 0, 0.18); + + @media (max-width: 600px) { + width: 92%; + } + } + + .donations__modal-content { + padding: 40px; + + @media (max-width: 600px) { + padding: 24px 20px; + } + } + + // Close button: X in upper-right corner. + .donations__modal-close { + position: absolute; + inset-block-start: 12px; + inset-inline-end: 12px; + background: transparent; + border: none; + cursor: pointer; + padding: 6px; + line-height: 1; + color: inherit; + font-size: 18px; + opacity: 0.6; + border-radius: 4px; + + &:hover, + &:focus-visible { + opacity: 1; + } + } +} + +// Prevent body scroll when modal is open. +body.donations-modal-open { + overflow: hidden; } From f351e696a522af732f2b6ed77240aab910714767 Mon Sep 17 00:00:00 2001 From: Angela Blake Date: Tue, 5 May 2026 17:22:22 -0500 Subject: [PATCH 02/11] Donations Block: modal display mode polish from testing - Fix block border: reset border/radius on wrapper (!important to override inline block-support styles) and forward them to the modal dialog instead; overflow:hidden on dialog enables border-radius clipping - Add trigger button alignment: contentAlignment now also applies text-align to the block wrapper in modal mode, aligning the trigger button left/center/right - Simplify icon picker to 4 icons (heart, gift, smiley, cup) in a single row; replace the "None" grid button with a Show icon ToggleControl consistent with other settings panels Co-Authored-By: Claude Sonnet 4.6 --- .../extensions/blocks/donations/controls.js | 69 +++++++------------ .../extensions/blocks/donations/donations.php | 52 +++++++++++++- .../extensions/blocks/donations/icons.js | 31 ++------- .../extensions/blocks/donations/view.scss | 9 ++- 4 files changed, 87 insertions(+), 74 deletions(-) diff --git a/projects/plugins/jetpack/extensions/blocks/donations/controls.js b/projects/plugins/jetpack/extensions/blocks/donations/controls.js index c1c707885c6..3a43763f57b 100644 --- a/projects/plugins/jetpack/extensions/blocks/donations/controls.js +++ b/projects/plugins/jetpack/extensions/blocks/donations/controls.js @@ -246,49 +246,32 @@ const Controls = props => { style={ { marginTop: 16 } } __nextHasNoMarginBottom={ true } /> -

- { __( 'Button icon', 'jetpack' ) } -

-
- - - - { TRIGGER_ICONS.map( ( { key, label, icon } ) => ( - - - - ) ) } -
+ setAttributes( { triggerIcon: value ? 'coffee' : 'none' } ) } + style={ { marginTop: 16 } } + __nextHasNoMarginBottom={ true } + /> + { triggerIcon !== 'none' && ( +
+ { TRIGGER_ICONS.map( ( { key, label, icon } ) => ( + + + + ) ) } +
+ ) } ) } diff --git a/projects/plugins/jetpack/extensions/blocks/donations/donations.php b/projects/plugins/jetpack/extensions/blocks/donations/donations.php index 70b074cfdf5..d4a5f3aa345 100644 --- a/projects/plugins/jetpack/extensions/blocks/donations/donations.php +++ b/projects/plugins/jetpack/extensions/blocks/donations/donations.php @@ -245,11 +245,15 @@ function render_block( $attr, $content ) { ); } + $display_mode = $attr['displayMode'] ?? 'inline'; $instance_id = wp_unique_id( 'jp-donations-' ); $instance_classes = $instance_id; if ( isset( $attr['tabsAppearance'] ) && 'buttons' === $attr['tabsAppearance'] ) { $instance_classes .= ' is-style-buttons'; } + if ( 'modal' === $display_mode ) { + $instance_classes .= ' is-modal-display'; + } $wrapper_attr_array = array( 'class' => $instance_classes ); if ( $default_interval ) { $wrapper_attr_array['data-default-interval'] = $default_interval; @@ -261,8 +265,6 @@ function render_block( $attr, $content ) { $choose_amount_html = wp_kses_post( $choose_amount_text ); $choose_amount_block = '' !== trim( $choose_amount_html ) ? '

' . $choose_amount_html . '

' : ''; - $display_mode = $attr['displayMode'] ?? 'inline'; - if ( 'modal' === $display_mode ) { $trigger_text = $attr['triggerButtonText'] ?? $default_texts['triggerButtonText']; $trigger_icon_key = $attr['triggerIcon'] ?? 'coffee'; @@ -524,6 +526,52 @@ function build_custom_styles( $attr, $scope ) { . $scope . ' .donations__donate-button{display:block;width:100%;box-sizing:border-box;text-align:center}'; } + // Modal display mode: the block-support border/radius are applied as inline + // styles on the wrapper div (which shows the trigger button in modal mode). + // Reset them on the wrapper with !important and forward them to the modal + // dialog instead. Also wire up trigger-button alignment via text-align. + if ( 'modal' === ( $attr['displayMode'] ?? 'inline' ) ) { + $rules[] = $scope . '{border:none!important;border-radius:0!important;overflow:visible}'; + + $supports_border = isset( $attr['style']['border'] ) && is_array( $attr['style']['border'] ) + ? $attr['style']['border'] + : array(); + $modal_decls = array(); + + // Uniform border color / style / width. + foreach ( array( 'color', 'style', 'width' ) as $prop ) { + $val = sanitize_css_value( $supports_border[ $prop ] ?? '' ); + if ( '' !== $val ) { + $modal_decls[] = 'border-' . $prop . ':' . $val; + } + } + // Per-side border (split BorderBoxControl output). + foreach ( array( 'top', 'right', 'bottom', 'left' ) as $side ) { + if ( ! isset( $supports_border[ $side ] ) || ! is_array( $supports_border[ $side ] ) ) { + continue; + } + foreach ( array( 'color', 'style', 'width' ) as $prop ) { + $val = sanitize_css_value( $supports_border[ $side ][ $prop ] ?? '' ); + if ( '' !== $val ) { + $modal_decls[] = 'border-' . $side . '-' . $prop . ':' . $val; + } + } + } + // Border radius. + $radius_val = $supports_border['radius'] ?? null; + $modal_decls = array_merge( $modal_decls, build_radius_decls( $radius_val ) ); + + if ( $modal_decls ) { + // overflow:hidden clips content to the border-radius. + $rules[] = $scope . ' .donations__modal-dialog{' . implode( ';', $modal_decls ) . ';overflow:hidden}'; + } + + // Trigger button alignment reuses the existing contentAlignment attribute. + if ( in_array( $content_alignment, array( 'left', 'center', 'right' ), true ) ) { + $rules[] = $scope . '{text-align:' . $content_alignment . '}'; + } + } + return implode( '', $rules ); } diff --git a/projects/plugins/jetpack/extensions/blocks/donations/icons.js b/projects/plugins/jetpack/extensions/blocks/donations/icons.js index d13a3a3b081..24f248dbe4c 100644 --- a/projects/plugins/jetpack/extensions/blocks/donations/icons.js +++ b/projects/plugins/jetpack/extensions/blocks/donations/icons.js @@ -1,5 +1,5 @@ import { __ } from '@wordpress/i18n'; -import { gift, people, starFilled, thumbsUp } from '@wordpress/icons'; +import { gift } from '@wordpress/icons'; import { SVG, Path } from '@wordpress/primitives'; // Heart — reuses the Donations block's own block.json icon path. @@ -23,20 +23,6 @@ const coffee = ( ); -// Tip jar — wide-mouth jar with a coin slot in the lid. -const tipJar = ( - - - -); - -// Hand with heart — open hand offering a heart. -const handWithHeart = ( - - - -); - // SVG path strings used by PHP (donations.php) for server-side render. // Keep in sync with the React icon definitions above and @wordpress/icons source. export const ICON_SVG_PATHS = { @@ -58,20 +44,13 @@ export const ICON_SVG_PATHS = { 'M15.5 9.5a1 1 0 100-2 1 1 0 000 2zm0 1.5a2.5 2.5 0 100-5 2.5 2.5 0 000 5zm-2.25 6v-2a2.75 2.75 0 00-2.75-2.75h-4A2.75 2.75 0 003.75 15v2h1.5v-2c0-.69.56-1.25 1.25-1.25h4c.69 0 1.25.56 1.25 1.25v2h1.5zm7-2v2h-1.5v-2c0-.69-.56-1.25-1.25-1.25H15v-1.5h2.5A2.75 2.75 0 0120.25 15zM9.5 8.5a1 1 0 11-2 0 1 1 0 012 0zm1.5 0a2.5 2.5 0 11-5 0 2.5 2.5 0 015 0z', }; -// Ordered list used to build the icon picker grid (3×3). +// Ordered list used to build the icon picker row (4 icons). // Keys match ICON_SVG_PATHS and the triggerIcon block attribute. export const TRIGGER_ICONS = [ { key: 'heart', label: __( 'Heart', 'jetpack' ), icon: heart }, { key: 'gift', label: __( 'Gift', 'jetpack' ), icon: gift }, - { key: 'star', label: __( 'Star', 'jetpack' ), icon: starFilled }, - { key: 'thumbs-up', label: __( 'Thumbs up', 'jetpack' ), icon: thumbsUp }, - { key: 'smiley', label: __( 'Smiley face', 'jetpack' ), icon: smiley }, - { key: 'coffee', label: __( 'Coffee cup', 'jetpack' ), icon: coffee }, - { key: 'tip-jar', label: __( 'Tip jar', 'jetpack' ), icon: tipJar }, - { key: 'hand-heart', label: __( 'Hand with heart', 'jetpack' ), icon: handWithHeart }, - { key: 'people', label: __( 'People', 'jetpack' ), icon: people }, + { key: 'smiley', label: __( 'Smiley', 'jetpack' ), icon: smiley }, + { key: 'coffee', label: __( 'Cup', 'jetpack' ), icon: coffee }, ]; -// @wordpress/icons exports needed by PHP path map (gift, starFilled, thumbsUp, people -// are React elements — PHP uses its own SVG strings for those). -export { gift, people, starFilled, thumbsUp }; +export { gift }; diff --git a/projects/plugins/jetpack/extensions/blocks/donations/view.scss b/projects/plugins/jetpack/extensions/blocks/donations/view.scss index 8bf05e14496..cb2cf2d952d 100644 --- a/projects/plugins/jetpack/extensions/blocks/donations/view.scss +++ b/projects/plugins/jetpack/extensions/blocks/donations/view.scss @@ -151,15 +151,15 @@ } } - // Modal dialog container. + // Modal dialog container — clips to border-radius; border/radius set via + // build_custom_styles when the block has border settings configured. .donations__modal-dialog { position: relative; width: 90%; max-width: 640px; max-height: 90vh; - overflow-y: auto; + overflow: hidden; background: var(--wp--style--color--background, #fff); - border-radius: inherit; box-shadow: 0 8px 40px rgba(0, 0, 0, 0.18); @media (max-width: 600px) { @@ -168,7 +168,10 @@ } .donations__modal-content { + overflow-y: auto; + max-height: 90vh; padding: 40px; + box-sizing: border-box; @media (max-width: 600px) { padding: 24px 20px; From 900181307929fda4aa3d95e34b1c0652068c52dd Mon Sep 17 00:00:00 2001 From: Angela Blake Date: Tue, 5 May 2026 17:25:20 -0500 Subject: [PATCH 03/11] Add changelog entry for modal display mode feature Co-Authored-By: Claude Sonnet 4.6 --- .../plugins/jetpack/changelog/add-donations-modal-display | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 projects/plugins/jetpack/changelog/add-donations-modal-display diff --git a/projects/plugins/jetpack/changelog/add-donations-modal-display b/projects/plugins/jetpack/changelog/add-donations-modal-display new file mode 100644 index 00000000000..2fdaa793478 --- /dev/null +++ b/projects/plugins/jetpack/changelog/add-donations-modal-display @@ -0,0 +1,4 @@ +Significance: minor +Type: enhancement + +Donations block: add modal display mode with trigger button, configurable icon, and animated overlay. From 08a9e4f28d1b26af73cbce1cd01ab4414ebe57f4 Mon Sep 17 00:00:00 2001 From: Angela Blake Date: Tue, 5 May 2026 17:34:44 -0500 Subject: [PATCH 04/11] Fix Phan type errors: cast float to string for format_price calls Co-Authored-By: Claude Sonnet 4.6 --- .../jetpack/extensions/blocks/donations/donations.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/projects/plugins/jetpack/extensions/blocks/donations/donations.php b/projects/plugins/jetpack/extensions/blocks/donations/donations.php index d4a5f3aa345..ac96f87dc13 100644 --- a/projects/plugins/jetpack/extensions/blocks/donations/donations.php +++ b/projects/plugins/jetpack/extensions/blocks/donations/donations.php @@ -380,7 +380,7 @@ function build_security_data_attrs( $attr, $currency ) { $attrs['data-min-error'] = sprintf( /* translators: %s: minimum donation amount formatted with currency symbol */ __( 'The minimum donation amount is %s.', 'jetpack' ), - \Jetpack_Currencies::format_price( $min_amount, $currency ) + \Jetpack_Currencies::format_price( (string) $min_amount, $currency ) ); } if ( null !== $max_amount ) { @@ -388,14 +388,14 @@ function build_security_data_attrs( $attr, $currency ) { $attrs['data-max-error'] = sprintf( /* translators: %s: maximum donation amount formatted with currency symbol */ __( 'The maximum donation amount is %s.', 'jetpack' ), - \Jetpack_Currencies::format_price( $max_amount, $currency ) + \Jetpack_Currencies::format_price( (string) $max_amount, $currency ) ); } $stripe_min = \Jetpack_Memberships::SUPPORTED_CURRENCIES[ $currency ] ?? 1; $attrs['data-stripe-min-error'] = sprintf( /* translators: %s: payment processor minimum donation amount formatted with currency symbol */ _x( 'The minimum donation amount is %s.', 'payment processor minimum', 'jetpack' ), - \Jetpack_Currencies::format_price( $stripe_min, $currency ) + \Jetpack_Currencies::format_price( (string) $stripe_min, $currency ) ); return $attrs; } From d246bd6b61f35992ead335eabf93b2b612fc127c Mon Sep 17 00:00:00 2001 From: Angela Blake Date: Tue, 5 May 2026 18:19:31 -0500 Subject: [PATCH 05/11] Donations pop-up mode: rename labels, show only trigger button in editor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Rename display mode labels: Inline → In-page, Button & Modal → Pop-up - Pop-up mode editor now shows just the trigger button (matching the frontend) instead of the full form preview - Remove dead editor styles for the modal form preview and hint text Co-Authored-By: Claude Sonnet 4.6 --- .../extensions/blocks/donations/controls.js | 4 +- .../extensions/blocks/donations/edit.js | 38 ++++++------------- .../extensions/blocks/donations/editor.scss | 33 +--------------- 3 files changed, 15 insertions(+), 60 deletions(-) diff --git a/projects/plugins/jetpack/extensions/blocks/donations/controls.js b/projects/plugins/jetpack/extensions/blocks/donations/controls.js index 3a43763f57b..0f2cb0e7d63 100644 --- a/projects/plugins/jetpack/extensions/blocks/donations/controls.js +++ b/projects/plugins/jetpack/extensions/blocks/donations/controls.js @@ -233,8 +233,8 @@ const Controls = props => { isBlock __nextHasNoMarginBottom={ true } > - - + + { displayMode === 'modal' && ( <> diff --git a/projects/plugins/jetpack/extensions/blocks/donations/edit.js b/projects/plugins/jetpack/extensions/blocks/donations/edit.js index b1435f1e78b..31016e5aded 100644 --- a/projects/plugins/jetpack/extensions/blocks/donations/edit.js +++ b/projects/plugins/jetpack/extensions/blocks/donations/edit.js @@ -208,37 +208,23 @@ const Edit = props => { } else if ( ! currency ) { // Memberships settings are still loading content = ; - } else { + } else if ( displayMode === 'modal' ) { const triggerIconEntry = TRIGGER_ICONS.find( ( { key } ) => key === triggerIcon ); const triggerLabel = triggerButtonText || __( 'Support me', 'jetpack' ); content = ( - <> - { displayMode === 'modal' && ( -
- -

- { __( 'Modal preview — form appears inside a modal when clicked', 'jetpack' ) } -

-
+ ); + } else { + content = ; } // When the first time modal is closed, update the user meta to mark the donation warning as dismissed diff --git a/projects/plugins/jetpack/extensions/blocks/donations/editor.scss b/projects/plugins/jetpack/extensions/blocks/donations/editor.scss index eca5c051259..62f5cc206d4 100644 --- a/projects/plugins/jetpack/extensions/blocks/donations/editor.scss +++ b/projects/plugins/jetpack/extensions/blocks/donations/editor.scss @@ -45,11 +45,7 @@ max-width: none; } - // Trigger button preview (modal display mode). - .donations__trigger-preview { - margin-block-end: 16px; - } - + // Trigger button preview (pop-up display mode). .donations__trigger-button { display: inline-flex; align-items: center; @@ -62,33 +58,6 @@ flex-shrink: 0; fill: currentColor; } - - .donations__trigger-hint { - font-size: 12px; - color: #757575; - margin: 6px 0 0; - font-style: italic; - } - - .donations__modal-form-preview { - border: 1px dashed #b0b0b0; - border-radius: 4px; - padding: 12px; - position: relative; - - &::before { - content: "Modal contents"; - position: absolute; - inset-block-start: -10px; - inset-inline-start: 12px; - background: #fff; - padding: 0 4px; - font-size: 11px; - color: #757575; - text-transform: uppercase; - letter-spacing: 0.05em; - } - } } .jetpack-donations__currency-toggle { From 03b8a93490b67ff3d996546b540b7b4d7160660a Mon Sep 17 00:00:00 2001 From: Angela Blake Date: Wed, 6 May 2026 10:24:11 -0500 Subject: [PATCH 06/11] Fix pop-up mode: restore inspector controls, remove border !important hack MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Import and render Controls in edit.js for pop-up mode so all inspector panels (Display, Settings, Security) are available regardless of mode - Remove border:none!important and border forwarding to modal dialog; block-support border now applies to the wrapper (trigger button) as expected — users can style it normally - Remove dead is-modal-display class that existed only to support the reset Co-Authored-By: Claude Sonnet 4.6 --- .../extensions/blocks/donations/donations.php | 45 +------------------ .../extensions/blocks/donations/edit.js | 24 +++++----- 2 files changed, 16 insertions(+), 53 deletions(-) diff --git a/projects/plugins/jetpack/extensions/blocks/donations/donations.php b/projects/plugins/jetpack/extensions/blocks/donations/donations.php index ac96f87dc13..94b7593276b 100644 --- a/projects/plugins/jetpack/extensions/blocks/donations/donations.php +++ b/projects/plugins/jetpack/extensions/blocks/donations/donations.php @@ -251,9 +251,6 @@ function render_block( $attr, $content ) { if ( isset( $attr['tabsAppearance'] ) && 'buttons' === $attr['tabsAppearance'] ) { $instance_classes .= ' is-style-buttons'; } - if ( 'modal' === $display_mode ) { - $instance_classes .= ' is-modal-display'; - } $wrapper_attr_array = array( 'class' => $instance_classes ); if ( $default_interval ) { $wrapper_attr_array['data-default-interval'] = $default_interval; @@ -526,47 +523,9 @@ function build_custom_styles( $attr, $scope ) { . $scope . ' .donations__donate-button{display:block;width:100%;box-sizing:border-box;text-align:center}'; } - // Modal display mode: the block-support border/radius are applied as inline - // styles on the wrapper div (which shows the trigger button in modal mode). - // Reset them on the wrapper with !important and forward them to the modal - // dialog instead. Also wire up trigger-button alignment via text-align. + // Pop-up display mode: wire up trigger-button alignment via text-align on + // the wrapper so the inline-flex button responds to the alignment toolbar. if ( 'modal' === ( $attr['displayMode'] ?? 'inline' ) ) { - $rules[] = $scope . '{border:none!important;border-radius:0!important;overflow:visible}'; - - $supports_border = isset( $attr['style']['border'] ) && is_array( $attr['style']['border'] ) - ? $attr['style']['border'] - : array(); - $modal_decls = array(); - - // Uniform border color / style / width. - foreach ( array( 'color', 'style', 'width' ) as $prop ) { - $val = sanitize_css_value( $supports_border[ $prop ] ?? '' ); - if ( '' !== $val ) { - $modal_decls[] = 'border-' . $prop . ':' . $val; - } - } - // Per-side border (split BorderBoxControl output). - foreach ( array( 'top', 'right', 'bottom', 'left' ) as $side ) { - if ( ! isset( $supports_border[ $side ] ) || ! is_array( $supports_border[ $side ] ) ) { - continue; - } - foreach ( array( 'color', 'style', 'width' ) as $prop ) { - $val = sanitize_css_value( $supports_border[ $side ][ $prop ] ?? '' ); - if ( '' !== $val ) { - $modal_decls[] = 'border-' . $side . '-' . $prop . ':' . $val; - } - } - } - // Border radius. - $radius_val = $supports_border['radius'] ?? null; - $modal_decls = array_merge( $modal_decls, build_radius_decls( $radius_val ) ); - - if ( $modal_decls ) { - // overflow:hidden clips content to the border-radius. - $rules[] = $scope . ' .donations__modal-dialog{' . implode( ';', $modal_decls ) . ';overflow:hidden}'; - } - - // Trigger button alignment reuses the existing contentAlignment attribute. if ( in_array( $content_alignment, array( 'left', 'center', 'right' ), true ) ) { $rules[] = $scope . '{text-align:' . $content_alignment . '}'; } diff --git a/projects/plugins/jetpack/extensions/blocks/donations/edit.js b/projects/plugins/jetpack/extensions/blocks/donations/edit.js index 31016e5aded..8e47bfdda43 100644 --- a/projects/plugins/jetpack/extensions/blocks/donations/edit.js +++ b/projects/plugins/jetpack/extensions/blocks/donations/edit.js @@ -12,6 +12,7 @@ import useIsUserConnected from '../../shared/use-is-user-connected'; import { store as membershipProductsStore } from '../../store/membership-products'; import { STORE_NAME as MEMBERSHIPS_PRODUCTS_STORE } from '../../store/membership-products/constants'; import buildCustomStyles from './build-custom-styles'; +import Controls from './controls'; import fetchDefaultProducts from './fetch-default-products'; import fetchStatus from './fetch-status'; import FirstTimeModal from './first-time-modal'; @@ -212,16 +213,19 @@ const Edit = props => { const triggerIconEntry = TRIGGER_ICONS.find( ( { key } ) => key === triggerIcon ); const triggerLabel = triggerButtonText || __( 'Support me', 'jetpack' ); content = ( - + <> + + + ); } else { content = ; From ccba0734dcb1dc249a5da0b52e29b4f761e1aa84 Mon Sep 17 00:00:00 2001 From: Angela Blake Date: Wed, 6 May 2026 11:10:42 -0500 Subject: [PATCH 07/11] Donations Pop-up mode: hide border controls, add manual block border for In-page mode Remove __experimentalBorder from block.json supports (which auto-serialized border onto the full block wrapper in both modes). Add blockBorder/blockBorderRadius attributes manually via custom BorderBoxControl + BorderRadiusControl in controls.js, rendered only when displayMode !== 'modal'. Border rules emit via the per-instance