Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Spacing Support: Update padding support to allow configurable sides #30607

Merged
merged 9 commits into from
Apr 30, 2021
16 changes: 14 additions & 2 deletions docs/reference-guides/block-api/block-supports.md
Original file line number Diff line number Diff line change
Expand Up @@ -329,16 +329,28 @@ supports: {
- Type: `Object`
- Default value: null
- Subproperties:
- `padding`: type `boolean`, default value `false`
- `padding`: type `boolean` or `array`, default value `false`

This value signals that a block supports some of the CSS style properties related to spacing. When it does, the block editor will show UI controls for the user to set their values, if [the theme declares support](/docs/how-to-guides/themes/theme-support.md##cover-block-padding).

```js
supports: {
padding: true, // Enable padding color UI control.
spacing: {
oandregal marked this conversation as resolved.
Show resolved Hide resolved
padding: true, // Enable padding UI control.
}
}
```

When the block declares support for a specific spacing property, the attributes definition is extended to include the `style` attribute.

- `style`: attribute of `object` type with no default assigned. This is added when `padding` support is declared. It stores the custom values set by the user.

```js
supports: {
spacing: {
padding: [ 'top', 'bottom' ], // Enable padding for arbitrary sides.
}
}
```

A spacing property may define an array of allowable sides that can be configured. When arbitrary sides are defined only UI controls for those sides are displayed.

This file was deleted.

1 change: 1 addition & 0 deletions packages/block-editor/src/hooks/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ import './font-size';
import './border-color';
import './layout';

export { useCustomSides } from './spacing';
export { getColorClassesAndStyles, useColorProps } from './use-color-props';
32 changes: 26 additions & 6 deletions packages/block-editor/src/hooks/padding.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ import { __experimentalBoxControl as BoxControl } from '@wordpress/components';
/**
* Internal dependencies
*/
import useEditorFeature from '../components/use-editor-feature';
import { SPACING_SUPPORT_KEY, useCustomSides } from './spacing';
import { cleanEmptyObject } from './utils';
import { useCustomUnits } from '../components/unit-control';

export const SPACING_SUPPORT_KEY = 'spacing';

const isWeb = Platform.OS === 'web';
const CSS_UNITS = [
{
Expand Down Expand Up @@ -43,10 +43,27 @@ const CSS_UNITS = [
},
];

const hasPaddingSupport = ( blockName ) => {
const spacingSupport = getBlockSupport( blockName, SPACING_SUPPORT_KEY );
return spacingSupport && spacingSupport.padding !== false;
};
/**
* Determines if there is padding support.
*
* @param {string|Object} blockType Block name or Block Type object.
* @return {boolean} Whether there is support.
*/
export function hasPaddingSupport( blockType ) {
const support = getBlockSupport( blockType, SPACING_SUPPORT_KEY );
return !! ( true === support || support?.padding );
}

/**
* Custom hook that checks if padding settings have been disabled.
*
* @param {string} name The name of the block.
* @return {boolean} Whether padding setting is disabled.
*/
export function useIsPaddingDisabled( { name: blockName } = {} ) {
const isDisabled = ! useEditorFeature( 'spacing.customPadding' );
return ! hasPaddingSupport( blockName ) || isDisabled;
}

/**
* Inspector control panel containing the padding related configuration
Expand All @@ -63,6 +80,7 @@ export function PaddingEdit( props ) {
} = props;

const units = useCustomUnits( CSS_UNITS );
const sides = useCustomSides( blockName, 'padding' );

if ( ! hasPaddingSupport( blockName ) ) {
return null;
Expand All @@ -72,6 +90,7 @@ export function PaddingEdit( props ) {
const newStyle = {
...style,
spacing: {
...style?.spacing,
padding: next,
},
};
Expand Down Expand Up @@ -102,6 +121,7 @@ export function PaddingEdit( props ) {
onChange={ onChange }
onChangeShowVisualizer={ onChangeShowVisualizer }
label={ __( 'Padding' ) }
sides={ sides }
units={ units }
/>
</>
Expand Down
90 changes: 90 additions & 0 deletions packages/block-editor/src/hooks/spacing.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/**
* WordPress dependencies
*/
import { PanelBody } from '@wordpress/components';
import { Platform } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import { getBlockSupport } from '@wordpress/blocks';

/**
* Internal dependencies
*/
import InspectorControls from '../components/inspector-controls';
import {
PaddingEdit,
hasPaddingSupport,
useIsPaddingDisabled,
} from './padding';

export const SPACING_SUPPORT_KEY = 'spacing';

/**
* Inspector controls for spacing support.
*
* @param {Object} props Block props.
* @return {WPElement} Inspector controls for spacing support features.
*/
export function SpacingPanel( props ) {
const isDisabled = useIsSpacingDisabled( props );
const isSupported = hasSpacingSupport( props.name );

if ( isDisabled || ! isSupported ) {
return null;
}

return (
<InspectorControls key="spacing">
<PanelBody title={ __( 'Spacing' ) }>
<PaddingEdit { ...props } />
</PanelBody>
</InspectorControls>
);
}

/**
* Determine whether there is block support for padding.
*
* @param {string} blockName Block name.
* @return {boolean} Whether there is support.
*/
export function hasSpacingSupport( blockName ) {
aaronrobertshaw marked this conversation as resolved.
Show resolved Hide resolved
if ( Platform.OS !== 'web' ) {
return false;
}

return hasPaddingSupport( blockName );
}

/**
* Determines whether spacing support has been disabled.
*
* @param {Object} props Block properties.
* @return {boolean} If spacing support is completely disabled.
*/
const useIsSpacingDisabled = ( props = {} ) => {
const paddingDisabled = useIsPaddingDisabled( props );

return paddingDisabled;
};

/**
* Custom hook to retrieve which padding/margin is supported
* e.g. top, right, bottom or left.
*
* Sides are opted into by default. It is only if a specific side is set to
* false that it is omitted.
*
* @param {string} blockName Block name.
* @param {string} feature The feature custom sides relate to e.g. padding or margins.
* @return {Object} Sides supporting custom margin.
*/
export function useCustomSides( blockName, feature ) {
const support = getBlockSupport( blockName, SPACING_SUPPORT_KEY );

// Skip when setting is boolean as theme isn't setting arbitrary sides.
if ( typeof support[ feature ] === 'boolean' ) {
return;
}

return support[ feature ];
}
18 changes: 3 additions & 15 deletions packages/block-editor/src/hooks/style.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@ import { createHigherOrderComponent } from '@wordpress/compose';
import { BORDER_SUPPORT_KEY, BorderPanel } from './border';
import { COLOR_SUPPORT_KEY, ColorEdit } from './color';
import { TypographyPanel, TYPOGRAPHY_SUPPORT_KEYS } from './typography';
import { SPACING_SUPPORT_KEY, PaddingEdit } from './padding';
import SpacingPanelControl from '../components/spacing-panel-control';
import { SPACING_SUPPORT_KEY, SpacingPanel } from './spacing';

const styleSupportKeys = [
...TYPOGRAPHY_SUPPORT_KEYS,
Expand Down Expand Up @@ -140,7 +139,7 @@ export function addSaveProps( props, blockType, attributes ) {
}

/**
* Filters registered block settings to extand the block edit wrapper
* Filters registered block settings to extend the block edit wrapper
* to apply the desired styles and classnames properly.
*
* @param {Object} settings Original block settings
Expand Down Expand Up @@ -173,23 +172,12 @@ export function addEditProps( settings ) {
*/
export const withBlockControls = createHigherOrderComponent(
( BlockEdit ) => ( props ) => {
const { name: blockName } = props;

const hasSpacingSupport = hasBlockSupport(
blockName,
SPACING_SUPPORT_KEY
);

return [
<TypographyPanel key="typography" { ...props } />,
<BorderPanel key="border" { ...props } />,
<ColorEdit key="colors" { ...props } />,
<BlockEdit key="edit" { ...props } />,
hasSpacingSupport && (
<SpacingPanelControl key="spacing">
<PaddingEdit { ...props } />
</SpacingPanelControl>
),
<SpacingPanel key="spacing" { ...props } />,
];
},
'withToolbarControls'
Expand Down
4 changes: 4 additions & 0 deletions packages/block-editor/src/hooks/test/style.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ describe( 'getInlineStyles', () => {
style: 'dotted',
color: '#21759b',
},
spacing: {
padding: { top: '10px' },
},
} )
).toEqual( {
backgroundColor: 'black',
Expand All @@ -33,6 +36,7 @@ describe( 'getInlineStyles', () => {
color: 'red',
lineHeight: 1.5,
fontSize: 10,
paddingTop: '10px',
} );
} );
} );
Expand Down
1 change: 1 addition & 0 deletions packages/block-editor/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import './hooks';
export {
getColorClassesAndStyles as __experimentalGetColorClassesAndStyles,
useColorProps as __experimentalUseColorProps,
useCustomSides as __experimentalUseCustomSides,
} from './hooks';
export * from './components';
export * from './utils';
Expand Down
59 changes: 40 additions & 19 deletions packages/edit-site/src/components/sidebar/spacing-panel.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
__experimentalBoxControl as BoxControl,
Copy link
Contributor

Choose a reason for hiding this comment

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

For another time in the future, it looks like the global styles UI could share a lot of code with the hooks (not specific to this PR). cc @nosolosw

Copy link
Contributor Author

Choose a reason for hiding this comment

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

On the topic of sharing, there could be some benefit for the block support hooks to be able to access the default theme.json style values e.g. to reset controls correctly. From memory, I don't think these values are currently available within the post editor's state like they are in the site editor.

So if a RangeControl is reset it might just be to 0 however the managed styles created from the theme.json might be 50. The result is visually the control doesn't really match what is seen in the editor.

Copy link
Member

Choose a reason for hiding this comment

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

For another time in the future, it looks like the global styles UI could share a lot of code with the hooks (not specific to this PR). cc @nosolosw

I'd like to see that very much. I think the current struggle is that the hooks depend on the block's attributes as input (example).

PanelBody,
} from '@wordpress/components';
import { __experimentalUseCustomSides as useCustomSides } from '@wordpress/block-editor';

/**
* Internal dependencies
Expand Down Expand Up @@ -42,7 +43,13 @@ const CSS_UNITS = [
},
];

export function useHasSpacingPanel( { supports, name } ) {
export function useHasSpacingPanel( context ) {
const hasPadding = useHasPadding( context );

return hasPadding;
}

export function useHasPadding( { name, supports } ) {
return (
useEditorFeature( 'spacing.customPadding', name ) &&
supports.includes( 'padding' )
Expand All @@ -65,29 +72,43 @@ function useCustomUnits( { units, contextName } ) {
return usedUnits.length === 0 ? false : usedUnits;
}

export default function SpacingPanel( {
context: { name },
getStyle,
setStyle,
} ) {
function filterValuesBySides( values, sides ) {
if ( ! sides ) {
// If no custom side configuration all sides are opted into by default.
return values;
}

// Only include sides opted into within filtered values.
const filteredValues = {};
sides.forEach( ( side ) => ( filteredValues[ side ] = values[ side ] ) );

return filteredValues;
}

export default function SpacingPanel( { context, getStyle, setStyle } ) {
const { name } = context;
const showPaddingControl = useHasPadding( context );
const units = useCustomUnits( { contextName: name, units: CSS_UNITS } );

const paddingValues = getStyle( name, 'padding' );
const setPaddingValues = ( { top, right, bottom, left } ) => {
setStyle( name, 'padding', {
top: top || paddingValues?.top,
right: right || paddingValues?.right,
bottom: bottom || paddingValues?.bottom,
left: left || paddingValues?.left,
} );
const paddingSides = useCustomSides( name, 'padding' );

const setPaddingValues = ( newPaddingValues ) => {
const padding = filterValuesBySides( newPaddingValues, paddingSides );
setStyle( name, 'padding', padding );
};

return (
<PanelBody title={ __( 'Spacing' ) }>
<BoxControl
values={ paddingValues }
onChange={ setPaddingValues }
label={ __( 'Padding' ) }
units={ units }
/>
{ showPaddingControl && (
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we hide the panel entirely if there's no padding control?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Unless I've missed something, we already do.

Within global-styles-sidebar.js there is a check using useHasSpacingPanel as to whether the SpacingPanel should be displayed. This matches the current approach for the typography, color and border panels.

The showPaddingControl check here within the component is because we'll soon be adding a control for margins as well.

Copy link
Contributor

Choose a reason for hiding this comment

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

I guess I was just wondering why this component (taken separately) renders an empty panel if padding is not shown (I'm sure this will change with the addition of margins though). This is not that important.

<BoxControl
values={ paddingValues }
onChange={ setPaddingValues }
label={ __( 'Padding' ) }
sides={ paddingSides }
units={ units }
/>
) }
</PanelBody>
);
}