Skip to content

Commit

Permalink
Behaviors UI (#49972)
Browse files Browse the repository at this point in the history
Co-authored-by: Carlos Bravo <carlos.bravo@automattic.com>
Co-authored-by: Mario Santos <santosguillamot@gmail.com>
  • Loading branch information
3 people authored May 24, 2023
1 parent 54fc4ec commit 03751d9
Show file tree
Hide file tree
Showing 20 changed files with 425 additions and 5 deletions.
2 changes: 1 addition & 1 deletion docs/reference-guides/core-blocks.md
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,7 @@ Insert an image to make a visual statement. ([Source](https://github.com/WordPre
- **Name:** core/image
- **Category:** media
- **Supports:** anchor, color (~~background~~, ~~text~~), filter (duotone)
- **Attributes:** align, alt, caption, height, href, id, linkClass, linkDestination, linkTarget, rel, sizeSlug, title, url, width
- **Attributes:** align, alt, behaviors, caption, height, href, id, linkClass, linkDestination, linkTarget, rel, sizeSlug, title, url, width

## Latest Comments

Expand Down
23 changes: 23 additions & 0 deletions docs/reference-guides/data/data-core-block-editor.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,29 @@ _Returns_

- `Array?`: The list of allowed block types.

### getBehaviors

Returns the behaviors registered with the editor.

Behaviors are named, reusable pieces of functionality that can be attached to blocks. They are registered with the editor using the `theme.json` file.

_Usage_

```js
const behaviors = select( blockEditorStore ).getBehaviors();
if ( behaviors?.lightbox ) {
// Do something with the lightbox.
}
```
_Parameters_
- _state_ `Object`: Editor state.
_Returns_
- `Object`: The editor behaviors object.
### getBlock
Returns a block given its client ID. This is a parsed copy of the block, containing its `blockName`, `clientId`, and current `attributes` state. This is not the block's registration settings, which must be retrieved from the blocks module registration store.
Expand Down
2 changes: 2 additions & 0 deletions lib/class-wp-theme-json-gutenberg.php
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,7 @@ class WP_Theme_JSON_Gutenberg {
'templateParts',
'title',
'version',
'behaviors',
);

/**
Expand Down Expand Up @@ -404,6 +405,7 @@ class WP_Theme_JSON_Gutenberg {
'textDecoration' => null,
'textTransform' => null,
),
'behaviors' => null,
);

/**
Expand Down
20 changes: 20 additions & 0 deletions lib/compat/wordpress-6.3/behaviors.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php
/**
* Behaviors.
*
* Updates the block editor settings with the theme's behaviors.
*
* @package gutenberg
*/

add_filter(
'block_editor_settings_all',
function( $settings ) {
$theme_data = WP_Theme_JSON_Resolver_Gutenberg::get_merged_data()->get_data();
if ( array_key_exists( 'behaviors', $theme_data ) ) {
$settings['behaviors'] = $theme_data['behaviors'];
}
return $settings;
},
PHP_INT_MAX
);
2 changes: 1 addition & 1 deletion lib/load.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ function gutenberg_is_experiment_enabled( $name ) {
require_once __DIR__ . '/compat/wordpress-6.3/theme-previews.php';
require_once __DIR__ . '/compat/wordpress-6.3/navigation-block-preloading.php';
require_once __DIR__ . '/compat/wordpress-6.3/link-template.php';
require_once __DIR__ . '/compat/wordpress-6.3/behaviors.php';

// Experimental.
if ( ! class_exists( 'WP_Rest_Customizer_Nonces' ) ) {
Expand Down Expand Up @@ -165,4 +166,3 @@ function gutenberg_is_experiment_enabled( $name ) {
require __DIR__ . '/block-supports/duotone.php';
require __DIR__ . '/block-supports/anchor.php';
require __DIR__ . '/block-supports/shadow.php';

12 changes: 12 additions & 0 deletions lib/theme.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
{
"version": 2,
"behaviors": {
"blocks": {
"core/image": {
"lightbox": false
}
}
},
"settings": {
"appearanceTools": false,
"useRootPaddingAwareAlignments": false,
Expand Down Expand Up @@ -450,6 +457,11 @@
"style": true,
"width": true
}
},
"core/image": {
"behaviors": {
"lightbox": true
}
}
}
},
Expand Down
21 changes: 18 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/block-editor/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
"change-case": "^4.1.2",
"classnames": "^2.3.1",
"colord": "^2.7.0",
"deepmerge": "^4.3.0",
"diff": "^4.0.2",
"dom-scroll-into-view": "^1.2.1",
"fast-deep-equal": "^3.1.3",
Expand Down
104 changes: 104 additions & 0 deletions packages/block-editor/src/hooks/behaviors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/**
* WordPress dependencies
*/
import { addFilter } from '@wordpress/hooks';
import { SelectControl } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
import { createHigherOrderComponent } from '@wordpress/compose';
import { select } from '@wordpress/data';

/**
* Internal dependencies
*/
import { InspectorControls } from '../components';
import { store as blockEditorStore } from '../store';

/**
* External dependencies
*/
import merge from 'deepmerge';

/**
* Override the default edit UI to include a new block inspector control for
* assigning behaviors to blocks if behaviors are enabled in the theme.json.
*
* Currently, only the `core/image` block is supported.
*
* @param {WPComponent} BlockEdit Original component.
*
* @return {WPComponent} Wrapped component.
*/
export const withBehaviors = createHigherOrderComponent( ( BlockEdit ) => {
return ( props ) => {
// Only add behaviors to the core/image block.
if ( props.name !== 'core/image' ) {
return <BlockEdit { ...props } />;
}

const settings =
select( blockEditorStore ).getSettings()?.__experimentalFeatures
?.blocks?.[ props.name ]?.behaviors;

if (
! settings ||
// If every behavior is disabled, do not show the behaviors inspector control.
Object.entries( settings ).every( ( [ , value ] ) => ! value )
) {
return <BlockEdit { ...props } />;
}

const { behaviors: blockBehaviors } = props.attributes;

// Get the theme behaviors for the block from the theme.json.
const themeBehaviors =
select( blockEditorStore ).getBehaviors()?.blocks?.[ props.name ];

// Block behaviors take precedence over theme behaviors.
const behaviors = merge( themeBehaviors, blockBehaviors || {} );

return (
<>
<BlockEdit { ...props } />
<InspectorControls group="advanced">
<SelectControl
__nextHasNoMarginBottom
label={ __( 'Behaviors' ) }
// At the moment we are only supporting one behavior (Lightbox)
value={ behaviors?.lightbox ? 'lightbox' : '' }
options={ Object.entries( settings )
.filter( ( [ , behaviorValue ] ) => behaviorValue ) // Filter out behaviors that are disabled.
.map( ( [ behaviorName ] ) => ( {
value: behaviorName,
label:
// Capitalize the first letter of the behavior name.
behaviorName[ 0 ].toUpperCase() +
behaviorName.slice( 1 ).toLowerCase(),
} ) )
.concat( {
value: '',
label: __( 'No behaviors' ),
} ) }
onChange={ ( nextValue ) => {
// If the user selects something, it means that they want to
// change the default value (true) so we save it in the attributes.
props.setAttributes( {
behaviors: {
lightbox: nextValue === 'lightbox',
},
} );
} }
hideCancelButton={ true }
help={ __( 'Add behaviors' ) }
size="__unstable-large"
/>
</InspectorControls>
</>
);
};
}, 'withBehaviors' );

addFilter(
'editor.BlockEdit',
'core/behaviors/with-inspector-control',
withBehaviors
);
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 @@ -20,6 +20,7 @@ import './layout';
import './content-lock-ui';
import './metadata';
import './metadata-name';
import './behaviors';

export { useCustomSides } from './dimensions';
export { useLayoutClasses, useLayoutStyles } from './layout';
Expand Down
24 changes: 24 additions & 0 deletions packages/block-editor/src/store/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -2490,6 +2490,30 @@ export function getSettings( state ) {
return state.settings;
}

/**
* Returns the behaviors registered with the editor.
*
* Behaviors are named, reusable pieces of functionality that can be
* attached to blocks. They are registered with the editor using the
* `theme.json` file.
*
* @example
*
* ```js
* const behaviors = select( blockEditorStore ).getBehaviors();
* if ( behaviors?.lightbox ) {
* // Do something with the lightbox.
* }
*```
*
* @param {Object} state Editor state.
*
* @return {Object} The editor behaviors object.
*/
export function getBehaviors( state ) {
return state.settings.behaviors;
}

/**
* Returns true if the most recent block change is be considered persistent, or
* false otherwise. A persistent change is one committed by BlockEditorProvider
Expand Down
3 changes: 3 additions & 0 deletions packages/block-library/src/image/block.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,9 @@
"source": "attribute",
"selector": "figure > a",
"attribute": "target"
},
"behaviors": {
"type": "object"
}
},
"supports": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ const BLOCK_EDITOR_SETTINGS = [
'__unstableIsPreviewMode',
'__unstableResolvedAssets',
'__unstableIsBlockBasedTheme',
'behaviors',
];

/**
Expand Down
Loading

1 comment on commit 03751d9

@github-actions
Copy link

Choose a reason for hiding this comment

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

Flaky tests detected in 03751d9.
Some tests passed with failed attempts. The failures may not be related to this commit but are still reported for visibility. See the documentation for more information.

🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/5073396028
📝 Reported issues:

Please sign in to comment.