Skip to content

feat(ui): add popup title panel to Block Editor document settings#1160

Open
s-acv2 wants to merge 4 commits intodevelopfrom
fix/block-editor-popup-title-panel
Open

feat(ui): add popup title panel to Block Editor document settings#1160
s-acv2 wants to merge 4 commits intodevelopfrom
fix/block-editor-popup-title-panel

Conversation

@s-acv2
Copy link

@s-acv2 s-acv2 commented Jan 19, 2026

Summary

  • Add a new Popup Title panel to the Block Editor sidebar
  • Allows users to set the popup title text
  • Allows users to toggle whether the title displays on the frontend

Changes

  • Add popup_title REST field with update_callback in RestAPI.php
  • Create popup-title-panel.tsx component with title input and display toggle
  • Use module-level storage for pending values with save-on-publish pattern
  • Subscribe to Block Editor save events to persist custom fields via REST API

Test Plan

  • Edit a popup in Block Editor
  • Find "Popup Title" panel in the right sidebar
  • Enter a title and verify typing works without focus loss
  • Toggle "Display on frontend" setting
  • Click Save/Update
  • Leave and re-open the popup - verify values persist
  • Check frontend - verify title displays (or not) based on toggle

Summary by CodeRabbit

  • New Features

    • Block editor panel to edit popup title and toggle its frontend display
    • Admin UI includes a "Display Title" checkbox for popups
    • Popup title and display setting persist via the editor and auto-save on publish/update
  • Style

    • Added styling for the new title display toggle
  • Accessibility

    • If title display is disabled, title remains available to screen readers instead of being omitted

Add a new Popup Title panel to the Block Editor sidebar that allows users
to set the popup title and toggle whether it displays on the frontend.

Changes:
- Add popup_title REST field with update_callback in RestAPI.php
- Create popup-title-panel.tsx component with title input and display toggle
- Use module-level storage for pending values with save-on-publish pattern
- Subscribe to Block Editor save events to persist custom fields via REST API
@coderabbitai
Copy link

coderabbitai bot commented Jan 19, 2026

Important

Review skipped

Review was skipped due to path filters

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json

CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including **/dist/** will override the default block on the dist directory, by removing the pattern from both the lists.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: c343b68e-7e1e-49e6-92bf-e94b609de262

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

Adds popup title storage and editing: a new popup_title REST field and meta handling, a Block Editor plugin panel to edit title and display toggle with debounced REST saves, admin UI/settings and template changes to control frontend display of popup titles.

Changes

Cohort / File(s) Summary
REST API: field registration
classes/Controllers/RestAPI.php
Registers popup_title REST field on popup post type with get/update callbacks, sanitization, string schema, and edit permission check.
Post type meta & sanitization
classes/Controllers/PostTypes.php
Exposes popup_title and popup_settings as REST meta, adds custom-fields support, and adds sanitize_popup_settings_meta() to validate/sanitize popup settings on REST saves.
Admin UI & settings
classes/Admin/Popups.php, assets/js/src/admin/popup-editor/editor.scss
Adds display_title checkbox to title meta and popup settings (default true) and corresponding admin CSS for the display toggle.
Block Editor plugin
packages/block-editor/src/index.ts, packages/block-editor/src/plugins/index.ts, packages/block-editor/src/plugins/popup-title-panel.tsx
Imports plugins entry, adds popup-title-panel plugin that renders PopupTitlePanel in PluginDocumentSettingPanel for popup post type; component manages local state, pendingValues, a save subscription, and issues debounced PATCHes to /popup-maker/v2/popups/{id} to persist popup_title and display setting.
Frontend template
templates/popup.php
Changes title rendering to respect display_title; renders title only when enabled and keeps a screen-reader-only fallback when title is present but display disabled.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant Editor as Gutenberg Editor
    participant Panel as PopupTitlePanel
    participant Store as WP Data Store
    participant API as REST API (/popup-maker/v2)
    participant DB as Post Meta

    User->>Panel: Edit title / toggle display
    Panel->>Panel: Update local state & pendingValues
    User->>Editor: Save post
    Editor->>Panel: Invoke save subscription
    Panel->>Panel: Check hasChanges / previous save state
    alt pending changes exist
        Panel->>API: PATCH /popups/{id} (popup_title + popup_settings)
        API->>DB: Persist meta (popup_title, popup_settings)
        DB-->>API: Success
        API-->>Panel: Response
        Panel->>Panel: Clear pendingValues, reset flags
        Panel->>Store: Update editor store (optional)
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(ui): add popup title panel to Block Editor document settings' accurately summarizes the primary change: introducing a new UI panel for popup title management in the Block Editor sidebar.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/block-editor-popup-title-panel

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@classes/Controllers/RestAPI.php`:
- Around line 357-373: Replace direct post meta access in the
register_rest_field call for 'popup_title' with the popup object methods: in the
'get_callback' use pum_get_popup($obj->ID) and return $popup ?
$popup->get_title() : ''; in the 'update_callback' retrieve the popup via
pum_get_popup($obj->ID) and call $popup->update_meta('popup_title',
sanitize_text_field($value)) only if $popup exists; keep schema and
permission_callback unchanged and mirror the pattern used by the existing
'popup_settings' handler.

In `@packages/block-editor/src/plugins/popup-title-panel.tsx`:
- Around line 93-99: The current .catch handler in the Promise chain inside
popup-title-panel.tsx (the handler that logs "Failed to save popup title
settings") only writes to console and leaves users unaware of save failures;
update the catch for the save call to also create a user-visible error notice
via the WordPress notices store (dispatch('core/notices').createErrorNotice or
the Popup Maker notification API) with a localized message, keep the
console.error for debugging, and ensure any UI state like
pendingValues.hasChanges remains accurate so the user can retry.
🧹 Nitpick comments (2)
packages/block-editor/src/plugins/popup-title-panel.tsx (2)

8-8: Consider using @popup-maker/i18n for consistency.

Per coding guidelines, prioritize @popup-maker/* packages over @wordpress/* when available. The @popup-maker/i18n package exports a __ function (see packages/i18n/src/index.ts).

Suggested change
-import { __ } from '@wordpress/i18n';
+import { __ } from '@popup-maker/i18n';

16-19: Local PopupSettings type may conflict with @popup-maker/core-data.

A PopupSettings type exists in @popup-maker/core-data. Consider importing and extending it, or renaming this local interface to avoid shadowing (e.g., LocalPopupSettings or PopupTitleSettings).

Comment on lines +357 to +373
// Register popup_title as REST field with explicit update_callback.
// This ensures saves work correctly with custom REST namespace.
register_rest_field( $post_type, 'popup_title', [
'get_callback' => function ( $obj ) {
return get_post_meta( $obj['id'], 'popup_title', true ) ?: '';
},
'update_callback' => function ( $value, $obj ) {
update_post_meta( $obj->ID, 'popup_title', sanitize_text_field( $value ) );
},
'schema' => [
'type' => 'string',
'description' => __( 'The popup title displayed inside the popup.', 'popup-maker' ),
],
'permission_callback' => function () use ( $edit_permission ) {
return current_user_can( $edit_permission );
},
] );
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find all popup_title references with context
rg -n "popup_title" --type=php -B2 -A2

Repository: PopupMaker/Popup-Maker

Length of output: 9973


🏁 Script executed:

#!/bin/bash
# Check the PUM_Model_Popup class for available methods
fd "class.*Popup" --type=php | head -20

Repository: PopupMaker/Popup-Maker

Length of output: 236


🏁 Script executed:

#!/bin/bash
# Look at the 'enabled' field pattern mentioned in the comment
sed -n '346,350p' classes/Controllers/RestAPI.php

Repository: PopupMaker/Popup-Maker

Length of output: 285


🏁 Script executed:

#!/bin/bash
# Check for get_setting/update_setting patterns in RestAPI.php
rg -n "get_setting|update_setting" classes/Controllers/RestAPI.php -B1 -A1

Repository: PopupMaker/Popup-Maker

Length of output: 275


Use the popup object methods for getting and updating popup_title in REST callbacks.

Per coding guidelines, use pum_get_popup() to retrieve the popup object, then call get_title() or update_meta() instead of direct get_post_meta()/update_post_meta() calls. The same file demonstrates this pattern in the popup_settings field handler (lines 390–391).

Suggested pattern (from lines 390–391):
'get_callback'    => function ( $obj ) {
    $popup = pum_get_popup( $obj->ID );
    return $popup ? $popup->get_title() : '';
},
'update_callback' => function ( $value, $obj ) {
    $popup = pum_get_popup( $obj->ID );
    if ( $popup ) {
        $popup->update_meta( 'popup_title', sanitize_text_field( $value ) );
    }
},
🤖 Prompt for AI Agents
In `@classes/Controllers/RestAPI.php` around lines 357 - 373, Replace direct post
meta access in the register_rest_field call for 'popup_title' with the popup
object methods: in the 'get_callback' use pum_get_popup($obj->ID) and return
$popup ? $popup->get_title() : ''; in the 'update_callback' retrieve the popup
via pum_get_popup($obj->ID) and call $popup->update_meta('popup_title',
sanitize_text_field($value)) only if $popup exists; keep schema and
permission_callback unchanged and mirror the pattern used by the existing
'popup_settings' handler.

Comment on lines +93 to +99
.then( () => {
pendingValues.hasChanges = false;
} )
.catch( ( error ) => {
// eslint-disable-next-line no-console
console.error( 'Failed to save popup title settings:', error );
} );
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Silent failure may confuse users when API save fails.

The .catch() only logs to console. If the REST API call fails (network issue, permission error, etc.), the user sees no indication that their popup title changes weren't saved. Consider displaying a notice using @wordpress/notices or the Popup Maker notification system.

Example improvement
import { dispatch } from '@wordpress/data';

// In the .catch handler:
.catch( ( error ) => {
	console.error( 'Failed to save popup title settings:', error );
	dispatch( 'core/notices' ).createErrorNotice(
		__( 'Failed to save popup title settings.', 'popup-maker' ),
		{ type: 'snackbar' }
	);
} );
🤖 Prompt for AI Agents
In `@packages/block-editor/src/plugins/popup-title-panel.tsx` around lines 93 -
99, The current .catch handler in the Promise chain inside popup-title-panel.tsx
(the handler that logs "Failed to save popup title settings") only writes to
console and leaves users unaware of save failures; update the catch for the save
call to also create a user-visible error notice via the WordPress notices store
(dispatch('core/notices').createErrorNotice or the Popup Maker notification API)
with a localized message, keep the console.error for debugging, and ensure any
UI state like pendingValues.hasChanges remains accurate so the user can retry.

- Add display_title setting to show/hide popup title on frontend
- Register popup_title and popup_settings meta for REST API support
- Update popup template with screen-reader fallback for accessibility
- Add display title checkbox toggle in classic editor
- Style the toggle with proper alignment and hover states
@github-actions
Copy link

❌ Commit Validation Error

Your commits don't pass validation. Common issues:

Format Requirements

Commits must follow conventional commit format:

type(scope): subject

[optional body]

[optional footer]

Valid types: feat, fix, improve, perf, refactor, docs, style, test, build, ci, chore, revert
Valid scopes: admin, conditions, cookies, frontend, popup, theme, triggers, forms, extensions, integrations, accessibility, performance, ui, ux, build, deps, tests, api, core, docs, release, support

Examples:

  • feat(triggers): add exit intent detection
  • improve(popup): enhance animation speed options
  • fix(forms): resolve Contact Form 7 tracking issue

See: https://www.conventionalcommits.org/

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (3)
classes/Controllers/PostTypes.php (3)

171-173: Auth callbacks use generic edit_posts instead of popup-specific capability.

The popup post type registration (lines 135-139) uses $this->container->get_permission( 'edit_popups' ), but these meta auth callbacks use the generic edit_posts capability. Consider using the same permission for consistency.

Also applies to: 190-192

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@classes/Controllers/PostTypes.php` around lines 171 - 173, The meta box auth
callbacks currently call current_user_can('edit_posts') which is inconsistent
with the popup post type permission; replace those auth_callback closures to
check the popup-specific capability by calling
current_user_can($this->container->get_permission('edit_popups')) (or otherwise
use $this->container->get_permission('edit_popups') inside the closure) so the
same permission used in the popup registration is enforced; update both
occurrences (the closure at the shown diff and the similar one around 190-192).

220-222: Nested arrays pass through without sanitization.

The comment indicates this is for conditions, triggers, and cookies, but the code allows any nested array to bypass sanitization. This could be a security concern if unexpected nested data is submitted via the REST API.

Consider applying recursive sanitization or explicitly validating that only known nested keys are allowed to pass through.

🔧 Proposed defensive approach
 			} elseif ( is_array( $setting ) ) {
-				// Nested arrays (conditions, triggers, cookies) pass through.
-				$sanitized[ $key ] = $setting;
+				// Only allow known complex setting keys to pass through.
+				$allowed_nested_keys = [ 'conditions', 'triggers', 'cookies' ];
+				if ( in_array( $key, $allowed_nested_keys, true ) ) {
+					$sanitized[ $key ] = $setting;
+				}
 			}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@classes/Controllers/PostTypes.php` around lines 220 - 222, The current branch
in Controllers\PostTypes.php that sets $sanitized[$key] = $setting for
is_array($setting) lets any nested array pass through unsanitized; update this
branch to either (a) recursively sanitize the array (walk arrays and apply the
same scalar sanitization/validation used elsewhere) or (b) enforce an explicit
whitelist so only known nested keys like 'conditions', 'triggers', and 'cookies'
are accepted and their inner keys/values are validated; implement this change in
the same method where $setting, $key and $sanitized are handled so nested arrays
are never blindly assigned without validation.

163-175: Dual popup_title REST registration should be documented or consolidated.

The register_post_meta with show_in_rest => true (PostTypes.php, lines 163-176) and register_rest_field (RestAPI.php, lines 359-375) both expose the same popup_title meta key in the REST API at different endpoints (/meta/popup_title and /popup_title). While this appears intentional—only popup_title uses this pattern, not other post meta—the dual registration creates API ambiguity without clear documentation explaining which endpoint clients should use.

Consider either:

  • Document why both registrations are necessary (block editor vs. custom namespace support)
  • Consolidate to a single, well-documented REST endpoint
  • Add a comment explaining the dual registration pattern
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@classes/Controllers/PostTypes.php` around lines 163 - 175, The popup_title
meta is being exposed twice via register_post_meta in PostTypes.php and
register_rest_field in RestAPI.php, creating ambiguous REST endpoints; either
consolidate to a single registration or add an explanatory comment and
documentation. Decide the canonical exposure (prefer register_post_meta(...,
show_in_rest => true) for block/editor compatibility), remove the duplicate
register_rest_field registration for 'popup_title' (or set show_in_rest => false
if you want the custom endpoint only), and add a brief comment in the remaining
registration explaining why this meta is exposed and which REST path clients
should use; update any tests or docs referencing both endpoints.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@classes/Controllers/PostTypes.php`:
- Around line 171-173: The meta box auth callbacks currently call
current_user_can('edit_posts') which is inconsistent with the popup post type
permission; replace those auth_callback closures to check the popup-specific
capability by calling
current_user_can($this->container->get_permission('edit_popups')) (or otherwise
use $this->container->get_permission('edit_popups') inside the closure) so the
same permission used in the popup registration is enforced; update both
occurrences (the closure at the shown diff and the similar one around 190-192).
- Around line 220-222: The current branch in Controllers\PostTypes.php that sets
$sanitized[$key] = $setting for is_array($setting) lets any nested array pass
through unsanitized; update this branch to either (a) recursively sanitize the
array (walk arrays and apply the same scalar sanitization/validation used
elsewhere) or (b) enforce an explicit whitelist so only known nested keys like
'conditions', 'triggers', and 'cookies' are accepted and their inner keys/values
are validated; implement this change in the same method where $setting, $key and
$sanitized are handled so nested arrays are never blindly assigned without
validation.
- Around line 163-175: The popup_title meta is being exposed twice via
register_post_meta in PostTypes.php and register_rest_field in RestAPI.php,
creating ambiguous REST endpoints; either consolidate to a single registration
or add an explanatory comment and documentation. Decide the canonical exposure
(prefer register_post_meta(..., show_in_rest => true) for block/editor
compatibility), remove the duplicate register_rest_field registration for
'popup_title' (or set show_in_rest => false if you want the custom endpoint
only), and add a brief comment in the remaining registration explaining why this
meta is exposed and which REST path clients should use; update any tests or docs
referencing both endpoints.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a5b834f5-29e5-4a48-84df-1856302d954b

📥 Commits

Reviewing files that changed from the base of the PR and between 9868d8a and f69314d.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (4)
  • assets/js/src/admin/popup-editor/editor.scss
  • classes/Admin/Popups.php
  • classes/Controllers/PostTypes.php
  • templates/popup.php

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant