From 09a7d05f7980ef36cc0a8c9d72eca46236aea3d8 Mon Sep 17 00:00:00 2001
From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com>
Date: Tue, 1 Feb 2022 13:53:32 +1000
Subject: [PATCH 01/27] Add support for individual side borders via
BorderBoxControl
---
lib/block-supports/border.php | 95 +++-
.../border-radius-control/style.scss | 8 +-
.../block-editor/src/hooks/border-color.js | 315 ----------
.../block-editor/src/hooks/border-style.js | 64 ---
.../block-editor/src/hooks/border-width.js | 139 -----
packages/block-editor/src/hooks/border.js | 537 +++++++++++++++---
packages/block-editor/src/hooks/index.js | 2 +-
.../src/hooks/use-border-props.js | 69 +--
packages/block-library/src/common.scss | 25 +-
packages/blocks/src/api/constants.js | 48 ++
.../components/global-styles/border-panel.js | 208 ++++---
.../test/use-global-styles-output.js | 2 +-
.../src/components/global-styles/utils.js | 16 +
phpunit/block-supports/border-test.php | 422 ++++++++++++--
14 files changed, 1159 insertions(+), 791 deletions(-)
delete mode 100644 packages/block-editor/src/hooks/border-color.js
delete mode 100644 packages/block-editor/src/hooks/border-style.js
delete mode 100644 packages/block-editor/src/hooks/border-width.js
diff --git a/lib/block-supports/border.php b/lib/block-supports/border.php
index 5ff0260944df2b..888180bc18319c 100644
--- a/lib/block-supports/border.php
+++ b/lib/block-supports/border.php
@@ -32,6 +32,12 @@ function gutenberg_register_border_support( $block_type ) {
'type' => 'string',
);
}
+
+ if ( $has_border_color_support && ! array_key_exists( 'sideBorderColors', $block_type->attributes ) ) {
+ $block_type->attributes['sideBorderColors'] = array(
+ 'type' => 'object',
+ );
+ }
}
/**
@@ -50,6 +56,10 @@ function gutenberg_apply_border_support( $block_type, $block_attributes ) {
$classes = array();
$styles = array();
+ $sides = array( 'top', 'right', 'bottom', 'left' );
+
+ $has_border_color_support = gutenberg_has_border_feature_support( $block_type, 'color' );
+ $has_border_width_support = gutenberg_has_border_feature_support( $block_type, 'width' );
// Border radius.
if (
@@ -88,7 +98,7 @@ function gutenberg_apply_border_support( $block_type, $block_attributes ) {
// Border width.
if (
- gutenberg_has_border_feature_support( $block_type, 'width' ) &&
+ $has_border_width_support &&
isset( $block_attributes['style']['border']['width'] ) &&
! gutenberg_should_skip_block_supports_serialization( $block_type, '__experimentalBorder', 'width' )
) {
@@ -104,7 +114,7 @@ function gutenberg_apply_border_support( $block_type, $block_attributes ) {
// Border color.
if (
- gutenberg_has_border_feature_support( $block_type, 'color' ) &&
+ $has_border_color_support &&
! gutenberg_should_skip_block_supports_serialization( $block_type, '__experimentalBorder', 'color' )
) {
$has_named_border_color = array_key_exists( 'borderColor', $block_attributes );
@@ -120,6 +130,39 @@ function gutenberg_apply_border_support( $block_type, $block_attributes ) {
$border_color = $block_attributes['style']['border']['color'];
$styles[] = sprintf( 'border-color: %s;', $border_color );
}
+
+ // Apply individual border sides' named color classes.
+ $has_named_side_border_colors = isset( $block_attributes['sideBorderColors'] );
+
+ if ( $has_named_side_border_colors ) {
+ foreach ( $sides as $side ) {
+ $named_border_color = _wp_array_get( $block_attributes, array( 'sideBorderColors', $side ), false );
+
+ if ( $named_border_color ) {
+ $classes[] = sprintf( 'has-border-%s-color', $side );
+ $classes[] = sprintf( 'has-%s-border-%s-color', $named_border_color, $side );
+ }
+ }
+ }
+ }
+
+ // Generate styles for individual border sides.
+ if ( $has_border_color_support || $has_border_width_support ) {
+ foreach ( $sides as $side ) {
+ $border = _wp_array_get( $block_attributes, array( 'style', 'border', $side ), false );
+
+ if ( is_array( $border ) && ! empty( $border ) ) {
+ $split_border_attributes = gutenberg_generate_individual_border_classes_and_styles( $side, $border, $block_type );
+
+ if ( $split_border_attributes['classes'] ) {
+ $classes[] = $split_border_attributes['classes'];
+ }
+
+ if ( $split_border_attributes['styles'] ) {
+ $styles [] = $split_border_attributes['styles'];
+ }
+ }
+ }
}
// Collect classes and styles.
@@ -136,6 +179,54 @@ function gutenberg_apply_border_support( $block_type, $block_attributes ) {
return $attributes;
}
+/**
+ * Generates CSS classes and longhand CSS styles for an individual side border.
+ *
+ * If some values are omitted from the border configuration, using shorthand
+ * styles would lead to `initial` values being used instead of the more
+ * desirable inherited values. This could also lead to browser inconsistencies.
+ *
+ * @param string $side The side the classes and styles are being generated for.
+ * @param array $border Array containing border color, style, and width values.
+ * @param WP_Block_type $block_type Block type.
+ *
+ * @return array CSS classes and longhand border styles for a single side.
+ */
+function gutenberg_generate_individual_border_classes_and_styles( $side, $border, $block_type ) {
+ $classes = array();
+ $styles = array();
+
+ if (
+ isset( $border['width'] ) &&
+ null !== $border['width'] &&
+ ! gutenberg_should_skip_block_supports_serialization( $block_type, '__experimentalBorder', 'width' )
+ ) {
+ $styles[] = sprintf( 'border-%s-width: %s;', $side, $border['width'] );
+ }
+
+ if (
+ isset( $border['style'] ) &&
+ null !== $border['style'] &&
+ ! gutenberg_should_skip_block_supports_serialization( $block_type, '__experimentalBorder', 'style' )
+ ) {
+ $styles[] = sprintf( 'border-%s-style: %s;', $side, $border['style'] );
+ }
+
+ if (
+ isset( $border['color'] ) &&
+ null !== $border['color'] &&
+ ! gutenberg_should_skip_block_supports_serialization( $block_type, '__experimentalBorder', 'color' )
+ ) {
+ $classes[] = sprintf( 'has-border-%s-color', $side );
+ $styles [] = sprintf( 'border-%s-color: %s;', $side, $border['color'] );
+ }
+
+ return array(
+ 'classes' => implode( ' ', $classes ),
+ 'styles' => implode( ' ', $styles ),
+ );
+}
+
/**
* Checks whether the current block type supports the border feature requested.
*
diff --git a/packages/block-editor/src/components/border-radius-control/style.scss b/packages/block-editor/src/components/border-radius-control/style.scss
index becab0d9de11e5..c6d5ebe277306c 100644
--- a/packages/block-editor/src/components/border-radius-control/style.scss
+++ b/packages/block-editor/src/components/border-radius-control/style.scss
@@ -11,12 +11,15 @@
align-items: flex-start;
> .components-unit-control-wrapper {
- width: calc(50% - 26px);
+ // width: calc(50% - 26px);
+ width: 110px;
margin-bottom: 0;
+ margin-right: #{ $grid-unit-10 };
+ flex-shrink: 0;
}
.components-range-control {
- width: calc(50% - 26px);
+ flex: 1;
margin-bottom: 0;
.components-base-control__field {
@@ -49,6 +52,7 @@
.component-border-radius-control__linked-button.has-icon {
display: flex;
justify-content: center;
+ margin-left: 2px;
svg {
margin-right: 0;
diff --git a/packages/block-editor/src/hooks/border-color.js b/packages/block-editor/src/hooks/border-color.js
deleted file mode 100644
index 204d4b7e0d28f0..00000000000000
--- a/packages/block-editor/src/hooks/border-color.js
+++ /dev/null
@@ -1,315 +0,0 @@
-/**
- * External dependencies
- */
-import classnames from 'classnames';
-
-/**
- * WordPress dependencies
- */
-import { addFilter } from '@wordpress/hooks';
-import { __ } from '@wordpress/i18n';
-import { createHigherOrderComponent } from '@wordpress/compose';
-import { useEffect, useState } from '@wordpress/element';
-
-/**
- * Internal dependencies
- */
-import ColorGradientSettingsDropdown from '../components/colors-gradients/dropdown';
-import useMultipleOriginColorsAndGradients from '../components/colors-gradients/use-multiple-origin-colors-and-gradients';
-import {
- getColorClassName,
- getColorObjectByColorValue,
- getColorObjectByAttributeValues,
-} from '../components/colors';
-import useSetting from '../components/use-setting';
-import {
- BORDER_SUPPORT_KEY,
- hasBorderSupport,
- removeBorderAttribute,
-} from './border';
-import { cleanEmptyObject, shouldSkipSerialization } from './utils';
-
-// Defining empty array here instead of inline avoids unnecessary re-renders of
-// color control.
-const EMPTY_ARRAY = [];
-
-/**
- * Inspector control panel containing the border color related configuration.
- *
- * There is deliberate overlap between the colors and borders block supports
- * relating to border color. It can be argued the border color controls could
- * be included within either, or both, the colors and borders panels in the
- * inspector controls. If they share the same block attributes it should not
- * matter.
- *
- * @param {Object} props Block properties.
- *
- * @return {WPElement} Border color edit element.
- */
-export function BorderColorEdit( props ) {
- const {
- attributes: { borderColor, style },
- setAttributes,
- } = props;
- const colorGradientSettings = useMultipleOriginColorsAndGradients();
- const availableColors = colorGradientSettings.colors.reduce(
- ( colors, origin ) => colors.concat( origin.colors ),
- []
- );
- const { color: customBorderColor } = style?.border || {};
- const [ colorValue, setColorValue ] = useState(
- () =>
- getColorObjectByAttributeValues(
- availableColors,
- borderColor,
- customBorderColor
- )?.color
- );
-
- // Detect changes in the color attributes and update the colorValue to keep the
- // UI in sync. This is necessary for situations when border controls interact with
- // each other: eg, setting the border width to zero causes the color and style
- // selections to be cleared.
- useEffect( () => {
- setColorValue(
- getColorObjectByAttributeValues(
- availableColors,
- borderColor,
- customBorderColor
- )?.color
- );
- }, [ borderColor, customBorderColor, availableColors ] );
-
- const onChangeColor = ( value ) => {
- setColorValue( value );
-
- const colorObject = getColorObjectByColorValue(
- availableColors,
- value
- );
- const newStyle = {
- ...style,
- border: {
- ...style?.border,
- color: colorObject?.slug ? undefined : value,
- },
- };
-
- // If empty slug, ensure undefined to remove attribute.
- const newNamedColor = colorObject?.slug ? colorObject.slug : undefined;
-
- setAttributes( {
- style: cleanEmptyObject( newStyle ),
- borderColor: newNamedColor,
- } );
- };
-
- const settings = [
- {
- label: __( 'Color' ),
- onColorChange: onChangeColor,
- colorValue,
- clearable: false,
- },
- ];
- return (
-
- );
-}
-
-/**
- * Checks if there is a current value in the border color block support
- * attributes.
- *
- * @param {Object} props Block props.
- * @return {boolean} Whether or not the block has a border color value set.
- */
-export function hasBorderColorValue( props ) {
- const {
- attributes: { borderColor, style },
- } = props;
-
- return !! borderColor || !! style?.border?.color;
-}
-
-/**
- * Resets the border color block support attributes. This can be used when
- * disabling the border color support controls for a block via a progressive
- * discovery panel.
- *
- * @param {Object} props Block props.
- * @param {Object} props.attributes Block's attributes.
- * @param {Object} props.setAttributes Function to set block's attributes.
- */
-export function resetBorderColor( { attributes = {}, setAttributes } ) {
- const { style } = attributes;
-
- setAttributes( {
- borderColor: undefined,
- style: removeBorderAttribute( style, 'color' ),
- } );
-}
-
-/**
- * Filters registered block settings, extending attributes to include
- * `borderColor` if needed.
- *
- * @param {Object} settings Original block settings.
- *
- * @return {Object} Updated block settings.
- */
-function addAttributes( settings ) {
- if ( ! hasBorderSupport( settings, 'color' ) ) {
- return settings;
- }
-
- // Allow blocks to specify default value if needed.
- if ( settings.attributes.borderColor ) {
- return settings;
- }
-
- // Add new borderColor attribute to block settings.
- return {
- ...settings,
- attributes: {
- ...settings.attributes,
- borderColor: {
- type: 'string',
- },
- },
- };
-}
-
-/**
- * Override props assigned to save component to inject border color.
- *
- * @param {Object} props Additional props applied to save element.
- * @param {Object} blockType Block type definition.
- * @param {Object} attributes Block's attributes.
- *
- * @return {Object} Filtered props to apply to save element.
- */
-function addSaveProps( props, blockType, attributes ) {
- if (
- ! hasBorderSupport( blockType, 'color' ) ||
- shouldSkipSerialization( blockType, BORDER_SUPPORT_KEY, 'color' )
- ) {
- return props;
- }
-
- const { borderColor, style } = attributes;
- const borderColorClass = getColorClassName( 'border-color', borderColor );
-
- const newClassName = classnames( props.className, {
- 'has-border-color': borderColor || style?.border?.color,
- [ borderColorClass ]: !! borderColorClass,
- } );
-
- // If we are clearing the last of the previous classes in `className`
- // set it to `undefined` to avoid rendering empty DOM attributes.
- props.className = newClassName ? newClassName : undefined;
-
- return props;
-}
-
-/**
- * Filters the registered block settings to apply border color styles and
- * classnames to the block edit wrapper.
- *
- * @param {Object} settings Original block settings.
- *
- * @return {Object} Filtered block settings.
- */
-function addEditProps( settings ) {
- if (
- ! hasBorderSupport( settings, 'color' ) ||
- shouldSkipSerialization( settings, BORDER_SUPPORT_KEY, 'color' )
- ) {
- return settings;
- }
-
- const existingGetEditWrapperProps = settings.getEditWrapperProps;
- settings.getEditWrapperProps = ( attributes ) => {
- let props = {};
-
- if ( existingGetEditWrapperProps ) {
- props = existingGetEditWrapperProps( attributes );
- }
-
- return addSaveProps( props, settings, attributes );
- };
-
- return settings;
-}
-
-/**
- * This adds inline styles for color palette colors.
- * Ideally, this is not needed and themes should load their palettes on the editor.
- *
- * @param {Function} BlockListBlock Original component.
- *
- * @return {Function} Wrapped component.
- */
-export const withBorderColorPaletteStyles = createHigherOrderComponent(
- ( BlockListBlock ) => ( props ) => {
- const { name, attributes } = props;
- const { borderColor } = attributes;
- const colors = useSetting( 'color.palette' ) || EMPTY_ARRAY;
-
- if (
- ! hasBorderSupport( name, 'color' ) ||
- shouldSkipSerialization( name, BORDER_SUPPORT_KEY, 'color' )
- ) {
- return ;
- }
-
- const extraStyles = {
- borderColor: borderColor
- ? getColorObjectByAttributeValues( colors, borderColor )?.color
- : undefined,
- };
-
- let wrapperProps = props.wrapperProps;
- wrapperProps = {
- ...props.wrapperProps,
- style: {
- ...extraStyles,
- ...props.wrapperProps?.style,
- },
- };
-
- return ;
- }
-);
-
-addFilter(
- 'blocks.registerBlockType',
- 'core/border/addAttributes',
- addAttributes
-);
-
-addFilter(
- 'blocks.getSaveContent.extraProps',
- 'core/border/addSaveProps',
- addSaveProps
-);
-
-addFilter(
- 'blocks.registerBlockType',
- 'core/border/addEditProps',
- addEditProps
-);
-
-addFilter(
- 'editor.BlockListBlock',
- 'core/border/with-border-color-palette-styles',
- withBorderColorPaletteStyles
-);
diff --git a/packages/block-editor/src/hooks/border-style.js b/packages/block-editor/src/hooks/border-style.js
deleted file mode 100644
index 1a3a5f923383c7..00000000000000
--- a/packages/block-editor/src/hooks/border-style.js
+++ /dev/null
@@ -1,64 +0,0 @@
-/**
- * Internal dependencies
- */
-import BorderStyleControl from '../components/border-style-control';
-import { cleanEmptyObject } from './utils';
-import { removeBorderAttribute } from './border';
-
-/**
- * Inspector control for configuring border style property.
- *
- * @param {Object} props Block properties.
- *
- * @return {WPElement} Border style edit element.
- */
-export const BorderStyleEdit = ( props ) => {
- const {
- attributes: { style },
- setAttributes,
- } = props;
-
- const onChange = ( newBorderStyle ) => {
- const newStyleAttributes = {
- ...style,
- border: {
- ...style?.border,
- style: newBorderStyle,
- },
- };
-
- setAttributes( { style: cleanEmptyObject( newStyleAttributes ) } );
- };
-
- return (
-
- );
-};
-
-/**
- * Checks if there is a current value in the border style block support
- * attributes.
- *
- * @param {Object} props Block props.
- * @return {boolean} Whether or not the block has a border style value set.
- */
-export function hasBorderStyleValue( props ) {
- return !! props.attributes.style?.border?.style;
-}
-
-/**
- * Resets the border style block support attribute. This can be used when
- * disabling the border style support control for a block via a progressive
- * discovery panel.
- *
- * @param {Object} props Block props.
- * @param {Object} props.attributes Block's attributes.
- * @param {Object} props.setAttributes Function to set block's attributes.
- */
-export function resetBorderStyle( { attributes = {}, setAttributes } ) {
- const { style } = attributes;
- setAttributes( { style: removeBorderAttribute( style, 'style' ) } );
-}
diff --git a/packages/block-editor/src/hooks/border-width.js b/packages/block-editor/src/hooks/border-width.js
deleted file mode 100644
index f6ce67d781f34f..00000000000000
--- a/packages/block-editor/src/hooks/border-width.js
+++ /dev/null
@@ -1,139 +0,0 @@
-/**
- * WordPress dependencies
- */
-import {
- __experimentalUnitControl as UnitControl,
- __experimentalUseCustomUnits as useCustomUnits,
-} from '@wordpress/components';
-import { useState } from '@wordpress/element';
-import { __ } from '@wordpress/i18n';
-
-/**
- * Internal dependencies
- */
-import { cleanEmptyObject } from './utils';
-import { removeBorderAttribute } from './border';
-import useSetting from '../components/use-setting';
-
-const MIN_BORDER_WIDTH = 0;
-
-/**
- * Inspector control for configuring border width property.
- *
- * @param {Object} props Block properties.
- *
- * @return {WPElement} Border width edit element.
- */
-export const BorderWidthEdit = ( props ) => {
- const {
- attributes: { borderColor, style },
- setAttributes,
- } = props;
-
- const { width, color: customBorderColor, style: borderStyle } =
- style?.border || {};
-
- // Used to temporarily track previous border color & style selections to be
- // able to restore them when border width changes from zero value.
- const [ styleSelection, setStyleSelection ] = useState();
- const [ colorSelection, setColorSelection ] = useState();
- const [ customColorSelection, setCustomColorSelection ] = useState();
-
- const onChange = ( newWidth ) => {
- let newStyle = {
- ...style,
- border: {
- ...style?.border,
- width: newWidth,
- },
- };
-
- // Used to clear named border color attribute.
- let borderPaletteColor = borderColor;
-
- const hasZeroWidth = parseFloat( newWidth ) === 0;
- const hadPreviousZeroWidth = parseFloat( width ) === 0;
-
- // Setting the border width explicitly to zero will also set the
- // border style to `none` and clear border color attributes.
- if ( hasZeroWidth && ! hadPreviousZeroWidth ) {
- // Before clearing color and style selections, keep track of
- // the current selections so they can be restored when the width
- // changes to a non-zero value.
- setColorSelection( borderColor );
- setCustomColorSelection( customBorderColor );
- setStyleSelection( borderStyle );
-
- // Clear style and color attributes.
- borderPaletteColor = undefined;
- newStyle.border.color = undefined;
- newStyle.border.style = 'none';
- }
-
- if ( ! hasZeroWidth && hadPreviousZeroWidth ) {
- // Restore previous border style selection if width is now not zero and
- // border style was 'none'. This is to support changes to the UI which
- // change the border style UI to a segmented control without a "none"
- // option.
- if ( borderStyle === 'none' ) {
- newStyle.border.style = styleSelection;
- }
-
- // Restore previous border color selection if width is no longer zero
- // and current border color is undefined.
- if ( borderColor === undefined ) {
- borderPaletteColor = colorSelection;
- newStyle.border.color = customColorSelection;
- }
- }
-
- // If width was reset, clean out undefined styles.
- if ( newWidth === undefined || newWidth === '' ) {
- newStyle = cleanEmptyObject( newStyle );
- }
-
- setAttributes( {
- borderColor: borderPaletteColor,
- style: newStyle,
- } );
- };
-
- const units = useCustomUnits( {
- availableUnits: useSetting( 'spacing.units' ) || [ 'px', 'em', 'rem' ],
- } );
-
- return (
-
- );
-};
-
-/**
- * Checks if there is a current value in the border width block support
- * attributes.
- *
- * @param {Object} props Block props.
- * @return {boolean} Whether or not the block has a border width value set.
- */
-export function hasBorderWidthValue( props ) {
- return !! props.attributes.style?.border?.width;
-}
-
-/**
- * Resets the border width block support attribute. This can be used when
- * disabling the border width support control for a block via a progressive
- * discovery panel.
- *
- * @param {Object} props Block props.
- * @param {Object} props.attributes Block's attributes.
- * @param {Object} props.setAttributes Function to set block's attributes.
- */
-export function resetBorderWidth( { attributes = {}, setAttributes } ) {
- const { style } = attributes;
- setAttributes( { style: removeBorderAttribute( style, 'width' ) } );
-}
diff --git a/packages/block-editor/src/hooks/border.js b/packages/block-editor/src/hooks/border.js
index f17eeab4e3de93..a54ce1797b8cd4 100644
--- a/packages/block-editor/src/hooks/border.js
+++ b/packages/block-editor/src/hooks/border.js
@@ -1,53 +1,169 @@
+/**
+ * External dependencies
+ */
+import classnames from 'classnames';
+
/**
* WordPress dependencies
*/
import { getBlockSupport } from '@wordpress/blocks';
-import { __experimentalToolsPanelItem as ToolsPanelItem } from '@wordpress/components';
+import {
+ __experimentalBorderBoxControl as BorderBoxControl,
+ __experimentalHasSplitBorders as hasSplitBorders,
+ __experimentalIsDefinedBorder as isDefinedBorder,
+ __experimentalToolsPanelItem as ToolsPanelItem,
+} from '@wordpress/components';
+import { createHigherOrderComponent } from '@wordpress/compose';
import { Platform } from '@wordpress/element';
+import { addFilter } from '@wordpress/hooks';
import { __ } from '@wordpress/i18n';
/**
* Internal dependencies
*/
-import {
- BorderColorEdit,
- hasBorderColorValue,
- resetBorderColor,
-} from './border-color';
import {
BorderRadiusEdit,
hasBorderRadiusValue,
resetBorderRadius,
} from './border-radius';
-import {
- BorderStyleEdit,
- hasBorderStyleValue,
- resetBorderStyle,
-} from './border-style';
-import {
- BorderWidthEdit,
- hasBorderWidthValue,
- resetBorderWidth,
-} from './border-width';
+import { getColorClassName } from '../components/colors';
import InspectorControls from '../components/inspector-controls';
+import useMultipleOriginColorsAndGradients from '../components/colors-gradients/use-multiple-origin-colors-and-gradients';
import useSetting from '../components/use-setting';
-import { cleanEmptyObject } from './utils';
+import { cleanEmptyObject, shouldSkipSerialization } from './utils';
export const BORDER_SUPPORT_KEY = '__experimentalBorder';
+const borderSides = [ 'top', 'right', 'bottom', 'left' ];
+
+const hasBorderValue = ( props ) => {
+ const { borderColor, sideBorderColors, style } = props.attributes;
+ return (
+ isDefinedBorder( style?.border ) ||
+ !! borderColor ||
+ !! sideBorderColors
+ );
+};
+
+// The border color, style, and width are omitted so they get undefined. The
+// border radius is separate and must retain its selection.
+const resetBorder = ( { attributes = {}, setAttributes } ) => {
+ const { style } = attributes;
+ setAttributes( {
+ borderColor: undefined,
+ sideBorderColors: undefined,
+ style: {
+ ...style,
+ border: cleanEmptyObject( {
+ radius: style?.border?.radius,
+ } ),
+ },
+ } );
+};
+
+const resetBorderFilter = ( newAttributes ) => ( {
+ ...newAttributes,
+ borderColor: undefined,
+ sideBorderColors: undefined,
+ style: {
+ ...newAttributes.style,
+ border: {
+ radius: newAttributes.style?.border?.radius,
+ },
+ },
+} );
+
+const getColorByProperty = ( colors, property, value ) => {
+ let matchedColor;
+
+ colors.some( ( origin ) =>
+ origin.colors.some( ( color ) => {
+ if ( color[ property ] === value ) {
+ matchedColor = color;
+ return true;
+ }
+
+ return false;
+ } )
+ );
+
+ return matchedColor;
+};
+
+export const getMultiOriginColor = ( { colors, namedColor, customColor } ) => {
+ // Search each origin (default, theme, or user) for matching color by name.
+ if ( namedColor ) {
+ const colorObject = getColorByProperty( colors, 'slug', namedColor );
+ if ( colorObject ) {
+ return colorObject;
+ }
+ }
+
+ // Skip if no custom color or matching named color.
+ if ( ! customColor ) {
+ return { color: undefined };
+ }
+
+ // Attempt to find color via custom color value or build new object.
+ const colorObject = getColorByProperty( colors, 'color', customColor );
+ return colorObject ? colorObject : { color: customColor };
+};
+
+const getBorderObject = ( attributes, colors ) => {
+ const { borderColor, sideBorderColors, style } = attributes;
+ const { border: borderStyles } = style || {};
+
+ // If we have a named color for a flat border. Fetch that color object and
+ // apply that color's value to the color property within the style object.
+ if ( borderColor ) {
+ const { color } = getMultiOriginColor( {
+ colors,
+ namedColor: borderColor,
+ } );
+
+ return color ? { ...borderStyles, color } : borderStyles;
+ }
+
+ // If we have named colors for the individual side borders, retrieve their
+ // related color objects and apply the real color values to the split
+ // border objects.
+ if ( sideBorderColors ) {
+ const hydratedBorderStyles = { ...borderStyles };
+
+ Object.entries( sideBorderColors ).forEach(
+ ( [ side, namedColor ] ) => {
+ const { color } = getMultiOriginColor( { colors, namedColor } );
+
+ if ( color ) {
+ hydratedBorderStyles[ side ] = {
+ ...hydratedBorderStyles[ side ],
+ color,
+ };
+ }
+ }
+ );
+
+ return hydratedBorderStyles;
+ }
+
+ // No named colors selected all color values if any should already be in
+ // the style's border object.
+ return borderStyles;
+};
+
export function BorderPanel( props ) {
- const { clientId } = props;
+ const { attributes, clientId, setAttributes } = props;
+ const { style } = attributes;
+ const { colors } = useMultipleOriginColorsAndGradients();
+ const isSupported = hasBorderSupport( props.name );
const isColorSupported =
useSetting( 'border.color' ) && hasBorderSupport( props.name, 'color' );
-
const isRadiusSupported =
useSetting( 'border.radius' ) &&
hasBorderSupport( props.name, 'radius' );
-
const isStyleSupported =
useSetting( 'border.style' ) && hasBorderSupport( props.name, 'style' );
-
const isWidthSupported =
useSetting( 'border.width' ) && hasBorderSupport( props.name, 'width' );
@@ -58,7 +174,7 @@ export function BorderPanel( props ) {
! isWidthSupported,
].every( Boolean );
- if ( isDisabled ) {
+ if ( isDisabled || ! isSupported ) {
return null;
}
@@ -67,61 +183,98 @@ export function BorderPanel( props ) {
'__experimentalDefaultControls',
] );
- const createResetAllFilter = (
- borderAttribute,
- topLevelAttributes = {}
- ) => ( newAttributes ) => ( {
- ...newAttributes,
- ...topLevelAttributes,
- style: {
- ...newAttributes.style,
- border: {
- ...newAttributes.style?.border,
- [ borderAttribute ]: undefined,
- },
- },
- } );
+ const showBorderByDefault =
+ defaultBorderControls?.color || defaultBorderControls?.width;
+
+ const onBorderChange = ( newBorder ) => {
+ // Filter out named colors and apply them to appropriate block
+ // attributes so that CSS classes can be used to apply those colors.
+ // e.g. has-primary-border-top-color.
+
+ let newBorderStyles = { ...newBorder };
+ let newBorderColor;
+ let newSideBorderColors;
+
+ // Split borders will store their named colors within the
+ // `sideBorderColors` block attribute.
+ if ( hasSplitBorders( newBorder ) ) {
+ // For each side check if the side has a color value set
+ // If so, determine if it belongs to a named color, in which case
+ // saved that named color to the block attribute and clear the
+ // style object's color property to avoid the inline style.
+ //
+ // This deliberately overwrites `newBorderStyles` to avoid mutating
+ // the passed object which causes problems otherwise.
+ newBorderStyles = {
+ top: { ...newBorder.top },
+ right: { ...newBorder.right },
+ bottom: { ...newBorder.bottom },
+ left: { ...newBorder.left },
+ };
+ newSideBorderColors = {};
+
+ borderSides.forEach( ( side ) => {
+ if ( newBorder[ side ]?.color ) {
+ const colorObject = getMultiOriginColor( {
+ colors,
+ customColor: newBorder[ side ]?.color,
+ } );
+
+ if ( colorObject.slug ) {
+ // If we have a named color, set the sides named color
+ // attribute and clear the saved style objects color value.
+ newSideBorderColors[ side ] = colorObject.slug;
+ newBorderStyles[ side ].color = undefined;
+ }
+ }
+ } );
+ } else if ( newBorder?.color ) {
+ // We have a flat border configuration. Apply named color slug to
+ // `borderColor` attribute and clear color style property if found.
+ const customColor = newBorder?.color;
+ const colorObject = getMultiOriginColor( { colors, customColor } );
+
+ if ( colorObject.slug ) {
+ newBorderColor = colorObject.slug;
+ newBorderStyles.color = undefined;
+ }
+ }
+
+ // Ensure previous border radius styles are maintained and clean
+ // overall result for empty objects or properties.
+ const newStyle = cleanEmptyObject( {
+ ...style,
+ border: { radius: style?.border?.radius, ...newBorderStyles },
+ } );
+
+ setAttributes( {
+ style: newStyle,
+ borderColor: newBorderColor,
+ sideBorderColors: newSideBorderColors,
+ } );
+ };
+
+ const hydratedBorder = getBorderObject( attributes, colors );
return (
- { isWidthSupported && (
- hasBorderWidthValue( props ) }
- label={ __( 'Width' ) }
- onDeselect={ () => resetBorderWidth( props ) }
- isShownByDefault={ defaultBorderControls?.width }
- resetAllFilter={ createResetAllFilter( 'width' ) }
- panelId={ clientId }
- >
-
-
- ) }
- { isStyleSupported && (
- hasBorderStyleValue( props ) }
- label={ __( 'Style' ) }
- onDeselect={ () => resetBorderStyle( props ) }
- isShownByDefault={ defaultBorderControls?.style }
- resetAllFilter={ createResetAllFilter( 'style' ) }
- panelId={ clientId }
- >
-
-
- ) }
- { isColorSupported && (
+ { ( isWidthSupported || isColorSupported ) && (
hasBorderColorValue( props ) }
- label={ __( 'Color' ) }
- onDeselect={ () => resetBorderColor( props ) }
- isShownByDefault={ defaultBorderControls?.color }
- resetAllFilter={ createResetAllFilter( 'color', {
- borderColor: undefined,
- } ) }
+ hasValue={ () => hasBorderValue( props ) }
+ label={ __( 'Border' ) }
+ onDeselect={ () => resetBorder( props ) }
+ isShownByDefault={ showBorderByDefault }
+ resetAllFilter={ resetBorderFilter }
panelId={ clientId }
>
-
+
) }
{ isRadiusSupported && (
@@ -130,7 +283,16 @@ export function BorderPanel( props ) {
label={ __( 'Radius' ) }
onDeselect={ () => resetBorderRadius( props ) }
isShownByDefault={ defaultBorderControls?.radius }
- resetAllFilter={ createResetAllFilter( 'radius' ) }
+ resetAllFilter={ ( newAttributes ) => ( {
+ ...newAttributes,
+ style: {
+ ...newAttributes.style,
+ border: {
+ ...newAttributes.style?.border,
+ radius: undefined,
+ },
+ },
+ } ) }
panelId={ clientId }
>
@@ -189,3 +351,234 @@ export function removeBorderAttribute( style, attribute ) {
},
} );
}
+
+/**
+ * Filters registered block settings, extending attributes to include
+ * `borderColor` if needed.
+ *
+ * @param {Object} settings Original block settings.
+ *
+ * @return {Object} Updated block settings.
+ */
+function addAttributes( settings ) {
+ if ( ! hasBorderSupport( settings, 'color' ) ) {
+ return settings;
+ }
+
+ // Allow blocks to specify border color values if needed.
+ const { attributes } = settings;
+
+ // Skip any adjustments if block already defines both border color
+ // attributes to set defaults etc.
+ if ( attributes.borderColor && attributes.sideBorderColors ) {
+ return settings;
+ }
+
+ // If we are missing border color attribute definition, add it.
+ if ( ! attributes.borderColor ) {
+ return {
+ ...settings,
+ attributes: {
+ ...attributes,
+ borderColor: { type: 'string' },
+ },
+ };
+ }
+
+ // We are missing attribute for side border colors, add it to existing
+ // attribute definitions.
+ return {
+ ...settings,
+ attributes: {
+ ...attributes,
+ sideBorderColors: { type: 'object' },
+ },
+ };
+}
+
+/**
+ * Override props assigned to save component to inject border color.
+ *
+ * @param {Object} props Additional props applied to save element.
+ * @param {Object} blockType Block type definition.
+ * @param {Object} attributes Block's attributes.
+ *
+ * @return {Object} Filtered props to apply to save element.
+ */
+function addSaveProps( props, blockType, attributes ) {
+ if (
+ ! hasBorderSupport( blockType, 'color' ) ||
+ shouldSkipSerialization( blockType )
+ ) {
+ return props;
+ }
+
+ const borderClasses = getBorderClasses( attributes );
+ const newClassName = classnames( props.className, borderClasses );
+
+ // If we are clearing the last of the previous classes in `className`
+ // set it to `undefined` to avoid rendering empty DOM attributes.
+ props.className = newClassName ? newClassName : undefined;
+
+ return props;
+}
+
+/**
+ * Generates a CSS class name consisting of all the applicable border color
+ * classes given the current block attributes.
+ *
+ * @param {Object} attributes Block's attributes.
+ *
+ * @return {string} CSS class name.
+ */
+export function getBorderClasses( attributes ) {
+ const { borderColor, style } = attributes;
+ const borderColorClass = getColorClassName( 'border-color', borderColor );
+
+ return classnames( {
+ 'has-border-color': borderColor || style?.border?.color,
+ [ borderColorClass ]: !! borderColorClass,
+ ...getSideBorderClasses( attributes ),
+ } );
+}
+
+/**
+ * Generates a collection of CSS classes for the block's current border color
+ * selections. The results are intended to be further processed via a call
+ * through `classnames()`.
+ *
+ * @param {Object} attributes Block attributes.
+ * @return {Object} CSS classes for side border colors.
+ */
+function getSideBorderClasses( attributes ) {
+ const { sideBorderColors, style } = attributes;
+
+ return borderSides.reduce( ( classes, side ) => {
+ const color = sideBorderColors?.[ side ];
+ const hasColor = color || style?.border?.[ side ]?.color;
+ const baseClassName = `border-${ side }-color`;
+ const colorClass = getColorClassName( baseClassName, color );
+
+ return {
+ ...classes,
+ [ `has-${ baseClassName }` ]: hasColor,
+ [ colorClass ]: !! colorClass,
+ };
+ }, {} );
+}
+
+/**
+ * Filters the registered block settings to apply border color styles and
+ * classnames to the block edit wrapper.
+ *
+ * @param {Object} settings Original block settings.
+ *
+ * @return {Object} Filtered block settings.
+ */
+function addEditProps( settings ) {
+ if (
+ ! hasBorderSupport( settings, 'color' ) ||
+ shouldSkipSerialization( settings )
+ ) {
+ return settings;
+ }
+
+ const existingGetEditWrapperProps = settings.getEditWrapperProps;
+ settings.getEditWrapperProps = ( attributes ) => {
+ let props = {};
+
+ if ( existingGetEditWrapperProps ) {
+ props = existingGetEditWrapperProps( attributes );
+ }
+
+ return addSaveProps( props, settings, attributes );
+ };
+
+ return settings;
+}
+
+/**
+ * This adds inline styles for color palette colors.
+ * Ideally, this is not needed and themes should load their palettes on the editor.
+ *
+ * @param {Function} BlockListBlock Original component.
+ *
+ * @return {Function} Wrapped component.
+ */
+export const withBorderColorPaletteStyles = createHigherOrderComponent(
+ ( BlockListBlock ) => ( props ) => {
+ const { name, attributes } = props;
+ const { borderColor, sideBorderColors } = attributes;
+ const { colors } = useMultipleOriginColorsAndGradients();
+
+ if (
+ ! hasBorderSupport( name, 'color' ) ||
+ shouldSkipSerialization( name )
+ ) {
+ return ;
+ }
+
+ const { color: borderColorValue } = getMultiOriginColor( {
+ colors,
+ namedColor: borderColor,
+ } );
+ const { color: borderTopColor } = getMultiOriginColor( {
+ colors,
+ namedColor: sideBorderColors?.top,
+ } );
+ const { color: borderRightColor } = getMultiOriginColor( {
+ colors,
+ namedColor: sideBorderColors?.right,
+ } );
+ const { color: borderBottomColor } = getMultiOriginColor( {
+ colors,
+ namedColor: sideBorderColors?.bottom,
+ } );
+ const { color: borderLeftColor } = getMultiOriginColor( {
+ colors,
+ namedColor: sideBorderColors?.left,
+ } );
+
+ const extraStyles = {
+ borderTopColor: borderTopColor || borderColorValue,
+ borderRightColor: borderRightColor || borderColorValue,
+ borderBottomColor: borderBottomColor || borderColorValue,
+ borderLeftColor: borderLeftColor || borderColorValue,
+ };
+
+ let wrapperProps = props.wrapperProps;
+ wrapperProps = {
+ ...props.wrapperProps,
+ style: {
+ ...extraStyles,
+ ...props.wrapperProps?.style,
+ },
+ };
+
+ return ;
+ }
+);
+
+addFilter(
+ 'blocks.registerBlockType',
+ 'core/border/addAttributes',
+ addAttributes
+);
+
+addFilter(
+ 'blocks.getSaveContent.extraProps',
+ 'core/border/addSaveProps',
+ addSaveProps
+);
+
+addFilter(
+ 'blocks.registerBlockType',
+ 'core/border/addEditProps',
+ addEditProps
+);
+
+addFilter(
+ 'editor.BlockListBlock',
+ 'core/border/with-border-color-palette-styles',
+ withBorderColorPaletteStyles
+);
diff --git a/packages/block-editor/src/hooks/index.js b/packages/block-editor/src/hooks/index.js
index 708dae62a39714..b18a175973c1d5 100644
--- a/packages/block-editor/src/hooks/index.js
+++ b/packages/block-editor/src/hooks/index.js
@@ -11,7 +11,7 @@ import './style';
import './color';
import './duotone';
import './font-size';
-import './border-color';
+import './border';
import './layout';
export { useCustomSides } from './dimensions';
diff --git a/packages/block-editor/src/hooks/use-border-props.js b/packages/block-editor/src/hooks/use-border-props.js
index d62d68365fe1b3..6a4ba6feddc693 100644
--- a/packages/block-editor/src/hooks/use-border-props.js
+++ b/packages/block-editor/src/hooks/use-border-props.js
@@ -1,46 +1,28 @@
-/**
- * External dependencies
- */
-import classnames from 'classnames';
-
/**
* Internal dependencies
*/
import { getInlineStyles } from './style';
-import {
- getColorClassName,
- getColorObjectByAttributeValues,
-} from '../components/colors';
-import useSetting from '../components/use-setting';
+import { getBorderClasses, getMultiOriginColor } from './border';
+import useMultipleOriginColorsAndGradients from '../components/colors-gradients/use-multiple-origin-colors-and-gradients';
// This utility is intended to assist where the serialization of the border
// block support is being skipped for a block but the border related CSS classes
// & styles still need to be generated so they can be applied to inner elements.
-const EMPTY_ARRAY = [];
-
/**
* Provides the CSS class names and inline styles for a block's border support
* attributes.
*
- * @param {Object} attributes Block attributes.
- * @param {string} attributes.borderColor Selected named border color.
- * @param {Object} attributes.style Block's styles attribute.
- *
+ * @param {Object} attributes Block attributes.
* @return {Object} Border block support derived CSS classes & styles.
*/
-export function getBorderClassesAndStyles( { borderColor, style } ) {
- const borderStyles = style?.border || {};
- const borderClass = getColorClassName( 'border-color', borderColor );
-
- const className = classnames( {
- [ borderClass ]: !! borderClass,
- 'has-border-color': borderColor || style?.border?.color,
- } );
+export function getBorderClassesAndStyles( attributes ) {
+ const border = attributes.style?.border || {};
+ const className = getBorderClasses( attributes );
return {
className: className || undefined,
- style: getInlineStyles( { border: borderStyles } ),
+ style: getInlineStyles( { border } ),
};
}
@@ -56,19 +38,42 @@ export function getBorderClassesAndStyles( { borderColor, style } ) {
* @return {Object} ClassName & style props from border block support.
*/
export function useBorderProps( attributes ) {
- const colors = useSetting( 'color.palette' ) || EMPTY_ARRAY;
+ const { colors } = useMultipleOriginColorsAndGradients();
const borderProps = getBorderClassesAndStyles( attributes );
+ const { borderColor, sideBorderColors } = attributes;
- // Force inline style to apply border color when themes do not load their
- // color stylesheets in the editor.
- if ( attributes.borderColor ) {
- const borderColorObject = getColorObjectByAttributeValues(
+ // Force inline styles to apply named border colors when themes do not load
+ // their color stylesheets in the editor.
+ if ( borderColor ) {
+ const borderColorObject = getMultiOriginColor( {
colors,
- attributes.borderColor
- );
+ namedColor: borderColor,
+ } );
borderProps.style.borderColor = borderColorObject.color;
}
+ if ( ! sideBorderColors ) {
+ return borderProps;
+ }
+
+ const sides = {
+ top: 'borderTopColor',
+ right: 'borderRightColor',
+ bottom: 'borderBottomColor',
+ left: 'borderLeftColor',
+ };
+
+ Object.entries( sides ).forEach( ( [ side, property ] ) => {
+ if ( sideBorderColors[ side ] ) {
+ const { color } = getMultiOriginColor( {
+ colors,
+ namedColor: sideBorderColors[ side ],
+ } );
+
+ borderProps.style[ property ] = color;
+ }
+ } );
+
return borderProps;
}
diff --git a/packages/block-library/src/common.scss b/packages/block-library/src/common.scss
index 2244a4c4fe79e1..f78efad2ac52b4 100644
--- a/packages/block-library/src/common.scss
+++ b/packages/block-library/src/common.scss
@@ -116,11 +116,34 @@
html :where(.has-border-color) {
border-style: solid;
}
+html :where(.has-border-top-color) {
+ border-top-style: solid;
+}
+html :where(.has-border-right-color) {
+ border-right-style: solid;
+}
+html :where(.has-border-bottom-color) {
+ border-bottom-style: solid;
+}
+html :where(.has-border-left-color) {
+ border-left-style: solid;
+}
html :where([style*="border-width"]) {
border-style: solid;
}
-
+html :where([style*="border-top-width"]) {
+ border-top-style: solid;
+}
+html :where([style*="border-right-width"]) {
+ border-right-style: solid;
+}
+html :where([style*="border-bottom-width"]) {
+ border-bottom-style: solid;
+}
+html :where([style*="border-left-width"]) {
+ border-left-style: solid;
+}
/**
* Provide baseline responsiveness for images.
diff --git a/packages/blocks/src/api/constants.js b/packages/blocks/src/api/constants.js
index c9b8d3e0e8f1d3..07e8bb1ddb580a 100644
--- a/packages/blocks/src/api/constants.js
+++ b/packages/blocks/src/api/constants.js
@@ -51,6 +51,54 @@ export const __EXPERIMENTAL_STYLE_PROPERTY = {
value: [ 'border', 'width' ],
support: [ '__experimentalBorder', 'width' ],
},
+ borderTopColor: {
+ value: [ 'border', 'top', 'color' ],
+ support: [ '__experimentalBorder', 'color' ],
+ },
+ borderTopStyle: {
+ value: [ 'border', 'top', 'style' ],
+ support: [ '__experimentalBorder', 'style' ],
+ },
+ borderTopWidth: {
+ value: [ 'border', 'top', 'width' ],
+ support: [ '__experimentalBorder', 'width' ],
+ },
+ borderRightColor: {
+ value: [ 'border', 'right', 'color' ],
+ support: [ '__experimentalBorder', 'color' ],
+ },
+ borderRightStyle: {
+ value: [ 'border', 'right', 'style' ],
+ support: [ '__experimentalBorder', 'style' ],
+ },
+ borderRightWidth: {
+ value: [ 'border', 'right', 'width' ],
+ support: [ '__experimentalBorder', 'width' ],
+ },
+ borderBottomColor: {
+ value: [ 'border', 'bottom', 'color' ],
+ support: [ '__experimentalBorder', 'color' ],
+ },
+ borderBottomStyle: {
+ value: [ 'border', 'bottom', 'style' ],
+ support: [ '__experimentalBorder', 'style' ],
+ },
+ borderBottomWidth: {
+ value: [ 'border', 'bottom', 'width' ],
+ support: [ '__experimentalBorder', 'width' ],
+ },
+ borderLeftColor: {
+ value: [ 'border', 'left', 'color' ],
+ support: [ '__experimentalBorder', 'color' ],
+ },
+ borderLeftStyle: {
+ value: [ 'border', 'left', 'style' ],
+ support: [ '__experimentalBorder', 'style' ],
+ },
+ borderLeftWidth: {
+ value: [ 'border', 'left', 'width' ],
+ support: [ '__experimentalBorder', 'width' ],
+ },
color: {
value: [ 'color', 'text' ],
support: [ 'color', 'text' ],
diff --git a/packages/edit-site/src/components/global-styles/border-panel.js b/packages/edit-site/src/components/global-styles/border-panel.js
index 5780797e2c186b..bc7ad5dcd18cb3 100644
--- a/packages/edit-site/src/components/global-styles/border-panel.js
+++ b/packages/edit-site/src/components/global-styles/border-panel.js
@@ -1,17 +1,15 @@
/**
* WordPress dependencies
*/
+import { __experimentalBorderRadiusControl as BorderRadiusControl } from '@wordpress/block-editor';
import {
- __experimentalBorderRadiusControl as BorderRadiusControl,
- __experimentalBorderStyleControl as BorderStyleControl,
- __experimentalColorGradientSettingsDropdown as ColorGradientSettingsDropdown,
-} from '@wordpress/block-editor';
-import {
+ __experimentalBorderBoxControl as BorderBoxControl,
+ __experimentalHasSplitBorders as hasSplitBorders,
+ __experimentalIsDefinedBorder as isDefinedBorder,
__experimentalToolsPanel as ToolsPanel,
__experimentalToolsPanelItem as ToolsPanelItem,
- __experimentalUnitControl as UnitControl,
- __experimentalUseCustomUnits as useCustomUnits,
} from '@wordpress/components';
+import { useCallback } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
/**
@@ -24,8 +22,6 @@ import {
useStyle,
} from './hooks';
-const MIN_BORDER_WIDTH = 0;
-
export function useHasBorderPanel( name ) {
const controls = [
useHasBorderColorControl( name ),
@@ -69,63 +65,45 @@ function useHasBorderWidthControl( name ) {
);
}
-export default function BorderPanel( { name } ) {
- // To better reflect if the user has customized a value we need to
- // ensure the style value being checked is from the `user` origin.
- const [ userBorderStyles ] = useStyle( 'border', name, 'user' );
- const createHasValueCallback = ( feature ) => () =>
- !! userBorderStyles?.[ feature ];
+function applyFallbackStyle( border ) {
+ if ( ! border ) {
+ return border;
+ }
- const createResetCallback = ( setStyle ) => () => setStyle( undefined );
-
- const handleOnChange = ( setStyle ) => ( value ) => {
- setStyle( value || undefined );
- };
-
- const units = useCustomUnits( {
- availableUnits: useSetting( 'spacing.units' )[ 0 ] || [
- 'px',
- 'em',
- 'rem',
- ],
- } );
-
- // Border width.
- const showBorderWidth = useHasBorderWidthControl( name );
- const [ borderWidthValue, setBorderWidth ] = useStyle(
- 'border.width',
- name
- );
+ if ( ! border.style && ( border.color || border.width ) ) {
+ return { ...border, style: 'solid' };
+ }
- // Border style.
- const showBorderStyle = useHasBorderStyleControl( name );
- const [ borderStyle, setBorderStyle ] = useStyle( 'border.style', name );
+ return border;
+}
- // When we set a border color or width ensure we have a style so the user
- // can see a visible border.
- const handleOnChangeWithStyle = ( setStyle ) => ( value ) => {
- if ( !! value && ! borderStyle ) {
- setBorderStyle( 'solid' );
- }
+function applyAllFallbackStyles( border ) {
+ if ( ! border ) {
+ return border;
+ }
+
+ if ( hasSplitBorders( border ) ) {
+ return {
+ top: applyFallbackStyle( border.top ),
+ right: applyFallbackStyle( border.right ),
+ bottom: applyFallbackStyle( border.bottom ),
+ left: applyFallbackStyle( border.left ),
+ };
+ }
+
+ return applyFallbackStyle( border );
+}
- setStyle( value || undefined );
- };
+export default function BorderPanel( { name } ) {
+ // To better reflect if the user has customized a value we need to
+ // ensure the style value being checked is from the `user` origin.
+ const [ userBorderStyles ] = useStyle( 'border', name, 'user' );
+ const [ border, setBorder ] = useStyle( 'border', name );
+ const colors = useColorsPerOrigin( name );
- // Border color.
const showBorderColor = useHasBorderColorControl( name );
- const [ borderColor, setBorderColor ] = useStyle( 'border.color', name );
- const disableCustomColors = ! useSetting( 'color.custom' )[ 0 ];
- const disableCustomGradients = ! useSetting( 'color.customGradient' )[ 0 ];
-
- const borderColorSettings = [
- {
- label: __( 'Color' ),
- colors: useColorsPerOrigin( name ),
- colorValue: borderColor,
- onColorChange: handleOnChangeWithStyle( setBorderColor ),
- clearable: false,
- },
- ];
+ const showBorderStyle = useHasBorderStyleControl( name );
+ const showBorderWidth = useHasBorderWidthControl( name );
// Border radius.
const showBorderRadius = useHasBorderRadiusControl( name );
@@ -141,60 +119,70 @@ export default function BorderPanel( { name } ) {
return !! borderValues;
};
- const resetAll = () => {
- setBorderColor( undefined );
- setBorderRadius( undefined );
- setBorderStyle( undefined );
- setBorderWidth( undefined );
+ const resetBorder = () => {
+ if ( hasBorderRadius() ) {
+ return setBorder( { radius: userBorderStyles.radius } );
+ }
+
+ setBorder( undefined );
};
+ const resetAll = useCallback( () => setBorder( undefined ), [ setBorder ] );
+ const onBorderChange = useCallback(
+ ( newBorder ) => {
+ // Ensure we have a visible border style when a border width or
+ // color is being selected.
+ const newBorderWithStyle = applyAllFallbackStyles( newBorder );
+
+ // As we can't conditionally generate styles based on if other
+ // style properties have been set we need to force split border
+ // definitions for user set border styles. Border radius is derived
+ // from the same property i.e. `border.radius` if it is a string
+ // that is used. The longhand border radii styles are only generated
+ // if that property is an object.
+ //
+ // For borders (color, style, and width) those are all properties on
+ // the `border` style property. This means if the theme.json defined
+ // split borders and the user condenses them into a flat border or
+ // vice-versa we'd get both sets of styles which would conflict.
+ const updatedBorder = ! hasSplitBorders( newBorderWithStyle )
+ ? {
+ top: newBorderWithStyle,
+ right: newBorderWithStyle,
+ bottom: newBorderWithStyle,
+ left: newBorderWithStyle,
+ }
+ : {
+ color: null,
+ style: null,
+ width: null,
+ ...newBorderWithStyle,
+ };
+
+ // As radius is maintained separately to color, style, and width
+ // maintain its value. Undefined values here will be cleaned when
+ // global styles are saved.
+ setBorder( { radius: border?.radius, ...updatedBorder } );
+ },
+ [ setBorder ]
+ );
+
return (
- { showBorderWidth && (
-
-
-
- ) }
- { showBorderStyle && (
-
-
-
- ) }
- { showBorderColor && (
+ { ( showBorderWidth || showBorderColor ) && (
isDefinedBorder( userBorderStyles ) }
+ label={ __( 'Border' ) }
+ onDeselect={ () => resetBorder() }
isShownByDefault={ true }
>
-
) }
@@ -202,12 +190,14 @@ export default function BorderPanel( { name } ) {
setBorderRadius( undefined ) }
isShownByDefault={ true }
>
{
+ setBorderRadius( value || undefined );
+ } }
/>
) }
diff --git a/packages/edit-site/src/components/global-styles/test/use-global-styles-output.js b/packages/edit-site/src/components/global-styles/test/use-global-styles-output.js
index 5d1c45af104a71..f329b51bb68740 100644
--- a/packages/edit-site/src/components/global-styles/test/use-global-styles-output.js
+++ b/packages/edit-site/src/components/global-styles/test/use-global-styles-output.js
@@ -377,7 +377,7 @@ describe( 'global styles renderer', () => {
};
expect( toStyles( tree, blockSelectors ) ).toEqual(
- '.wp-site-blocks > * { margin-top: 0; margin-bottom: 0; }.wp-site-blocks > * + * { margin-top: var( --wp--style--block-gap ); }body{background-color: red;margin: 10px;padding: 10px;}h1{font-size: 42px;}.wp-block-group{margin-top: 10px;margin-right: 20px;margin-bottom: 30px;margin-left: 40px;padding-top: 11px;padding-right: 22px;padding-bottom: 33px;padding-left: 44px;}h1,h2,h3,h4,h5,h6{color: orange;}h1 a,h2 a,h3 a,h4 a,h5 a,h6 a{color: hotpink;}.has-white-color{color: var(--wp--preset--color--white) !important;}.has-white-background-color{background-color: var(--wp--preset--color--white) !important;}.has-white-border-color{border-color: var(--wp--preset--color--white) !important;}.has-black-color{color: var(--wp--preset--color--black) !important;}.has-black-background-color{background-color: var(--wp--preset--color--black) !important;}.has-black-border-color{border-color: var(--wp--preset--color--black) !important;}h1.has-blue-color,h2.has-blue-color,h3.has-blue-color,h4.has-blue-color,h5.has-blue-color,h6.has-blue-color{color: var(--wp--preset--color--blue) !important;}h1.has-blue-background-color,h2.has-blue-background-color,h3.has-blue-background-color,h4.has-blue-background-color,h5.has-blue-background-color,h6.has-blue-background-color{background-color: var(--wp--preset--color--blue) !important;}h1.has-blue-border-color,h2.has-blue-border-color,h3.has-blue-border-color,h4.has-blue-border-color,h5.has-blue-border-color,h6.has-blue-border-color{border-color: var(--wp--preset--color--blue) !important;}'
+ '.wp-site-blocks > * { margin-top: 0; margin-bottom: 0; }.wp-site-blocks > * + * { margin-top: var( --wp--style--block-gap ); }body{background-color: red;margin: 10px;padding: 10px;}h1{font-size: 42px;}.wp-block-group{margin-top: 10px;margin-right: 20px;margin-bottom: 30px;margin-left: 40px;padding-top: 11px;padding-right: 22px;padding-bottom: 33px;padding-left: 44px;}h1,h2,h3,h4,h5,h6{color: orange;}h1 a,h2 a,h3 a,h4 a,h5 a,h6 a{color: hotpink;}.has-white-color{color: var(--wp--preset--color--white) !important;}.has-white-background-color{background-color: var(--wp--preset--color--white) !important;}.has-white-border-color{border-color: var(--wp--preset--color--white) !important;}.has-white-border-top-color{border-top-color: var(--wp--preset--color--white) !important;}.has-white-border-right-color{border-right-color: var(--wp--preset--color--white) !important;}.has-white-border-bottom-color{border-bottom-color: var(--wp--preset--color--white) !important;}.has-white-border-left-color{border-left-color: var(--wp--preset--color--white) !important;}.has-black-color{color: var(--wp--preset--color--black) !important;}.has-black-background-color{background-color: var(--wp--preset--color--black) !important;}.has-black-border-color{border-color: var(--wp--preset--color--black) !important;}.has-black-border-top-color{border-top-color: var(--wp--preset--color--black) !important;}.has-black-border-right-color{border-right-color: var(--wp--preset--color--black) !important;}.has-black-border-bottom-color{border-bottom-color: var(--wp--preset--color--black) !important;}.has-black-border-left-color{border-left-color: var(--wp--preset--color--black) !important;}h1.has-blue-color,h2.has-blue-color,h3.has-blue-color,h4.has-blue-color,h5.has-blue-color,h6.has-blue-color{color: var(--wp--preset--color--blue) !important;}h1.has-blue-background-color,h2.has-blue-background-color,h3.has-blue-background-color,h4.has-blue-background-color,h5.has-blue-background-color,h6.has-blue-background-color{background-color: var(--wp--preset--color--blue) !important;}h1.has-blue-border-color,h2.has-blue-border-color,h3.has-blue-border-color,h4.has-blue-border-color,h5.has-blue-border-color,h6.has-blue-border-color{border-color: var(--wp--preset--color--blue) !important;}h1.has-blue-border-top-color,h2.has-blue-border-top-color,h3.has-blue-border-top-color,h4.has-blue-border-top-color,h5.has-blue-border-top-color,h6.has-blue-border-top-color{border-top-color: var(--wp--preset--color--blue) !important;}h1.has-blue-border-right-color,h2.has-blue-border-right-color,h3.has-blue-border-right-color,h4.has-blue-border-right-color,h5.has-blue-border-right-color,h6.has-blue-border-right-color{border-right-color: var(--wp--preset--color--blue) !important;}h1.has-blue-border-bottom-color,h2.has-blue-border-bottom-color,h3.has-blue-border-bottom-color,h4.has-blue-border-bottom-color,h5.has-blue-border-bottom-color,h6.has-blue-border-bottom-color{border-bottom-color: var(--wp--preset--color--blue) !important;}h1.has-blue-border-left-color,h2.has-blue-border-left-color,h3.has-blue-border-left-color,h4.has-blue-border-left-color,h5.has-blue-border-left-color,h6.has-blue-border-left-color{border-left-color: var(--wp--preset--color--blue) !important;}'
);
} );
} );
diff --git a/packages/edit-site/src/components/global-styles/utils.js b/packages/edit-site/src/components/global-styles/utils.js
index 8f815fd3e3daae..80009ffa0cac8b 100644
--- a/packages/edit-site/src/components/global-styles/utils.js
+++ b/packages/edit-site/src/components/global-styles/utils.js
@@ -36,6 +36,22 @@ export const PRESET_METADATA = [
classSuffix: 'border-color',
propertyName: 'border-color',
},
+ {
+ classSuffix: 'border-top-color',
+ propertyName: 'border-top-color',
+ },
+ {
+ classSuffix: 'border-right-color',
+ propertyName: 'border-right-color',
+ },
+ {
+ classSuffix: 'border-bottom-color',
+ propertyName: 'border-bottom-color',
+ },
+ {
+ classSuffix: 'border-left-color',
+ propertyName: 'border-left-color',
+ },
],
},
{
diff --git a/phpunit/block-supports/border-test.php b/phpunit/block-supports/border-test.php
index 11e9891c192c95..81ad14fba4d9cf 100644
--- a/phpunit/block-supports/border-test.php
+++ b/phpunit/block-supports/border-test.php
@@ -23,32 +23,89 @@ function tearDown() {
parent::tearDown();
}
- function test_border_color_slug_with_numbers_is_kebab_cased_properly() {
- $this->test_block_name = 'test/border-color-slug-with-numbers-is-kebab-cased-properly';
+ /**
+ * Registers a new block for testing border support.
+ *
+ * @param string $block_name Name for the test block.
+ * @param array $supports Array defining block support configuration.
+ *
+ * @return WP_Block_Type The block type for the newly registered test block.
+ */
+ private function register_bordered_block_with_support( $block_name, $supports = array() ) {
+ $this->test_block_name = $block_name;
register_block_type(
$this->test_block_name,
array(
'api_version' => 2,
'attributes' => array(
- 'borderColor' => array(
+ 'borderColor' => array(
'type' => 'string',
),
- 'style' => array(
+ 'sideBorderColors' => array(
'type' => 'object',
),
- ),
- 'supports' => array(
- '__experimentalBorder' => array(
- 'color' => true,
- 'radius' => true,
- 'width' => true,
- 'style' => true,
+ 'style' => array(
+ 'type' => 'object',
),
),
+ 'supports' => $supports,
+ )
+ );
+ $registry = WP_Block_Type_Registry::get_instance();
+
+ return $registry->get_registered( $this->test_block_name );
+ }
+
+ function test_border_object_with_no_styles() {
+ $block_type = self::register_bordered_block_with_support(
+ 'test/border-object-with-no-styles',
+ array(
+ '__experimentalBorder' => array(
+ 'color' => true,
+ 'radius' => true,
+ 'width' => true,
+ 'style' => true,
+ ),
+ )
+ );
+ $block_attrs = array( 'style' => array( 'border' => array() ) );
+ $actual = gutenberg_apply_border_support( $block_type, $block_attrs );
+ $expected = array();
+
+ $this->assertSame( $expected, $actual );
+ }
+
+ function test_border_object_with_invalid_style_prop() {
+ $block_type = self::register_bordered_block_with_support(
+ 'test/border-object-with-invalid-style-prop',
+ array(
+ '__experimentalBorder' => array(
+ 'color' => true,
+ 'radius' => true,
+ 'width' => true,
+ 'style' => true,
+ ),
+ )
+ );
+ $block_attrs = array( 'style' => array( 'border' => array( 'invalid' => '10px' ) ) );
+ $actual = gutenberg_apply_border_support( $block_type, $block_attrs );
+ $expected = array();
+
+ $this->assertSame( $expected, $actual );
+ }
+
+ function test_border_color_slug_with_numbers_is_kebab_cased_properly() {
+ $block_type = self::register_bordered_block_with_support(
+ 'test/border-color-slug-with-numbers-is-kebab-cased-properly',
+ array(
+ '__experimentalBorder' => array(
+ 'color' => true,
+ 'radius' => true,
+ 'width' => true,
+ 'style' => true,
+ ),
)
);
- $registry = WP_Block_Type_Registry::get_instance();
- $block_type = $registry->get_registered( $this->test_block_name );
$block_atts = array(
'borderColor' => 'red',
'style' => array(
@@ -69,30 +126,19 @@ function test_border_color_slug_with_numbers_is_kebab_cased_properly() {
$this->assertSame( $expected, $actual );
}
- function test_border_with_skipped_serialization_block_supports() {
- $this->test_block_name = 'test/border-with-skipped-serialization-block-supports';
- register_block_type(
- $this->test_block_name,
+ function test_flat_border_with_skipped_serialization() {
+ $block_type = self::register_bordered_block_with_support(
+ 'test/flat-border-with-skipped-serialization',
array(
- 'api_version' => 2,
- 'attributes' => array(
- 'style' => array(
- 'type' => 'object',
- ),
- ),
- 'supports' => array(
- '__experimentalBorder' => array(
- 'color' => true,
- 'radius' => true,
- 'width' => true,
- 'style' => true,
- '__experimentalSkipSerialization' => true,
- ),
+ '__experimentalBorder' => array(
+ 'color' => true,
+ 'radius' => true,
+ 'width' => true,
+ 'style' => true,
+ '__experimentalSkipSerialization' => true,
),
)
);
- $registry = WP_Block_Type_Registry::get_instance();
- $block_type = $registry->get_registered( $this->test_block_name );
$block_atts = array(
'style' => array(
'border' => array(
@@ -110,30 +156,19 @@ function test_border_with_skipped_serialization_block_supports() {
$this->assertSame( $expected, $actual );
}
- function test_radius_with_individual_skipped_serialization_block_supports() {
- $this->test_block_name = 'test/radius-with-individual-skipped-serialization-block-supports';
- register_block_type(
- $this->test_block_name,
+ function test_flat_border_with_individual_skipped_serialization() {
+ $block_type = self::register_bordered_block_with_support(
+ 'test/flat-border-with-individual-skipped-serialization',
array(
- 'api_version' => 2,
- 'attributes' => array(
- 'style' => array(
- 'type' => 'object',
- ),
- ),
- 'supports' => array(
- '__experimentalBorder' => array(
- 'color' => true,
- 'radius' => true,
- 'width' => true,
- 'style' => true,
- '__experimentalSkipSerialization' => array( 'radius', 'color' ),
- ),
+ '__experimentalBorder' => array(
+ 'color' => true,
+ 'radius' => true,
+ 'width' => true,
+ 'style' => true,
+ '__experimentalSkipSerialization' => array( 'radius', 'color' ),
),
)
);
- $registry = WP_Block_Type_Registry::get_instance();
- $block_type = $registry->get_registered( $this->test_block_name );
$block_atts = array(
'style' => array(
'border' => array(
@@ -152,4 +187,285 @@ function test_radius_with_individual_skipped_serialization_block_supports() {
$this->assertSame( $expected, $actual );
}
+
+ function test_split_border_radius() {
+ $block_type = self::register_bordered_block_with_support(
+ 'test/split-border-radius',
+ array(
+ '__experimentalBorder' => array(
+ 'radius' => true,
+ ),
+ )
+ );
+ $block_attrs = array(
+ 'style' => array(
+ 'border' => array(
+ 'radius' => array(
+ 'topLeft' => '1em',
+ 'topRight' => '2rem',
+ 'bottomLeft' => '30px',
+ 'bottomRight' => '4vh',
+ ),
+ ),
+ ),
+ );
+ $actual = gutenberg_apply_border_support( $block_type, $block_attrs );
+ $expected = array(
+ 'style' => 'border-top-left-radius: 1em; border-top-right-radius: 2rem; border-bottom-left-radius: 30px; border-bottom-right-radius: 4vh;',
+ );
+
+ $this->assertSame( $expected, $actual );
+ }
+
+ function test_flat_border_with_custom_color() {
+ $block_type = self::register_bordered_block_with_support(
+ 'test/flat-border-with-custom-color',
+ array(
+ '__experimentalBorder' => array(
+ 'color' => true,
+ 'width' => true,
+ 'style' => true,
+ ),
+ )
+ );
+ $block_attrs = array(
+ 'style' => array(
+ 'border' => array(
+ 'color' => '#72aee6',
+ 'width' => '2px',
+ 'style' => 'dashed',
+ ),
+ ),
+ );
+ $actual = gutenberg_apply_border_support( $block_type, $block_attrs );
+ $expected = array(
+ 'class' => 'has-border-color',
+ 'style' => 'border-style: dashed; border-width: 2px; border-color: #72aee6;',
+ );
+
+ $this->assertSame( $expected, $actual );
+ }
+
+ function test_split_borders_with_custom_colors() {
+ $block_type = self::register_bordered_block_with_support(
+ 'test/split-borders-with-custom-colors',
+ array(
+ '__experimentalBorder' => array(
+ 'color' => true,
+ 'width' => true,
+ 'style' => true,
+ ),
+ )
+ );
+ $block_attrs = array(
+ 'style' => array(
+ 'border' => array(
+ 'top' => array(
+ 'color' => '#72aee6',
+ 'width' => '2px',
+ 'style' => 'dashed',
+ ),
+ 'right' => array(
+ 'color' => '#e65054',
+ 'width' => '0.25rem',
+ 'style' => 'dotted',
+ ),
+ 'bottom' => array(
+ 'color' => '#007017',
+ 'width' => '0.5em',
+ 'style' => 'solid',
+ ),
+ 'left' => array(
+ 'color' => '#f6f7f7',
+ 'width' => '1px',
+ 'style' => 'solid',
+ ),
+ ),
+ ),
+ );
+ $actual = gutenberg_apply_border_support( $block_type, $block_attrs );
+ $expected = array(
+ 'class' => 'has-border-top-color has-border-right-color has-border-bottom-color has-border-left-color',
+ 'style' => 'border-top-width: 2px; border-top-style: dashed; border-top-color: #72aee6; border-right-width: 0.25rem; border-right-style: dotted; border-right-color: #e65054; border-bottom-width: 0.5em; border-bottom-style: solid; border-bottom-color: #007017; border-left-width: 1px; border-left-style: solid; border-left-color: #f6f7f7;',
+ );
+
+ $this->assertSame( $expected, $actual );
+ }
+
+ function test_split_borders_with_skipped_serialization() {
+ $block_type = self::register_bordered_block_with_support(
+ 'test/split-borders-with-skipped-serialization',
+ array(
+ '__experimentalBorder' => array(
+ 'color' => true,
+ 'width' => true,
+ 'style' => true,
+ '__experimentalSkipSerialization' => true,
+ ),
+ )
+ );
+ $block_attrs = array(
+ 'style' => array(
+ 'border' => array(
+ 'top' => array(
+ 'color' => '#72aee6',
+ 'width' => '2px',
+ 'style' => 'dashed',
+ ),
+ 'right' => array(
+ 'color' => '#e65054',
+ 'width' => '0.25rem',
+ 'style' => 'dotted',
+ ),
+ 'bottom' => array(
+ 'color' => '#007017',
+ 'width' => '0.5em',
+ 'style' => 'solid',
+ ),
+ 'left' => array(
+ 'color' => '#f6f7f7',
+ 'width' => '1px',
+ 'style' => 'solid',
+ ),
+ ),
+ ),
+ );
+ $actual = gutenberg_apply_border_support( $block_type, $block_attrs );
+ $expected = array();
+
+ $this->assertSame( $expected, $actual );
+ }
+
+ function test_split_borders_with_skipped_individual_feature_serialization() {
+ $block_type = self::register_bordered_block_with_support(
+ 'test/split-borders-with-skipped-individual-feature-serialization',
+ array(
+ '__experimentalBorder' => array(
+ 'color' => true,
+ 'width' => true,
+ 'style' => true,
+ '__experimentalSkipSerialization' => array( 'width', 'style' ),
+ ),
+ )
+ );
+ $block_attrs = array(
+ 'style' => array(
+ 'border' => array(
+ 'top' => array(
+ 'color' => '#72aee6',
+ 'width' => '2px',
+ 'style' => 'dashed',
+ ),
+ 'right' => array(
+ 'color' => '#e65054',
+ 'width' => '0.25rem',
+ 'style' => 'dotted',
+ ),
+ 'bottom' => array(
+ 'color' => '#007017',
+ 'width' => '0.5em',
+ 'style' => 'solid',
+ ),
+ 'left' => array(
+ 'color' => '#f6f7f7',
+ 'width' => '1px',
+ 'style' => 'solid',
+ ),
+ ),
+ ),
+ );
+ $actual = gutenberg_apply_border_support( $block_type, $block_attrs );
+ $expected = array(
+ 'class' => 'has-border-top-color has-border-right-color has-border-bottom-color has-border-left-color',
+ 'style' => 'border-top-color: #72aee6; border-right-color: #e65054; border-bottom-color: #007017; border-left-color: #f6f7f7;',
+ );
+
+ $this->assertSame( $expected, $actual );
+ }
+
+ function test_partial_split_borders() {
+ $block_type = self::register_bordered_block_with_support(
+ 'test/partial-split-borders',
+ array(
+ '__experimentalBorder' => array(
+ 'color' => true,
+ 'width' => true,
+ 'style' => true,
+ ),
+ )
+ );
+ $block_attrs = array(
+ 'style' => array(
+ 'border' => array(
+ 'top' => array(
+ 'color' => '#72aee6',
+ 'width' => '2px',
+ 'style' => 'dashed',
+ ),
+ 'right' => array(
+ 'color' => '#e65054',
+ 'width' => '0.25rem',
+ ),
+ 'left' => array(
+ 'style' => 'solid',
+ ),
+ ),
+ ),
+ );
+ $actual = gutenberg_apply_border_support( $block_type, $block_attrs );
+ $expected = array(
+ 'class' => 'has-border-top-color has-border-right-color',
+ 'style' => 'border-top-width: 2px; border-top-style: dashed; border-top-color: #72aee6; border-right-width: 0.25rem; border-right-color: #e65054; border-left-style: solid;',
+ );
+
+ $this->assertSame( $expected, $actual );
+ }
+
+ function test_split_borders_with_named_colors() {
+ $block_type = self::register_bordered_block_with_support(
+ 'test/split-borders-with-named-colors',
+ array(
+ '__experimentalBorder' => array(
+ 'color' => true,
+ 'width' => true,
+ 'style' => true,
+ ),
+ )
+ );
+ $block_attrs = array(
+ 'sideBorderColors' => array(
+ 'top' => 'red',
+ 'right' => 'green',
+ 'bottom' => 'blue',
+ 'left' => 'yellow',
+ ),
+ 'style' => array(
+ 'border' => array(
+ 'top' => array(
+ 'width' => '2px',
+ 'style' => 'dashed',
+ ),
+ 'right' => array(
+ 'width' => '0.25rem',
+ 'style' => 'dotted',
+ ),
+ 'bottom' => array(
+ 'width' => '0.5em',
+ 'style' => 'solid',
+ ),
+ 'left' => array(
+ 'width' => '1px',
+ 'style' => 'solid',
+ ),
+ ),
+ ),
+ );
+ $actual = gutenberg_apply_border_support( $block_type, $block_attrs );
+ $expected = array(
+ 'class' => 'has-border-top-color has-red-border-top-color has-border-right-color has-green-border-right-color has-border-bottom-color has-blue-border-bottom-color has-border-left-color has-yellow-border-left-color',
+ 'style' => 'border-top-width: 2px; border-top-style: dashed; border-right-width: 0.25rem; border-right-style: dotted; border-bottom-width: 0.5em; border-bottom-style: solid; border-left-width: 1px; border-left-style: solid;',
+ );
+
+ $this->assertSame( $expected, $actual );
+ }
}
From 136e6af08bb8fdfb99594a6807979d33af717798 Mon Sep 17 00:00:00 2001
From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com>
Date: Tue, 1 Feb 2022 18:01:50 +1000
Subject: [PATCH 02/27] Tweak table styles to be less ugly with split borders
---
packages/block-library/src/table/style.scss | 43 +++++++++++++++++++++
1 file changed, 43 insertions(+)
diff --git a/packages/block-library/src/table/style.scss b/packages/block-library/src/table/style.scss
index a617d2261234e1..17278889448d5c 100644
--- a/packages/block-library/src/table/style.scss
+++ b/packages/block-library/src/table/style.scss
@@ -112,6 +112,49 @@
}
}
+ .has-border-top-color {
+ > *,
+ tr:first-child {
+ border-top-color: inherit;
+
+ th,
+ td {
+ border-top-color: inherit;
+ }
+ }
+ }
+ .has-border-right-color {
+ > *,
+ tr,
+ th,
+ td:last-child {
+ border-right-color: inherit;
+ }
+ }
+ .has-border-bottom-color {
+ > *,
+ tr:last-child {
+ border-bottom-color: inherit;
+
+ th,
+ td {
+ border-bottom-color: inherit;
+ }
+ }
+
+ tr:not(:last-child) {
+ border-bottom-color: currentColor;
+ }
+ }
+ .has-border-left-color {
+ > *,
+ tr,
+ th,
+ td:first-child {
+ border-left-color: inherit;
+ }
+ }
+
table[style*="border-style"] {
> *,
tr,
From 9b026e71788d3b6a93597aec6c951002eda04c18 Mon Sep 17 00:00:00 2001
From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com>
Date: Thu, 3 Feb 2022 17:42:08 +1000
Subject: [PATCH 03/27] Update Search block for individual border support
---
packages/block-library/src/search/edit.js | 20 ++-
packages/block-library/src/search/index.php | 139 +++++++++++++++-----
2 files changed, 119 insertions(+), 40 deletions(-)
diff --git a/packages/block-library/src/search/edit.js b/packages/block-library/src/search/edit.js
index 05faf4624bea90..1457423e4d60e0 100644
--- a/packages/block-library/src/search/edit.js
+++ b/packages/block-library/src/search/edit.js
@@ -103,8 +103,6 @@ export default function SearchEdit( {
} );
}, [ insertedInNavigationBlock ] );
const borderRadius = style?.border?.radius;
- const borderColor = style?.border?.color;
- const borderWidth = style?.border?.width;
const borderProps = useBorderProps( attributes );
// Check for old deprecated numerical border radius. Done as a separate
@@ -392,10 +390,18 @@ export default function SearchEdit( {
radius ? `calc(${ radius } + ${ DEFAULT_INNER_PADDING })` : undefined;
const getWrapperStyles = () => {
- const styles = {
- borderColor,
- borderWidth: isButtonPositionInside ? borderWidth : undefined,
- };
+ const styles = isButtonPositionInside
+ ? borderProps.style
+ : {
+ borderRadius: borderProps.style?.borderRadius,
+ borderTopLeftRadius: borderProps.style?.borderTopLeftRadius,
+ borderTopRightRadius:
+ borderProps.style?.borderTopRightRadius,
+ borderBottomLeftRadius:
+ borderProps.style?.borderBottomLeftRadius,
+ borderBottomRightRadius:
+ borderProps.style?.borderBottomRightRadius,
+ };
const isNonZeroBorderRadius =
borderRadius !== undefined && parseInt( borderRadius, 10 ) !== 0;
@@ -417,11 +423,11 @@ export default function SearchEdit( {
} = borderRadius;
return {
+ ...styles,
borderTopLeftRadius: padBorderRadius( topLeft ),
borderTopRightRadius: padBorderRadius( topRight ),
borderBottomLeftRadius: padBorderRadius( bottomLeft ),
borderBottomRightRadius: padBorderRadius( bottomRight ),
- ...styles,
};
}
diff --git a/packages/block-library/src/search/index.php b/packages/block-library/src/search/index.php
index 94ec6fc4ac7f53..61a92e15eef381 100644
--- a/packages/block-library/src/search/index.php
+++ b/packages/block-library/src/search/index.php
@@ -171,6 +171,67 @@ function classnames_for_block_core_search( $attributes ) {
return implode( ' ', $classnames );
}
+/**
+ * This generates a CSS rule for the given border property and side if provided.
+ * Based on whether the Search block is configured to display the button inside
+ * or not, the generated rule is injected into the appropriate collection of
+ * styles for later application in the block's markup.
+ *
+ * @param array $attributes The block attributes.
+ * @param string $property Border property to generate rule for e.g. width or color.
+ * @param string $side Optional side border. The dictates the value retrieved and final CSS property.
+ * @param array $wrapper_styles Current collection of wrapper styles.
+ * @param array $button_styles Current collection of button styles.
+ * @param array $input_styles Current collection of input styles.
+ *
+ * @return void
+ */
+function apply_block_core_search_border_style( $attributes, $property, $side, &$wrapper_styles, &$button_styles, &$input_styles ) {
+ $is_button_inside = 'button-inside' === _wp_array_get( $attributes, array( 'buttonPosition' ), false );
+
+ $path = array( 'style', 'border', $property );
+
+ if ( $side ) {
+ array_splice( $path, 2, 0, $side );
+ }
+
+ $value = _wp_array_get( $attributes, $path, false );
+
+ if ( empty( $value ) ) {
+ return;
+ }
+
+ $property_suffix = $side ? sprintf( '%s-%s', $side, $property ) : $property;
+
+ if ( $is_button_inside ) {
+ $wrapper_styles[] = sprintf( 'border-%s: %s;', $property_suffix, esc_attr( $value ) );
+ } else {
+ $button_styles[] = sprintf( 'border-%s: %s;', $property_suffix, esc_attr( $value ) );
+ $input_styles[] = sprintf( 'border-%s: %s;', $property_suffix, esc_attr( $value ) );
+ }
+}
+
+/**
+ * This adds CSS rules for a given border property e.g. width or color. It
+ * injects rules into the provided wrapper, button and input style arrays for
+ * uniform "flat" borders or those with individual sides configured.
+ *
+ * @param array $attributes The block attributes.
+ * @param string $property Border property to generate rule for e.g. width or color.
+ * @param array $wrapper_styles Current collection of wrapper styles.
+ * @param array $button_styles Current collection of button styles.
+ * @param array $input_styles Current collection of input styles.
+ *
+ * @return void
+ */
+function apply_block_core_search_border_styles( $attributes, $property, &$wrapper_styles, &$button_styles, &$input_styles ) {
+ apply_block_core_search_border_style( $attributes, $property, null, $wrapper_styles, $button_styles, $input_styles );
+ apply_block_core_search_border_style( $attributes, $property, 'top', $wrapper_styles, $button_styles, $input_styles );
+ apply_block_core_search_border_style( $attributes, $property, 'right', $wrapper_styles, $button_styles, $input_styles );
+ apply_block_core_search_border_style( $attributes, $property, 'bottom', $wrapper_styles, $button_styles, $input_styles );
+ apply_block_core_search_border_style( $attributes, $property, 'left', $wrapper_styles, $button_styles, $input_styles );
+}
+
/**
* Builds an array of inline styles for the search block.
*
@@ -201,19 +262,10 @@ function styles_for_block_core_search( $attributes ) {
);
}
- // Add border width styles.
- $has_border_width = ! empty( $attributes['style']['border']['width'] );
-
- if ( $has_border_width ) {
- $border_width = $attributes['style']['border']['width'];
-
- if ( $is_button_inside ) {
- $wrapper_styles[] = sprintf( 'border-width: %s;', esc_attr( $border_width ) );
- } else {
- $button_styles[] = sprintf( 'border-width: %s;', esc_attr( $border_width ) );
- $input_styles[] = sprintf( 'border-width: %s;', esc_attr( $border_width ) );
- }
- }
+ // Add border width and color styles.
+ apply_block_core_search_border_styles( $attributes, 'width', $wrapper_styles, $button_styles, $input_styles );
+ apply_block_core_search_border_styles( $attributes, 'color', $wrapper_styles, $button_styles, $input_styles );
+ apply_block_core_search_border_styles( $attributes, 'style', $wrapper_styles, $button_styles, $input_styles );
// Add border radius styles.
$has_border_radius = ! empty( $attributes['style']['border']['radius'] );
@@ -269,21 +321,6 @@ function styles_for_block_core_search( $attributes ) {
}
}
- // Add border color styles.
- $has_border_color = ! empty( $attributes['style']['border']['color'] );
-
- if ( $has_border_color ) {
- $border_color = $attributes['style']['border']['color'];
-
- // Apply wrapper border color if button placed inside.
- if ( $is_button_inside ) {
- $wrapper_styles[] = sprintf( 'border-color: %s;', esc_attr( $border_color ) );
- } else {
- $button_styles[] = sprintf( 'border-color: %s;', esc_attr( $border_color ) );
- $input_styles[] = sprintf( 'border-color: %s;', esc_attr( $border_color ) );
- }
- }
-
// Add color styles.
$has_text_color = ! empty( $attributes['style']['color']['text'] );
if ( $has_text_color ) {
@@ -315,13 +352,49 @@ function styles_for_block_core_search( $attributes ) {
* @return string The border color classnames to be applied to the block elements.
*/
function get_border_color_classes_for_block_core_search( $attributes ) {
+ $border_color_classes = array();
$has_custom_border_color = ! empty( $attributes['style']['border']['color'] );
- $border_color_classes = ! empty( $attributes['borderColor'] ) ? sprintf( 'has-border-color has-%s-border-color', $attributes['borderColor'] ) : '';
- // If there's a border color style and no `borderColor` text string, we still want to add the generic `has-border-color` class name to the element.
- if ( $has_custom_border_color && empty( $attributes['borderColor'] ) ) {
- $border_color_classes = 'has-border-color';
+ $has_named_border_color = ! empty( $attributes['borderColor'] );
+
+ if ( $has_custom_border_color || $has_named_border_color ) {
+ $border_color_classes[] = 'has-border-color';
+ }
+
+ if ( $has_named_border_color ) {
+ $border_color_classes[] = sprintf( 'has-%s-border-color', esc_attr( $attributes['borderColor'] ) );
+ }
+
+ $border_color_classes[] = get_side_border_color_classes_for_block_core_search( $attributes, 'top' );
+ $border_color_classes[] = get_side_border_color_classes_for_block_core_search( $attributes, 'right' );
+ $border_color_classes[] = get_side_border_color_classes_for_block_core_search( $attributes, 'bottom' );
+ $border_color_classes[] = get_side_border_color_classes_for_block_core_search( $attributes, 'left' );
+
+ return implode( ' ', $border_color_classes );
+}
+
+/**
+ * Generates required CSS classes for individual border side color selections.
+ *
+ * @param array $attributes The block attributes.
+ * @param string $side Border side i.e. `top`, `right`, `bottom`, or `left`.
+ *
+ * @return string
+ */
+function get_side_border_color_classes_for_block_core_search( $attributes, $side ) {
+ $classes = array();
+
+ $has_custom_border_color = ! empty( $attributes['style']['border'][ $side ]['color'] );
+ $has_named_border_color = ! empty( $attributes['sideBorderColors'][ $side ] );
+
+ if ( $has_custom_border_color || $has_named_border_color ) {
+ $classes[] = sprintf( 'has-border-%s-color', esc_attr( $side ) );
}
- return $border_color_classes;
+
+ if ( $has_named_border_color ) {
+ $classes[] = sprintf( 'has-%s-border-%s-color', esc_attr( $attributes['sideBorderColors'][ $side ] ), esc_attr( $side ) );
+ }
+
+ return implode( ' ', $classes );
}
/**
From 7ed686ca661f8ab03551277e12f9e2ac085e101d Mon Sep 17 00:00:00 2001
From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com>
Date: Wed, 23 Mar 2022 18:37:30 +1000
Subject: [PATCH 04/27] Extend theme.json class to support individual borders
---
.../class-wp-theme-json-gutenberg.php | 195 ++++++++++++++++++
phpunit/class-wp-theme-json-test.php | 10 +-
2 files changed, 200 insertions(+), 5 deletions(-)
diff --git a/lib/compat/wordpress-6.0/class-wp-theme-json-gutenberg.php b/lib/compat/wordpress-6.0/class-wp-theme-json-gutenberg.php
index 8868185360acdb..1669d1f972cdd6 100644
--- a/lib/compat/wordpress-6.0/class-wp-theme-json-gutenberg.php
+++ b/lib/compat/wordpress-6.0/class-wp-theme-json-gutenberg.php
@@ -15,6 +15,160 @@
* @access private
*/
class WP_Theme_JSON_Gutenberg extends WP_Theme_JSON_5_9 {
+ /**
+ * Presets are a set of values that serve
+ * to bootstrap some styles: colors, font sizes, etc.
+ *
+ * They are a unkeyed array of values such as:
+ *
+ * ```php
+ * array(
+ * array(
+ * 'slug' => 'unique-name-within-the-set',
+ * 'name' => 'Name for the UI',
+ * => 'value'
+ * ),
+ * )
+ * ```
+ *
+ * This contains the necessary metadata to process them:
+ *
+ * - path => where to find the preset within the settings section
+ * - override => whether a theme preset with the same slug as a default preset
+ * can override it
+ * - value_key => the key that represents the value
+ * - value_func => optionally, instead of value_key, a function to generate
+ * the value that takes a preset as an argument
+ * (either value_key or value_func should be present)
+ * - css_vars => template string to use in generating the CSS Custom Property.
+ * Example output: "--wp--preset--duotone--blue: " will generate as many CSS Custom Properties as presets defined
+ * substituting the $slug for the slug's value for each preset value.
+ * - classes => array containing a structure with the classes to
+ * generate for the presets, where for each array item
+ * the key is the class name and the value the property name.
+ * The "$slug" substring will be replaced by the slug of each preset.
+ * For example:
+ * 'classes' => array(
+ * '.has-$slug-color' => 'color',
+ * '.has-$slug-background-color' => 'background-color',
+ * '.has-$slug-border-color' => 'border-color',
+ * )
+ * - properties => array of CSS properties to be used by kses to
+ * validate the content of each preset
+ * by means of the remove_insecure_properties method.
+ */
+ const PRESETS_METADATA = array(
+ array(
+ 'path' => array( 'color', 'palette' ),
+ 'prevent_override' => array( 'color', 'defaultPalette' ),
+ 'use_default_presets' => array( 'color', 'defaultPalette' ),
+ 'use_default_names' => false,
+ 'value_key' => 'color',
+ 'css_vars' => '--wp--preset--color--$slug',
+ 'classes' => array(
+ '.has-$slug-color' => 'color',
+ '.has-$slug-background-color' => 'background-color',
+ '.has-$slug-border-color' => 'border-color',
+ '.has-$slug-border-top-color' => 'border-top-color',
+ '.has-$slug-border-right-color' => 'border-right-color',
+ '.has-$slug-border-bottom-color' => 'border-bottom-color',
+ '.has-$slug-border-left-color' => 'border-left-color',
+ ),
+ 'properties' => array( 'color', 'background-color', 'border-color' ),
+ ),
+ array(
+ 'path' => array( 'color', 'gradients' ),
+ 'prevent_override' => array( 'color', 'defaultGradients' ),
+ 'use_default_presets' => array( 'color', 'defaultGradients' ),
+ 'use_default_names' => false,
+ 'value_key' => 'gradient',
+ 'css_vars' => '--wp--preset--gradient--$slug',
+ 'classes' => array( '.has-$slug-gradient-background' => 'background' ),
+ 'properties' => array( 'background' ),
+ ),
+ array(
+ 'path' => array( 'color', 'duotone' ),
+ 'prevent_override' => array( 'color', 'defaultDuotone' ),
+ 'use_default_presets' => array( 'color', 'defaultDuotone' ),
+ 'use_default_names' => false,
+ 'value_func' => 'gutenberg_get_duotone_filter_property',
+ 'css_vars' => '--wp--preset--duotone--$slug',
+ 'classes' => array(),
+ 'properties' => array( 'filter' ),
+ ),
+ array(
+ 'path' => array( 'typography', 'fontSizes' ),
+ 'prevent_override' => false,
+ 'use_default_presets' => true,
+ 'use_default_names' => true,
+ 'value_key' => 'size',
+ 'css_vars' => '--wp--preset--font-size--$slug',
+ 'classes' => array( '.has-$slug-font-size' => 'font-size' ),
+ 'properties' => array( 'font-size' ),
+ ),
+ array(
+ 'path' => array( 'typography', 'fontFamilies' ),
+ 'prevent_override' => false,
+ 'use_default_presets' => true,
+ 'use_default_names' => false,
+ 'value_key' => 'fontFamily',
+ 'css_vars' => '--wp--preset--font-family--$slug',
+ 'classes' => array( '.has-$slug-font-family' => 'font-family' ),
+ 'properties' => array( 'font-family' ),
+ ),
+ );
+
+ /**
+ * Metadata for style properties.
+ *
+ * Each element is a direct mapping from the CSS property name to the
+ * path to the value in theme.json & block attributes.
+ */
+ const PROPERTIES_METADATA = array(
+ 'background' => array( 'color', 'gradient' ),
+ 'background-color' => array( 'color', 'background' ),
+ 'border-radius' => array( 'border', 'radius' ),
+ 'border-top-left-radius' => array( 'border', 'radius', 'topLeft' ),
+ 'border-top-right-radius' => array( 'border', 'radius', 'topRight' ),
+ 'border-bottom-left-radius' => array( 'border', 'radius', 'bottomLeft' ),
+ 'border-bottom-right-radius' => array( 'border', 'radius', 'bottomRight' ),
+ 'border-color' => array( 'border', 'color' ),
+ 'border-width' => array( 'border', 'width' ),
+ 'border-style' => array( 'border', 'style' ),
+ 'border-top-color' => array( 'border', 'top', 'color' ),
+ 'border-top-width' => array( 'border', 'top', 'width' ),
+ 'border-top-style' => array( 'border', 'top', 'style' ),
+ 'border-right-color' => array( 'border', 'right', 'color' ),
+ 'border-right-width' => array( 'border', 'right', 'width' ),
+ 'border-right-style' => array( 'border', 'right', 'style' ),
+ 'border-bottom-color' => array( 'border', 'bottom', 'color' ),
+ 'border-bottom-width' => array( 'border', 'bottom', 'width' ),
+ 'border-bottom-style' => array( 'border', 'bottom', 'style' ),
+ 'border-left-color' => array( 'border', 'left', 'color' ),
+ 'border-left-width' => array( 'border', 'left', 'width' ),
+ 'border-left-style' => array( 'border', 'left', 'style' ),
+ 'color' => array( 'color', 'text' ),
+ 'font-family' => array( 'typography', 'fontFamily' ),
+ 'font-size' => array( 'typography', 'fontSize' ),
+ 'font-style' => array( 'typography', 'fontStyle' ),
+ 'font-weight' => array( 'typography', 'fontWeight' ),
+ 'letter-spacing' => array( 'typography', 'letterSpacing' ),
+ 'line-height' => array( 'typography', 'lineHeight' ),
+ 'margin' => array( 'spacing', 'margin' ),
+ 'margin-top' => array( 'spacing', 'margin', 'top' ),
+ 'margin-right' => array( 'spacing', 'margin', 'right' ),
+ 'margin-bottom' => array( 'spacing', 'margin', 'bottom' ),
+ 'margin-left' => array( 'spacing', 'margin', 'left' ),
+ 'padding' => array( 'spacing', 'padding' ),
+ 'padding-top' => array( 'spacing', 'padding', 'top' ),
+ 'padding-right' => array( 'spacing', 'padding', 'right' ),
+ 'padding-bottom' => array( 'spacing', 'padding', 'bottom' ),
+ 'padding-left' => array( 'spacing', 'padding', 'left' ),
+ '--wp--style--block-gap' => array( 'spacing', 'blockGap' ),
+ 'text-decoration' => array( 'typography', 'textDecoration' ),
+ 'text-transform' => array( 'typography', 'textTransform' ),
+ 'filter' => array( 'filter', 'duotone' ),
+ );
/**
* Presets are a set of values that serve
@@ -197,6 +351,47 @@ class WP_Theme_JSON_Gutenberg extends WP_Theme_JSON_5_9 {
),
);
+ /**
+ * The valid properties under the styles key.
+ *
+ * @var array
+ */
+ const VALID_STYLES = array(
+ 'border' => array(
+ 'color' => null,
+ 'radius' => null,
+ 'style' => null,
+ 'width' => null,
+ 'top' => null,
+ 'right' => null,
+ 'bottom' => null,
+ 'left' => null,
+ ),
+ 'color' => array(
+ 'background' => null,
+ 'gradient' => null,
+ 'text' => null,
+ ),
+ 'filter' => array(
+ 'duotone' => null,
+ ),
+ 'spacing' => array(
+ 'margin' => null,
+ 'padding' => null,
+ 'blockGap' => 'top',
+ ),
+ 'typography' => array(
+ 'fontFamily' => null,
+ 'fontSize' => null,
+ 'fontStyle' => null,
+ 'fontWeight' => null,
+ 'letterSpacing' => null,
+ 'lineHeight' => null,
+ 'textDecoration' => null,
+ 'textTransform' => null,
+ ),
+ );
+
/**
* Returns the current theme's wanted patterns(slugs) to be
* registered from Pattern Directory.
diff --git a/phpunit/class-wp-theme-json-test.php b/phpunit/class-wp-theme-json-test.php
index 89240870399dc9..986600d6f75686 100644
--- a/phpunit/class-wp-theme-json-test.php
+++ b/phpunit/class-wp-theme-json-test.php
@@ -529,7 +529,7 @@ function test_get_stylesheet() {
$variables = 'body{--wp--preset--color--grey: grey;--wp--preset--font-family--small: 14px;--wp--preset--font-family--big: 41px;}.wp-block-group{--wp--custom--base-font: 16;--wp--custom--line-height--small: 1.2;--wp--custom--line-height--medium: 1.4;--wp--custom--line-height--large: 1.8;}';
$styles = 'body { margin: 0; }body{color: var(--wp--preset--color--grey);}.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }.wp-site-blocks > * { margin-block-start: 0; margin-block-end: 0; }.wp-site-blocks > * + * { margin-block-start: var( --wp--style--block-gap ); }a{background-color: #333;color: #111;}.wp-block-group{border-radius: 10px;padding: 24px;}.wp-block-group a{color: #111;}h1,h2,h3,h4,h5,h6{color: #123456;}h1 a,h2 a,h3 a,h4 a,h5 a,h6 a{background-color: #333;color: #111;font-size: 60px;}.wp-block-post-date{color: #123456;}.wp-block-post-date a{background-color: #777;color: #555;}.wp-block-image{border-top-left-radius: 10px;border-bottom-right-radius: 1em;margin-bottom: 30px;}';
- $presets = '.has-grey-color{color: var(--wp--preset--color--grey) !important;}.has-grey-background-color{background-color: var(--wp--preset--color--grey) !important;}.has-grey-border-color{border-color: var(--wp--preset--color--grey) !important;}.has-small-font-family{font-family: var(--wp--preset--font-family--small) !important;}.has-big-font-family{font-family: var(--wp--preset--font-family--big) !important;}';
+ $presets = '.has-grey-color{color: var(--wp--preset--color--grey) !important;}.has-grey-background-color{background-color: var(--wp--preset--color--grey) !important;}.has-grey-border-color{border-color: var(--wp--preset--color--grey) !important;}.has-grey-border-top-color{border-top-color: var(--wp--preset--color--grey) !important;}.has-grey-border-right-color{border-right-color: var(--wp--preset--color--grey) !important;}.has-grey-border-bottom-color{border-bottom-color: var(--wp--preset--color--grey) !important;}.has-grey-border-left-color{border-left-color: var(--wp--preset--color--grey) !important;}.has-small-font-family{font-family: var(--wp--preset--font-family--small) !important;}.has-big-font-family{font-family: var(--wp--preset--font-family--big) !important;}';
$all = $variables . $styles . $presets;
$this->assertEquals( $all, $theme_json->get_stylesheet() );
$this->assertEquals( $styles, $theme_json->get_stylesheet( array( 'styles' ) ) );
@@ -559,7 +559,7 @@ function test_get_stylesheet_preset_classes_work_with_compounded_selectors() {
);
$this->assertEquals(
- 'h1.has-white-color,h2.has-white-color,h3.has-white-color,h4.has-white-color,h5.has-white-color,h6.has-white-color{color: var(--wp--preset--color--white) !important;}h1.has-white-background-color,h2.has-white-background-color,h3.has-white-background-color,h4.has-white-background-color,h5.has-white-background-color,h6.has-white-background-color{background-color: var(--wp--preset--color--white) !important;}h1.has-white-border-color,h2.has-white-border-color,h3.has-white-border-color,h4.has-white-border-color,h5.has-white-border-color,h6.has-white-border-color{border-color: var(--wp--preset--color--white) !important;}',
+ 'h1.has-white-color,h2.has-white-color,h3.has-white-color,h4.has-white-color,h5.has-white-color,h6.has-white-color{color: var(--wp--preset--color--white) !important;}h1.has-white-background-color,h2.has-white-background-color,h3.has-white-background-color,h4.has-white-background-color,h5.has-white-background-color,h6.has-white-background-color{background-color: var(--wp--preset--color--white) !important;}h1.has-white-border-color,h2.has-white-border-color,h3.has-white-border-color,h4.has-white-border-color,h5.has-white-border-color,h6.has-white-border-color{border-color: var(--wp--preset--color--white) !important;}h1.has-white-border-top-color,h2.has-white-border-top-color,h3.has-white-border-top-color,h4.has-white-border-top-color,h5.has-white-border-top-color,h6.has-white-border-top-color{border-top-color: var(--wp--preset--color--white) !important;}h1.has-white-border-right-color,h2.has-white-border-right-color,h3.has-white-border-right-color,h4.has-white-border-right-color,h5.has-white-border-right-color,h6.has-white-border-right-color{border-right-color: var(--wp--preset--color--white) !important;}h1.has-white-border-bottom-color,h2.has-white-border-bottom-color,h3.has-white-border-bottom-color,h4.has-white-border-bottom-color,h5.has-white-border-bottom-color,h6.has-white-border-bottom-color{border-bottom-color: var(--wp--preset--color--white) !important;}h1.has-white-border-left-color,h2.has-white-border-left-color,h3.has-white-border-left-color,h4.has-white-border-left-color,h5.has-white-border-left-color,h6.has-white-border-left-color{border-left-color: var(--wp--preset--color--white) !important;}',
$theme_json->get_stylesheet( array( 'presets' ) )
);
}
@@ -595,7 +595,7 @@ function test_get_stylesheet_preset_rules_come_after_block_rules() {
);
$styles = 'body { margin: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }.wp-block-group{color: red;}';
- $presets = '.wp-block-group.has-grey-color{color: var(--wp--preset--color--grey) !important;}.wp-block-group.has-grey-background-color{background-color: var(--wp--preset--color--grey) !important;}.wp-block-group.has-grey-border-color{border-color: var(--wp--preset--color--grey) !important;}';
+ $presets = '.wp-block-group.has-grey-color{color: var(--wp--preset--color--grey) !important;}.wp-block-group.has-grey-background-color{background-color: var(--wp--preset--color--grey) !important;}.wp-block-group.has-grey-border-color{border-color: var(--wp--preset--color--grey) !important;}.wp-block-group.has-grey-border-top-color{border-top-color: var(--wp--preset--color--grey) !important;}.wp-block-group.has-grey-border-right-color{border-right-color: var(--wp--preset--color--grey) !important;}.wp-block-group.has-grey-border-bottom-color{border-bottom-color: var(--wp--preset--color--grey) !important;}.wp-block-group.has-grey-border-left-color{border-left-color: var(--wp--preset--color--grey) !important;}';
$variables = '.wp-block-group{--wp--preset--color--grey: grey;}';
$all = $variables . $styles . $presets;
$this->assertEquals( $all, $theme_json->get_stylesheet() );
@@ -637,7 +637,7 @@ function test_get_stylesheet_generates_proper_classes_and_css_vars_from_slugs()
);
$this->assertEquals(
- '.has-grey-color{color: var(--wp--preset--color--grey) !important;}.has-dark-grey-color{color: var(--wp--preset--color--dark-grey) !important;}.has-light-grey-color{color: var(--wp--preset--color--light-grey) !important;}.has-white-2-black-color{color: var(--wp--preset--color--white-2-black) !important;}.has-grey-background-color{background-color: var(--wp--preset--color--grey) !important;}.has-dark-grey-background-color{background-color: var(--wp--preset--color--dark-grey) !important;}.has-light-grey-background-color{background-color: var(--wp--preset--color--light-grey) !important;}.has-white-2-black-background-color{background-color: var(--wp--preset--color--white-2-black) !important;}.has-grey-border-color{border-color: var(--wp--preset--color--grey) !important;}.has-dark-grey-border-color{border-color: var(--wp--preset--color--dark-grey) !important;}.has-light-grey-border-color{border-color: var(--wp--preset--color--light-grey) !important;}.has-white-2-black-border-color{border-color: var(--wp--preset--color--white-2-black) !important;}',
+ '.has-grey-color{color: var(--wp--preset--color--grey) !important;}.has-dark-grey-color{color: var(--wp--preset--color--dark-grey) !important;}.has-light-grey-color{color: var(--wp--preset--color--light-grey) !important;}.has-white-2-black-color{color: var(--wp--preset--color--white-2-black) !important;}.has-grey-background-color{background-color: var(--wp--preset--color--grey) !important;}.has-dark-grey-background-color{background-color: var(--wp--preset--color--dark-grey) !important;}.has-light-grey-background-color{background-color: var(--wp--preset--color--light-grey) !important;}.has-white-2-black-background-color{background-color: var(--wp--preset--color--white-2-black) !important;}.has-grey-border-color{border-color: var(--wp--preset--color--grey) !important;}.has-dark-grey-border-color{border-color: var(--wp--preset--color--dark-grey) !important;}.has-light-grey-border-color{border-color: var(--wp--preset--color--light-grey) !important;}.has-white-2-black-border-color{border-color: var(--wp--preset--color--white-2-black) !important;}.has-grey-border-top-color{border-top-color: var(--wp--preset--color--grey) !important;}.has-dark-grey-border-top-color{border-top-color: var(--wp--preset--color--dark-grey) !important;}.has-light-grey-border-top-color{border-top-color: var(--wp--preset--color--light-grey) !important;}.has-white-2-black-border-top-color{border-top-color: var(--wp--preset--color--white-2-black) !important;}.has-grey-border-right-color{border-right-color: var(--wp--preset--color--grey) !important;}.has-dark-grey-border-right-color{border-right-color: var(--wp--preset--color--dark-grey) !important;}.has-light-grey-border-right-color{border-right-color: var(--wp--preset--color--light-grey) !important;}.has-white-2-black-border-right-color{border-right-color: var(--wp--preset--color--white-2-black) !important;}.has-grey-border-bottom-color{border-bottom-color: var(--wp--preset--color--grey) !important;}.has-dark-grey-border-bottom-color{border-bottom-color: var(--wp--preset--color--dark-grey) !important;}.has-light-grey-border-bottom-color{border-bottom-color: var(--wp--preset--color--light-grey) !important;}.has-white-2-black-border-bottom-color{border-bottom-color: var(--wp--preset--color--white-2-black) !important;}.has-grey-border-left-color{border-left-color: var(--wp--preset--color--grey) !important;}.has-dark-grey-border-left-color{border-left-color: var(--wp--preset--color--dark-grey) !important;}.has-light-grey-border-left-color{border-left-color: var(--wp--preset--color--light-grey) !important;}.has-white-2-black-border-left-color{border-left-color: var(--wp--preset--color--white-2-black) !important;}',
$theme_json->get_stylesheet( array( 'presets' ) )
);
$this->assertEquals(
@@ -680,7 +680,7 @@ public function test_get_stylesheet_preset_values_are_marked_as_important() {
);
$this->assertEquals(
- 'body{--wp--preset--color--grey: grey;}body { margin: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }p{background-color: blue;color: red;font-size: 12px;line-height: 1.3;}.has-grey-color{color: var(--wp--preset--color--grey) !important;}.has-grey-background-color{background-color: var(--wp--preset--color--grey) !important;}.has-grey-border-color{border-color: var(--wp--preset--color--grey) !important;}',
+ 'body{--wp--preset--color--grey: grey;}body { margin: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }p{background-color: blue;color: red;font-size: 12px;line-height: 1.3;}.has-grey-color{color: var(--wp--preset--color--grey) !important;}.has-grey-background-color{background-color: var(--wp--preset--color--grey) !important;}.has-grey-border-color{border-color: var(--wp--preset--color--grey) !important;}.has-grey-border-top-color{border-top-color: var(--wp--preset--color--grey) !important;}.has-grey-border-right-color{border-right-color: var(--wp--preset--color--grey) !important;}.has-grey-border-bottom-color{border-bottom-color: var(--wp--preset--color--grey) !important;}.has-grey-border-left-color{border-left-color: var(--wp--preset--color--grey) !important;}',
$theme_json->get_stylesheet()
);
}
From 45f6393a34a12e6c85a31f2bb857dc044b55b75e Mon Sep 17 00:00:00 2001
From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com>
Date: Fri, 25 Mar 2022 18:06:18 +1000
Subject: [PATCH 05/27] Update positioning of border control popovers
---
packages/block-editor/src/hooks/border.js | 11 +++++
packages/block-editor/src/hooks/border.scss | 48 ++++++++++++++++++
.../components/global-styles/border-panel.js | 12 +++++
.../src/components/sidebar/style.scss | 49 +++++++++++++++++++
4 files changed, 120 insertions(+)
diff --git a/packages/block-editor/src/hooks/border.js b/packages/block-editor/src/hooks/border.js
index a54ce1797b8cd4..86820c202cf1e1 100644
--- a/packages/block-editor/src/hooks/border.js
+++ b/packages/block-editor/src/hooks/border.js
@@ -270,6 +270,17 @@ export function BorderPanel( props ) {
diff --git a/packages/edit-site/src/components/sidebar/style.scss b/packages/edit-site/src/components/sidebar/style.scss
index b0e3cdfd1463e7..ff47afcccdc0ab 100644
--- a/packages/edit-site/src/components/sidebar/style.scss
+++ b/packages/edit-site/src/components/sidebar/style.scss
@@ -86,3 +86,52 @@
font-size: $helptext-font-size;
line-height: 1;
}
+
+
+.edit-site-global-styles-sidebar__border-box-control__popover,
+.edit-site-global-styles-sidebar__border-box-control__popover-top,
+.edit-site-global-styles-sidebar__border-box-control__popover-right,
+.edit-site-global-styles-sidebar__border-box-control__popover-bottom,
+.edit-site-global-styles-sidebar__border-box-control__popover-left {
+ .components-popover__content {
+ width: 282px;
+ }
+}
+
+$split-border-control-offset: 55px;
+
+@include break-medium() {
+ .edit-site-global-styles-sidebar__border-box-control__popover,
+ .edit-site-global-styles-sidebar__border-box-control__popover-left {
+ .components-popover__content {
+ margin-right: #{ $grid-unit-50 + $grid-unit-15 } !important;
+ }
+ }
+
+ .edit-site-global-styles-sidebar__border-box-control__popover-top,
+ .edit-site-global-styles-sidebar__border-box-control__popover-bottom {
+ .components-popover__content {
+ margin-right: #{ $grid-unit-50 + $grid-unit-15 + $split-border-control-offset } !important;
+ }
+ }
+
+ .edit-site-global-styles-sidebar__border-box-control__popover-right {
+ .components-popover__content {
+ margin-right: #{ $grid-unit-50 + $grid-unit-15 + ( $split-border-control-offset * 2 )} !important;
+ }
+ }
+
+ .edit-site-global-styles-sidebar__border-box-control__popover,
+ .edit-site-global-styles-sidebar__border-box-control__popover-top,
+ .edit-site-global-styles-sidebar__border-box-control__popover-right,
+ .edit-site-global-styles-sidebar__border-box-control__popover-bottom,
+ .edit-site-global-styles-sidebar__border-box-control__popover-left {
+ &.is-from-top .components-popover__content {
+ margin-top: #{ -($grid-unit-50 + $grid-unit-15) } !important;
+ }
+
+ &.is-from-bottom .components-popover__content {
+ margin-bottom: #{ -($grid-unit-50 + $grid-unit-15) } !important;
+ }
+ }
+}
From 344314a1bd5c2ddd17edd1f435f1d8aa590a90f9 Mon Sep 17 00:00:00 2001
From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com>
Date: Fri, 1 Apr 2022 14:21:01 +1000
Subject: [PATCH 06/27] Update border related properties in theme.json schema
---
schemas/json/theme.json | 83 ++++++++++++++++++++++++++++++++++++++++-
1 file changed, 82 insertions(+), 1 deletion(-)
diff --git a/schemas/json/theme.json b/schemas/json/theme.json
index ce276b8f330474..75e346e0982f5b 100644
--- a/schemas/json/theme.json
+++ b/schemas/json/theme.json
@@ -681,7 +681,32 @@
},
"radius": {
"description": "Sets the `border-radius` CSS property.",
- "type": "string"
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "object",
+ "properties": {
+ "topLeft": {
+ "description": "Sets the `border-top-left-radius` CSS property.",
+ "type": "string"
+ },
+ "topRight": {
+ "description": "Sets the `border-top-right-radius` CSS property.",
+ "type": "string"
+ },
+ "bottomLeft": {
+ "description": "Sets the `border-bottom-left-radius` CSS property.",
+ "type": "string"
+ },
+ "bottomRight": {
+ "description": "Sets the `border-bottom-right-radius` CSS property.",
+ "type": "string"
+ }
+ }
+ }
+ ]
},
"style": {
"description": "Sets the `border-style` CSS property.",
@@ -690,6 +715,62 @@
"width": {
"description": "Sets the `border-width` CSS property.",
"type": "string"
+ },
+ "top": {
+ "color": {
+ "description": "Sets the `border-top-color` CSS property.",
+ "type": "string"
+ },
+ "style": {
+ "description": "Sets the `border-top-style` CSS property.",
+ "type": "string"
+ },
+ "width": {
+ "description": "Sets the `border-top-width` CSS property.",
+ "type": "string"
+ }
+ },
+ "right": {
+ "color": {
+ "description": "Sets the `border-right-color` CSS property.",
+ "type": "string"
+ },
+ "style": {
+ "description": "Sets the `border-right-style` CSS property.",
+ "type": "string"
+ },
+ "width": {
+ "description": "Sets the `border-right-width` CSS property.",
+ "type": "string"
+ }
+ },
+ "bottom": {
+ "color": {
+ "description": "Sets the `border-bottom-color` CSS property.",
+ "type": "string"
+ },
+ "style": {
+ "description": "Sets the `border-bottom-style` CSS property.",
+ "type": "string"
+ },
+ "width": {
+ "description": "Sets the `border-bottom-width` CSS property.",
+ "type": "string"
+ }
+ },
+ "left": {
+ "color": {
+ "description": "Sets the `border-left-color` CSS property.",
+ "type": "string"
+ },
+ "style": {
+ "description": "Sets the `border-left-style` CSS property.",
+ "type": "string"
+ },
+ "width": {
+ "description": "Sets the `border-left-width` CSS property.",
+ "type": "string"
+ }
}
},
"additionalProperties": false
From 6fdef6f0d4571c36753e978576fbd064e6b7cf81 Mon Sep 17 00:00:00 2001
From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com>
Date: Fri, 1 Apr 2022 14:46:17 +1000
Subject: [PATCH 07/27] Update living theme.json md file
---
.../theme-json-reference/theme-json-living.md | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/docs/reference-guides/theme-json-reference/theme-json-living.md b/docs/reference-guides/theme-json-reference/theme-json-living.md
index fd4c9c7113900b..12e32f54b5b6a8 100644
--- a/docs/reference-guides/theme-json-reference/theme-json-living.md
+++ b/docs/reference-guides/theme-json-reference/theme-json-living.md
@@ -115,9 +115,13 @@ Border styles.
| Property | Type | Props |
| --- | --- |--- |
| color | string | |
-| radius | string | |
+| radius | string, object | topLeft, topRight, bottomLeft, bottomRight |
| style | string | |
| width | string | |
+| top | object | color, style, width |
+| right | object | color, style, width |
+| bottom | object | color, style, width |
+| left | object | color, style, width |
---
From 5ec71d4629b6a81ee0620b8d19f561baa30084ef Mon Sep 17 00:00:00 2001
From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com>
Date: Fri, 1 Apr 2022 15:08:07 +1000
Subject: [PATCH 08/27] Keep linter happy
---
.../theme-json-reference/theme-json-living.md | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/docs/reference-guides/theme-json-reference/theme-json-living.md b/docs/reference-guides/theme-json-reference/theme-json-living.md
index 12e32f54b5b6a8..632bf534e5cd4b 100644
--- a/docs/reference-guides/theme-json-reference/theme-json-living.md
+++ b/docs/reference-guides/theme-json-reference/theme-json-living.md
@@ -115,13 +115,13 @@ Border styles.
| Property | Type | Props |
| --- | --- |--- |
| color | string | |
-| radius | string, object | topLeft, topRight, bottomLeft, bottomRight |
+| radius | undefined | |
| style | string | |
| width | string | |
-| top | object | color, style, width |
-| right | object | color, style, width |
-| bottom | object | color, style, width |
-| left | object | color, style, width |
+| top | undefined | |
+| right | undefined | |
+| bottom | undefined | |
+| left | undefined | |
---
From 8672d6a86b0944494a8680082020104d1ee6cf9b Mon Sep 17 00:00:00 2001
From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com>
Date: Fri, 1 Apr 2022 17:28:39 +1000
Subject: [PATCH 09/27] Fix border skip serialization checks
---
packages/block-editor/src/hooks/border.js | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/packages/block-editor/src/hooks/border.js b/packages/block-editor/src/hooks/border.js
index 86820c202cf1e1..3de5f2718f663b 100644
--- a/packages/block-editor/src/hooks/border.js
+++ b/packages/block-editor/src/hooks/border.js
@@ -419,7 +419,7 @@ function addAttributes( settings ) {
function addSaveProps( props, blockType, attributes ) {
if (
! hasBorderSupport( blockType, 'color' ) ||
- shouldSkipSerialization( blockType )
+ shouldSkipSerialization( blockType, BORDER_SUPPORT_KEY, 'color' )
) {
return props;
}
@@ -489,7 +489,7 @@ function getSideBorderClasses( attributes ) {
function addEditProps( settings ) {
if (
! hasBorderSupport( settings, 'color' ) ||
- shouldSkipSerialization( settings )
+ shouldSkipSerialization( settings, BORDER_SUPPORT_KEY, 'color' )
) {
return settings;
}
@@ -524,7 +524,7 @@ export const withBorderColorPaletteStyles = createHigherOrderComponent(
if (
! hasBorderSupport( name, 'color' ) ||
- shouldSkipSerialization( name )
+ shouldSkipSerialization( name, BORDER_SUPPORT_KEY, 'color' )
) {
return ;
}
From eb92cbf0eb19ce61afd87bf7389165afe658a700 Mon Sep 17 00:00:00 2001
From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com>
Date: Mon, 4 Apr 2022 15:44:48 +1000
Subject: [PATCH 10/27] Fix typos
---
lib/block-supports/border.php | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/lib/block-supports/border.php b/lib/block-supports/border.php
index 888180bc18319c..7aa22486187b48 100644
--- a/lib/block-supports/border.php
+++ b/lib/block-supports/border.php
@@ -44,7 +44,7 @@ function gutenberg_register_border_support( $block_type ) {
* Adds CSS classes and inline styles for border styles to the incoming
* attributes array. This will be applied to the block markup in the front-end.
*
- * @param WP_Block_type $block_type Block type.
+ * @param WP_Block_Type $block_type Block type.
* @param array $block_attributes Block attributes.
*
* @return array Border CSS classes and inline styles.
@@ -159,7 +159,7 @@ function gutenberg_apply_border_support( $block_type, $block_attributes ) {
}
if ( $split_border_attributes['styles'] ) {
- $styles [] = $split_border_attributes['styles'];
+ $styles[] = $split_border_attributes['styles'];
}
}
}
@@ -188,7 +188,7 @@ function gutenberg_apply_border_support( $block_type, $block_attributes ) {
*
* @param string $side The side the classes and styles are being generated for.
* @param array $border Array containing border color, style, and width values.
- * @param WP_Block_type $block_type Block type.
+ * @param WP_Block_Type $block_type Block type.
*
* @return array CSS classes and longhand border styles for a single side.
*/
From 301f7556ef2a8f93492eb788a27e18bf122f13d5 Mon Sep 17 00:00:00 2001
From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com>
Date: Mon, 4 Apr 2022 15:50:59 +1000
Subject: [PATCH 11/27] Only implode classes and styles in primary application
function
---
lib/block-supports/border.php | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/lib/block-supports/border.php b/lib/block-supports/border.php
index 7aa22486187b48..c19884a0cc13c3 100644
--- a/lib/block-supports/border.php
+++ b/lib/block-supports/border.php
@@ -155,11 +155,11 @@ function gutenberg_apply_border_support( $block_type, $block_attributes ) {
$split_border_attributes = gutenberg_generate_individual_border_classes_and_styles( $side, $border, $block_type );
if ( $split_border_attributes['classes'] ) {
- $classes[] = $split_border_attributes['classes'];
+ $classes = array_merge( $classes, $split_border_attributes['classes'] );
}
if ( $split_border_attributes['styles'] ) {
- $styles[] = $split_border_attributes['styles'];
+ $styles = array_merge( $styles, $split_border_attributes['styles'] );
}
}
}
@@ -222,8 +222,8 @@ function gutenberg_generate_individual_border_classes_and_styles( $side, $border
}
return array(
- 'classes' => implode( ' ', $classes ),
- 'styles' => implode( ' ', $styles ),
+ 'classes' => $classes,
+ 'styles' => $styles,
);
}
From e3266b655cf7efbbd1e7573bfb41fe922adb91f8 Mon Sep 17 00:00:00 2001
From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com>
Date: Mon, 4 Apr 2022 15:52:57 +1000
Subject: [PATCH 12/27] Remove unneeded border radius control class
---
.../block-editor/src/components/border-radius-control/style.scss | 1 -
1 file changed, 1 deletion(-)
diff --git a/packages/block-editor/src/components/border-radius-control/style.scss b/packages/block-editor/src/components/border-radius-control/style.scss
index c6d5ebe277306c..a08e89250df2b2 100644
--- a/packages/block-editor/src/components/border-radius-control/style.scss
+++ b/packages/block-editor/src/components/border-radius-control/style.scss
@@ -11,7 +11,6 @@
align-items: flex-start;
> .components-unit-control-wrapper {
- // width: calc(50% - 26px);
width: 110px;
margin-bottom: 0;
margin-right: #{ $grid-unit-10 };
From 0b828e4426cd89b2e7ddea10a65e0245bf6413f4 Mon Sep 17 00:00:00 2001
From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com>
Date: Mon, 4 Apr 2022 16:06:16 +1000
Subject: [PATCH 13/27] Improve comment punctuation
---
packages/block-editor/src/hooks/border.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/block-editor/src/hooks/border.js b/packages/block-editor/src/hooks/border.js
index 3de5f2718f663b..bc622be1fd6c91 100644
--- a/packages/block-editor/src/hooks/border.js
+++ b/packages/block-editor/src/hooks/border.js
@@ -146,7 +146,7 @@ const getBorderObject = ( attributes, colors ) => {
return hydratedBorderStyles;
}
- // No named colors selected all color values if any should already be in
+ // No named colors selected. All color values, if any, should already be in
// the style's border object.
return borderStyles;
};
From ec3ffb9c3afcfd8e23fba72358b66c5ea803c0fa Mon Sep 17 00:00:00 2001
From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com>
Date: Mon, 4 Apr 2022 16:47:30 +1000
Subject: [PATCH 14/27] Prevent empty sideBorderColors object for split borders
---
packages/block-editor/src/hooks/border.js | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/packages/block-editor/src/hooks/border.js b/packages/block-editor/src/hooks/border.js
index bc622be1fd6c91..91618afa1b64be 100644
--- a/packages/block-editor/src/hooks/border.js
+++ b/packages/block-editor/src/hooks/border.js
@@ -250,7 +250,11 @@ export function BorderPanel( props ) {
setAttributes( {
style: newStyle,
borderColor: newBorderColor,
- sideBorderColors: newSideBorderColors,
+ sideBorderColors:
+ !! newSideBorderColors &&
+ Object.keys( newSideBorderColors ).length > 0
+ ? newSideBorderColors
+ : undefined,
} );
};
From 649f51e84cabc091e2459f33898c7f8db84099aa Mon Sep 17 00:00:00 2001
From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com>
Date: Mon, 4 Apr 2022 16:53:49 +1000
Subject: [PATCH 15/27] Add explanatory comment
---
packages/block-library/src/table/style.scss | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/packages/block-library/src/table/style.scss b/packages/block-library/src/table/style.scss
index 17278889448d5c..210b26fb732761 100644
--- a/packages/block-library/src/table/style.scss
+++ b/packages/block-library/src/table/style.scss
@@ -142,6 +142,11 @@
}
}
+ // Border support classes and styles are applied on the table block
+ // itself. This means that without the rule below every table row would
+ // have a bottom border matching the color of the table's border.
+ // This style gives the best visual appearance and most expected result
+ // until we can control individual table rows or cells via inner blocks.
tr:not(:last-child) {
border-bottom-color: currentColor;
}
From ad51e605501574458984e66f62af302562224be7 Mon Sep 17 00:00:00 2001
From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com>
Date: Mon, 4 Apr 2022 17:10:58 +1000
Subject: [PATCH 16/27] Enable alpha colors for borders
---
packages/block-editor/src/hooks/border.js | 1 +
packages/edit-site/src/components/global-styles/border-panel.js | 1 +
2 files changed, 2 insertions(+)
diff --git a/packages/block-editor/src/hooks/border.js b/packages/block-editor/src/hooks/border.js
index 91618afa1b64be..903de04547f041 100644
--- a/packages/block-editor/src/hooks/border.js
+++ b/packages/block-editor/src/hooks/border.js
@@ -273,6 +273,7 @@ export function BorderPanel( props ) {
>
Date: Thu, 7 Apr 2022 11:01:01 +1000
Subject: [PATCH 17/27] Fix whitespace typo
Co-authored-by: Ramon
---
lib/block-supports/border.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/block-supports/border.php b/lib/block-supports/border.php
index c19884a0cc13c3..0f868e2bfc4c56 100644
--- a/lib/block-supports/border.php
+++ b/lib/block-supports/border.php
@@ -148,7 +148,7 @@ function gutenberg_apply_border_support( $block_type, $block_attributes ) {
// Generate styles for individual border sides.
if ( $has_border_color_support || $has_border_width_support ) {
- foreach ( $sides as $side ) {
+ foreach ( $sides as $side ) {
$border = _wp_array_get( $block_attributes, array( 'style', 'border', $side ), false );
if ( is_array( $border ) && ! empty( $border ) ) {
From 516d819e743e7b5492571002f44ef55b73b3d766 Mon Sep 17 00:00:00 2001
From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com>
Date: Fri, 8 Apr 2022 14:20:42 +1000
Subject: [PATCH 18/27] Switch to preset approach for named colors
This change is approach was driven by a desire to not add any new block support related attributes and css classes. The prior implementation added a lot of additional CSS classes, bloated the block class lists, and would have needed migration and backwards compatibility support.
This new approach relies on being able to add inline styles with `var()` calls to use preset colors so switching themes still updates colors as expected and without the need to update blocks.
This approach is intended to be temporary until the new styles engine implements a cleaner result and the preset vars are required to be stored in the border color properties.
---
lib/block-supports/border.php | 40 ++---
.../class-wp-theme-json-gutenberg.php | 103 -------------
packages/block-editor/src/hooks/border.js | 143 ++++++------------
.../src/hooks/use-border-props.js | 24 +--
packages/block-library/src/common.scss | 8 +-
packages/block-library/src/search/index.php | 40 ++---
packages/block-library/src/table/style.scss | 12 +-
.../test/use-global-styles-output.js | 2 +-
.../src/components/global-styles/utils.js | 16 --
phpunit/block-supports/border-test.php | 39 +++--
phpunit/class-wp-theme-json-test.php | 10 +-
11 files changed, 105 insertions(+), 332 deletions(-)
diff --git a/lib/block-supports/border.php b/lib/block-supports/border.php
index 0f868e2bfc4c56..1c31e111516fef 100644
--- a/lib/block-supports/border.php
+++ b/lib/block-supports/border.php
@@ -32,12 +32,6 @@ function gutenberg_register_border_support( $block_type ) {
'type' => 'string',
);
}
-
- if ( $has_border_color_support && ! array_key_exists( 'sideBorderColors', $block_type->attributes ) ) {
- $block_type->attributes['sideBorderColors'] = array(
- 'type' => 'object',
- );
- }
}
/**
@@ -130,20 +124,6 @@ function gutenberg_apply_border_support( $block_type, $block_attributes ) {
$border_color = $block_attributes['style']['border']['color'];
$styles[] = sprintf( 'border-color: %s;', $border_color );
}
-
- // Apply individual border sides' named color classes.
- $has_named_side_border_colors = isset( $block_attributes['sideBorderColors'] );
-
- if ( $has_named_side_border_colors ) {
- foreach ( $sides as $side ) {
- $named_border_color = _wp_array_get( $block_attributes, array( 'sideBorderColors', $side ), false );
-
- if ( $named_border_color ) {
- $classes[] = sprintf( 'has-border-%s-color', $side );
- $classes[] = sprintf( 'has-%s-border-%s-color', $named_border_color, $side );
- }
- }
- }
}
// Generate styles for individual border sides.
@@ -212,13 +192,19 @@ function gutenberg_generate_individual_border_classes_and_styles( $side, $border
$styles[] = sprintf( 'border-%s-style: %s;', $side, $border['style'] );
}
- if (
- isset( $border['color'] ) &&
- null !== $border['color'] &&
- ! gutenberg_should_skip_block_supports_serialization( $block_type, '__experimentalBorder', 'color' )
- ) {
- $classes[] = sprintf( 'has-border-%s-color', $side );
- $styles [] = sprintf( 'border-%s-color: %s;', $side, $border['color'] );
+ $border_color = _wp_array_get( $border, array( 'color' ), null );
+ $named_border_color = _wp_array_get( $border, array( 'colorSlug' ), null );
+
+ $should_skip_color_serialization = gutenberg_should_skip_block_supports_serialization( $block_type, '__experimentalBorder', 'color' );
+
+ if ( $border_color && ! $should_skip_color_serialization ) {
+ $has_color_preset = strpos( $border_color, 'var:preset|color|' ) !== false;
+
+ if ( $has_color_preset && $named_border_color ) {
+ $styles [] = sprintf( 'border-%s-color: var(--wp--preset--color--%s);', $side, $named_border_color );
+ } else {
+ $styles [] = sprintf( 'border-%s-color: %s;', $side, $border['color'] );
+ }
}
return array(
diff --git a/lib/compat/wordpress-6.0/class-wp-theme-json-gutenberg.php b/lib/compat/wordpress-6.0/class-wp-theme-json-gutenberg.php
index 1669d1f972cdd6..668082863114fa 100644
--- a/lib/compat/wordpress-6.0/class-wp-theme-json-gutenberg.php
+++ b/lib/compat/wordpress-6.0/class-wp-theme-json-gutenberg.php
@@ -15,109 +15,6 @@
* @access private
*/
class WP_Theme_JSON_Gutenberg extends WP_Theme_JSON_5_9 {
- /**
- * Presets are a set of values that serve
- * to bootstrap some styles: colors, font sizes, etc.
- *
- * They are a unkeyed array of values such as:
- *
- * ```php
- * array(
- * array(
- * 'slug' => 'unique-name-within-the-set',
- * 'name' => 'Name for the UI',
- * => 'value'
- * ),
- * )
- * ```
- *
- * This contains the necessary metadata to process them:
- *
- * - path => where to find the preset within the settings section
- * - override => whether a theme preset with the same slug as a default preset
- * can override it
- * - value_key => the key that represents the value
- * - value_func => optionally, instead of value_key, a function to generate
- * the value that takes a preset as an argument
- * (either value_key or value_func should be present)
- * - css_vars => template string to use in generating the CSS Custom Property.
- * Example output: "--wp--preset--duotone--blue: " will generate as many CSS Custom Properties as presets defined
- * substituting the $slug for the slug's value for each preset value.
- * - classes => array containing a structure with the classes to
- * generate for the presets, where for each array item
- * the key is the class name and the value the property name.
- * The "$slug" substring will be replaced by the slug of each preset.
- * For example:
- * 'classes' => array(
- * '.has-$slug-color' => 'color',
- * '.has-$slug-background-color' => 'background-color',
- * '.has-$slug-border-color' => 'border-color',
- * )
- * - properties => array of CSS properties to be used by kses to
- * validate the content of each preset
- * by means of the remove_insecure_properties method.
- */
- const PRESETS_METADATA = array(
- array(
- 'path' => array( 'color', 'palette' ),
- 'prevent_override' => array( 'color', 'defaultPalette' ),
- 'use_default_presets' => array( 'color', 'defaultPalette' ),
- 'use_default_names' => false,
- 'value_key' => 'color',
- 'css_vars' => '--wp--preset--color--$slug',
- 'classes' => array(
- '.has-$slug-color' => 'color',
- '.has-$slug-background-color' => 'background-color',
- '.has-$slug-border-color' => 'border-color',
- '.has-$slug-border-top-color' => 'border-top-color',
- '.has-$slug-border-right-color' => 'border-right-color',
- '.has-$slug-border-bottom-color' => 'border-bottom-color',
- '.has-$slug-border-left-color' => 'border-left-color',
- ),
- 'properties' => array( 'color', 'background-color', 'border-color' ),
- ),
- array(
- 'path' => array( 'color', 'gradients' ),
- 'prevent_override' => array( 'color', 'defaultGradients' ),
- 'use_default_presets' => array( 'color', 'defaultGradients' ),
- 'use_default_names' => false,
- 'value_key' => 'gradient',
- 'css_vars' => '--wp--preset--gradient--$slug',
- 'classes' => array( '.has-$slug-gradient-background' => 'background' ),
- 'properties' => array( 'background' ),
- ),
- array(
- 'path' => array( 'color', 'duotone' ),
- 'prevent_override' => array( 'color', 'defaultDuotone' ),
- 'use_default_presets' => array( 'color', 'defaultDuotone' ),
- 'use_default_names' => false,
- 'value_func' => 'gutenberg_get_duotone_filter_property',
- 'css_vars' => '--wp--preset--duotone--$slug',
- 'classes' => array(),
- 'properties' => array( 'filter' ),
- ),
- array(
- 'path' => array( 'typography', 'fontSizes' ),
- 'prevent_override' => false,
- 'use_default_presets' => true,
- 'use_default_names' => true,
- 'value_key' => 'size',
- 'css_vars' => '--wp--preset--font-size--$slug',
- 'classes' => array( '.has-$slug-font-size' => 'font-size' ),
- 'properties' => array( 'font-size' ),
- ),
- array(
- 'path' => array( 'typography', 'fontFamilies' ),
- 'prevent_override' => false,
- 'use_default_presets' => true,
- 'use_default_names' => false,
- 'value_key' => 'fontFamily',
- 'css_vars' => '--wp--preset--font-family--$slug',
- 'classes' => array( '.has-$slug-font-family' => 'font-family' ),
- 'properties' => array( 'font-family' ),
- ),
- );
-
/**
* Metadata for style properties.
*
diff --git a/packages/block-editor/src/hooks/border.js b/packages/block-editor/src/hooks/border.js
index 903de04547f041..11b4569a6ccc1e 100644
--- a/packages/block-editor/src/hooks/border.js
+++ b/packages/block-editor/src/hooks/border.js
@@ -37,12 +37,8 @@ export const BORDER_SUPPORT_KEY = '__experimentalBorder';
const borderSides = [ 'top', 'right', 'bottom', 'left' ];
const hasBorderValue = ( props ) => {
- const { borderColor, sideBorderColors, style } = props.attributes;
- return (
- isDefinedBorder( style?.border ) ||
- !! borderColor ||
- !! sideBorderColors
- );
+ const { borderColor, style } = props.attributes;
+ return isDefinedBorder( style?.border ) || !! borderColor;
};
// The border color, style, and width are omitted so they get undefined. The
@@ -51,7 +47,6 @@ const resetBorder = ( { attributes = {}, setAttributes } ) => {
const { style } = attributes;
setAttributes( {
borderColor: undefined,
- sideBorderColors: undefined,
style: {
...style,
border: cleanEmptyObject( {
@@ -64,7 +59,6 @@ const resetBorder = ( { attributes = {}, setAttributes } ) => {
const resetBorderFilter = ( newAttributes ) => ( {
...newAttributes,
borderColor: undefined,
- sideBorderColors: undefined,
style: {
...newAttributes.style,
border: {
@@ -110,7 +104,7 @@ export const getMultiOriginColor = ( { colors, namedColor, customColor } ) => {
};
const getBorderObject = ( attributes, colors ) => {
- const { borderColor, sideBorderColors, style } = attributes;
+ const { borderColor, style } = attributes;
const { border: borderStyles } = style || {};
// If we have a named color for a flat border. Fetch that color object and
@@ -124,31 +118,30 @@ const getBorderObject = ( attributes, colors ) => {
return color ? { ...borderStyles, color } : borderStyles;
}
+ // Individual side border color slugs are stored within the border style
+ // object. If we don't have a border styles object we have nothing further
+ // to hydrate.
+ if ( ! borderStyles ) {
+ return borderStyles;
+ }
+
// If we have named colors for the individual side borders, retrieve their
// related color objects and apply the real color values to the split
// border objects.
- if ( sideBorderColors ) {
- const hydratedBorderStyles = { ...borderStyles };
-
- Object.entries( sideBorderColors ).forEach(
- ( [ side, namedColor ] ) => {
- const { color } = getMultiOriginColor( { colors, namedColor } );
-
- if ( color ) {
- hydratedBorderStyles[ side ] = {
- ...hydratedBorderStyles[ side ],
- color,
- };
- }
- }
- );
-
- return hydratedBorderStyles;
- }
+ const hydratedBorderStyles = { ...borderStyles };
+ borderSides.forEach( ( side ) => {
+ const namedColor = hydratedBorderStyles[ side ]?.colorSlug;
+ const { color } = getMultiOriginColor( { colors, namedColor } );
+
+ if ( color ) {
+ hydratedBorderStyles[ side ] = {
+ ...hydratedBorderStyles[ side ],
+ color,
+ };
+ }
+ } );
- // No named colors selected. All color values, if any, should already be in
- // the style's border object.
- return borderStyles;
+ return hydratedBorderStyles;
};
export function BorderPanel( props ) {
@@ -193,15 +186,14 @@ export function BorderPanel( props ) {
let newBorderStyles = { ...newBorder };
let newBorderColor;
- let newSideBorderColors;
// Split borders will store their named colors within the
- // `sideBorderColors` block attribute.
+ // `colorSlug` property of their respective style object.
if ( hasSplitBorders( newBorder ) ) {
// For each side check if the side has a color value set
// If so, determine if it belongs to a named color, in which case
- // saved that named color to the block attribute and clear the
- // style object's color property to avoid the inline style.
+ // saved that named color to the `colorSlug` property and clear the
+ // color property.
//
// This deliberately overwrites `newBorderStyles` to avoid mutating
// the passed object which causes problems otherwise.
@@ -211,7 +203,6 @@ export function BorderPanel( props ) {
bottom: { ...newBorder.bottom },
left: { ...newBorder.left },
};
- newSideBorderColors = {};
borderSides.forEach( ( side ) => {
if ( newBorder[ side ]?.color ) {
@@ -221,10 +212,13 @@ export function BorderPanel( props ) {
} );
if ( colorObject.slug ) {
- // If we have a named color, set the sides named color
- // attribute and clear the saved style objects color value.
- newSideBorderColors[ side ] = colorObject.slug;
- newBorderStyles[ side ].color = undefined;
+ // If we have a named color, set the sides color slug
+ // and update the saved style object's color value to
+ // reflect the matching preset CSS var.
+ newBorderStyles[ side ].colorSlug = colorObject.slug;
+ newBorderStyles[
+ side
+ ].color = `var:preset|color|${ colorObject.slug }`;
}
}
} );
@@ -250,11 +244,6 @@ export function BorderPanel( props ) {
setAttributes( {
style: newStyle,
borderColor: newBorderColor,
- sideBorderColors:
- !! newSideBorderColors &&
- Object.keys( newSideBorderColors ).length > 0
- ? newSideBorderColors
- : undefined,
} );
};
@@ -381,33 +370,19 @@ function addAttributes( settings ) {
return settings;
}
- // Allow blocks to specify border color values if needed.
- const { attributes } = settings;
-
- // Skip any adjustments if block already defines both border color
- // attributes to set defaults etc.
- if ( attributes.borderColor && attributes.sideBorderColors ) {
+ // Allow blocks to specify default value if needed.
+ if ( settings.attributes.borderColor ) {
return settings;
}
- // If we are missing border color attribute definition, add it.
- if ( ! attributes.borderColor ) {
- return {
- ...settings,
- attributes: {
- ...attributes,
- borderColor: { type: 'string' },
- },
- };
- }
-
- // We are missing attribute for side border colors, add it to existing
- // attribute definitions.
+ // Add new borderColor attribute to block settings.
return {
...settings,
attributes: {
- ...attributes,
- sideBorderColors: { type: 'object' },
+ ...settings.attributes,
+ borderColor: {
+ type: 'string',
+ },
},
};
}
@@ -454,35 +429,9 @@ export function getBorderClasses( attributes ) {
return classnames( {
'has-border-color': borderColor || style?.border?.color,
[ borderColorClass ]: !! borderColorClass,
- ...getSideBorderClasses( attributes ),
} );
}
-/**
- * Generates a collection of CSS classes for the block's current border color
- * selections. The results are intended to be further processed via a call
- * through `classnames()`.
- *
- * @param {Object} attributes Block attributes.
- * @return {Object} CSS classes for side border colors.
- */
-function getSideBorderClasses( attributes ) {
- const { sideBorderColors, style } = attributes;
-
- return borderSides.reduce( ( classes, side ) => {
- const color = sideBorderColors?.[ side ];
- const hasColor = color || style?.border?.[ side ]?.color;
- const baseClassName = `border-${ side }-color`;
- const colorClass = getColorClassName( baseClassName, color );
-
- return {
- ...classes,
- [ `has-${ baseClassName }` ]: hasColor,
- [ colorClass ]: !! colorClass,
- };
- }, {} );
-}
-
/**
* Filters the registered block settings to apply border color styles and
* classnames to the block edit wrapper.
@@ -524,7 +473,7 @@ function addEditProps( settings ) {
export const withBorderColorPaletteStyles = createHigherOrderComponent(
( BlockListBlock ) => ( props ) => {
const { name, attributes } = props;
- const { borderColor, sideBorderColors } = attributes;
+ const { borderColor, style } = attributes;
const { colors } = useMultipleOriginColorsAndGradients();
if (
@@ -540,19 +489,19 @@ export const withBorderColorPaletteStyles = createHigherOrderComponent(
} );
const { color: borderTopColor } = getMultiOriginColor( {
colors,
- namedColor: sideBorderColors?.top,
+ namedColor: style?.border?.top?.colorSlug,
} );
const { color: borderRightColor } = getMultiOriginColor( {
colors,
- namedColor: sideBorderColors?.right,
+ namedColor: style?.border?.right?.colorSlug,
} );
const { color: borderBottomColor } = getMultiOriginColor( {
colors,
- namedColor: sideBorderColors?.bottom,
+ namedColor: style?.border?.bottom?.colorSlug,
} );
const { color: borderLeftColor } = getMultiOriginColor( {
colors,
- namedColor: sideBorderColors?.left,
+ namedColor: style?.border?.left?.colorSlug,
} );
const extraStyles = {
@@ -566,8 +515,8 @@ export const withBorderColorPaletteStyles = createHigherOrderComponent(
wrapperProps = {
...props.wrapperProps,
style: {
- ...extraStyles,
...props.wrapperProps?.style,
+ ...extraStyles,
},
};
diff --git a/packages/block-editor/src/hooks/use-border-props.js b/packages/block-editor/src/hooks/use-border-props.js
index 6a4ba6feddc693..0afe1bb70cb5a8 100644
--- a/packages/block-editor/src/hooks/use-border-props.js
+++ b/packages/block-editor/src/hooks/use-border-props.js
@@ -40,7 +40,7 @@ export function getBorderClassesAndStyles( attributes ) {
export function useBorderProps( attributes ) {
const { colors } = useMultipleOriginColorsAndGradients();
const borderProps = getBorderClassesAndStyles( attributes );
- const { borderColor, sideBorderColors } = attributes;
+ const { borderColor } = attributes;
// Force inline styles to apply named border colors when themes do not load
// their color stylesheets in the editor.
@@ -53,27 +53,5 @@ export function useBorderProps( attributes ) {
borderProps.style.borderColor = borderColorObject.color;
}
- if ( ! sideBorderColors ) {
- return borderProps;
- }
-
- const sides = {
- top: 'borderTopColor',
- right: 'borderRightColor',
- bottom: 'borderBottomColor',
- left: 'borderLeftColor',
- };
-
- Object.entries( sides ).forEach( ( [ side, property ] ) => {
- if ( sideBorderColors[ side ] ) {
- const { color } = getMultiOriginColor( {
- colors,
- namedColor: sideBorderColors[ side ],
- } );
-
- borderProps.style[ property ] = color;
- }
- } );
-
return borderProps;
}
diff --git a/packages/block-library/src/common.scss b/packages/block-library/src/common.scss
index f78efad2ac52b4..5819a43c93ed52 100644
--- a/packages/block-library/src/common.scss
+++ b/packages/block-library/src/common.scss
@@ -116,16 +116,16 @@
html :where(.has-border-color) {
border-style: solid;
}
-html :where(.has-border-top-color) {
+html :where([style*="border-top-color"]) {
border-top-style: solid;
}
-html :where(.has-border-right-color) {
+html :where([style*="border-right-color"]) {
border-right-style: solid;
}
-html :where(.has-border-bottom-color) {
+html :where([style*="border-bottom-color"]) {
border-bottom-style: solid;
}
-html :where(.has-border-left-color) {
+html :where([style*="border-left-color"]) {
border-left-style: solid;
}
diff --git a/packages/block-library/src/search/index.php b/packages/block-library/src/search/index.php
index 61a92e15eef381..d836558f9a3b25 100644
--- a/packages/block-library/src/search/index.php
+++ b/packages/block-library/src/search/index.php
@@ -201,6 +201,16 @@ function apply_block_core_search_border_style( $attributes, $property, $side, &$
return;
}
+ if ( 'color' === $property && $side ) {
+ $slug_path = array( 'style', 'border', $side, 'colorSlug' );
+ $named_color_value = _wp_array_get( $attributes, $slug_path, false );
+ $has_color_preset = strpos( $value, 'var:preset|color|' ) !== false;
+
+ if ( $has_color_preset && $named_color_value ) {
+ $value = sprintf( 'var(--wp--preset--color--%s)', $named_color_value );
+ }
+ }
+
$property_suffix = $side ? sprintf( '%s-%s', $side, $property ) : $property;
if ( $is_button_inside ) {
@@ -364,39 +374,9 @@ function get_border_color_classes_for_block_core_search( $attributes ) {
$border_color_classes[] = sprintf( 'has-%s-border-color', esc_attr( $attributes['borderColor'] ) );
}
- $border_color_classes[] = get_side_border_color_classes_for_block_core_search( $attributes, 'top' );
- $border_color_classes[] = get_side_border_color_classes_for_block_core_search( $attributes, 'right' );
- $border_color_classes[] = get_side_border_color_classes_for_block_core_search( $attributes, 'bottom' );
- $border_color_classes[] = get_side_border_color_classes_for_block_core_search( $attributes, 'left' );
-
return implode( ' ', $border_color_classes );
}
-/**
- * Generates required CSS classes for individual border side color selections.
- *
- * @param array $attributes The block attributes.
- * @param string $side Border side i.e. `top`, `right`, `bottom`, or `left`.
- *
- * @return string
- */
-function get_side_border_color_classes_for_block_core_search( $attributes, $side ) {
- $classes = array();
-
- $has_custom_border_color = ! empty( $attributes['style']['border'][ $side ]['color'] );
- $has_named_border_color = ! empty( $attributes['sideBorderColors'][ $side ] );
-
- if ( $has_custom_border_color || $has_named_border_color ) {
- $classes[] = sprintf( 'has-border-%s-color', esc_attr( $side ) );
- }
-
- if ( $has_named_border_color ) {
- $classes[] = sprintf( 'has-%s-border-%s-color', esc_attr( $attributes['sideBorderColors'][ $side ] ), esc_attr( $side ) );
- }
-
- return implode( ' ', $classes );
-}
-
/**
* Returns color classnames depending on whether there are named or custom text and background colors.
*
diff --git a/packages/block-library/src/table/style.scss b/packages/block-library/src/table/style.scss
index 210b26fb732761..52ff4f640aac8e 100644
--- a/packages/block-library/src/table/style.scss
+++ b/packages/block-library/src/table/style.scss
@@ -112,7 +112,7 @@
}
}
- .has-border-top-color {
+ table[style*="border-top-color"] {
> *,
tr:first-child {
border-top-color: inherit;
@@ -122,8 +122,12 @@
border-top-color: inherit;
}
}
+
+ tr:not(:first-child) {
+ border-top-color: currentColor;
+ }
}
- .has-border-right-color {
+ table[style*="border-right-color"] {
> *,
tr,
th,
@@ -131,7 +135,7 @@
border-right-color: inherit;
}
}
- .has-border-bottom-color {
+ table[style*="border-bottom-color"] {
> *,
tr:last-child {
border-bottom-color: inherit;
@@ -151,7 +155,7 @@
border-bottom-color: currentColor;
}
}
- .has-border-left-color {
+ table[style*="border-left-color"] {
> *,
tr,
th,
diff --git a/packages/edit-site/src/components/global-styles/test/use-global-styles-output.js b/packages/edit-site/src/components/global-styles/test/use-global-styles-output.js
index f329b51bb68740..5d1c45af104a71 100644
--- a/packages/edit-site/src/components/global-styles/test/use-global-styles-output.js
+++ b/packages/edit-site/src/components/global-styles/test/use-global-styles-output.js
@@ -377,7 +377,7 @@ describe( 'global styles renderer', () => {
};
expect( toStyles( tree, blockSelectors ) ).toEqual(
- '.wp-site-blocks > * { margin-top: 0; margin-bottom: 0; }.wp-site-blocks > * + * { margin-top: var( --wp--style--block-gap ); }body{background-color: red;margin: 10px;padding: 10px;}h1{font-size: 42px;}.wp-block-group{margin-top: 10px;margin-right: 20px;margin-bottom: 30px;margin-left: 40px;padding-top: 11px;padding-right: 22px;padding-bottom: 33px;padding-left: 44px;}h1,h2,h3,h4,h5,h6{color: orange;}h1 a,h2 a,h3 a,h4 a,h5 a,h6 a{color: hotpink;}.has-white-color{color: var(--wp--preset--color--white) !important;}.has-white-background-color{background-color: var(--wp--preset--color--white) !important;}.has-white-border-color{border-color: var(--wp--preset--color--white) !important;}.has-white-border-top-color{border-top-color: var(--wp--preset--color--white) !important;}.has-white-border-right-color{border-right-color: var(--wp--preset--color--white) !important;}.has-white-border-bottom-color{border-bottom-color: var(--wp--preset--color--white) !important;}.has-white-border-left-color{border-left-color: var(--wp--preset--color--white) !important;}.has-black-color{color: var(--wp--preset--color--black) !important;}.has-black-background-color{background-color: var(--wp--preset--color--black) !important;}.has-black-border-color{border-color: var(--wp--preset--color--black) !important;}.has-black-border-top-color{border-top-color: var(--wp--preset--color--black) !important;}.has-black-border-right-color{border-right-color: var(--wp--preset--color--black) !important;}.has-black-border-bottom-color{border-bottom-color: var(--wp--preset--color--black) !important;}.has-black-border-left-color{border-left-color: var(--wp--preset--color--black) !important;}h1.has-blue-color,h2.has-blue-color,h3.has-blue-color,h4.has-blue-color,h5.has-blue-color,h6.has-blue-color{color: var(--wp--preset--color--blue) !important;}h1.has-blue-background-color,h2.has-blue-background-color,h3.has-blue-background-color,h4.has-blue-background-color,h5.has-blue-background-color,h6.has-blue-background-color{background-color: var(--wp--preset--color--blue) !important;}h1.has-blue-border-color,h2.has-blue-border-color,h3.has-blue-border-color,h4.has-blue-border-color,h5.has-blue-border-color,h6.has-blue-border-color{border-color: var(--wp--preset--color--blue) !important;}h1.has-blue-border-top-color,h2.has-blue-border-top-color,h3.has-blue-border-top-color,h4.has-blue-border-top-color,h5.has-blue-border-top-color,h6.has-blue-border-top-color{border-top-color: var(--wp--preset--color--blue) !important;}h1.has-blue-border-right-color,h2.has-blue-border-right-color,h3.has-blue-border-right-color,h4.has-blue-border-right-color,h5.has-blue-border-right-color,h6.has-blue-border-right-color{border-right-color: var(--wp--preset--color--blue) !important;}h1.has-blue-border-bottom-color,h2.has-blue-border-bottom-color,h3.has-blue-border-bottom-color,h4.has-blue-border-bottom-color,h5.has-blue-border-bottom-color,h6.has-blue-border-bottom-color{border-bottom-color: var(--wp--preset--color--blue) !important;}h1.has-blue-border-left-color,h2.has-blue-border-left-color,h3.has-blue-border-left-color,h4.has-blue-border-left-color,h5.has-blue-border-left-color,h6.has-blue-border-left-color{border-left-color: var(--wp--preset--color--blue) !important;}'
+ '.wp-site-blocks > * { margin-top: 0; margin-bottom: 0; }.wp-site-blocks > * + * { margin-top: var( --wp--style--block-gap ); }body{background-color: red;margin: 10px;padding: 10px;}h1{font-size: 42px;}.wp-block-group{margin-top: 10px;margin-right: 20px;margin-bottom: 30px;margin-left: 40px;padding-top: 11px;padding-right: 22px;padding-bottom: 33px;padding-left: 44px;}h1,h2,h3,h4,h5,h6{color: orange;}h1 a,h2 a,h3 a,h4 a,h5 a,h6 a{color: hotpink;}.has-white-color{color: var(--wp--preset--color--white) !important;}.has-white-background-color{background-color: var(--wp--preset--color--white) !important;}.has-white-border-color{border-color: var(--wp--preset--color--white) !important;}.has-black-color{color: var(--wp--preset--color--black) !important;}.has-black-background-color{background-color: var(--wp--preset--color--black) !important;}.has-black-border-color{border-color: var(--wp--preset--color--black) !important;}h1.has-blue-color,h2.has-blue-color,h3.has-blue-color,h4.has-blue-color,h5.has-blue-color,h6.has-blue-color{color: var(--wp--preset--color--blue) !important;}h1.has-blue-background-color,h2.has-blue-background-color,h3.has-blue-background-color,h4.has-blue-background-color,h5.has-blue-background-color,h6.has-blue-background-color{background-color: var(--wp--preset--color--blue) !important;}h1.has-blue-border-color,h2.has-blue-border-color,h3.has-blue-border-color,h4.has-blue-border-color,h5.has-blue-border-color,h6.has-blue-border-color{border-color: var(--wp--preset--color--blue) !important;}'
);
} );
} );
diff --git a/packages/edit-site/src/components/global-styles/utils.js b/packages/edit-site/src/components/global-styles/utils.js
index 80009ffa0cac8b..8f815fd3e3daae 100644
--- a/packages/edit-site/src/components/global-styles/utils.js
+++ b/packages/edit-site/src/components/global-styles/utils.js
@@ -36,22 +36,6 @@ export const PRESET_METADATA = [
classSuffix: 'border-color',
propertyName: 'border-color',
},
- {
- classSuffix: 'border-top-color',
- propertyName: 'border-top-color',
- },
- {
- classSuffix: 'border-right-color',
- propertyName: 'border-right-color',
- },
- {
- classSuffix: 'border-bottom-color',
- propertyName: 'border-bottom-color',
- },
- {
- classSuffix: 'border-left-color',
- propertyName: 'border-left-color',
- },
],
},
{
diff --git a/phpunit/block-supports/border-test.php b/phpunit/block-supports/border-test.php
index 81ad14fba4d9cf..25ca1b9cc19f64 100644
--- a/phpunit/block-supports/border-test.php
+++ b/phpunit/block-supports/border-test.php
@@ -41,9 +41,6 @@ private function register_bordered_block_with_support( $block_name, $supports =
'borderColor' => array(
'type' => 'string',
),
- 'sideBorderColors' => array(
- 'type' => 'object',
- ),
'style' => array(
'type' => 'object',
),
@@ -285,7 +282,6 @@ function test_split_borders_with_custom_colors() {
);
$actual = gutenberg_apply_border_support( $block_type, $block_attrs );
$expected = array(
- 'class' => 'has-border-top-color has-border-right-color has-border-bottom-color has-border-left-color',
'style' => 'border-top-width: 2px; border-top-style: dashed; border-top-color: #72aee6; border-right-width: 0.25rem; border-right-style: dotted; border-right-color: #e65054; border-bottom-width: 0.5em; border-bottom-style: solid; border-bottom-color: #007017; border-left-width: 1px; border-left-style: solid; border-left-color: #f6f7f7;',
);
@@ -376,7 +372,6 @@ function test_split_borders_with_skipped_individual_feature_serialization() {
);
$actual = gutenberg_apply_border_support( $block_type, $block_attrs );
$expected = array(
- 'class' => 'has-border-top-color has-border-right-color has-border-bottom-color has-border-left-color',
'style' => 'border-top-color: #72aee6; border-right-color: #e65054; border-bottom-color: #007017; border-left-color: #f6f7f7;',
);
@@ -414,7 +409,6 @@ function test_partial_split_borders() {
);
$actual = gutenberg_apply_border_support( $block_type, $block_attrs );
$expected = array(
- 'class' => 'has-border-top-color has-border-right-color',
'style' => 'border-top-width: 2px; border-top-style: dashed; border-top-color: #72aee6; border-right-width: 0.25rem; border-right-color: #e65054; border-left-style: solid;',
);
@@ -433,37 +427,38 @@ function test_split_borders_with_named_colors() {
)
);
$block_attrs = array(
- 'sideBorderColors' => array(
- 'top' => 'red',
- 'right' => 'green',
- 'bottom' => 'blue',
- 'left' => 'yellow',
- ),
'style' => array(
'border' => array(
'top' => array(
- 'width' => '2px',
- 'style' => 'dashed',
+ 'width' => '2px',
+ 'style' => 'dashed',
+ 'color' => 'var:preset|color|red',
+ 'colorSlug' => 'red',
),
'right' => array(
- 'width' => '0.25rem',
- 'style' => 'dotted',
+ 'width' => '0.25rem',
+ 'style' => 'dotted',
+ 'color' => 'var:preset|color|green',
+ 'colorSlug' => 'green',
),
'bottom' => array(
- 'width' => '0.5em',
- 'style' => 'solid',
+ 'width' => '0.5em',
+ 'style' => 'solid',
+ 'color' => 'var:preset|color|blue',
+ 'colorSlug' => 'blue',
),
'left' => array(
- 'width' => '1px',
- 'style' => 'solid',
+ 'width' => '1px',
+ 'style' => 'solid',
+ 'color' => 'var:preset|color|yellow',
+ 'colorSlug' => 'yellow',
),
),
),
);
$actual = gutenberg_apply_border_support( $block_type, $block_attrs );
$expected = array(
- 'class' => 'has-border-top-color has-red-border-top-color has-border-right-color has-green-border-right-color has-border-bottom-color has-blue-border-bottom-color has-border-left-color has-yellow-border-left-color',
- 'style' => 'border-top-width: 2px; border-top-style: dashed; border-right-width: 0.25rem; border-right-style: dotted; border-bottom-width: 0.5em; border-bottom-style: solid; border-left-width: 1px; border-left-style: solid;',
+ 'style' => 'border-top-width: 2px; border-top-style: dashed; border-top-color: var(--wp--preset--color--red); border-right-width: 0.25rem; border-right-style: dotted; border-right-color: var(--wp--preset--color--green); border-bottom-width: 0.5em; border-bottom-style: solid; border-bottom-color: var(--wp--preset--color--blue); border-left-width: 1px; border-left-style: solid; border-left-color: var(--wp--preset--color--yellow);',
);
$this->assertSame( $expected, $actual );
diff --git a/phpunit/class-wp-theme-json-test.php b/phpunit/class-wp-theme-json-test.php
index 986600d6f75686..89240870399dc9 100644
--- a/phpunit/class-wp-theme-json-test.php
+++ b/phpunit/class-wp-theme-json-test.php
@@ -529,7 +529,7 @@ function test_get_stylesheet() {
$variables = 'body{--wp--preset--color--grey: grey;--wp--preset--font-family--small: 14px;--wp--preset--font-family--big: 41px;}.wp-block-group{--wp--custom--base-font: 16;--wp--custom--line-height--small: 1.2;--wp--custom--line-height--medium: 1.4;--wp--custom--line-height--large: 1.8;}';
$styles = 'body { margin: 0; }body{color: var(--wp--preset--color--grey);}.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }.wp-site-blocks > * { margin-block-start: 0; margin-block-end: 0; }.wp-site-blocks > * + * { margin-block-start: var( --wp--style--block-gap ); }a{background-color: #333;color: #111;}.wp-block-group{border-radius: 10px;padding: 24px;}.wp-block-group a{color: #111;}h1,h2,h3,h4,h5,h6{color: #123456;}h1 a,h2 a,h3 a,h4 a,h5 a,h6 a{background-color: #333;color: #111;font-size: 60px;}.wp-block-post-date{color: #123456;}.wp-block-post-date a{background-color: #777;color: #555;}.wp-block-image{border-top-left-radius: 10px;border-bottom-right-radius: 1em;margin-bottom: 30px;}';
- $presets = '.has-grey-color{color: var(--wp--preset--color--grey) !important;}.has-grey-background-color{background-color: var(--wp--preset--color--grey) !important;}.has-grey-border-color{border-color: var(--wp--preset--color--grey) !important;}.has-grey-border-top-color{border-top-color: var(--wp--preset--color--grey) !important;}.has-grey-border-right-color{border-right-color: var(--wp--preset--color--grey) !important;}.has-grey-border-bottom-color{border-bottom-color: var(--wp--preset--color--grey) !important;}.has-grey-border-left-color{border-left-color: var(--wp--preset--color--grey) !important;}.has-small-font-family{font-family: var(--wp--preset--font-family--small) !important;}.has-big-font-family{font-family: var(--wp--preset--font-family--big) !important;}';
+ $presets = '.has-grey-color{color: var(--wp--preset--color--grey) !important;}.has-grey-background-color{background-color: var(--wp--preset--color--grey) !important;}.has-grey-border-color{border-color: var(--wp--preset--color--grey) !important;}.has-small-font-family{font-family: var(--wp--preset--font-family--small) !important;}.has-big-font-family{font-family: var(--wp--preset--font-family--big) !important;}';
$all = $variables . $styles . $presets;
$this->assertEquals( $all, $theme_json->get_stylesheet() );
$this->assertEquals( $styles, $theme_json->get_stylesheet( array( 'styles' ) ) );
@@ -559,7 +559,7 @@ function test_get_stylesheet_preset_classes_work_with_compounded_selectors() {
);
$this->assertEquals(
- 'h1.has-white-color,h2.has-white-color,h3.has-white-color,h4.has-white-color,h5.has-white-color,h6.has-white-color{color: var(--wp--preset--color--white) !important;}h1.has-white-background-color,h2.has-white-background-color,h3.has-white-background-color,h4.has-white-background-color,h5.has-white-background-color,h6.has-white-background-color{background-color: var(--wp--preset--color--white) !important;}h1.has-white-border-color,h2.has-white-border-color,h3.has-white-border-color,h4.has-white-border-color,h5.has-white-border-color,h6.has-white-border-color{border-color: var(--wp--preset--color--white) !important;}h1.has-white-border-top-color,h2.has-white-border-top-color,h3.has-white-border-top-color,h4.has-white-border-top-color,h5.has-white-border-top-color,h6.has-white-border-top-color{border-top-color: var(--wp--preset--color--white) !important;}h1.has-white-border-right-color,h2.has-white-border-right-color,h3.has-white-border-right-color,h4.has-white-border-right-color,h5.has-white-border-right-color,h6.has-white-border-right-color{border-right-color: var(--wp--preset--color--white) !important;}h1.has-white-border-bottom-color,h2.has-white-border-bottom-color,h3.has-white-border-bottom-color,h4.has-white-border-bottom-color,h5.has-white-border-bottom-color,h6.has-white-border-bottom-color{border-bottom-color: var(--wp--preset--color--white) !important;}h1.has-white-border-left-color,h2.has-white-border-left-color,h3.has-white-border-left-color,h4.has-white-border-left-color,h5.has-white-border-left-color,h6.has-white-border-left-color{border-left-color: var(--wp--preset--color--white) !important;}',
+ 'h1.has-white-color,h2.has-white-color,h3.has-white-color,h4.has-white-color,h5.has-white-color,h6.has-white-color{color: var(--wp--preset--color--white) !important;}h1.has-white-background-color,h2.has-white-background-color,h3.has-white-background-color,h4.has-white-background-color,h5.has-white-background-color,h6.has-white-background-color{background-color: var(--wp--preset--color--white) !important;}h1.has-white-border-color,h2.has-white-border-color,h3.has-white-border-color,h4.has-white-border-color,h5.has-white-border-color,h6.has-white-border-color{border-color: var(--wp--preset--color--white) !important;}',
$theme_json->get_stylesheet( array( 'presets' ) )
);
}
@@ -595,7 +595,7 @@ function test_get_stylesheet_preset_rules_come_after_block_rules() {
);
$styles = 'body { margin: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }.wp-block-group{color: red;}';
- $presets = '.wp-block-group.has-grey-color{color: var(--wp--preset--color--grey) !important;}.wp-block-group.has-grey-background-color{background-color: var(--wp--preset--color--grey) !important;}.wp-block-group.has-grey-border-color{border-color: var(--wp--preset--color--grey) !important;}.wp-block-group.has-grey-border-top-color{border-top-color: var(--wp--preset--color--grey) !important;}.wp-block-group.has-grey-border-right-color{border-right-color: var(--wp--preset--color--grey) !important;}.wp-block-group.has-grey-border-bottom-color{border-bottom-color: var(--wp--preset--color--grey) !important;}.wp-block-group.has-grey-border-left-color{border-left-color: var(--wp--preset--color--grey) !important;}';
+ $presets = '.wp-block-group.has-grey-color{color: var(--wp--preset--color--grey) !important;}.wp-block-group.has-grey-background-color{background-color: var(--wp--preset--color--grey) !important;}.wp-block-group.has-grey-border-color{border-color: var(--wp--preset--color--grey) !important;}';
$variables = '.wp-block-group{--wp--preset--color--grey: grey;}';
$all = $variables . $styles . $presets;
$this->assertEquals( $all, $theme_json->get_stylesheet() );
@@ -637,7 +637,7 @@ function test_get_stylesheet_generates_proper_classes_and_css_vars_from_slugs()
);
$this->assertEquals(
- '.has-grey-color{color: var(--wp--preset--color--grey) !important;}.has-dark-grey-color{color: var(--wp--preset--color--dark-grey) !important;}.has-light-grey-color{color: var(--wp--preset--color--light-grey) !important;}.has-white-2-black-color{color: var(--wp--preset--color--white-2-black) !important;}.has-grey-background-color{background-color: var(--wp--preset--color--grey) !important;}.has-dark-grey-background-color{background-color: var(--wp--preset--color--dark-grey) !important;}.has-light-grey-background-color{background-color: var(--wp--preset--color--light-grey) !important;}.has-white-2-black-background-color{background-color: var(--wp--preset--color--white-2-black) !important;}.has-grey-border-color{border-color: var(--wp--preset--color--grey) !important;}.has-dark-grey-border-color{border-color: var(--wp--preset--color--dark-grey) !important;}.has-light-grey-border-color{border-color: var(--wp--preset--color--light-grey) !important;}.has-white-2-black-border-color{border-color: var(--wp--preset--color--white-2-black) !important;}.has-grey-border-top-color{border-top-color: var(--wp--preset--color--grey) !important;}.has-dark-grey-border-top-color{border-top-color: var(--wp--preset--color--dark-grey) !important;}.has-light-grey-border-top-color{border-top-color: var(--wp--preset--color--light-grey) !important;}.has-white-2-black-border-top-color{border-top-color: var(--wp--preset--color--white-2-black) !important;}.has-grey-border-right-color{border-right-color: var(--wp--preset--color--grey) !important;}.has-dark-grey-border-right-color{border-right-color: var(--wp--preset--color--dark-grey) !important;}.has-light-grey-border-right-color{border-right-color: var(--wp--preset--color--light-grey) !important;}.has-white-2-black-border-right-color{border-right-color: var(--wp--preset--color--white-2-black) !important;}.has-grey-border-bottom-color{border-bottom-color: var(--wp--preset--color--grey) !important;}.has-dark-grey-border-bottom-color{border-bottom-color: var(--wp--preset--color--dark-grey) !important;}.has-light-grey-border-bottom-color{border-bottom-color: var(--wp--preset--color--light-grey) !important;}.has-white-2-black-border-bottom-color{border-bottom-color: var(--wp--preset--color--white-2-black) !important;}.has-grey-border-left-color{border-left-color: var(--wp--preset--color--grey) !important;}.has-dark-grey-border-left-color{border-left-color: var(--wp--preset--color--dark-grey) !important;}.has-light-grey-border-left-color{border-left-color: var(--wp--preset--color--light-grey) !important;}.has-white-2-black-border-left-color{border-left-color: var(--wp--preset--color--white-2-black) !important;}',
+ '.has-grey-color{color: var(--wp--preset--color--grey) !important;}.has-dark-grey-color{color: var(--wp--preset--color--dark-grey) !important;}.has-light-grey-color{color: var(--wp--preset--color--light-grey) !important;}.has-white-2-black-color{color: var(--wp--preset--color--white-2-black) !important;}.has-grey-background-color{background-color: var(--wp--preset--color--grey) !important;}.has-dark-grey-background-color{background-color: var(--wp--preset--color--dark-grey) !important;}.has-light-grey-background-color{background-color: var(--wp--preset--color--light-grey) !important;}.has-white-2-black-background-color{background-color: var(--wp--preset--color--white-2-black) !important;}.has-grey-border-color{border-color: var(--wp--preset--color--grey) !important;}.has-dark-grey-border-color{border-color: var(--wp--preset--color--dark-grey) !important;}.has-light-grey-border-color{border-color: var(--wp--preset--color--light-grey) !important;}.has-white-2-black-border-color{border-color: var(--wp--preset--color--white-2-black) !important;}',
$theme_json->get_stylesheet( array( 'presets' ) )
);
$this->assertEquals(
@@ -680,7 +680,7 @@ public function test_get_stylesheet_preset_values_are_marked_as_important() {
);
$this->assertEquals(
- 'body{--wp--preset--color--grey: grey;}body { margin: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }p{background-color: blue;color: red;font-size: 12px;line-height: 1.3;}.has-grey-color{color: var(--wp--preset--color--grey) !important;}.has-grey-background-color{background-color: var(--wp--preset--color--grey) !important;}.has-grey-border-color{border-color: var(--wp--preset--color--grey) !important;}.has-grey-border-top-color{border-top-color: var(--wp--preset--color--grey) !important;}.has-grey-border-right-color{border-right-color: var(--wp--preset--color--grey) !important;}.has-grey-border-bottom-color{border-bottom-color: var(--wp--preset--color--grey) !important;}.has-grey-border-left-color{border-left-color: var(--wp--preset--color--grey) !important;}',
+ 'body{--wp--preset--color--grey: grey;}body { margin: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }p{background-color: blue;color: red;font-size: 12px;line-height: 1.3;}.has-grey-color{color: var(--wp--preset--color--grey) !important;}.has-grey-background-color{background-color: var(--wp--preset--color--grey) !important;}.has-grey-border-color{border-color: var(--wp--preset--color--grey) !important;}',
$theme_json->get_stylesheet()
);
}
From 2c8fcca3dbb80ecce472301a452a5e3e718fd320 Mon Sep 17 00:00:00 2001
From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com>
Date: Fri, 8 Apr 2022 15:15:26 +1000
Subject: [PATCH 19/27] Fix linting errors
---
phpunit/block-supports/border-test.php | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/phpunit/block-supports/border-test.php b/phpunit/block-supports/border-test.php
index 25ca1b9cc19f64..76d4ed2a936317 100644
--- a/phpunit/block-supports/border-test.php
+++ b/phpunit/block-supports/border-test.php
@@ -38,10 +38,10 @@ private function register_bordered_block_with_support( $block_name, $supports =
array(
'api_version' => 2,
'attributes' => array(
- 'borderColor' => array(
+ 'borderColor' => array(
'type' => 'string',
),
- 'style' => array(
+ 'style' => array(
'type' => 'object',
),
),
@@ -427,7 +427,7 @@ function test_split_borders_with_named_colors() {
)
);
$block_attrs = array(
- 'style' => array(
+ 'style' => array(
'border' => array(
'top' => array(
'width' => '2px',
From 5e700d160c2f2628fd72aee5e82b05f452915ce6 Mon Sep 17 00:00:00 2001
From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com>
Date: Fri, 8 Apr 2022 15:41:48 +1000
Subject: [PATCH 20/27] Clean up split border style generation
---
lib/block-supports/border.php | 25 +++++++------------------
1 file changed, 7 insertions(+), 18 deletions(-)
diff --git a/lib/block-supports/border.php b/lib/block-supports/border.php
index 1c31e111516fef..39bfe16e39900a 100644
--- a/lib/block-supports/border.php
+++ b/lib/block-supports/border.php
@@ -132,15 +132,8 @@ function gutenberg_apply_border_support( $block_type, $block_attributes ) {
$border = _wp_array_get( $block_attributes, array( 'style', 'border', $side ), false );
if ( is_array( $border ) && ! empty( $border ) ) {
- $split_border_attributes = gutenberg_generate_individual_border_classes_and_styles( $side, $border, $block_type );
-
- if ( $split_border_attributes['classes'] ) {
- $classes = array_merge( $classes, $split_border_attributes['classes'] );
- }
-
- if ( $split_border_attributes['styles'] ) {
- $styles = array_merge( $styles, $split_border_attributes['styles'] );
- }
+ $split_border_styles = gutenberg_generate_individual_border_classes_and_styles( $side, $border, $block_type );
+ $styles = array_merge( $styles, $split_border_styles );
}
}
}
@@ -160,21 +153,20 @@ function gutenberg_apply_border_support( $block_type, $block_attributes ) {
}
/**
- * Generates CSS classes and longhand CSS styles for an individual side border.
+ * Generates longhand CSS styles for an individual side border.
*
* If some values are omitted from the border configuration, using shorthand
* styles would lead to `initial` values being used instead of the more
* desirable inherited values. This could also lead to browser inconsistencies.
*
- * @param string $side The side the classes and styles are being generated for.
+ * @param string $side The side the styles are being generated for.
* @param array $border Array containing border color, style, and width values.
* @param WP_Block_Type $block_type Block type.
*
- * @return array CSS classes and longhand border styles for a single side.
+ * @return array Longhand CSS border styles for a single side.
*/
function gutenberg_generate_individual_border_classes_and_styles( $side, $border, $block_type ) {
- $classes = array();
- $styles = array();
+ $styles = array();
if (
isset( $border['width'] ) &&
@@ -207,10 +199,7 @@ function gutenberg_generate_individual_border_classes_and_styles( $side, $border
}
}
- return array(
- 'classes' => $classes,
- 'styles' => $styles,
- );
+ return $styles;
}
/**
From 4c1d414857ea6d32c69e4404b1732596a27bf2a1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9?= <583546+oandregal@users.noreply.github.com>
Date: Fri, 8 Apr 2022 09:08:12 +0200
Subject: [PATCH 21/27] Take info from style.border.side.color value instead of
colorSlug
---
lib/block-supports/border.php | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/lib/block-supports/border.php b/lib/block-supports/border.php
index 39bfe16e39900a..b81f7c8e2bbb06 100644
--- a/lib/block-supports/border.php
+++ b/lib/block-supports/border.php
@@ -184,16 +184,16 @@ function gutenberg_generate_individual_border_classes_and_styles( $side, $border
$styles[] = sprintf( 'border-%s-style: %s;', $side, $border['style'] );
}
- $border_color = _wp_array_get( $border, array( 'color' ), null );
- $named_border_color = _wp_array_get( $border, array( 'colorSlug' ), null );
+ $border_color = _wp_array_get( $border, array( 'color' ), null );
$should_skip_color_serialization = gutenberg_should_skip_block_supports_serialization( $block_type, '__experimentalBorder', 'color' );
if ( $border_color && ! $should_skip_color_serialization ) {
$has_color_preset = strpos( $border_color, 'var:preset|color|' ) !== false;
-
- if ( $has_color_preset && $named_border_color ) {
- $styles [] = sprintf( 'border-%s-color: var(--wp--preset--color--%s);', $side, $named_border_color );
+
+ if ( $has_color_preset ) {
+ $named_color_slug = substr( $border_color, strrpos( $border_color, '|' ) + 1 );
+ $styles [] = sprintf( 'border-%s-color: var(--wp--preset--color--%s);', $side, $named_color_slug );
} else {
$styles [] = sprintf( 'border-%s-color: %s;', $side, $border['color'] );
}
From 5f74e1bb25dc8f23120fe2f5b0b7af299d7407d6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9?= <583546+oandregal@users.noreply.github.com>
Date: Fri, 8 Apr 2022 09:42:07 +0200
Subject: [PATCH 22/27] Take info from style.border.side.color value instead of
colorSlug (client hook)
---
packages/block-editor/src/hooks/border.js | 40 ++++++++++++++---------
1 file changed, 24 insertions(+), 16 deletions(-)
diff --git a/packages/block-editor/src/hooks/border.js b/packages/block-editor/src/hooks/border.js
index 11b4569a6ccc1e..be5c03b1a8c0e1 100644
--- a/packages/block-editor/src/hooks/border.js
+++ b/packages/block-editor/src/hooks/border.js
@@ -130,10 +130,14 @@ const getBorderObject = ( attributes, colors ) => {
// border objects.
const hydratedBorderStyles = { ...borderStyles };
borderSides.forEach( ( side ) => {
- const namedColor = hydratedBorderStyles[ side ]?.colorSlug;
- const { color } = getMultiOriginColor( { colors, namedColor } );
-
- if ( color ) {
+ const colorSlug = getColorSlugFromVariable(
+ hydratedBorderStyles[ side ]?.color
+ );
+ if ( colorSlug ) {
+ const { color } = getMultiOriginColor( {
+ colors,
+ namedColor: colorSlug,
+ } );
hydratedBorderStyles[ side ] = {
...hydratedBorderStyles[ side ],
color,
@@ -144,6 +148,14 @@ const getBorderObject = ( attributes, colors ) => {
return hydratedBorderStyles;
};
+function getColorSlugFromVariable( value ) {
+ const namedColor = /var:preset\|color\|(.+)/.exec( value );
+ if ( namedColor && namedColor[ 1 ] ) {
+ return namedColor[ 1 ];
+ }
+ return null;
+}
+
export function BorderPanel( props ) {
const { attributes, clientId, setAttributes } = props;
const { style } = attributes;
@@ -187,13 +199,10 @@ export function BorderPanel( props ) {
let newBorderStyles = { ...newBorder };
let newBorderColor;
- // Split borders will store their named colors within the
- // `colorSlug` property of their respective style object.
if ( hasSplitBorders( newBorder ) ) {
// For each side check if the side has a color value set
// If so, determine if it belongs to a named color, in which case
- // saved that named color to the `colorSlug` property and clear the
- // color property.
+ // we update the color property.
//
// This deliberately overwrites `newBorderStyles` to avoid mutating
// the passed object which causes problems otherwise.
@@ -212,10 +221,6 @@ export function BorderPanel( props ) {
} );
if ( colorObject.slug ) {
- // If we have a named color, set the sides color slug
- // and update the saved style object's color value to
- // reflect the matching preset CSS var.
- newBorderStyles[ side ].colorSlug = colorObject.slug;
newBorderStyles[
side
].color = `var:preset|color|${ colorObject.slug }`;
@@ -489,19 +494,22 @@ export const withBorderColorPaletteStyles = createHigherOrderComponent(
} );
const { color: borderTopColor } = getMultiOriginColor( {
colors,
- namedColor: style?.border?.top?.colorSlug,
+ namedColor: getColorSlugFromVariable( style?.border?.top?.color ),
} );
const { color: borderRightColor } = getMultiOriginColor( {
colors,
- namedColor: style?.border?.right?.colorSlug,
+ namedColor: getColorSlugFromVariable( style?.border?.right?.color ),
} );
+
const { color: borderBottomColor } = getMultiOriginColor( {
colors,
- namedColor: style?.border?.bottom?.colorSlug,
+ namedColor: getColorSlugFromVariable(
+ style?.border?.bottom?.color
+ ),
} );
const { color: borderLeftColor } = getMultiOriginColor( {
colors,
- namedColor: style?.border?.left?.colorSlug,
+ namedColor: getColorSlugFromVariable( style?.border?.left?.color ),
} );
const extraStyles = {
From 06de97676defb440c6a1748598628747af03a9c4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9?= <583546+oandregal@users.noreply.github.com>
Date: Fri, 8 Apr 2022 09:44:59 +0200
Subject: [PATCH 23/27] Take info from style.border.side.color (search block)
---
packages/block-library/src/search/index.php | 10 ++++------
1 file changed, 4 insertions(+), 6 deletions(-)
diff --git a/packages/block-library/src/search/index.php b/packages/block-library/src/search/index.php
index d836558f9a3b25..8bf1e5e5ee2db1 100644
--- a/packages/block-library/src/search/index.php
+++ b/packages/block-library/src/search/index.php
@@ -202,12 +202,10 @@ function apply_block_core_search_border_style( $attributes, $property, $side, &$
}
if ( 'color' === $property && $side ) {
- $slug_path = array( 'style', 'border', $side, 'colorSlug' );
- $named_color_value = _wp_array_get( $attributes, $slug_path, false );
- $has_color_preset = strpos( $value, 'var:preset|color|' ) !== false;
-
- if ( $has_color_preset && $named_color_value ) {
- $value = sprintf( 'var(--wp--preset--color--%s)', $named_color_value );
+ $has_color_preset = strpos( $value, 'var:preset|color|' ) !== false;
+ if ( $has_color_preset ) {
+ $named_color_value = substr( $value, strrpos( $value, '|' ) + 1 );
+ $value = sprintf( 'var(--wp--preset--color--%s)', $named_color_value );
}
}
From f260382189e731c420207491356048e428dab166 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9?= <583546+oandregal@users.noreply.github.com>
Date: Fri, 8 Apr 2022 09:48:02 +0200
Subject: [PATCH 24/27] Fix PHP lint issues
---
lib/block-supports/border.php | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/lib/block-supports/border.php b/lib/block-supports/border.php
index b81f7c8e2bbb06..9953f7fe2fefbc 100644
--- a/lib/block-supports/border.php
+++ b/lib/block-supports/border.php
@@ -190,10 +190,9 @@ function gutenberg_generate_individual_border_classes_and_styles( $side, $border
if ( $border_color && ! $should_skip_color_serialization ) {
$has_color_preset = strpos( $border_color, 'var:preset|color|' ) !== false;
-
if ( $has_color_preset ) {
$named_color_slug = substr( $border_color, strrpos( $border_color, '|' ) + 1 );
- $styles [] = sprintf( 'border-%s-color: var(--wp--preset--color--%s);', $side, $named_color_slug );
+ $styles [] = sprintf( 'border-%s-color: var(--wp--preset--color--%s);', $side, $named_color_slug );
} else {
$styles [] = sprintf( 'border-%s-color: %s;', $side, $border['color'] );
}
From 461717d1150e68a461890c0996a52711927da992 Mon Sep 17 00:00:00 2001
From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com>
Date: Tue, 12 Apr 2022 16:51:04 +1000
Subject: [PATCH 25/27] Remove obsolete colorSlug attributes from border test
---
phpunit/block-supports/border-test.php | 28 +++++++++++---------------
1 file changed, 12 insertions(+), 16 deletions(-)
diff --git a/phpunit/block-supports/border-test.php b/phpunit/block-supports/border-test.php
index 76d4ed2a936317..3ec9bbe4f53874 100644
--- a/phpunit/block-supports/border-test.php
+++ b/phpunit/block-supports/border-test.php
@@ -430,28 +430,24 @@ function test_split_borders_with_named_colors() {
'style' => array(
'border' => array(
'top' => array(
- 'width' => '2px',
- 'style' => 'dashed',
- 'color' => 'var:preset|color|red',
- 'colorSlug' => 'red',
+ 'width' => '2px',
+ 'style' => 'dashed',
+ 'color' => 'var:preset|color|red',
),
'right' => array(
- 'width' => '0.25rem',
- 'style' => 'dotted',
- 'color' => 'var:preset|color|green',
- 'colorSlug' => 'green',
+ 'width' => '0.25rem',
+ 'style' => 'dotted',
+ 'color' => 'var:preset|color|green',
),
'bottom' => array(
- 'width' => '0.5em',
- 'style' => 'solid',
- 'color' => 'var:preset|color|blue',
- 'colorSlug' => 'blue',
+ 'width' => '0.5em',
+ 'style' => 'solid',
+ 'color' => 'var:preset|color|blue',
),
'left' => array(
- 'width' => '1px',
- 'style' => 'solid',
- 'color' => 'var:preset|color|yellow',
- 'colorSlug' => 'yellow',
+ 'width' => '1px',
+ 'style' => 'solid',
+ 'color' => 'var:preset|color|yellow',
),
),
),
From 84f4f4b296f44e0843000a3e64fece3f02eda771 Mon Sep 17 00:00:00 2001
From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com>
Date: Wed, 13 Apr 2022 12:05:53 +1000
Subject: [PATCH 26/27] Prevent editor forced shorthand styles applying
incorrect styles
---
packages/block-library/src/table/edit.js | 11 ++++++++++-
packages/block-library/src/table/editor.scss | 13 +++++++++++++
2 files changed, 23 insertions(+), 1 deletion(-)
diff --git a/packages/block-library/src/table/edit.js b/packages/block-library/src/table/edit.js
index c60a810be52179..70134a955b34b1 100644
--- a/packages/block-library/src/table/edit.js
+++ b/packages/block-library/src/table/edit.js
@@ -25,6 +25,7 @@ import {
TextControl,
ToggleControl,
ToolbarDropdownMenu,
+ __experimentalHasSplitBorders as hasSplitBorders,
} from '@wordpress/components';
import {
alignLeft,
@@ -477,7 +478,15 @@ function TableEdit( {
className={ classnames(
colorProps.className,
borderProps.className,
- { 'has-fixed-layout': hasFixedLayout }
+ {
+ 'has-fixed-layout': hasFixedLayout,
+ // This is required in the editor only to overcome
+ // the fact the editor rewrites individual border
+ // widths into a shorthand format.
+ 'has-individual-borders': hasSplitBorders(
+ attributes?.style?.border
+ ),
+ }
) }
style={ { ...colorProps.style, ...borderProps.style } }
>
diff --git a/packages/block-library/src/table/editor.scss b/packages/block-library/src/table/editor.scss
index 03003030833796..ca6cfc3d0da8cb 100644
--- a/packages/block-library/src/table/editor.scss
+++ b/packages/block-library/src/table/editor.scss
@@ -43,6 +43,19 @@
figcaption {
@include caption-style-theme();
}
+
+ // This is only required in the editor to overcome the fact the editor
+ // rewrites border width styles into shorthand.
+ table.has-individual-borders {
+ > *,
+ tr,
+ th,
+ td {
+ border-width: $border-width;
+ border-style: solid;
+ border-color: currentColor;
+ }
+ }
}
.blocks-table__placeholder-form.blocks-table__placeholder-form {
From ea485d5dfaea5d325e42251bc76f1599c5563261 Mon Sep 17 00:00:00 2001
From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com>
Date: Wed, 13 Apr 2022 14:23:03 +1000
Subject: [PATCH 27/27] Make skip serialization check consistent
---
lib/block-supports/border.php | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/lib/block-supports/border.php b/lib/block-supports/border.php
index 9953f7fe2fefbc..d4d18fd6cb687e 100644
--- a/lib/block-supports/border.php
+++ b/lib/block-supports/border.php
@@ -186,9 +186,10 @@ function gutenberg_generate_individual_border_classes_and_styles( $side, $border
$border_color = _wp_array_get( $border, array( 'color' ), null );
- $should_skip_color_serialization = gutenberg_should_skip_block_supports_serialization( $block_type, '__experimentalBorder', 'color' );
-
- if ( $border_color && ! $should_skip_color_serialization ) {
+ if (
+ $border_color &&
+ ! gutenberg_should_skip_block_supports_serialization( $block_type, '__experimentalBorder', 'color' )
+ ) {
$has_color_preset = strpos( $border_color, 'var:preset|color|' ) !== false;
if ( $has_color_preset ) {
$named_color_slug = substr( $border_color, strrpos( $border_color, '|' ) + 1 );