From 50504fdceb96401a76624d9be44347bc31b08f59 Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Wed, 17 Mar 2021 18:39:55 +1000 Subject: [PATCH 01/13] Add border color, style and width support --- lib/block-supports/border.php | 110 ++++++++-- lib/class-wp-theme-json.php | 4 + lib/experimental-default-theme.json | 5 +- lib/global-styles.php | 1 + .../components/border-style-control/index.js | 64 ++++++ .../border-style-control/style.scss | 14 ++ packages/block-editor/src/components/index.js | 1 + .../block-editor/src/hooks/border-color.js | 197 ++++++++++++++++++ .../block-editor/src/hooks/border-style.js | 70 +++++++ .../block-editor/src/hooks/border-width.js | 79 +++++++ packages/block-editor/src/hooks/border.js | 33 ++- packages/block-editor/src/hooks/index.js | 1 + packages/block-editor/src/hooks/test/style.js | 10 +- packages/block-editor/src/style.scss | 1 + phpunit/class-wp-theme-json-test.php | 10 +- 15 files changed, 563 insertions(+), 37 deletions(-) create mode 100644 packages/block-editor/src/components/border-style-control/index.js create mode 100644 packages/block-editor/src/components/border-style-control/style.scss create mode 100644 packages/block-editor/src/hooks/border-color.js create mode 100644 packages/block-editor/src/hooks/border-style.js create 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 0a29d3753cecc..ee5b2dfbc0360 100644 --- a/lib/block-supports/border.php +++ b/lib/block-supports/border.php @@ -6,26 +6,36 @@ */ /** - * Registers the style attribute used by the border feature if needed for block types that - * support borders. + * Registers the style attribute used by the border feature if needed for block + * types that support borders. * * @param WP_Block_Type $block_type Block Type. */ function gutenberg_register_border_support( $block_type ) { - // Determine border related features supported. - // Border width, style etc can be added in the future. - $has_border_radius_support = gutenberg_block_has_support( $block_type, array( '__experimentalBorder', 'radius' ), false ); + // Determine if any border related features are supported. + $has_border_color_support = gutenberg_has_border_support( $block_type, 'color' ); + $has_border_support = + gutenberg_has_border_support( $block_type, 'radius' ) || + gutenberg_has_border_support( $block_type, 'style' ) || + gutenberg_has_border_support( $block_type, 'width' ) || + $has_border_color_support; // Setup attributes and styles within that if needed. if ( ! $block_type->attributes ) { $block_type->attributes = array(); } - if ( $has_border_radius_support && ! array_key_exists( 'style', $block_type->attributes ) ) { + if ( $has_border_support && ! array_key_exists( 'style', $block_type->attributes ) ) { $block_type->attributes['style'] = array( 'type' => 'object', ); } + + if ( $has_border_color_support && ! array_key_exists( 'borderColor', $block_type->attributes ) ) { + $block_type->attributes['borderColor'] = array( + 'type' => 'string', + ); + } } /** @@ -38,33 +48,61 @@ function gutenberg_register_border_support( $block_type ) { * @return array Border CSS classes and inline styles. */ function gutenberg_apply_border_support( $block_type, $block_attributes ) { - $border_support = _wp_array_get( $block_type->supports, array( '__experimentalBorder' ), false ); - - if ( - is_array( $border_support ) && - array_key_exists( '__experimentalSkipSerialization', $border_support ) && - $border_support['__experimentalSkipSerialization'] - ) { + if ( gutenberg_skip_border_serialization( $block_type ) ) { return array(); } - // Arrays used to ease addition of further border related features in future. - $styles = array(); + $classes = array(); + $styles = array(); - // Border Radius. - $has_border_radius_support = gutenberg_block_has_support( $block_type, array( '__experimentalBorder', 'radius' ), false ); - if ( $has_border_radius_support ) { + // Border radius. + if ( gutenberg_has_border_support( $block_type, 'radius' ) ) { if ( isset( $block_attributes['style']['border']['radius'] ) ) { $border_radius = (int) $block_attributes['style']['border']['radius']; $styles[] = sprintf( 'border-radius: %dpx;', $border_radius ); } } - // Border width, style etc can be added here. + // Border style. + if ( gutenberg_has_border_support( $block_type, 'style' ) ) { + if ( isset( $block_attributes['style']['border']['style'] ) ) { + $border_style = $block_attributes['style']['border']['style']; + $styles[] = sprintf( 'border-style: %s;', $border_style ); + } + } + + // Border width. + if ( gutenberg_has_border_support( $block_type, 'width' ) ) { + if ( isset( $block_attributes['style']['border']['width'] ) ) { + $border_width = intval( $block_attributes['style']['border']['width'] ); + $styles[] = sprintf( 'border-width: %dpx;', $border_width ); + } + } + + // Border color. + if ( gutenberg_has_border_support( $block_type, 'color' ) ) { + $has_named_border_color = array_key_exists( 'borderColor', $block_attributes ); + $has_custom_border_color = isset( $block_attributes['style']['border']['color'] ); + + if ( $has_named_border_color || $has_custom_border_color ) { + $classes[] = 'has-border-color'; + } + + if ( $has_named_border_color ) { + $classes[] = sprintf( 'has-%s-border-color', $block_attributes['borderColor'] ); + } elseif ( $has_custom_border_color ) { + $border_color = $block_attributes['style']['border']['color']; + $styles[] = sprintf( 'border-color: %s;', $border_color ); + } + } // Collect classes and styles. $attributes = array(); + if ( ! empty( $classes ) ) { + $attributes['class'] = implode( ' ', $classes ); + } + if ( ! empty( $styles ) ) { $attributes['style'] = implode( ' ', $styles ); } @@ -72,6 +110,40 @@ function gutenberg_apply_border_support( $block_type, $block_attributes ) { return $attributes; } +/** + * Checks whether serialization of the current block's border properties should + * occur. + * + * @param WP_Block_type $block_type Block type. + * + * @return boolean + */ +function gutenberg_skip_border_serialization( $block_type ) { + $border_support = _wp_array_get( $block_type->supports, array( '__experimentalBorder' ), false ); + + return is_array( $border_support ) && + array_key_exists( '__experimentalSkipSerialization', $border_support ) && + $border_support['__experimentalSkipSerialization']; +} + +/** + * Checks whether the current block type supports the feature requested. + * + * @param WP_Block_Type $block_type Block type to check for support. + * @param string $feature Name of the feature to check support for. + * @param mixed $default Fallback value for feature support, defaults to false. + * + * @return boolean Whether or not the feature is supported. + */ +function gutenberg_has_border_support( $block_type, $feature, $default = false ) { + $block_support = false; + if ( property_exists( $block_type, 'supports' ) ) { + $block_support = _wp_array_get( $block_type->supports, array( '__experimentalBorder' ), $default ); + } + + return true === $block_support || ( is_array( $block_support ) && _wp_array_get( $block_support, array( $feature ), false ) ); +} + // Register the block support. WP_Block_Supports::get_instance()->register( 'border', diff --git a/lib/class-wp-theme-json.php b/lib/class-wp-theme-json.php index 147725d66a19b..3a7ccbd7c8c2c 100644 --- a/lib/class-wp-theme-json.php +++ b/lib/class-wp-theme-json.php @@ -198,6 +198,10 @@ class WP_Theme_JSON { 'class_suffix' => 'background-color', 'property_name' => 'background-color', ), + array( + 'class_suffix' => 'border-color', + 'property_name' => 'border-color', + ), ), ), array( diff --git a/lib/experimental-default-theme.json b/lib/experimental-default-theme.json index da910ca84747a..dea9d96966d34 100644 --- a/lib/experimental-default-theme.json +++ b/lib/experimental-default-theme.json @@ -171,7 +171,10 @@ "units": [ "px", "em", "rem", "vh", "vw" ] }, "border": { - "customRadius": false + "customColor": false, + "customRadius": false, + "customStyle": false, + "customWidth": false } } } diff --git a/lib/global-styles.php b/lib/global-styles.php index 4bef8e7b93751..c3e7a6da9cfcf 100644 --- a/lib/global-styles.php +++ b/lib/global-styles.php @@ -260,6 +260,7 @@ function gutenberg_global_styles_include_support_for_wp_variables( $allow_css, $ $allowed_preset_attributes = array( 'background', 'background-color', + 'border-color', 'color', 'font-family', 'font-size', diff --git a/packages/block-editor/src/components/border-style-control/index.js b/packages/block-editor/src/components/border-style-control/index.js new file mode 100644 index 0000000000000..ee2ad2482cd3c --- /dev/null +++ b/packages/block-editor/src/components/border-style-control/index.js @@ -0,0 +1,64 @@ +/** + * WordPress dependencies + */ +import { CustomSelectControl } from '@wordpress/components'; +import { __ } from '@wordpress/i18n'; + +const DEFAULT_STYLE = { + key: 'default', + name: __( 'Default' ), + style: { borderStyle: undefined }, +}; + +const BORDER_STYLES = [ + DEFAULT_STYLE, + { + key: 'none', + name: __( 'None' ), + style: { borderStyle: 'none' }, + }, + { + key: 'solid', + name: __( 'Solid' ), + style: { borderStyle: 'solid' }, + }, + { + key: 'dashed', + name: __( 'Dashed' ), + style: { borderStyle: 'dashed' }, + }, + { + key: 'dotted', + name: __( 'Dotted' ), + style: { borderStyle: 'dotted' }, + }, +]; + +/** + * Control to display border style options. + * + * @param {Object} props Component props. + * @param {Object} props.onChange Handler for changing border style selection. + * @param {Object} props.value Currently selected border style value. + * + * @return {WPElement} Custom border style select control. + */ +export default function BorderStyleControl( { onChange, value } ) { + const style = BORDER_STYLES.find( ( option ) => option.key === value ); + + return ( +
+ + selectedItem.key === 'default' + ? onChange( undefined ) + : onChange( selectedItem.key ) + } + /> +
+ ); +} diff --git a/packages/block-editor/src/components/border-style-control/style.scss b/packages/block-editor/src/components/border-style-control/style.scss new file mode 100644 index 0000000000000..827f31bca718c --- /dev/null +++ b/packages/block-editor/src/components/border-style-control/style.scss @@ -0,0 +1,14 @@ +.components-border-style-control__select { + margin-bottom: 24px; + + button { + width: 100%; + } + + ul { + li, + li:last-child { + margin: 6px; + } + } +} diff --git a/packages/block-editor/src/components/index.js b/packages/block-editor/src/components/index.js index ac7471317acbe..dc88ae5a36deb 100644 --- a/packages/block-editor/src/components/index.js +++ b/packages/block-editor/src/components/index.js @@ -32,6 +32,7 @@ export { BlockVerticalAlignmentToolbar, BlockVerticalAlignmentControl, } from './block-vertical-alignment-control'; +export { default as __experimentalBorderStyleControl } from './border-style-control'; export { default as ButtonBlockerAppender } from './button-block-appender'; export { default as ColorPalette } from './color-palette'; export { default as ColorPaletteControl } from './color-palette/control'; diff --git a/packages/block-editor/src/hooks/border-color.js b/packages/block-editor/src/hooks/border-color.js new file mode 100644 index 0000000000000..c9ab4f4fead23 --- /dev/null +++ b/packages/block-editor/src/hooks/border-color.js @@ -0,0 +1,197 @@ +/** + * External dependencies + */ +import classnames from 'classnames'; + +/** + * WordPress dependencies + */ +import { addFilter } from '@wordpress/hooks'; +import { getBlockSupport, hasBlockSupport } from '@wordpress/blocks'; +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import ColorGradientControl from '../components/colors-gradients/control'; +import { + getColorClassName, + getColorObjectByColorValue, +} from '../components/colors'; +import useEditorFeature from '../components/use-editor-feature'; +import { BORDER_SUPPORT_KEY } from './border'; +import { cleanEmptyObject } from './utils'; + +const BORDER_COLOR_SUPPORT_KEY = 'color'; +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 colors = useEditorFeature( 'color.palette' ) || EMPTY_ARRAY; + + const disableCustomColors = ! useEditorFeature( 'color.custom' ); + const disableCustomGradients = ! useEditorFeature( 'color.customGradient' ); + + if ( useIsBorderColorDisabled( props ) ) { + return null; + } + + const onChangeColor = ( value ) => { + const colorObject = getColorObjectByColorValue( colors, value ); + const newStyle = { + ...style, + border: { + ...style?.border, + color: colorObject?.slug ? undefined : value, + }, + }; + + const newNamedColor = colorObject?.slug ? colorObject.slug : undefined; + + setAttributes( { + style: cleanEmptyObject( newStyle ), + borderColor: newNamedColor, + } ); + }; + + return ( + + ); +} + +/** + * Determines if there is border color support. + * + * @param {string|Object} blockType Block name or Block Type object. + * @return {boolean} Whether there is support. + */ +export function hasBorderColorSupport( blockType ) { + const support = getBlockSupport( blockType, BORDER_SUPPORT_KEY ); + return !! ( true === support || support?.color ); +} + +/** + * Custom hook that checks if border color settings have been disabled. + * + * @param {string} name The name of the block. + * @return {boolean} Whether border color setting is disabled. + */ +export function useIsBorderColorDisabled( { name: blockName } = {} ) { + const isDisabled = ! useEditorFeature( 'border.customColor' ); + return ! hasBorderColorSupport( blockName ) || isDisabled; +} + +/** + * 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 ( ! hasBlockSupport( settings, BORDER_COLOR_SUPPORT_KEY ) ) { + return settings; + } + + // Allow blocks to specify default value if needed. + if ( ! settings.attributes.borderColor ) { + Object.assign( settings.attributes, { + borderColor: { + type: 'string', + }, + } ); + } + + return settings; +} + +/** + * 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 ( ! hasBlockSupport( blockType, BORDER_COLOR_SUPPORT_KEY ) ) { + 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, + } ); + + 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 ( ! hasBlockSupport( settings, BORDER_COLOR_SUPPORT_KEY ) ) { + return settings; + } + + const existingGetEditWrapperProps = settings.getEditWrapperProps; + settings.getEditWrapperProps = ( attributes ) => { + let props = {}; + + if ( existingGetEditWrapperProps ) { + props = existingGetEditWrapperProps( attributes ); + } + + return addSaveProps( props, settings, attributes ); + }; + + return settings; +} + +addFilter( + 'blocks.registerBlockType', + 'core/border/addAttributes', + addAttributes +); + +addFilter( + 'blocks.getSaveContent.extraProps', + 'core/border/addSaveProps', + addSaveProps +); + +addFilter( + 'blocks.registerBlockType', + 'core/border/addEditProps', + addEditProps +); diff --git a/packages/block-editor/src/hooks/border-style.js b/packages/block-editor/src/hooks/border-style.js new file mode 100644 index 0000000000000..5248ceff2e8d2 --- /dev/null +++ b/packages/block-editor/src/hooks/border-style.js @@ -0,0 +1,70 @@ +/** + * WordPress dependencies + */ +import { getBlockSupport } from '@wordpress/blocks'; + +/** + * Internal dependencies + */ +import BorderStyleControl from '../components/border-style-control'; +import useEditorFeature from '../components/use-editor-feature'; +import { BORDER_SUPPORT_KEY } from './border'; +import { cleanEmptyObject } from './utils'; + +/** + * 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; + + if ( useIsBorderStyleDisabled( props ) ) { + return null; + } + + const onChange = ( newBorderStyle ) => { + const newStyleAttributes = { + ...style, + border: { + ...style?.border, + style: newBorderStyle, + }, + }; + + setAttributes( { style: cleanEmptyObject( newStyleAttributes ) } ); + }; + + return ( + + ); +}; + +/** + * Determines if there is border style support. + * + * @param {string|Object} blockType Block name or block type object. + * @return {boolean} Whether there is support. + */ +export const hasBorderStyleSupport = ( blockType ) => { + const support = getBlockSupport( blockType, BORDER_SUPPORT_KEY ); + return !! ( true === support || support?.style ); +}; + +/** + * Custom hook that checks if border style settings have been disabled. + * + * @param {string} blockName The name of the block to determine support scope. + * @return {boolean} Whether or not border style is disabled. + */ +export const useIsBorderStyleDisabled = ( { name: blockName } = {} ) => { + const isDisabled = ! useEditorFeature( 'border.customStyle' ); + return ! hasBorderStyleSupport( blockName ) || isDisabled; +}; diff --git a/packages/block-editor/src/hooks/border-width.js b/packages/block-editor/src/hooks/border-width.js new file mode 100644 index 0000000000000..3ec210319b3de --- /dev/null +++ b/packages/block-editor/src/hooks/border-width.js @@ -0,0 +1,79 @@ +/** + * WordPress dependencies + */ +import { getBlockSupport } from '@wordpress/blocks'; +import { RangeControl } from '@wordpress/components'; +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import useEditorFeature from '../components/use-editor-feature'; +import { BORDER_SUPPORT_KEY } from './border'; +import { cleanEmptyObject } from './utils'; + +const MIN_BORDER_WIDTH = 0; +const MAX_BORDER_WIDTH = 50; + +/** + * Inspector control for configuring border width property. + * + * @param {Object} props Block properties. + * @return {WPElement} Border width edit element. + */ +export const BorderWidthEdit = ( props ) => { + const { + attributes: { style }, + setAttributes, + } = props; + + if ( useIsBorderWidthDisabled( props ) ) { + return null; + } + + const onChange = ( newWidth ) => { + const newStyle = { + ...style, + border: { + ...style?.border, + width: newWidth, + }, + }; + + setAttributes( { style: cleanEmptyObject( newStyle ) } ); + }; + + return ( + + ); +}; + +/** + * Determines if there is border width support. + * + * @param {string|Object} blockType Block name or block type object. + * @return {boolean} Whether there is support. + */ +export const hasBorderWidthSupport = ( blockType ) => { + const support = getBlockSupport( blockType, BORDER_SUPPORT_KEY ); + return !! ( true === support || support?.width ); +}; + +/** + * Custom hook that checks if border width settings have been disabled. + * + * @param {string} blockName The name of the block to determine support scope. + * @return {boolean} Whether or not border width is disabled. + */ +export const useIsBorderWidthDisabled = ( { name: blockName } = {} ) => { + const isDisabled = ! useEditorFeature( 'border.customWidth' ); + return ! hasBorderWidthSupport( blockName ) || isDisabled; +}; diff --git a/packages/block-editor/src/hooks/border.js b/packages/block-editor/src/hooks/border.js index 31bb6f4170c27..ad943ec2b1ae1 100644 --- a/packages/block-editor/src/hooks/border.js +++ b/packages/block-editor/src/hooks/border.js @@ -10,7 +10,10 @@ import { __ } from '@wordpress/i18n'; * Internal dependencies */ import InspectorControls from '../components/inspector-controls'; +import { BorderColorEdit, useIsBorderColorDisabled } from './border-color'; import { BorderRadiusEdit, useIsBorderRadiusDisabled } from './border-radius'; +import { BorderStyleEdit, useIsBorderStyleDisabled } from './border-style'; +import { BorderWidthEdit, useIsBorderWidthDisabled } from './border-width'; export const BORDER_SUPPORT_KEY = '__experimentalBorder'; @@ -24,15 +27,18 @@ export function BorderPanel( props ) { return ( - + + + + ); } /** - * Determine whether there is block support for borders. + * Determine whether there is block support for border properties. * * @param {string} blockName Block name. * @return {boolean} Whether there is support. @@ -44,9 +50,13 @@ export function hasBorderSupport( blockName ) { const support = getBlockSupport( blockName, BORDER_SUPPORT_KEY ); - // Further border properties to be added in future iterations. - // e.g. support && ( support.radius || support.width || support.style ) - return !! ( true === support || support?.radius ); + return !! ( + true === support || + support?.color || + support?.radius || + support?.width || + support?.style + ); } /** @@ -57,11 +67,12 @@ export function hasBorderSupport( blockName ) { * @return {boolean} If border support is completely disabled. */ const useIsBorderDisabled = ( props = {} ) => { - // Further border properties to be added in future iterations. - // e.g. const configs = [ - // useIsBorderRadiusDisabled( props ), - // useIsBorderWidthDisabled( props ), - // ]; - const configs = [ useIsBorderRadiusDisabled( props ) ]; + const configs = [ + useIsBorderColorDisabled( props ), + useIsBorderRadiusDisabled( props ), + useIsBorderStyleDisabled( props ), + useIsBorderWidthDisabled( props ), + ]; + return configs.every( Boolean ); }; diff --git a/packages/block-editor/src/hooks/index.js b/packages/block-editor/src/hooks/index.js index c087e994ba9eb..49b11cc8b4074 100644 --- a/packages/block-editor/src/hooks/index.js +++ b/packages/block-editor/src/hooks/index.js @@ -8,4 +8,5 @@ import './generated-class-name'; import './style'; import './color'; import './font-size'; +import './border-color'; import './layout'; diff --git a/packages/block-editor/src/hooks/test/style.js b/packages/block-editor/src/hooks/test/style.js index afa1d45856b2a..0cd3c70d9f2b6 100644 --- a/packages/block-editor/src/hooks/test/style.js +++ b/packages/block-editor/src/hooks/test/style.js @@ -17,11 +17,19 @@ describe( 'getInlineStyles', () => { getInlineStyles( { color: { text: 'red', background: 'black' }, typography: { lineHeight: 1.5, fontSize: 10 }, - border: { radius: 10 }, + border: { + radius: 10, + width: 3, + style: 'dotted', + color: '#21759b', + }, } ) ).toEqual( { backgroundColor: 'black', + borderColor: '#21759b', borderRadius: 10, + borderStyle: 'dotted', + borderWidth: 3, color: 'red', lineHeight: 1.5, fontSize: 10, diff --git a/packages/block-editor/src/style.scss b/packages/block-editor/src/style.scss index f80eeefd3be42..745e64a980d2a 100644 --- a/packages/block-editor/src/style.scss +++ b/packages/block-editor/src/style.scss @@ -27,6 +27,7 @@ @import "./components/block-types-list/style.scss"; @import "./components/block-variation-picker/style.scss"; @import "./components/block-variation-transforms/style.scss"; +@import "./components/border-style-control/style.scss"; @import "./components/button-block-appender/style.scss"; @import "./components/colors-gradients/style.scss"; @import "./components/contrast-checker/style.scss"; diff --git a/phpunit/class-wp-theme-json-test.php b/phpunit/class-wp-theme-json-test.php index 8721bbeafc956..41f6e0a5c079a 100644 --- a/phpunit/class-wp-theme-json-test.php +++ b/phpunit/class-wp-theme-json-test.php @@ -245,11 +245,11 @@ function test_get_stylesheet() { ); $this->assertEquals( - ':root{--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;}:root{--wp--style--color--link: #111;color: var(--wp--preset--color--grey);}.wp-block-group{padding-top: 12px;padding-bottom: 24px;}.has-grey-color{color: grey !important;}.has-grey-background-color{background-color: grey !important;}', + ':root{--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;}:root{--wp--style--color--link: #111;color: var(--wp--preset--color--grey);}.wp-block-group{padding-top: 12px;padding-bottom: 24px;}.has-grey-color{color: grey !important;}.has-grey-background-color{background-color: grey !important;}.has-grey-border-color{border-color: grey !important;}', $theme_json->get_stylesheet() ); $this->assertEquals( - ':root{--wp--style--color--link: #111;color: var(--wp--preset--color--grey);}.wp-block-group{padding-top: 12px;padding-bottom: 24px;}.has-grey-color{color: grey !important;}.has-grey-background-color{background-color: grey !important;}', + ':root{--wp--style--color--link: #111;color: var(--wp--preset--color--grey);}.wp-block-group{padding-top: 12px;padding-bottom: 24px;}.has-grey-color{color: grey !important;}.has-grey-background-color{background-color: grey !important;}.has-grey-border-color{border-color: grey !important;}', $theme_json->get_stylesheet( 'block_styles' ) ); $this->assertEquals( @@ -284,11 +284,11 @@ function test_get_stylesheet_preset_rules_come_after_block_rules() { ); $this->assertEquals( - '.wp-block-group{--wp--preset--color--grey: grey;}.wp-block-group{color: red;}.wp-block-group.has-grey-color{color: grey !important;}.wp-block-group.has-grey-background-color{background-color: grey !important;}', + '.wp-block-group{--wp--preset--color--grey: grey;}.wp-block-group{color: red;}.wp-block-group.has-grey-color{color: grey !important;}.wp-block-group.has-grey-background-color{background-color: grey !important;}.wp-block-group.has-grey-border-color{border-color: grey !important;}', $theme_json->get_stylesheet() ); $this->assertEquals( - '.wp-block-group{color: red;}.wp-block-group.has-grey-color{color: grey !important;}.wp-block-group.has-grey-background-color{background-color: grey !important;}', + '.wp-block-group{color: red;}.wp-block-group.has-grey-color{color: grey !important;}.wp-block-group.has-grey-background-color{background-color: grey !important;}.wp-block-group.has-grey-border-color{border-color: grey !important;}', $theme_json->get_stylesheet( 'block_styles' ) ); } @@ -324,7 +324,7 @@ public function test_get_stylesheet_preset_values_are_marked_as_important() { ); $this->assertEquals( - ':root{--wp--preset--color--grey: grey;}h2.wp-block-post-title{background-color: blue;color: red;font-size: 12px;line-height: 1.3;}.has-grey-color{color: grey !important;}.has-grey-background-color{background-color: grey !important;}', + ':root{--wp--preset--color--grey: grey;}h2.wp-block-post-title{background-color: blue;color: red;font-size: 12px;line-height: 1.3;}.has-grey-color{color: grey !important;}.has-grey-background-color{background-color: grey !important;}.has-grey-border-color{border-color: grey !important;}', $theme_json->get_stylesheet() ); } From 45e0be3c5f20d5a1321208cd17ab7dda7de7ebf1 Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Wed, 24 Mar 2021 11:32:10 +1000 Subject: [PATCH 02/13] Pass colors and gradients to border color control Passing these props prevents the need for the ColorGradientControl to retrieve the colors a second time. --- packages/block-editor/src/hooks/border-color.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/block-editor/src/hooks/border-color.js b/packages/block-editor/src/hooks/border-color.js index c9ab4f4fead23..d7f0292cf5555 100644 --- a/packages/block-editor/src/hooks/border-color.js +++ b/packages/block-editor/src/hooks/border-color.js @@ -73,6 +73,8 @@ export function BorderColorEdit( props ) { Date: Wed, 24 Mar 2021 12:43:13 +1000 Subject: [PATCH 03/13] Return new object instead of mutating passed settings --- .../block-editor/src/hooks/border-color.js | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/packages/block-editor/src/hooks/border-color.js b/packages/block-editor/src/hooks/border-color.js index d7f0292cf5555..cf4e79c0f724e 100644 --- a/packages/block-editor/src/hooks/border-color.js +++ b/packages/block-editor/src/hooks/border-color.js @@ -112,20 +112,25 @@ export function useIsBorderColorDisabled( { name: blockName } = {} ) { * @return {Object} Updated block settings. */ function addAttributes( settings ) { - if ( ! hasBlockSupport( settings, BORDER_COLOR_SUPPORT_KEY ) ) { + if ( ! hasBorderColorSupport( settings ) ) { return settings; } // Allow blocks to specify default value if needed. - if ( ! settings.attributes.borderColor ) { - Object.assign( settings.attributes, { + if ( settings.attributes.borderColor ) { + return settings; + } + + // Add new borderColor attribute to block settings. + return { + ...settings, + attributes: { + ...settings.attributes, borderColor: { type: 'string', }, - } ); - } - - return settings; + }, + }; } /** From c5a2d641ad32eb02f9eecb6463995aa72e1193b3 Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Wed, 24 Mar 2021 13:26:30 +1000 Subject: [PATCH 04/13] Refactor individual feature support checks --- .../block-editor/src/hooks/border-color.js | 23 ++++--------------- .../block-editor/src/hooks/border-radius.js | 16 ++----------- .../block-editor/src/hooks/border-style.js | 20 ++-------------- .../block-editor/src/hooks/border-width.js | 16 ++----------- packages/block-editor/src/hooks/border.js | 12 ++++++++++ 5 files changed, 23 insertions(+), 64 deletions(-) diff --git a/packages/block-editor/src/hooks/border-color.js b/packages/block-editor/src/hooks/border-color.js index cf4e79c0f724e..65d91e5bebfed 100644 --- a/packages/block-editor/src/hooks/border-color.js +++ b/packages/block-editor/src/hooks/border-color.js @@ -7,7 +7,6 @@ import classnames from 'classnames'; * WordPress dependencies */ import { addFilter } from '@wordpress/hooks'; -import { getBlockSupport, hasBlockSupport } from '@wordpress/blocks'; import { __ } from '@wordpress/i18n'; /** @@ -19,10 +18,9 @@ import { getColorObjectByColorValue, } from '../components/colors'; import useEditorFeature from '../components/use-editor-feature'; -import { BORDER_SUPPORT_KEY } from './border'; +import { hasBorderFeatureSupport } from './border'; import { cleanEmptyObject } from './utils'; -const BORDER_COLOR_SUPPORT_KEY = 'color'; const EMPTY_ARRAY = []; /** @@ -82,17 +80,6 @@ export function BorderColorEdit( props ) { ); } -/** - * Determines if there is border color support. - * - * @param {string|Object} blockType Block name or Block Type object. - * @return {boolean} Whether there is support. - */ -export function hasBorderColorSupport( blockType ) { - const support = getBlockSupport( blockType, BORDER_SUPPORT_KEY ); - return !! ( true === support || support?.color ); -} - /** * Custom hook that checks if border color settings have been disabled. * @@ -101,7 +88,7 @@ export function hasBorderColorSupport( blockType ) { */ export function useIsBorderColorDisabled( { name: blockName } = {} ) { const isDisabled = ! useEditorFeature( 'border.customColor' ); - return ! hasBorderColorSupport( blockName ) || isDisabled; + return ! hasBorderFeatureSupport( 'color', blockName ) || isDisabled; } /** @@ -112,7 +99,7 @@ export function useIsBorderColorDisabled( { name: blockName } = {} ) { * @return {Object} Updated block settings. */ function addAttributes( settings ) { - if ( ! hasBorderColorSupport( settings ) ) { + if ( ! hasBorderFeatureSupport( 'color', settings ) ) { return settings; } @@ -142,7 +129,7 @@ function addAttributes( settings ) { * @return {Object} Filtered props to apply to save element. */ function addSaveProps( props, blockType, attributes ) { - if ( ! hasBlockSupport( blockType, BORDER_COLOR_SUPPORT_KEY ) ) { + if ( ! hasBorderFeatureSupport( 'color', blockType ) ) { return props; } @@ -167,7 +154,7 @@ function addSaveProps( props, blockType, attributes ) { * @return {Object} Filtered block settings. */ function addEditProps( settings ) { - if ( ! hasBlockSupport( settings, BORDER_COLOR_SUPPORT_KEY ) ) { + if ( ! hasBorderFeatureSupport( 'color', settings ) ) { return settings; } diff --git a/packages/block-editor/src/hooks/border-radius.js b/packages/block-editor/src/hooks/border-radius.js index 7ced3df65401a..fc074809e2706 100644 --- a/packages/block-editor/src/hooks/border-radius.js +++ b/packages/block-editor/src/hooks/border-radius.js @@ -1,7 +1,6 @@ /** * WordPress dependencies */ -import { getBlockSupport } from '@wordpress/blocks'; import { RangeControl } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; @@ -9,7 +8,7 @@ import { __ } from '@wordpress/i18n'; * Internal dependencies */ import useEditorFeature from '../components/use-editor-feature'; -import { BORDER_SUPPORT_KEY } from './border'; +import { hasBorderFeatureSupport } from './border'; import { cleanEmptyObject } from './utils'; const MIN_BORDER_RADIUS_VALUE = 0; @@ -60,17 +59,6 @@ export function BorderRadiusEdit( props ) { ); } -/** - * Determines if there is border radius support. - * - * @param {string|Object} blockType Block name or Block Type object. - * @return {boolean} Whether there is support. - */ -export function hasBorderRadiusSupport( blockType ) { - const support = getBlockSupport( blockType, BORDER_SUPPORT_KEY ); - return !! ( true === support || support?.radius ); -} - /** * Custom hook that checks if border radius settings have been disabled. * @@ -79,5 +67,5 @@ export function hasBorderRadiusSupport( blockType ) { */ export function useIsBorderRadiusDisabled( { name: blockName } = {} ) { const isDisabled = ! useEditorFeature( 'border.customRadius' ); - return ! hasBorderRadiusSupport( blockName ) || isDisabled; + return ! hasBorderFeatureSupport( 'radius', blockName ) || isDisabled; } diff --git a/packages/block-editor/src/hooks/border-style.js b/packages/block-editor/src/hooks/border-style.js index 5248ceff2e8d2..2af46c5b9ee6f 100644 --- a/packages/block-editor/src/hooks/border-style.js +++ b/packages/block-editor/src/hooks/border-style.js @@ -1,14 +1,9 @@ -/** - * WordPress dependencies - */ -import { getBlockSupport } from '@wordpress/blocks'; - /** * Internal dependencies */ import BorderStyleControl from '../components/border-style-control'; import useEditorFeature from '../components/use-editor-feature'; -import { BORDER_SUPPORT_KEY } from './border'; +import { hasBorderFeatureSupport } from './border'; import { cleanEmptyObject } from './utils'; /** @@ -47,17 +42,6 @@ export const BorderStyleEdit = ( props ) => { ); }; -/** - * Determines if there is border style support. - * - * @param {string|Object} blockType Block name or block type object. - * @return {boolean} Whether there is support. - */ -export const hasBorderStyleSupport = ( blockType ) => { - const support = getBlockSupport( blockType, BORDER_SUPPORT_KEY ); - return !! ( true === support || support?.style ); -}; - /** * Custom hook that checks if border style settings have been disabled. * @@ -66,5 +50,5 @@ export const hasBorderStyleSupport = ( blockType ) => { */ export const useIsBorderStyleDisabled = ( { name: blockName } = {} ) => { const isDisabled = ! useEditorFeature( 'border.customStyle' ); - return ! hasBorderStyleSupport( blockName ) || isDisabled; + return ! hasBorderFeatureSupport( 'style', blockName ) || isDisabled; }; diff --git a/packages/block-editor/src/hooks/border-width.js b/packages/block-editor/src/hooks/border-width.js index 3ec210319b3de..be8afd3b1d066 100644 --- a/packages/block-editor/src/hooks/border-width.js +++ b/packages/block-editor/src/hooks/border-width.js @@ -1,7 +1,6 @@ /** * WordPress dependencies */ -import { getBlockSupport } from '@wordpress/blocks'; import { RangeControl } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; @@ -9,7 +8,7 @@ import { __ } from '@wordpress/i18n'; * Internal dependencies */ import useEditorFeature from '../components/use-editor-feature'; -import { BORDER_SUPPORT_KEY } from './border'; +import { hasBorderFeatureSupport } from './border'; import { cleanEmptyObject } from './utils'; const MIN_BORDER_WIDTH = 0; @@ -56,17 +55,6 @@ export const BorderWidthEdit = ( props ) => { ); }; -/** - * Determines if there is border width support. - * - * @param {string|Object} blockType Block name or block type object. - * @return {boolean} Whether there is support. - */ -export const hasBorderWidthSupport = ( blockType ) => { - const support = getBlockSupport( blockType, BORDER_SUPPORT_KEY ); - return !! ( true === support || support?.width ); -}; - /** * Custom hook that checks if border width settings have been disabled. * @@ -75,5 +63,5 @@ export const hasBorderWidthSupport = ( blockType ) => { */ export const useIsBorderWidthDisabled = ( { name: blockName } = {} ) => { const isDisabled = ! useEditorFeature( 'border.customWidth' ); - return ! hasBorderWidthSupport( blockName ) || isDisabled; + return ! hasBorderFeatureSupport( 'width', blockName ) || isDisabled; }; diff --git a/packages/block-editor/src/hooks/border.js b/packages/block-editor/src/hooks/border.js index ad943ec2b1ae1..7b4230217546e 100644 --- a/packages/block-editor/src/hooks/border.js +++ b/packages/block-editor/src/hooks/border.js @@ -59,6 +59,18 @@ export function hasBorderSupport( blockName ) { ); } +/** + * Determines if there a specific border feature is supported. + * + * @param {string} feature Name of the border feature e.g.`radius` + * @param {string|Object} blockType Block name or block type object. + * @return { boolean } Whether the border feature is supported. + */ +export function hasBorderFeatureSupport( feature, blockType ) { + const support = getBlockSupport( blockType, BORDER_SUPPORT_KEY ); + return !! ( true === support || ( support && support[ feature ] ) ); +} + /** * Determines whether there is any block support for borders e.g. border radius, * style, width etc. From 5f8006fd038f7ab14e6359da487b5af1ac5e7a60 Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Thu, 25 Mar 2021 14:44:23 +1000 Subject: [PATCH 05/13] Add support for skipping serialization of border color --- packages/block-editor/src/hooks/border-color.js | 12 +++++++++--- packages/block-editor/src/hooks/border.js | 14 +++++++++++++- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/packages/block-editor/src/hooks/border-color.js b/packages/block-editor/src/hooks/border-color.js index 65d91e5bebfed..78214013115d7 100644 --- a/packages/block-editor/src/hooks/border-color.js +++ b/packages/block-editor/src/hooks/border-color.js @@ -18,7 +18,7 @@ import { getColorObjectByColorValue, } from '../components/colors'; import useEditorFeature from '../components/use-editor-feature'; -import { hasBorderFeatureSupport } from './border'; +import { hasBorderFeatureSupport, shouldSkipSerialization } from './border'; import { cleanEmptyObject } from './utils'; const EMPTY_ARRAY = []; @@ -129,7 +129,10 @@ function addAttributes( settings ) { * @return {Object} Filtered props to apply to save element. */ function addSaveProps( props, blockType, attributes ) { - if ( ! hasBorderFeatureSupport( 'color', blockType ) ) { + if ( + ! hasBorderFeatureSupport( 'color', blockType ) || + shouldSkipSerialization( blockType ) + ) { return props; } @@ -154,7 +157,10 @@ function addSaveProps( props, blockType, attributes ) { * @return {Object} Filtered block settings. */ function addEditProps( settings ) { - if ( ! hasBorderFeatureSupport( 'color', settings ) ) { + if ( + ! hasBorderFeatureSupport( 'color', settings ) || + shouldSkipSerialization( settings ) + ) { return settings; } diff --git a/packages/block-editor/src/hooks/border.js b/packages/block-editor/src/hooks/border.js index 7b4230217546e..39f8dfdf5cc6e 100644 --- a/packages/block-editor/src/hooks/border.js +++ b/packages/block-editor/src/hooks/border.js @@ -64,13 +64,25 @@ export function hasBorderSupport( blockName ) { * * @param {string} feature Name of the border feature e.g.`radius` * @param {string|Object} blockType Block name or block type object. - * @return { boolean } Whether the border feature is supported. + * @return {boolean} Whether the border feature is supported. */ export function hasBorderFeatureSupport( feature, blockType ) { const support = getBlockSupport( blockType, BORDER_SUPPORT_KEY ); return !! ( true === support || ( support && support[ feature ] ) ); } +/** + * Check whether serialization of border classes and styles should be skipped. + * + * @param {string|Object} blockType Block name or block type object. + * @return {boolean} Whether serialization of border properties should occur. + */ +export function shouldSkipSerialization( blockType ) { + const support = getBlockSupport( blockType, BORDER_SUPPORT_KEY ); + + return support?.__experimentalSkipSerialization; +} + /** * Determines whether there is any block support for borders e.g. border radius, * style, width etc. From 97de73f8663857826d81cd2c6e14ab46ccb41221 Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Thu, 25 Mar 2021 15:01:05 +1000 Subject: [PATCH 06/13] Add comment about enforcing undefined for color slug --- packages/block-editor/src/hooks/border-color.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/block-editor/src/hooks/border-color.js b/packages/block-editor/src/hooks/border-color.js index 78214013115d7..ffc1abf64b8de 100644 --- a/packages/block-editor/src/hooks/border-color.js +++ b/packages/block-editor/src/hooks/border-color.js @@ -59,6 +59,7 @@ export function BorderColorEdit( props ) { }, }; + // If empty slug, ensure undefined to remove attribute. const newNamedColor = colorObject?.slug ? colorObject.slug : undefined; setAttributes( { From 6c6290eab4e0dbc03771302309dae80c597957d2 Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Thu, 25 Mar 2021 15:10:00 +1000 Subject: [PATCH 07/13] Add comment explaining EMPTY_ARRAY avoiding rerenders --- packages/block-editor/src/hooks/border-color.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/block-editor/src/hooks/border-color.js b/packages/block-editor/src/hooks/border-color.js index ffc1abf64b8de..99cc46804f427 100644 --- a/packages/block-editor/src/hooks/border-color.js +++ b/packages/block-editor/src/hooks/border-color.js @@ -21,6 +21,8 @@ import useEditorFeature from '../components/use-editor-feature'; import { hasBorderFeatureSupport, shouldSkipSerialization } from './border'; import { cleanEmptyObject } from './utils'; +// Defining empty array here instead of inline avoids unnecessary re-renders of +// color control. const EMPTY_ARRAY = []; /** From 9add20ed2c88fd05391b3a4758d8b4da17215b09 Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Thu, 25 Mar 2021 15:23:17 +1000 Subject: [PATCH 08/13] Add clarifying comment around clearing `className` --- packages/block-editor/src/hooks/border-color.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/block-editor/src/hooks/border-color.js b/packages/block-editor/src/hooks/border-color.js index 99cc46804f427..1ce2855677f4e 100644 --- a/packages/block-editor/src/hooks/border-color.js +++ b/packages/block-editor/src/hooks/border-color.js @@ -147,6 +147,8 @@ function addSaveProps( props, blockType, attributes ) { [ 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; From 9bb2c829b7078aefa79f5fcc369fc4ad3c215a95 Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Wed, 31 Mar 2021 17:23:58 +1000 Subject: [PATCH 09/13] Add border block support to global styles sidebar --- .../src/components/sidebar/border-panel.js | 147 ++++++++++++++++++ .../sidebar/global-styles-sidebar.js | 9 ++ .../components/sidebar/typography-panel.js | 10 +- 3 files changed, 161 insertions(+), 5 deletions(-) create mode 100644 packages/edit-site/src/components/sidebar/border-panel.js diff --git a/packages/edit-site/src/components/sidebar/border-panel.js b/packages/edit-site/src/components/sidebar/border-panel.js new file mode 100644 index 0000000000000..9b95c4d861b56 --- /dev/null +++ b/packages/edit-site/src/components/sidebar/border-panel.js @@ -0,0 +1,147 @@ +/** + * WordPress dependencies + */ +import { + __experimentalBorderStyleControl as BorderStyleControl, + __experimentalColorGradientControl as ColorGradientControl, +} from '@wordpress/block-editor'; +import { PanelBody, RangeControl } from '@wordpress/components'; +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import { useEditorFeature } from '../editor/utils'; + +const MIN_BORDER_RADIUS_VALUE = 0; +const MAX_BORDER_RADIUS_VALUE = 50; +const MIN_BORDER_WIDTH = 0; +const MAX_BORDER_WIDTH = 50; + +// Defining empty array here instead of inline avoids unnecessary re-renders of +// color control. +const EMPTY_ARRAY = []; + +export function useHasBorderPanel( { supports, name } ) { + const controls = [ + useHasBorderColorControl( { supports, name } ), + useHasBorderRadiusControl( { supports, name } ), + useHasBorderStyleControl( { supports, name } ), + useHasBorderWidthControl( { supports, name } ), + ]; + + return controls.every( Boolean ); +} + +function useHasBorderColorControl( { supports, name } ) { + return ( + useEditorFeature( 'border.customColor', name ) && + supports.includes( 'borderColor' ) + ); +} + +function useHasBorderRadiusControl( { supports, name } ) { + return ( + useEditorFeature( 'border.customRadius', name ) && + supports.includes( 'borderRadius' ) + ); +} + +function useHasBorderStyleControl( { supports, name } ) { + return ( + useEditorFeature( 'border.customStyle', name ) && + supports.includes( 'borderStyle' ) + ); +} + +function useHasBorderWidthControl( { supports, name } ) { + return ( + useEditorFeature( 'border.customWidth', name ) && + supports.includes( 'borderWidth' ) + ); +} + +export default function BorderPanel( { + context: { supports, name }, + getStyle, + setStyle, +} ) { + // Border style. + const hasBorderStyle = useHasBorderStyleControl( { supports, name } ); + const borderStyle = getStyle( name, 'borderStyle' ); + + // Border width. + const hasBorderWidth = useHasBorderWidthControl( { supports, name } ); + const borderWidthValue = parseInt( + getStyle( name, 'borderWidth' ) || 0, + 10 + ); + + // Border radius. + const hasBorderRadius = useHasBorderRadiusControl( { supports, name } ); + const borderRadiusValue = parseInt( + getStyle( name, 'borderRadius' ) || 0, + 10 + ); + + // Border color. + const colors = useEditorFeature( 'color.palette' ) || EMPTY_ARRAY; + const disableCustomColors = ! useEditorFeature( 'color.custom' ); + const disableCustomGradients = ! useEditorFeature( 'color.customGradient' ); + const hasBorderColor = useHasBorderColorControl( { supports, name } ); + const borderColor = getStyle( name, 'borderColor' ); + + return ( + + { hasBorderStyle && ( + + setStyle( name, 'borderStyle', value ) + } + /> + ) } + { hasBorderWidth && ( + { + const widthStyle = value ? `${ value }px` : undefined; + setStyle( name, 'borderWidth', widthStyle ); + } } + /> + ) } + { hasBorderRadius && ( + { + const radiusStyle = value ? `${ value }px` : undefined; + setStyle( name, 'borderRadius', radiusStyle ); + } } + /> + ) } + { hasBorderColor && ( + + setStyle( name, 'borderColor', value ) + } + /> + ) } + + ); +} diff --git a/packages/edit-site/src/components/sidebar/global-styles-sidebar.js b/packages/edit-site/src/components/sidebar/global-styles-sidebar.js index 126fb563e7d27..f0017eb703d05 100644 --- a/packages/edit-site/src/components/sidebar/global-styles-sidebar.js +++ b/packages/edit-site/src/components/sidebar/global-styles-sidebar.js @@ -29,6 +29,7 @@ import { default as TypographyPanel, useHasTypographyPanel, } from './typography-panel'; +import { default as BorderPanel, useHasBorderPanel } from './border-panel'; import { default as ColorPanel, useHasColorPanel } from './color-panel'; import { default as SpacingPanel, useHasSpacingPanel } from './spacing-panel'; @@ -40,6 +41,7 @@ function GlobalStylesPanel( { getSetting, setSetting, } ) { + const hasBorderPanel = useHasBorderPanel( context ); const hasColorPanel = useHasColorPanel( context ); const hasTypographyPanel = useHasTypographyPanel( context ); const hasSpacingPanel = useHasSpacingPanel( context ); @@ -75,6 +77,13 @@ function GlobalStylesPanel( { setStyle={ setStyle } /> ) } + { hasBorderPanel && ( + + ) } ); if ( ! wrapperPanelTitle ) { diff --git a/packages/edit-site/src/components/sidebar/typography-panel.js b/packages/edit-site/src/components/sidebar/typography-panel.js index 558f2bf11414d..766a738a42cd2 100644 --- a/packages/edit-site/src/components/sidebar/typography-panel.js +++ b/packages/edit-site/src/components/sidebar/typography-panel.js @@ -16,9 +16,9 @@ import { useEditorFeature } from '../editor/utils'; export function useHasTypographyPanel( { supports, name } ) { const hasLineHeight = useHasLineHeightControl( { supports, name } ); - const hasFontAppearence = useHasAppearenceControl( { supports, name } ); + const hasFontAppearance = useHasAppearanceControl( { supports, name } ); return ( - hasLineHeight || hasFontAppearence || supports.includes( 'fontSize' ) + hasLineHeight || hasFontAppearance || supports.includes( 'fontSize' ) ); } @@ -29,7 +29,7 @@ function useHasLineHeightControl( { supports, name } ) { ); } -function useHasAppearenceControl( { supports, name } ) { +function useHasAppearanceControl( { supports, name } ) { const hasFontStyles = useEditorFeature( 'typography.customFontStyle', name ) && supports.includes( 'fontStyle' ); @@ -57,7 +57,7 @@ export default function TypographyPanel( { useEditorFeature( 'typography.customFontWeight', name ) && supports.includes( 'fontWeight' ); const hasLineHeightEnabled = useHasLineHeightControl( { supports, name } ); - const hasAppearenceControl = useHasAppearenceControl( { supports, name } ); + const hasAppearanceControl = useHasAppearanceControl( { supports, name } ); return ( @@ -88,7 +88,7 @@ export default function TypographyPanel( { } /> ) } - { hasAppearenceControl && ( + { hasAppearanceControl && ( Date: Mon, 12 Apr 2021 16:34:38 +1000 Subject: [PATCH 10/13] Add filter to force inline border color style in editor This fixes issue where themes don't load their palettes in the editor. Approach is the same as the colors block support that was missed on first pass updating the borders block support. --- .../block-editor/src/hooks/border-color.js | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/packages/block-editor/src/hooks/border-color.js b/packages/block-editor/src/hooks/border-color.js index 1ce2855677f4e..25088245b4e25 100644 --- a/packages/block-editor/src/hooks/border-color.js +++ b/packages/block-editor/src/hooks/border-color.js @@ -8,6 +8,7 @@ import classnames from 'classnames'; */ import { addFilter } from '@wordpress/hooks'; import { __ } from '@wordpress/i18n'; +import { createHigherOrderComponent } from '@wordpress/compose'; /** * Internal dependencies @@ -16,6 +17,7 @@ import ColorGradientControl from '../components/colors-gradients/control'; import { getColorClassName, getColorObjectByColorValue, + getColorObjectByAttributeValues, } from '../components/colors'; import useEditorFeature from '../components/use-editor-feature'; import { hasBorderFeatureSupport, shouldSkipSerialization } from './border'; @@ -183,6 +185,45 @@ function addEditProps( settings ) { 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 = useEditorFeature( 'color.palette' ) || EMPTY_ARRAY; + + if ( + ! hasBorderFeatureSupport( 'color', name ) || + shouldSkipSerialization( name ) + ) { + 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', @@ -200,3 +241,9 @@ addFilter( 'core/border/addEditProps', addEditProps ); + +addFilter( + 'editor.BlockListBlock', + 'core/border/with-border-color-palette-styles', + withBorderColorPaletteStyles +); From d87a2999b0cac30d479628b24ed4b9169a40fd93 Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Wed, 14 Apr 2021 18:57:08 +1000 Subject: [PATCH 11/13] Use gutenberg_block_has_support and unnest support checks --- lib/block-supports/border.php | 61 +++++++++++++---------------------- 1 file changed, 23 insertions(+), 38 deletions(-) diff --git a/lib/block-supports/border.php b/lib/block-supports/border.php index ee5b2dfbc0360..8ad249ff95d33 100644 --- a/lib/block-supports/border.php +++ b/lib/block-supports/border.php @@ -13,11 +13,11 @@ */ function gutenberg_register_border_support( $block_type ) { // Determine if any border related features are supported. - $has_border_color_support = gutenberg_has_border_support( $block_type, 'color' ); + $has_border_color_support = gutenberg_block_has_support( $block_type, array( '__experimentalBorder', 'color' ) ); $has_border_support = - gutenberg_has_border_support( $block_type, 'radius' ) || - gutenberg_has_border_support( $block_type, 'style' ) || - gutenberg_has_border_support( $block_type, 'width' ) || + gutenberg_block_has_support( $block_type, array( '__experimentalBorder', 'radius' ) ) || + gutenberg_block_has_support( $block_type, array( '__experimentalBorder', 'style' ) ) || + gutenberg_block_has_support( $block_type, array( '__experimentalBorder', 'width' ) ) || $has_border_color_support; // Setup attributes and styles within that if needed. @@ -56,31 +56,34 @@ function gutenberg_apply_border_support( $block_type, $block_attributes ) { $styles = array(); // Border radius. - if ( gutenberg_has_border_support( $block_type, 'radius' ) ) { - if ( isset( $block_attributes['style']['border']['radius'] ) ) { - $border_radius = (int) $block_attributes['style']['border']['radius']; - $styles[] = sprintf( 'border-radius: %dpx;', $border_radius ); - } + if ( + gutenberg_block_has_support( $block_type, array( '__experimentalBorder', 'radius' ) ) && + isset( $block_attributes['style']['border']['radius'] ) + ) { + $border_radius = (int) $block_attributes['style']['border']['radius']; + $styles[] = sprintf( 'border-radius: %dpx;', $border_radius ); } // Border style. - if ( gutenberg_has_border_support( $block_type, 'style' ) ) { - if ( isset( $block_attributes['style']['border']['style'] ) ) { - $border_style = $block_attributes['style']['border']['style']; - $styles[] = sprintf( 'border-style: %s;', $border_style ); - } + if ( + gutenberg_block_has_support( $block_type, array( '__experimentalBorder', 'style' ) ) && + isset( $block_attributes['style']['border']['style'] ) + ) { + $border_style = $block_attributes['style']['border']['style']; + $styles[] = sprintf( 'border-style: %s;', $border_style ); } // Border width. - if ( gutenberg_has_border_support( $block_type, 'width' ) ) { - if ( isset( $block_attributes['style']['border']['width'] ) ) { - $border_width = intval( $block_attributes['style']['border']['width'] ); - $styles[] = sprintf( 'border-width: %dpx;', $border_width ); - } + if ( + gutenberg_block_has_support( $block_type, array( '__experimentalBorder', 'width' ) ) && + isset( $block_attributes['style']['border']['width'] ) + ) { + $border_width = intval( $block_attributes['style']['border']['width'] ); + $styles[] = sprintf( 'border-width: %dpx;', $border_width ); } // Border color. - if ( gutenberg_has_border_support( $block_type, 'color' ) ) { + if ( gutenberg_block_has_support( $block_type, array( '__experimentalBorder', 'color' ) ) ) { $has_named_border_color = array_key_exists( 'borderColor', $block_attributes ); $has_custom_border_color = isset( $block_attributes['style']['border']['color'] ); @@ -126,24 +129,6 @@ function gutenberg_skip_border_serialization( $block_type ) { $border_support['__experimentalSkipSerialization']; } -/** - * Checks whether the current block type supports the feature requested. - * - * @param WP_Block_Type $block_type Block type to check for support. - * @param string $feature Name of the feature to check support for. - * @param mixed $default Fallback value for feature support, defaults to false. - * - * @return boolean Whether or not the feature is supported. - */ -function gutenberg_has_border_support( $block_type, $feature, $default = false ) { - $block_support = false; - if ( property_exists( $block_type, 'supports' ) ) { - $block_support = _wp_array_get( $block_type->supports, array( '__experimentalBorder' ), $default ); - } - - return true === $block_support || ( is_array( $block_support ) && _wp_array_get( $block_support, array( $feature ), false ) ); -} - // Register the block support. WP_Block_Supports::get_instance()->register( 'border', From f1f3c4b32373718eef349c5c2e44d7a5473b8c20 Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Wed, 14 Apr 2021 18:57:44 +1000 Subject: [PATCH 12/13] Refactor to handle disabled/support checks in root border hook --- .../block-editor/src/hooks/border-color.js | 25 ++---- .../block-editor/src/hooks/border-radius.js | 17 ---- .../block-editor/src/hooks/border-style.js | 17 ---- .../block-editor/src/hooks/border-width.js | 17 ---- packages/block-editor/src/hooks/border.js | 87 +++++++++++-------- 5 files changed, 54 insertions(+), 109 deletions(-) diff --git a/packages/block-editor/src/hooks/border-color.js b/packages/block-editor/src/hooks/border-color.js index 25088245b4e25..511983d35fab1 100644 --- a/packages/block-editor/src/hooks/border-color.js +++ b/packages/block-editor/src/hooks/border-color.js @@ -20,7 +20,7 @@ import { getColorObjectByAttributeValues, } from '../components/colors'; import useEditorFeature from '../components/use-editor-feature'; -import { hasBorderFeatureSupport, shouldSkipSerialization } from './border'; +import { hasBorderSupport, shouldSkipSerialization } from './border'; import { cleanEmptyObject } from './utils'; // Defining empty array here instead of inline avoids unnecessary re-renders of @@ -49,10 +49,6 @@ export function BorderColorEdit( props ) { const disableCustomColors = ! useEditorFeature( 'color.custom' ); const disableCustomGradients = ! useEditorFeature( 'color.customGradient' ); - if ( useIsBorderColorDisabled( props ) ) { - return null; - } - const onChangeColor = ( value ) => { const colorObject = getColorObjectByColorValue( colors, value ); const newStyle = { @@ -85,17 +81,6 @@ export function BorderColorEdit( props ) { ); } -/** - * Custom hook that checks if border color settings have been disabled. - * - * @param {string} name The name of the block. - * @return {boolean} Whether border color setting is disabled. - */ -export function useIsBorderColorDisabled( { name: blockName } = {} ) { - const isDisabled = ! useEditorFeature( 'border.customColor' ); - return ! hasBorderFeatureSupport( 'color', blockName ) || isDisabled; -} - /** * Filters registered block settings, extending attributes to include * `borderColor` if needed. @@ -104,7 +89,7 @@ export function useIsBorderColorDisabled( { name: blockName } = {} ) { * @return {Object} Updated block settings. */ function addAttributes( settings ) { - if ( ! hasBorderFeatureSupport( 'color', settings ) ) { + if ( ! hasBorderSupport( settings, 'color' ) ) { return settings; } @@ -135,7 +120,7 @@ function addAttributes( settings ) { */ function addSaveProps( props, blockType, attributes ) { if ( - ! hasBorderFeatureSupport( 'color', blockType ) || + ! hasBorderSupport( blockType, 'color' ) || shouldSkipSerialization( blockType ) ) { return props; @@ -165,7 +150,7 @@ function addSaveProps( props, blockType, attributes ) { */ function addEditProps( settings ) { if ( - ! hasBorderFeatureSupport( 'color', settings ) || + ! hasBorderSupport( settings, 'color' ) || shouldSkipSerialization( settings ) ) { return settings; @@ -199,7 +184,7 @@ export const withBorderColorPaletteStyles = createHigherOrderComponent( const colors = useEditorFeature( 'color.palette' ) || EMPTY_ARRAY; if ( - ! hasBorderFeatureSupport( 'color', name ) || + ! hasBorderSupport( name, 'color' ) || shouldSkipSerialization( name ) ) { return ; diff --git a/packages/block-editor/src/hooks/border-radius.js b/packages/block-editor/src/hooks/border-radius.js index fc074809e2706..50e9dece97eaf 100644 --- a/packages/block-editor/src/hooks/border-radius.js +++ b/packages/block-editor/src/hooks/border-radius.js @@ -7,8 +7,6 @@ import { __ } from '@wordpress/i18n'; /** * Internal dependencies */ -import useEditorFeature from '../components/use-editor-feature'; -import { hasBorderFeatureSupport } from './border'; import { cleanEmptyObject } from './utils'; const MIN_BORDER_RADIUS_VALUE = 0; @@ -26,10 +24,6 @@ export function BorderRadiusEdit( props ) { setAttributes, } = props; - if ( useIsBorderRadiusDisabled( props ) ) { - return null; - } - const onChange = ( newRadius ) => { let newStyle = { ...style, @@ -58,14 +52,3 @@ export function BorderRadiusEdit( props ) { /> ); } - -/** - * Custom hook that checks if border radius settings have been disabled. - * - * @param {string} name The name of the block. - * @return {boolean} Whether border radius setting is disabled. - */ -export function useIsBorderRadiusDisabled( { name: blockName } = {} ) { - const isDisabled = ! useEditorFeature( 'border.customRadius' ); - return ! hasBorderFeatureSupport( 'radius', blockName ) || isDisabled; -} diff --git a/packages/block-editor/src/hooks/border-style.js b/packages/block-editor/src/hooks/border-style.js index 2af46c5b9ee6f..9f1b1e49b2143 100644 --- a/packages/block-editor/src/hooks/border-style.js +++ b/packages/block-editor/src/hooks/border-style.js @@ -2,8 +2,6 @@ * Internal dependencies */ import BorderStyleControl from '../components/border-style-control'; -import useEditorFeature from '../components/use-editor-feature'; -import { hasBorderFeatureSupport } from './border'; import { cleanEmptyObject } from './utils'; /** @@ -18,10 +16,6 @@ export const BorderStyleEdit = ( props ) => { setAttributes, } = props; - if ( useIsBorderStyleDisabled( props ) ) { - return null; - } - const onChange = ( newBorderStyle ) => { const newStyleAttributes = { ...style, @@ -41,14 +35,3 @@ export const BorderStyleEdit = ( props ) => { /> ); }; - -/** - * Custom hook that checks if border style settings have been disabled. - * - * @param {string} blockName The name of the block to determine support scope. - * @return {boolean} Whether or not border style is disabled. - */ -export const useIsBorderStyleDisabled = ( { name: blockName } = {} ) => { - const isDisabled = ! useEditorFeature( 'border.customStyle' ); - return ! hasBorderFeatureSupport( 'style', blockName ) || isDisabled; -}; diff --git a/packages/block-editor/src/hooks/border-width.js b/packages/block-editor/src/hooks/border-width.js index be8afd3b1d066..24e25f6a06385 100644 --- a/packages/block-editor/src/hooks/border-width.js +++ b/packages/block-editor/src/hooks/border-width.js @@ -7,8 +7,6 @@ import { __ } from '@wordpress/i18n'; /** * Internal dependencies */ -import useEditorFeature from '../components/use-editor-feature'; -import { hasBorderFeatureSupport } from './border'; import { cleanEmptyObject } from './utils'; const MIN_BORDER_WIDTH = 0; @@ -26,10 +24,6 @@ export const BorderWidthEdit = ( props ) => { setAttributes, } = props; - if ( useIsBorderWidthDisabled( props ) ) { - return null; - } - const onChange = ( newWidth ) => { const newStyle = { ...style, @@ -54,14 +48,3 @@ export const BorderWidthEdit = ( props ) => { /> ); }; - -/** - * Custom hook that checks if border width settings have been disabled. - * - * @param {string} blockName The name of the block to determine support scope. - * @return {boolean} Whether or not border width is disabled. - */ -export const useIsBorderWidthDisabled = ( { name: blockName } = {} ) => { - const isDisabled = ! useEditorFeature( 'border.customWidth' ); - return ! hasBorderFeatureSupport( 'width', blockName ) || isDisabled; -}; diff --git a/packages/block-editor/src/hooks/border.js b/packages/block-editor/src/hooks/border.js index 39f8dfdf5cc6e..9b4aa499f54cd 100644 --- a/packages/block-editor/src/hooks/border.js +++ b/packages/block-editor/src/hooks/border.js @@ -10,10 +10,11 @@ import { __ } from '@wordpress/i18n'; * Internal dependencies */ import InspectorControls from '../components/inspector-controls'; -import { BorderColorEdit, useIsBorderColorDisabled } from './border-color'; -import { BorderRadiusEdit, useIsBorderRadiusDisabled } from './border-radius'; -import { BorderStyleEdit, useIsBorderStyleDisabled } from './border-style'; -import { BorderWidthEdit, useIsBorderWidthDisabled } from './border-width'; +import useEditorFeature from '../components/use-editor-feature'; +import { BorderColorEdit } from './border-color'; +import { BorderRadiusEdit } from './border-radius'; +import { BorderStyleEdit } from './border-style'; +import { BorderWidthEdit } from './border-width'; export const BORDER_SUPPORT_KEY = '__experimentalBorder'; @@ -21,6 +22,22 @@ export function BorderPanel( props ) { const isDisabled = useIsBorderDisabled( props ); const isSupported = hasBorderSupport( props.name ); + const isColorSupported = + useEditorFeature( 'border.customColor' ) && + hasBorderSupport( props.name, 'color' ); + + const isRadiusSupported = + useEditorFeature( 'border.customRadius' ) && + hasBorderSupport( props.name, 'radius' ); + + const isStyleSupported = + useEditorFeature( 'border.customStyle' ) && + hasBorderSupport( props.name, 'style' ); + + const isWidthSupported = + useEditorFeature( 'border.customWidth' ) && + hasBorderSupport( props.name, 'width' ); + if ( isDisabled || ! isSupported ) { return null; } @@ -28,10 +45,10 @@ export function BorderPanel( props ) { return ( - - - - + { isStyleSupported && } + { isWidthSupported && } + { isRadiusSupported && } + { isColorSupported && } ); @@ -40,35 +57,31 @@ export function BorderPanel( props ) { /** * Determine whether there is block support for border properties. * - * @param {string} blockName Block name. - * @return {boolean} Whether there is support. + * @param {string} blockName Block name. + * @param {string} feature Border feature to check support for. + * @return {boolean} Whether there is support. */ -export function hasBorderSupport( blockName ) { +export function hasBorderSupport( blockName, feature = 'any' ) { if ( Platform.OS !== 'web' ) { return false; } const support = getBlockSupport( blockName, BORDER_SUPPORT_KEY ); - return !! ( - true === support || - support?.color || - support?.radius || - support?.width || - support?.style - ); -} + if ( support === true ) { + return true; + } -/** - * Determines if there a specific border feature is supported. - * - * @param {string} feature Name of the border feature e.g.`radius` - * @param {string|Object} blockType Block name or block type object. - * @return {boolean} Whether the border feature is supported. - */ -export function hasBorderFeatureSupport( feature, blockType ) { - const support = getBlockSupport( blockType, BORDER_SUPPORT_KEY ); - return !! ( true === support || ( support && support[ feature ] ) ); + if ( feature === 'any' ) { + return !! ( + support?.color || + support?.radius || + support?.width || + support?.style + ); + } + + return !! support?.[ feature ]; } /** @@ -84,18 +97,16 @@ export function shouldSkipSerialization( blockType ) { } /** - * Determines whether there is any block support for borders e.g. border radius, - * style, width etc. + * Determines if all border support features have been disabled. * - * @param {Object} props Block properties. - * @return {boolean} If border support is completely disabled. + * @return {boolean} If border support is completely disabled. */ -const useIsBorderDisabled = ( props = {} ) => { +const useIsBorderDisabled = () => { const configs = [ - useIsBorderColorDisabled( props ), - useIsBorderRadiusDisabled( props ), - useIsBorderStyleDisabled( props ), - useIsBorderWidthDisabled( props ), + ! useEditorFeature( 'border.customColor' ), + ! useEditorFeature( 'border.customRadius' ), + ! useEditorFeature( 'border.customStyle' ), + ! useEditorFeature( 'border.customWidth' ), ]; return configs.every( Boolean ); From c96ec94fbf653d546800b9bf6d8a69ed9b473214 Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Fri, 16 Apr 2021 16:30:17 +1000 Subject: [PATCH 13/13] Fix border support check --- lib/block-supports/border.php | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/lib/block-supports/border.php b/lib/block-supports/border.php index 8ad249ff95d33..3bdc2253534c9 100644 --- a/lib/block-supports/border.php +++ b/lib/block-supports/border.php @@ -13,12 +13,8 @@ */ function gutenberg_register_border_support( $block_type ) { // Determine if any border related features are supported. + $has_border_support = gutenberg_block_has_support( $block_type, array( '__experimentalBorder' ) ); $has_border_color_support = gutenberg_block_has_support( $block_type, array( '__experimentalBorder', 'color' ) ); - $has_border_support = - gutenberg_block_has_support( $block_type, array( '__experimentalBorder', 'radius' ) ) || - gutenberg_block_has_support( $block_type, array( '__experimentalBorder', 'style' ) ) || - gutenberg_block_has_support( $block_type, array( '__experimentalBorder', 'width' ) ) || - $has_border_color_support; // Setup attributes and styles within that if needed. if ( ! $block_type->attributes ) {