From f6efbc2ba29ca1b278cb1895ae9e55cdac020523 Mon Sep 17 00:00:00 2001 From: tellthemachines Date: Thu, 6 Jul 2023 11:32:04 +1000 Subject: [PATCH 1/7] Update layout documentation after stabilisation (#52316) --- docs/explanations/architecture/styles.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/explanations/architecture/styles.md b/docs/explanations/architecture/styles.md index faf2649efd13a..5b147b2bbe6ea 100644 --- a/docs/explanations/architecture/styles.md +++ b/docs/explanations/architecture/styles.md @@ -511,9 +511,7 @@ The global styles UI in the site editor has a screen for per-block styles. The l In addition to styles at the individual block level and in global styles, there is the concept of layout styles that are output for both blocks-based and classic themes. -The layout block support is an experimental approach for outputting common layout styles that are shared between blocks that are used for creating layouts. Layout styles are useful for providing common styling for any block that is a container for other blocks. Examples of blocks that depend on these layout styles include Group, Row, Columns, Buttons, and Social Icons. - -While the feature is part of WordPress core, it is still flagged as experimental in the sense that the features and output are still undergoing active development. It is therefore not yet a stable feature from the perspective of 3rd party blocks, as the API is likely to change. The feature is enabled in core blocks via the `layout` setting under `supports` in a block's `block.json` file. +The layout block support outputs common layout styles that are shared between blocks used for creating layouts. Layout styles are useful for providing common styling for any block that is a container for other blocks. Examples of blocks that depend on these layout styles include Group, Row, Columns, Buttons, and Social Icons. The feature is enabled in core blocks via the `layout` setting under `supports` in a block's `block.json` file. There are two primary places where Layout styles are output: @@ -523,22 +521,23 @@ Base layout styles are those styles that are common to all blocks that opt in to Base layout styles are output from within [the main PHP class](https://github.com/WordPress/wordpress-develop/blob/trunk/src/wp-includes/class-wp-theme-json.php) that handles global styles, and form part of the global styles stylesheet. In order to provide support for core blocks in classic themes, these styles are always output, irrespective of whether the theme provides its own `theme.json` file. -Common layout definitions are stored in [the core `theme.json` file](https://github.com/WordPress/wordpress-develop/blob/trunk/src/wp-includes/theme.json), but are not intended to be extended or overridden by themes. +Common layout definitions are stored in [the core layout block support file](https://github.com/WordPress/wordpress-develop/blob/trunk/src/wp-includes/block-supports/layout.php). #### Individual layout styles -When a block that opts in to the experimental layout support is rendered, two things are processed and added to the output via [`layout.php`](https://github.com/WordPress/wordpress-develop/blob/trunk/src/wp-includes/block-supports/layout.php): +When a block that opts in to layout support is rendered, two things are processed and added to the output via [`layout.php`](https://github.com/WordPress/wordpress-develop/blob/trunk/src/wp-includes/block-supports/layout.php): - Semantic class names are added to the block markup to indicate which layout settings are in use. For example, `is-layout-flow` is for blocks (such as Group) that use the default/flow layout, and `is-content-justification-right` is added when a user sets a block to use right justification. - Individual styles are generated for non-default layout values that are set on the individual block being rendered. These styles are attached to the block via a container class name using the form `wp-container-$id` where the `$id` is a [unique number](https://developer.wordpress.org/reference/functions/wp_unique_id/). #### Available layout types -There are currently three layout types in use: +There are currently four layout types in use: - Default/Flow: Items are stacked vertically. The parent container block is set to `display: flow` and the spacing between children is handled via vertical margins. - Constrained: Items are stacked vertically, using the same spacing logic as the Flow layout. Features constrained widths for child content, outputting widths for standard content size and wide size. Defaults to using global `contentSize` and `wideSize` values set in `settings.layout` in the `theme.json`. - Flex: Items are displayed using a Flexbox layout. Defaults to a horizontal orientation. Spacing between children is handled via the `gap` CSS property. +- Grid: Items are displayed using a Grid layout. Defaults to an `auto-fill` approach to column generation but can also be set to a fixed number of columns. Spacing between children is handled via the `gap` CSS property. For controlling spacing between blocks, and enabling block spacing controls see: [What is blockGap and how can I use it?](https://developer.wordpress.org/block-editor/how-to-guides/themes/theme-json/#what-is-blockgap-and-how-can-i-use-it). @@ -546,7 +545,7 @@ For controlling spacing between blocks, and enabling block spacing controls see: The layout block support is designed to enable control over layout features from within the block and site editors. Where possible, try to use the features of the blocks to determine particular layout requirements rather than relying upon additional stylesheets. -For themes that wish to target container blocks in order to add or adjust particular styles, the block's class name is often the best class name to use. Class names such as `wp-block-group` or `wp-block-columns` are usually reliable class names for targeting a particular block. +For themes that wish to target container blocks in order to add or adjust particular styles, the block's class name is often the best class name to use. Class names such as `wp-block-group` or `wp-block-columns` are usually reliable class names for targeting a particular block. In addition to block and layout classnames, there is also a classname composed of block and layout together: for example, for a Group block with a constrained layout it will be `wp-block-group-is-layout-constrained`. For targeting a block that uses a particular layout type, avoid targeting `wp-container-` as container classes may not always be present in the rendered markup. @@ -559,6 +558,7 @@ The current semantic class names that can be output by the Layout block support - `is-layout-flow`: Blocks that use the Default/Flow layout type. - `is-layout-constrained`: Blocks that use the Constrained layout type. - `is-layout-flex`: Blocks that use the Flex layout type. +- `is-layout-grid`: Blocks that used the Grid layout type. - `wp-container-$id`: Where `$id` is a semi-random number. A container class that only exists when the block contains non-default Layout values. This class should not be used directly for any CSS targeting as it may or may not be present. - `is-horizontal`: When a block explicitly sets `orientation` to `horizontal`. - `is-vertical`: When a block explicitly sets `orientation` to `vertical`. From 00cfeee55e19be3f590748dd5d5f8a23c93d0a00 Mon Sep 17 00:00:00 2001 From: Glen Davies Date: Thu, 6 Jul 2023 14:48:40 +1200 Subject: [PATCH 2/7] Patterns: Add handling of sync status to the wp-admin patterns list page (#52346) --- .../src/components/import-form/index.js | 4 ++-- packages/list-reusable-blocks/src/index.js | 4 +--- packages/list-reusable-blocks/src/utils/export.js | 2 ++ packages/list-reusable-blocks/src/utils/import.js | 10 ++++++++-- .../editor/various/manage-reusable-blocks.spec.js | 2 +- 5 files changed, 14 insertions(+), 8 deletions(-) diff --git a/packages/list-reusable-blocks/src/components/import-form/index.js b/packages/list-reusable-blocks/src/components/import-form/index.js index 3b3daa49571f4..9ba1589e52f39 100644 --- a/packages/list-reusable-blocks/src/components/import-form/index.js +++ b/packages/list-reusable-blocks/src/components/import-form/index.js @@ -49,8 +49,8 @@ function ImportForm( { instanceId, onUpload } ) { case 'Invalid JSON file': uiMessage = __( 'Invalid JSON file' ); break; - case 'Invalid Reusable block JSON file': - uiMessage = __( 'Invalid Reusable block JSON file' ); + case 'Invalid Pattern JSON file': + uiMessage = __( 'Invalid Pattern JSON file' ); break; default: uiMessage = __( 'Unknown error' ); diff --git a/packages/list-reusable-blocks/src/index.js b/packages/list-reusable-blocks/src/index.js index 3c9945139856f..4440ba1c49f05 100644 --- a/packages/list-reusable-blocks/src/index.js +++ b/packages/list-reusable-blocks/src/index.js @@ -31,9 +31,7 @@ document.addEventListener( 'DOMContentLoaded', () => { const showNotice = () => { const notice = document.createElement( 'div' ); notice.className = 'notice notice-success is-dismissible'; - notice.innerHTML = `

${ __( - 'Reusable block imported successfully!' - ) }

`; + notice.innerHTML = `

${ __( 'Pattern imported successfully!' ) }

`; const headerEnd = document.querySelector( '.wp-header-end' ); if ( ! headerEnd ) { diff --git a/packages/list-reusable-blocks/src/utils/export.js b/packages/list-reusable-blocks/src/utils/export.js index 0f70931c50080..4075c7576f134 100644 --- a/packages/list-reusable-blocks/src/utils/export.js +++ b/packages/list-reusable-blocks/src/utils/export.js @@ -25,11 +25,13 @@ async function exportReusableBlock( id ) { } ); const title = post.title.raw; const content = post.content.raw; + const syncStatus = post.wp_pattern_sync_status; const fileContent = JSON.stringify( { __file: 'wp_block', title, content, + syncStatus, }, null, 2 diff --git a/packages/list-reusable-blocks/src/utils/import.js b/packages/list-reusable-blocks/src/utils/import.js index 84c28b5fcfc80..465fb080ce8df 100644 --- a/packages/list-reusable-blocks/src/utils/import.js +++ b/packages/list-reusable-blocks/src/utils/import.js @@ -27,9 +27,11 @@ async function importReusableBlock( file ) { ! parsedContent.title || ! parsedContent.content || typeof parsedContent.title !== 'string' || - typeof parsedContent.content !== 'string' + typeof parsedContent.content !== 'string' || + ( parsedContent.syncStatus && + typeof parsedContent.syncStatus !== 'string' ) ) { - throw new Error( 'Invalid Reusable block JSON file' ); + throw new Error( 'Invalid Pattern JSON file' ); } const postType = await apiFetch( { path: `/wp/v2/types/wp_block` } ); const reusableBlock = await apiFetch( { @@ -38,6 +40,10 @@ async function importReusableBlock( file ) { title: parsedContent.title, content: parsedContent.content, status: 'publish', + meta: + parsedContent.syncStatus === 'unsynced' + ? { wp_pattern_sync_status: parsedContent.syncStatus } + : undefined, }, method: 'POST', } ); diff --git a/test/e2e/specs/editor/various/manage-reusable-blocks.spec.js b/test/e2e/specs/editor/various/manage-reusable-blocks.spec.js index 64d09dc39af72..bb390d2b39a8e 100644 --- a/test/e2e/specs/editor/various/manage-reusable-blocks.spec.js +++ b/test/e2e/specs/editor/various/manage-reusable-blocks.spec.js @@ -35,7 +35,7 @@ test.describe( 'Managing reusable blocks', () => { // Wait for the success notice. await expect( - page.locator( 'text=Reusable block imported successfully!' ) + page.locator( 'text=Pattern imported successfully!' ) ).toBeVisible(); // Refresh the page. From 892f9a657a10153ddd532bf8df8dd198302bc9e0 Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Thu, 6 Jul 2023 11:46:36 +0400 Subject: [PATCH 3/7] Query Pagination: Set 'clientId' as useSelect dependency (#52358) --- .../src/query-pagination/edit.js | 31 ++++++++++--------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/packages/block-library/src/query-pagination/edit.js b/packages/block-library/src/query-pagination/edit.js index 8ab1f63377949..7598eba5c1cac 100644 --- a/packages/block-library/src/query-pagination/edit.js +++ b/packages/block-library/src/query-pagination/edit.js @@ -34,20 +34,23 @@ export default function QueryPaginationEdit( { setAttributes, clientId, } ) { - const hasNextPreviousBlocks = useSelect( ( select ) => { - const { getBlocks } = select( blockEditorStore ); - const innerBlocks = getBlocks( clientId ); - /** - * Show the `paginationArrow` and `showLabel` controls only if a - * `QueryPaginationNext/Previous` block exists. - */ - return innerBlocks?.find( ( innerBlock ) => { - return [ - 'core/query-pagination-next', - 'core/query-pagination-previous', - ].includes( innerBlock.name ); - } ); - }, [] ); + const hasNextPreviousBlocks = useSelect( + ( select ) => { + const { getBlocks } = select( blockEditorStore ); + const innerBlocks = getBlocks( clientId ); + /** + * Show the `paginationArrow` and `showLabel` controls only if a + * `QueryPaginationNext/Previous` block exists. + */ + return innerBlocks?.find( ( innerBlock ) => { + return [ + 'core/query-pagination-next', + 'core/query-pagination-previous', + ].includes( innerBlock.name ); + } ); + }, + [ clientId ] + ); const blockProps = useBlockProps(); const innerBlocksProps = useInnerBlocksProps( blockProps, { template: TEMPLATE, From 9e1f63739f18334761367808b09535f7b83cf4d8 Mon Sep 17 00:00:00 2001 From: tellthemachines Date: Thu, 6 Jul 2023 17:52:45 +1000 Subject: [PATCH 4/7] =?UTF-8?q?Fix=20react-dropdown-menu=20version=20to=20?= =?UTF-8?q?avoid=20breaking=20change=20from=20one=20of=20=E2=80=A6=20(#523?= =?UTF-8?q?56)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix react-dropdown-menu version to avoid breaking change from one if its dependencies. * Changelog update * move changelog entry to the right place * Update package-lock --- package-lock.json | 4 ++-- packages/components/CHANGELOG.md | 4 ++++ packages/components/package.json | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 31da77f8ca62d..f9e56d0a6b9a1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17427,7 +17427,7 @@ "@emotion/styled": "^11.6.0", "@emotion/utils": "^1.0.0", "@floating-ui/react-dom": "1.0.0", - "@radix-ui/react-dropdown-menu": "^2.0.4", + "@radix-ui/react-dropdown-menu": "2.0.4", "@use-gesture/react": "^10.2.24", "@wordpress/a11y": "file:packages/a11y", "@wordpress/compose": "file:packages/compose", @@ -46493,7 +46493,7 @@ "optionator": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", - "integrity": "sha512-oCOQ8AIC2ciLy/sE2ehafRBleBgDLvzGhBRRev87sP7ovnbvQfqpc3XFI0DhHey2OfVoNV91W+GPC6B3540/5Q==", + "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", "dev": true, "requires": { "deep-is": "~0.1.3", diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 03f0ad1563448..6e38eb22213fc 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### Bug Fix + +- `Popover`: Pin `react-dropdown-menu` version to avoid breaking changes in dependency updates. ([52356](https://github.com/WordPress/gutenberg/pull/52356)). + ## 25.3.0 (2023-07-05) ### Enhancements diff --git a/packages/components/package.json b/packages/components/package.json index 2b6595215011e..74220c11013e2 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -39,7 +39,7 @@ "@emotion/styled": "^11.6.0", "@emotion/utils": "^1.0.0", "@floating-ui/react-dom": "1.0.0", - "@radix-ui/react-dropdown-menu": "^2.0.4", + "@radix-ui/react-dropdown-menu": "2.0.4", "@use-gesture/react": "^10.2.24", "@wordpress/a11y": "file:../a11y", "@wordpress/compose": "file:../compose", From cea0afda8a086ba12eb634202ad2a6f771655756 Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Thu, 6 Jul 2023 09:53:18 +0200 Subject: [PATCH 5/7] [RNMobile] Fix a crash related to Reanimated when closing the editor (#52320) * Update block drop position using Reanimated's shared value Seems there's some kind of incompatibility on calling a JS function from a worklet invoked from a gesture handler. For this reason, the logic to set the dropping insertion point has been updated. It now uses a Reanimated's shared value to keep the dragging over position and `useDerivedValue` hook to listen for changes. * Update `react-native-editor` changelog --- .../block-draggable/index.native.js | 10 +- .../use-block-drop-zone/index.native.js | 93 +++++++++++++------ packages/react-native-editor/CHANGELOG.md | 1 + 3 files changed, 73 insertions(+), 31 deletions(-) diff --git a/packages/block-editor/src/components/block-draggable/index.native.js b/packages/block-editor/src/components/block-draggable/index.native.js index a6deca474dd56..d153843cae6a4 100644 --- a/packages/block-editor/src/components/block-draggable/index.native.js +++ b/packages/block-editor/src/components/block-draggable/index.native.js @@ -109,8 +109,12 @@ const BlockDraggableWrapper = ( { children, isRTL } ) => { draggingScrollHandler( event ); }; - const { onBlockDragOver, onBlockDragEnd, onBlockDrop, targetBlockIndex } = - useBlockDropZone(); + const { + onBlockDragOverWorklet, + onBlockDragEnd, + onBlockDrop, + targetBlockIndex, + } = useBlockDropZone(); // Stop dragging blocks if the block draggable is unmounted. useEffect( () => { @@ -184,7 +188,7 @@ const BlockDraggableWrapper = ( { children, isRTL } ) => { chip.y.value = dragPosition.y; currentYPosition.value = dragPosition.y; - runOnJS( onBlockDragOver )( { x, y: y + scroll.offsetY.value } ); + onBlockDragOverWorklet( { x, y: y + scroll.offsetY.value } ); // Update scrolling velocity scrollOnDragOver( dragPosition.y ); diff --git a/packages/block-editor/src/components/use-block-drop-zone/index.native.js b/packages/block-editor/src/components/use-block-drop-zone/index.native.js index 5a64803aa4bc2..4f00880873c2f 100644 --- a/packages/block-editor/src/components/use-block-drop-zone/index.native.js +++ b/packages/block-editor/src/components/use-block-drop-zone/index.native.js @@ -1,14 +1,17 @@ /** * External dependencies */ -import { useSharedValue } from 'react-native-reanimated'; +import { + runOnJS, + useDerivedValue, + useSharedValue, +} from 'react-native-reanimated'; /** * WordPress dependencies */ import { useSelect } from '@wordpress/data'; import { useCallback } from '@wordpress/element'; -import { useThrottle } from '@wordpress/compose'; /** * Internal dependencies @@ -18,6 +21,8 @@ import { useBlockListContext } from '../block-list/block-list-context'; import { getDistanceToNearestEdge } from '../../utils/math'; import useOnBlockDrop from '../use-on-block-drop'; +const UPDATE_TARGET_BLOCK_INDEX_THRESHOLD = 20; // In pixels + /** @typedef {import('../../utils/math').WPPoint} WPPoint */ /** @@ -111,6 +116,14 @@ export default function useBlockDropZone( { rootClientId: targetRootClientId = '', } = {} ) { const targetBlockIndex = useSharedValue( null ); + const dragPosition = { + x: useSharedValue( 0 ), + y: useSharedValue( 0 ), + }; + const prevDragPosition = { + x: useSharedValue( 0 ), + y: useSharedValue( 0 ), + }; const { getBlockListSettings, getSettings } = useSelect( blockEditorStore ); const { blocksLayouts, getBlockLayoutsOrderedByYCoord } = @@ -118,43 +131,67 @@ export default function useBlockDropZone( { const getSortedBlocksLayouts = useCallback( () => { return getBlockLayoutsOrderedByYCoord( blocksLayouts.current ); + // We use the value of `blocksLayouts` as the dependency. + // eslint-disable-next-line react-hooks/exhaustive-deps }, [ blocksLayouts.current ] ); const isRTL = getSettings().isRTL; const onBlockDrop = useOnBlockDrop(); - const throttled = useThrottle( - useCallback( - ( event ) => { - const sortedBlockLayouts = getSortedBlocksLayouts(); - - const targetIndex = getNearestBlockIndex( - sortedBlockLayouts, - { x: event.x, y: event.y }, - getBlockListSettings( targetRootClientId )?.orientation, - isRTL - ); - if ( targetIndex !== null ) { - targetBlockIndex.value = targetIndex ?? 0; - } - }, - [ - getSortedBlocksLayouts, - getNearestBlockIndex, - getBlockListSettings, - targetBlockIndex, - ] - ), - 200 + const updateTargetBlockIndex = useCallback( + ( event ) => { + const sortedBlockLayouts = getSortedBlocksLayouts(); + + const targetIndex = getNearestBlockIndex( + sortedBlockLayouts, + { x: event.x, y: event.y }, + getBlockListSettings( targetRootClientId )?.orientation, + isRTL + ); + if ( targetIndex !== null ) { + targetBlockIndex.value = targetIndex ?? 0; + } + }, + [ + getSortedBlocksLayouts, + getBlockListSettings, + targetRootClientId, + isRTL, + targetBlockIndex, + ] ); + useDerivedValue( () => { + const x = dragPosition.x.value; + const y = dragPosition.y.value; + const prevX = prevDragPosition.x.value; + const prevY = prevDragPosition.y.value; + // `updateTargetBlockIndex` performs expensive calculations, so we throttle + // the call using a offset threshold based on the dragging position. + if ( + Math.abs( x - prevX ) >= UPDATE_TARGET_BLOCK_INDEX_THRESHOLD || + Math.abs( y - prevY ) >= UPDATE_TARGET_BLOCK_INDEX_THRESHOLD + ) { + runOnJS( updateTargetBlockIndex )( { x, y } ); + prevDragPosition.x.value = x; + prevDragPosition.y.value = y; + return true; + } + return false; + } ); + return { - onBlockDragOver( event ) { - throttled( event ); + onBlockDragOver( { x, y } ) { + dragPosition.x.value = x; + dragPosition.y.value = y; + }, + onBlockDragOverWorklet( { x, y } ) { + 'worklet'; + dragPosition.x.value = x; + dragPosition.y.value = y; }, onBlockDragEnd() { - throttled.cancel(); targetBlockIndex.value = null; }, onBlockDrop: ( event ) => { diff --git a/packages/react-native-editor/CHANGELOG.md b/packages/react-native-editor/CHANGELOG.md index 772a11a3dbd80..d43144db54722 100644 --- a/packages/react-native-editor/CHANGELOG.md +++ b/packages/react-native-editor/CHANGELOG.md @@ -11,6 +11,7 @@ For each user feature we should also add a importance categorization label to i ## Unreleased - [*] Rename "Reusable blocks" to "Synced patterns", aligning with the web editor. [#51704] +- [**] Fix a crash related to Reanimated when closing the editor [#52320] ## 1.98.1 - [*] fix: Display heading level dropdown icons and labels [#52004] From e72a740e189be3d47024e803f3709cac06c4ff31 Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Thu, 6 Jul 2023 12:45:40 +0400 Subject: [PATCH 6/7] Template Parts: Set attributes 'area' as useSelect dependency (#52330) --- packages/block-library/src/template-part/edit/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/block-library/src/template-part/edit/index.js b/packages/block-library/src/template-part/edit/index.js index b7ae4f6043afb..a4d2e5f2a017e 100644 --- a/packages/block-library/src/template-part/edit/index.js +++ b/packages/block-library/src/template-part/edit/index.js @@ -77,7 +77,7 @@ export default function TemplatePartEdit( { area: _area, }; }, - [ templatePartId, clientId ] + [ templatePartId, attributes.area, clientId ] ); const { templateParts } = useAlternativeTemplateParts( area, From bc663f6418c2775b24b3e62a7c639f2d72eb1b51 Mon Sep 17 00:00:00 2001 From: Marin Atanasov <8436925+tyxla@users.noreply.github.com> Date: Thu, 6 Jul 2023 13:45:44 +0300 Subject: [PATCH 7/7] Block Editor: Add README for BlockControls (#52366) --- .../src/components/block-controls/README.md | 89 +++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 packages/block-editor/src/components/block-controls/README.md diff --git a/packages/block-editor/src/components/block-controls/README.md b/packages/block-editor/src/components/block-controls/README.md new file mode 100644 index 0000000000000..ee02e96e08b4d --- /dev/null +++ b/packages/block-editor/src/components/block-controls/README.md @@ -0,0 +1,89 @@ +# BlockControls + +When the user selects a particular block, a toolbar positioned above the selected block displays a set of control buttons. Certain block-level controls are automatically included in the toolbar under specific circumstances. For example, there is a control for converting the block into a different type or when the focused element is a RichText component. + +With `BlockControls`, you can customize the toolbar to include controls specific to your block type. If the return value of your block type's `edit` function includes a `BlockControls` element, the controls nested inside it will be shown in the selected block's toolbar. + +![Screenshot of the block controls of a Paragraph block inside the block editor](https://raw.githubusercontent.com/WordPress/gutenberg/HEAD/docs/assets/toolbar-text.png) + +## Usage + +```jsx +/** + * WordPress dependencies + */ +import { + BlockControls, + __experimentalBlockAlignmentMatrixControl as BlockAlignmentMatrixControl, + useBlockProps, +} from '@wordpress/block-editor'; +import { __ } from '@wordpress/i18n'; + +export default function MyBlockEdit( { attributes, setAttributes } ) { + const blockProps = useBlockProps( { + className: 'my-block__custom-class', + } ); + const { contentPosition } = attributes; + + return ( +
+ { + + + setAttributes( { + contentPosition: nextPosition, + } ) + } + /> + + } +
+ ); +} + +/// ... + +; +``` + +See [this custom block tutorial page](/docs/how-to-guides/block-tutorial/block-controls-toolbar-and-sidebar.md) for more information and block controls examples. + +Furthermore, the READMEs of various components inside the block editor package and the components package include examples that also utilize `BlockControls` and can be a good reference. + +### Props + +The component accepts the following props: + +### `group` + +Group of the block controls. Allows you to create and render multiple groups of block controls. + +- Type: `string` +- Default: `default` +- Required: No + +### `controls` + +Allows overriding the default `controls` if the `default` group is used. + +See [this custom block tutorial page](/docs/how-to-guides/block-tutorial/block-controls-toolbar-and-sidebar.md) for more details and examples with block controls. + +- Type: `array` + +### `children` + +Additional control components to be rendered. + +- Type: `Element` +- Required: No. + + +### `__experimentalShareWithChildBlocks` + +Whether the additional block controls should be added to the block toolbars of child blocks. + +- Type: `boolean` +- Default: `false`