-
Notifications
You must be signed in to change notification settings - Fork 57
fix: remove colour label changes to avoid bleeding into other blocks #4703
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: trunk
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -221,16 +221,18 @@ private static function render_social_flat( array $attributes, WP_Block $block, | |
|
|
||
| /** | ||
| * Get wrapper attributes (class, style, etc.) for the block. | ||
| * Sets block context so core includes default class, custom className, and other supports. | ||
| * Color serialization is skipped via block.json so colors are applied only as CSS vars. | ||
| * | ||
| * @param WP_Block $block Block instance. | ||
| * @param array $attributes Block attributes. | ||
| * @param int $icon_size Icon size in pixels. | ||
| * @return string HTML attributes for the wrapper element. | ||
| */ | ||
| private static function get_block_wrapper_attributes( WP_Block $block, array $attributes, int $icon_size ): string { | ||
| $previous = \WP_Block_Supports::$block_to_render ?? null; | ||
| // Inner blocks have already rendered by this point in the InnerBlocks | ||
| // path, leaving $block_to_render pointing at the last child — so the | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: Good that the rationale is documented now. The actual reason for the snapshot is more defensive than "the inner blocks have already rendered": each inner |
||
| // wrapper would otherwise be built from the wrong block's attributes | ||
| // and lose this block's className, spacing, default class, etc. | ||
| $previous = \WP_Block_Supports::$block_to_render ?? null; | ||
| \WP_Block_Supports::$block_to_render = $block->parsed_block; | ||
|
|
||
| $wrapper_attributes = get_block_wrapper_attributes( | ||
|
|
@@ -246,33 +248,21 @@ private static function get_block_wrapper_attributes( WP_Block $block, array $at | |
| } | ||
|
|
||
| /** | ||
| * Convert a preset token (var:preset|type|slug) to a CSS variable reference. | ||
| * | ||
| * @param string $value Raw value, e.g. "var:preset|color|primary" or "#fff". | ||
| * @return string CSS value, e.g. "var(--wp--preset--color--primary)" or "#fff". | ||
| */ | ||
| private static function preset_to_css( string $value ): string { | ||
| if ( preg_match( '/^var:preset\|([^|]+)\|(.+)$/', $value, $matches ) ) { | ||
| return sprintf( 'var(--wp--preset--%s--%s)', $matches[1], $matches[2] ); | ||
| } | ||
| return $value; | ||
| } | ||
|
|
||
| /** | ||
| * Resolve a color value from attributes (preset slug or custom style token). | ||
| * Resolve a color value from the block's icon color attributes. | ||
| * Prefers the preset slug (so theme switches reflect new palette values) | ||
| * and falls back to the saved hex/CSS value when no preset is set. | ||
| * | ||
| * @param array $attributes Block attributes. | ||
| * @param string $preset_key Top-level preset attribute key (e.g. "textColor"). | ||
| * @param string $style_key Key under style.color (e.g. "text"). | ||
| * @param string $preset_key Preset slug attribute key (e.g. "iconColor"). | ||
| * @param string $value_key Resolved CSS value attribute key (e.g. "iconColorValue"). | ||
| * @return string|null CSS color value or null. | ||
| */ | ||
| private static function resolve_color( array $attributes, string $preset_key, string $style_key ): ?string { | ||
| private static function resolve_color( array $attributes, string $preset_key, string $value_key ): ?string { | ||
| if ( ! empty( $attributes[ $preset_key ] ) && is_string( $attributes[ $preset_key ] ) ) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Suggestion: Either make the fallback symmetric (prefer |
||
| return sprintf( 'var(--wp--preset--color--%s)', $attributes[ $preset_key ] ); | ||
| } | ||
| $custom = $attributes['style']['color'][ $style_key ] ?? null; | ||
| if ( ! empty( $custom ) && is_string( $custom ) ) { | ||
| return self::preset_to_css( $custom ); | ||
| if ( ! empty( $attributes[ $value_key ] ) && is_string( $attributes[ $value_key ] ) ) { | ||
| return $attributes[ $value_key ]; | ||
| } | ||
| return null; | ||
| } | ||
|
|
@@ -281,7 +271,6 @@ private static function resolve_color( array $attributes, string $preset_key, st | |
| * Build wrapper inline style with CSS variables for icon sizing and color. | ||
| * Margin is handled natively by get_block_wrapper_attributes(). | ||
| * Gap is handled by WP layout support (outputs scoped <style> tag per block). | ||
| * Color classes/inline styles are skipped via __experimentalSkipSerialization in block.json. | ||
| * | ||
| * @param array $attributes Block attributes. | ||
| * @param int $icon_size Icon size in pixels. | ||
|
|
@@ -292,8 +281,8 @@ private static function get_wrapper_style( array $attributes, int $icon_size ): | |
| $is_brand = ! empty( $attributes['className'] ) && str_contains( $attributes['className'], 'is-style-brand' ); | ||
|
|
||
| if ( ! $is_brand ) { | ||
| $icon_color = self::resolve_color( $attributes, 'textColor', 'text' ); | ||
| $icon_background = self::resolve_color( $attributes, 'backgroundColor', 'background' ); | ||
| $icon_color = self::resolve_color( $attributes, 'iconColor', 'iconColorValue' ); | ||
| $icon_background = self::resolve_color( $attributes, 'iconBackgroundColor', 'iconBackgroundColorValue' ); | ||
|
laurelfulford marked this conversation as resolved.
|
||
|
|
||
| if ( null !== $icon_color ) { | ||
| $parts[] = sprintf( '--icon-color: %s;', $icon_color ); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,7 +2,17 @@ | |
| * WordPress dependencies | ||
| */ | ||
| import { useContext, useEffect, useMemo, useRef, useState } from '@wordpress/element'; | ||
| import { BlockControls, useBlockProps, useInnerBlocksProps, InspectorControls } from '@wordpress/block-editor'; | ||
| import { | ||
| BlockControls, | ||
| useBlockProps, | ||
| useInnerBlocksProps, | ||
| InspectorControls, | ||
| withColors, | ||
| // eslint-disable-next-line @wordpress/no-unsafe-wp-apis | ||
| __experimentalColorGradientSettingsDropdown as ColorGradientSettingsDropdown, | ||
| // eslint-disable-next-line @wordpress/no-unsafe-wp-apis | ||
| __experimentalUseMultipleOriginColorsAndGradients as useMultipleOriginColorsAndGradients, | ||
| } from '@wordpress/block-editor'; | ||
| import { PanelBody, SelectControl, Button, ToolbarButton, ToolbarGroup, Tooltip } from '@wordpress/components'; | ||
| import { useSelect, useDispatch } from '@wordpress/data'; | ||
| import { __ } from '@wordpress/i18n'; | ||
|
|
@@ -28,91 +38,63 @@ const fetchAllServiceKeys = () => { | |
| return allServiceKeysCache; | ||
| }; | ||
|
|
||
| const presetToVar = value => { | ||
| if ( typeof value !== 'string' ) { | ||
| return value; | ||
| } | ||
| return value.replace( /^var:preset\|([^|]+)\|(.+)$/, 'var(--wp--preset--$1--$2)' ); | ||
| }; | ||
|
|
||
| const resolveColor = ( presetSlug, customValue ) => { | ||
| if ( presetSlug ) { | ||
| return `var(--wp--preset--color--${ presetSlug })`; | ||
| } | ||
| if ( typeof customValue === 'string' ) { | ||
| return presetToVar( customValue ) || customValue; | ||
| } | ||
| return undefined; | ||
| }; | ||
|
|
||
| /** | ||
| * Edit component for the Author Social Links inner block. | ||
| * | ||
| * @param {Object} props Block props. | ||
| * @param {Object} props.attributes Block attributes. | ||
| * @param {Function} props.setAttributes Function to update attributes. | ||
| * @param {string} props.clientId Block client ID. | ||
| * @param {Object} props Block props. | ||
| * @param {Object} props.attributes Block attributes. | ||
| * @param {Function} props.setAttributes Function to update attributes. | ||
| * @param {string} props.clientId Block client ID. | ||
| * @param {Object} props.iconColor Resolved icon color (from withColors). | ||
| * @param {Function} props.setIconColor Setter for icon color (from withColors). | ||
| * @param {Object} props.iconBackgroundColor Resolved icon background color (from withColors). | ||
| * @param {Function} props.setIconBackgroundColor Setter for icon background color (from withColors). | ||
| * @return {JSX.Element} The edit component. | ||
| */ | ||
| export default function Edit( { attributes, setAttributes, clientId } ) { | ||
| function Edit( { attributes, setAttributes, clientId, iconColor, setIconColor, iconBackgroundColor, setIconBackgroundColor } ) { | ||
| const AuthorContext = getSharedAuthorContext(); | ||
| const author = useContext( AuthorContext ); | ||
| const { iconSize, style: styleAttr, textColor, backgroundColor, className } = attributes; | ||
| const { iconSize, iconColorValue, iconBackgroundColorValue, className } = attributes; | ||
| const hasPopulated = useRef( false ); | ||
| const [ allServiceKeys, setAllServiceKeys ] = useState( null ); // null = loading | ||
|
|
||
| const isBrand = ( className || '' ).split( ' ' ).includes( 'is-style-brand' ); | ||
| const iconSizeValue = typeof iconSize === 'number' ? iconSize : parseInt( iconSize ?? 24, 10 ) || 24; | ||
| const iconColor = ! isBrand ? resolveColor( textColor, styleAttr?.color?.text ) : undefined; | ||
| const iconBackground = ! isBrand ? resolveColor( backgroundColor, styleAttr?.color?.background ) : undefined; | ||
|
|
||
| // Hide color panel when "Brand" is active; rename labels when "Default". | ||
| useEffect( () => { | ||
| const sidebar = document.querySelector( '.interface-complementary-area' ); | ||
| if ( ! sidebar ) { | ||
| return; | ||
| } | ||
|
|
||
| const COLOR_LABEL_MAP = { | ||
| Text: __( 'Icon color', 'newspack-plugin' ), | ||
| Background: __( 'Icon background', 'newspack-plugin' ), | ||
| }; | ||
|
|
||
| const updateColorPanel = container => { | ||
| container.querySelectorAll( '.color-block-support-panel' ).forEach( el => { | ||
| el.style.display = isBrand ? 'none' : ''; | ||
| } ); | ||
|
|
||
| if ( isBrand ) { | ||
| return; | ||
| } | ||
|
|
||
| container.querySelectorAll( '.block-editor-panel-color-gradient-settings__color-name' ).forEach( el => { | ||
| if ( COLOR_LABEL_MAP[ el.textContent ] ) { | ||
| el.textContent = COLOR_LABEL_MAP[ el.textContent ]; | ||
| } | ||
| } ); | ||
| container.querySelectorAll( '.components-menu-item__item' ).forEach( el => { | ||
| if ( COLOR_LABEL_MAP[ el.textContent ] ) { | ||
| el.textContent = COLOR_LABEL_MAP[ el.textContent ]; | ||
| } | ||
| } ); | ||
| }; | ||
|
|
||
| updateColorPanel( sidebar ); | ||
|
|
||
| const observer = new MutationObserver( () => updateColorPanel( sidebar ) ); | ||
| observer.observe( sidebar, { childList: true, subtree: true } ); | ||
|
|
||
| return () => observer.disconnect(); | ||
| }, [ isBrand ] ); | ||
| const resolvedIconColor = ! isBrand ? iconColor?.color || iconColorValue : undefined; | ||
| const resolvedIconBackground = ! isBrand ? iconBackgroundColor?.color || iconBackgroundColorValue : undefined; | ||
|
laurelfulford marked this conversation as resolved.
|
||
|
|
||
| // Color panel is hidden entirely when the "Brand" style is active — | ||
| // brand uses each service's own colors, so neither icon nor background | ||
| // apply. The settings render as ToolsPanelItems directly inside the | ||
| // Styles tab's Color slot, so they integrate with WP's native panel | ||
| // (no nested panel-in-a-panel). | ||
| const colorGradientSettings = useMultipleOriginColorsAndGradients(); | ||
| const colorSettings = [ | ||
| { | ||
| colorValue: iconColor?.color || iconColorValue, | ||
| onColorChange: value => { | ||
| setIconColor( value ); | ||
| setAttributes( { iconColorValue: value } ); | ||
| }, | ||
| label: __( 'Icon color', 'newspack-plugin' ), | ||
| }, | ||
| { | ||
| colorValue: iconBackgroundColor?.color || iconBackgroundColorValue, | ||
| onColorChange: value => { | ||
| setIconBackgroundColor( value ); | ||
| setAttributes( { iconBackgroundColorValue: value } ); | ||
| }, | ||
| label: __( 'Icon background', 'newspack-plugin' ), | ||
| }, | ||
| ]; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Suggestion: Worth manually confirming that picking a theme preset colour writes to |
||
|
|
||
| const blockProps = useBlockProps( { | ||
| className: 'author-profile-social__list', | ||
| style: { | ||
| '--icon-size': `${ roundIconSize( iconSizeValue ) }px`, | ||
| ...( iconColor && { '--icon-color': iconColor } ), | ||
| ...( iconBackground && { '--icon-background': iconBackground } ), | ||
| ...( resolvedIconColor && { '--icon-color': resolvedIconColor } ), | ||
| ...( resolvedIconBackground && { '--icon-background': resolvedIconBackground } ), | ||
| }, | ||
| } ); | ||
|
|
||
|
|
@@ -201,7 +183,17 @@ export default function Edit( { attributes, setAttributes, clientId } ) { | |
| ) } | ||
| </PanelBody> | ||
| </InspectorControls> | ||
| { ! isBrand && ( | ||
| <InspectorControls group="color"> | ||
| <ColorGradientSettingsDropdown settings={ colorSettings } panelId={ clientId } { ...colorGradientSettings } /> | ||
| </InspectorControls> | ||
| ) } | ||
| <ul { ...innerBlocksProps } /> | ||
| </> | ||
| ); | ||
| } | ||
|
|
||
| export default withColors( { | ||
| iconColor: 'icon-color', | ||
| iconBackgroundColor: 'icon-background-color', | ||
| } )( Edit ); | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Blocker: Removing
color.text/color.backgroundsupports without adeprecatedregistration is a silent-data-loss path for the ~30 days of posts (production since v6.37.0 / 2026-04-13) that have been savingtextColor/backgroundColor/style.color.text/style.color.backgroundunder the previous schema. Becausesave: () => <InnerBlocks.Content />returns no attribute markup, block validation won't fail loudly — the old keys are dropped from parsed attributes on first load, and on the frontend the new PHPresolve_color()never reads them, so the configured colours disappear silently and the data is gone on next resave. Verified in-env by saving a post with the old shape and loading the frontend: no--icon-color/--icon-backgroundset despite the saved attrs.Needs a
deprecatedentry registering the previous attribute shape with amigrate(attrs)mappingtextColor → iconColor,style.color.text → iconColorValue,backgroundColor → iconBackgroundColor,style.color.background → iconBackgroundColorValue(and stripping the emptystyle.color).