Skip to content

feat: add block defaults for heading theme margin and icon for icon l…#3676

Open
Arukuen wants to merge 3 commits into
developfrom
feat/3673/3674-block-defaults
Open

feat: add block defaults for heading theme margin and icon for icon l…#3676
Arukuen wants to merge 3 commits into
developfrom
feat/3673/3674-block-defaults

Conversation

@Arukuen
Copy link
Copy Markdown
Contributor

@Arukuen Arukuen commented Jan 20, 2026

…ist block

fixes #3673
fixes #3674

Summary by CodeRabbit

  • New Features

    • Added a Block Defaults section in Editor Settings to manage default block behaviors.
    • Heading blocks can default to theme margins with separate toggles for posts and non-posts.
    • Icon List blocks can have a configurable default icon and will initialize icons when unset.
  • Chores

    • Added server-side SVG sanitization for icon defaults.
    • Admin UI: new icon-setting control and styling for managing default icons.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jan 20, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6863c0a0-bdd6-494d-a4bb-a9e47212611d

📥 Commits

Reviewing files that changed from the base of the PR and between 7cae2eb and 658d0d7.

📒 Files selected for processing (1)
  • src/block/icon-list/util.js

📝 Walkthrough

Walkthrough

This pull request adds admin-facing defaults for Heading margins and Icon List defaults, initializes those defaults in block edit code, introduces an AdminIconSetting component and styles, updates IconControl behavior, and registers/sanitizes new editor settings in PHP.

Changes

Cohort / File(s) Summary
Block Initialization Logic
src/block/heading/edit.js, src/block/icon-list/edit.js
Introduced useEffect hooks with post-type-aware initialization. Heading blocks now default useThemeTextMargins based on post type and stackable settings. Icon-list blocks initialize icon from settings with fallback.
Icon-list Schema
src/block/icon-list/schema.js
Changed default icon value from DEFAULT_SVG to empty string to enable detection of unset attributes; updated import accordingly.
Admin UI Components
src/components/admin-icon-setting/index.js, src/components/admin-icon-setting/editor.scss
New AdminIconSetting component for icon management in admin UI. Added SCSS styling with custom property for accent color.
IconControl Enhancement
src/components/icon-control/index.js
Wired allowReset prop through to BaseControl; exposed as part of defaultProps instead of hardcoded constant.
Backend Settings
src/editor-settings.php
Added three new settings registrations: heading default margins for posts/non-posts and icon-list default icon. Introduced sanitize_svg_setting() method for secure SVG sanitization.
Admin Settings UI
src/welcome/admin.js
Added new "Block Defaults" settings section with controls for heading margins and icon-list default icon; integrated new AdminIconSetting component.

Sequence Diagram(s)

sequenceDiagram
    participant Block as Heading Block Edit
    participant Effect as useEffect Hook
    participant Select as useSelect (core/editor)
    participant Settings as Stackable Settings
    participant Attr as setAttributes

    Block->>Effect: Component mounts
    Effect->>Select: Query current post type
    Select-->>Effect: Return post type
    Effect->>Settings: Fetch useThemeTextMargins default based on post type
    Settings-->>Effect: Return default margin setting
    alt useThemeTextMargins is undefined/empty
        Effect->>Attr: Set useThemeTextMargins to determined default
        Attr-->>Block: Update block attributes
    else Already set
        Effect-->>Block: No changes
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

  • #3585: Modifies IconControl component behavior and admin settings UI integration, with overlapping changes to inspector controls and component props.
  • #3633: Updates editor settings infrastructure and admin welcome/settings page integration, sharing backend settings wiring patterns.

Poem

🐰 Defaults dance where margins meet,
Icons settle, safe and neat,
Settings bloom in block-wise ways,
SVGs cleansed for safer days!
A rabbit's hop through config bliss

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed Title clearly identifies the primary changes: adding block defaults for heading theme margins and icon list default icon.
Linked Issues check ✅ Passed The PR successfully implements all requirements from #3673 and #3674: admin settings for heading theme margins (posts/non-posts), default icon picker for icon list, proper initialization logic, and fallback behavior.
Out of Scope Changes check ✅ Passed All changes directly support the linked issues' objectives. Minor scope expansion includes AdminIconSetting component and IconControl prop refactoring, both necessary for the icon list default feature.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/3673/3674-block-defaults

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.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jan 20, 2026

🤖 Pull request artifacts

file commit
pr3676-stackable-3676-merge.zip 658d0d7

github-actions Bot added a commit that referenced this pull request Jan 20, 2026
Copy link
Copy Markdown

@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: 1

🤖 Fix all issues with AI agents
In `@src/editor-settings.php`:
- Around line 286-298: sanitize_svg_setting currently uses brittle regexes that
miss unquoted event handlers, data: URLs, xlink/href javascript targets, <use>
references and animation elements, and does not handle preg_replace returning
null; update it to perform robust sanitization by either (A) replacing the
ad-hoc regex approach in sanitize_svg_setting with WordPress's wp_kses using a
restrictive SVG-specific allowed tags/attributes whitelist (including removal of
on* attributes regardless of quoting, stripping href/xlink:href values that
start with javascript: or data:, and disallowing <use>, <animate>, <set>), or
(B) if you keep regexes, add patterns to remove unquoted on* attributes, strip
any href/xlink:href attributes whose values begin with javascript: or data:,
remove <use>, <animate>, <set> elements, and after each preg_replace check for
null and handle by returning an empty string or logging and returning safe
output; make these changes inside the sanitize_svg_setting function to ensure
all dangerous patterns are covered.
🧹 Nitpick comments (4)
src/block/icon-list/edit.js (1)

208-213: Missing dependencies in useEffect may cause lint warnings.

The effect references attributes.icon, setAttributes, and settings but has an empty dependency array. While the intent is to run once on mount, this pattern typically triggers React's exhaustive-deps lint rule.

If this is intentional (and linting allows it), consider adding a suppression comment to clarify intent. Otherwise, the pattern used in heading/edit.js (also with empty deps) suggests this is acceptable in this codebase.

Optional: Add lint suppression for clarity
 	// Set icon default value from setting on first load
 	useEffect( () => {
 		if ( attributes.icon === undefined || attributes.icon === '' ) {
 			setAttributes( { icon: settings.stackable_icon_list_block_default_icon || DEFAULT_SVG } )
 		}
+	// eslint-disable-next-line react-hooks/exhaustive-deps
 	}, [] )
src/block/heading/edit.js (1)

91-102: postType is not in the dependency array but is used in the effect.

The effect uses postType from useSelect to determine the default margin setting, but postType is not in the dependency array. While post type rarely changes during editing, if it ever does (e.g., in some custom editor scenarios), the effect won't recalculate.

Given this is an initialization effect meant to run once, and post type switching is extremely rare, this is likely acceptable. Consider adding a comment or lint suppression for clarity.

Optional: Add lint suppression for clarity
 	// Set useThemeTextMargins default value from setting on first load
 	useEffect( () => {
 		if ( attributes.useThemeTextMargins === undefined || attributes.useThemeTextMargins === '' ) {
 			const isPost = postType === 'post'
 
 			const defaultThemeMargins = isPost
 				? !! settings.stackable_enable_heading_default_theme_margins_posts
 				: !! settings.stackable_enable_heading_default_theme_margins_non_posts
 
 			setAttributes( { useThemeTextMargins: defaultThemeMargins } )
 		}
+	// eslint-disable-next-line react-hooks/exhaustive-deps
 	}, [] )
src/editor-settings.php (1)

244-267: Consider using rest_sanitize_boolean for boolean settings.

The new boolean settings use sanitize_text_field as the sanitize callback. While this follows the pattern used elsewhere in this file (lines 84, 132, etc.), using rest_sanitize_boolean (as on line 39) would be more semantically correct for boolean types.

This is a minor consistency issue and doesn't affect functionality since the REST API handles boolean coercion.

Optional: Use rest_sanitize_boolean for boolean settings
 			register_setting(
 				'stackable_editor_settings',
 				'stackable_enable_heading_default_theme_margins_posts',
 				array(
 					'type' => 'boolean',
 					'description' => __( "When enabled, newly added Stackable Heading blocks in Posts will use the theme's default margins automatically.", STACKABLE_I18N ),
-					'sanitize_callback' => 'sanitize_text_field',
+					'sanitize_callback' => 'rest_sanitize_boolean',
 					'show_in_rest' => true,
 					'default' => false,
 				)
 			);

 			register_setting(
 				'stackable_editor_settings',
 				'stackable_enable_heading_default_theme_margins_non_posts',
 				array(
 					'type' => 'boolean',
 					'description' => __( "When enabled, newly added Stackable Heading blocks in non-Post content (Pages and custom post types) will use the theme's default margins automatically.", STACKABLE_I18N ),
-					'sanitize_callback' => 'sanitize_text_field',
+					'sanitize_callback' => 'rest_sanitize_boolean',
 					'show_in_rest' => true,
 					'default' => false,
 				)
 			);
src/components/admin-icon-setting/index.js (1)

16-18: Consider simplifying the onChange handler.

The inline arrow function can be replaced with a direct reference to props.onChange since it just passes the argument through without transformation.

♻️ Suggested simplification
-				onChange={ icon => {
-					props.onChange( icon )
-				} }
+				onChange={ props.onChange }

Comment thread src/editor-settings.php
Comment on lines +286 to +298
public function sanitize_svg_setting( $input ) {
if ( empty( $input ) ) {
return '';
}

// Remove scripts, event handlers, foreignObject, iframe, embeds
$input = preg_replace( '/<\s*(script|iframe|embed|object|foreignObject)[^>]*>.*?<\s*\/\s*\1\s*>/is', '', $input );
$input = preg_replace( '/on\w+\s*=\s*"[^"]*"/i', '', $input );
$input = preg_replace( "/on\w+\s*=\s*'[^']*'/i", '', $input );
$input = preg_replace( '/javascript:/i', '', $input );

return $input;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

SVG sanitization may be incomplete for security-critical use.

The current implementation provides basic protection but has gaps that could allow XSS:

  1. Unquoted event handlers: The regex only handles quoted attributes (on\w+=\s*"..." and '...'), missing unquoted values like onclick=alert(1).

  2. Missing dangerous patterns:

    • data: URLs (e.g., xlink:href="data:text/html,<script>...")
    • xlink:href and href attributes pointing to javascript:
    • <use> elements referencing external content
    • <animate>, <set> elements that can trigger scripts
  3. Error handling: preg_replace returns null on error; this should be handled.

Proposed improvements for more robust sanitization
 public function sanitize_svg_setting( $input ) {
 	if ( empty( $input ) ) {
 		return '';
 	}

 	// Remove scripts, event handlers, foreignObject, iframe, embeds
 	$input = preg_replace( '/<\s*(script|iframe|embed|object|foreignObject)[^>]*>.*?<\s*\/\s*\1\s*>/is', '', $input );
+	// Remove potentially dangerous elements
+	$input = preg_replace( '/<\s*(use|animate|set|animateTransform)[^>]*\/?>/is', '', $input );
 	$input = preg_replace( '/on\w+\s*=\s*"[^"]*"/i', '', $input );
 	$input = preg_replace( "/on\w+\s*=\s*'[^']*'/i", '', $input );
+	// Handle unquoted event handlers
+	$input = preg_replace( '/on\w+\s*=\s*[^\s>]+/i', '', $input );
 	$input = preg_replace( '/javascript:/i', '', $input );
+	// Remove data: URLs and xlink:href with dangerous protocols
+	$input = preg_replace( '/xlink:href\s*=\s*["\'][^"\']*(?:javascript:|data:)[^"\']*["\']/i', '', $input );
+	$input = preg_replace( '/href\s*=\s*["\'][^"\']*(?:javascript:|data:)[^"\']*["\']/i', '', $input );

+	// Handle preg_replace errors
+	if ( $input === null ) {
+		return '';
+	}
+
 	return $input;
 }

Alternatively, consider using WordPress's built-in wp_kses with an SVG-specific allowed tags/attributes list, or a dedicated SVG sanitization library for more comprehensive protection.

🤖 Prompt for AI Agents
In `@src/editor-settings.php` around lines 286 - 298, sanitize_svg_setting
currently uses brittle regexes that miss unquoted event handlers, data: URLs,
xlink/href javascript targets, <use> references and animation elements, and does
not handle preg_replace returning null; update it to perform robust sanitization
by either (A) replacing the ad-hoc regex approach in sanitize_svg_setting with
WordPress's wp_kses using a restrictive SVG-specific allowed tags/attributes
whitelist (including removal of on* attributes regardless of quoting, stripping
href/xlink:href values that start with javascript: or data:, and disallowing
<use>, <animate>, <set>), or (B) if you keep regexes, add patterns to remove
unquoted on* attributes, strip any href/xlink:href attributes whose values begin
with javascript: or data:, remove <use>, <animate>, <set> elements, and after
each preg_replace check for null and handle by returning an empty string or
logging and returning safe output; make these changes inside the
sanitize_svg_setting function to ensure all dangerous patterns are covered.

github-actions Bot added a commit that referenced this pull request May 25, 2026
github-actions Bot added a commit that referenced this pull request May 25, 2026
@Arukuen
Copy link
Copy Markdown
Contributor Author

Arukuen commented May 25, 2026

These failed checks should pass after this PR is merged.

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.

Add setting for default icon for icon list block Add settings for Heading default follow theme margins

1 participant