From 37dbd03cc1e3b7098aa4047e03994ba69478f7ad Mon Sep 17 00:00:00 2001
From: Nik Tsekouras <ntsekouras@outlook.com>
Date: Wed, 30 Aug 2023 13:35:09 +0300
Subject: [PATCH 01/34] [Commands]: Fix `move to` command condition for
 registering (#54049)

---
 .../block-editor/src/components/use-block-commands/index.js  | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/packages/block-editor/src/components/use-block-commands/index.js b/packages/block-editor/src/components/use-block-commands/index.js
index 184c33714e710..4c3dfc71ea4f2 100644
--- a/packages/block-editor/src/components/use-block-commands/index.js
+++ b/packages/block-editor/src/components/use-block-commands/index.js
@@ -126,6 +126,7 @@ const useActionsCommands = () => {
 		getBlocksByClientId,
 		canMoveBlocks,
 		canRemoveBlocks,
+		getBlockCount,
 	} = useSelect( blockEditorStore );
 	const { getDefaultBlockName, getGroupingBlockName } =
 		useSelect( blocksStore );
@@ -189,7 +190,9 @@ const useActionsCommands = () => {
 		);
 	} );
 	const canRemove = canRemoveBlocks( clientIds, rootClientId );
-	const canMove = canMoveBlocks( clientIds, rootClientId );
+	const canMove =
+		canMoveBlocks( clientIds, rootClientId ) &&
+		getBlockCount( rootClientId ) !== 1;
 
 	const commands = [
 		{

From cd5d5fee5edca95cd029bb18601c49dec10b35a6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Greg=20Zi=C3=B3=C5=82kowski?= <grzegorz@gziolo.pl>
Date: Wed, 30 Aug 2023 13:13:54 +0200
Subject: [PATCH 02/34] Docs: Extend the information about using `render` with
 `block.json` (#53973)

* Docs: Extend the information about using `render` with `block.json`

* Refactor the example to use a block attribute

* Reword the note for render for clarity
---
 docs/reference-guides/block-api/block-metadata.md | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/docs/reference-guides/block-api/block-metadata.md b/docs/reference-guides/block-api/block-metadata.md
index 216509ab1df13..ba24ff0f58401 100644
--- a/docs/reference-guides/block-api/block-metadata.md
+++ b/docs/reference-guides/block-api/block-metadata.md
@@ -601,6 +601,16 @@ PHP file to use when rendering the block type on the server to show on the front
 -   `$content` (`string`): The block default content.
 -   `$block` (`WP_Block`): The block instance.
 
+An example implementation of the `render.php` file defined with `render` could look like:
+
+```php
+<div <?php echo get_block_wrapper_attributes(); ?>>
+	<?php echo esc_html( $attributes['label'] ); ?>
+</div>
+```
+
+_Note: This file loads for every instance of the block type when rendering the page HTML on the server. Accounting for that is essential when declaring functions or classes in the file. The simplest way to avoid the risk of errors is to consume that shared logic from another file._
+
 ## Assets
 
 ### `WPDefinedPath`

From 0ae22b5edd875442e7e92e984e591714402b8ec1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Petter=20Walb=C3=B8=20Johnsg=C3=A5rd?= <petter@dekode.no>
Date: Wed, 30 Aug 2023 13:37:50 +0200
Subject: [PATCH 03/34] Search block: Fix width input field (#53952)

---
 packages/block-library/src/search/edit.js  | 10 ++++++++--
 packages/block-library/src/search/utils.js |  1 -
 2 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/packages/block-library/src/search/edit.js b/packages/block-library/src/search/edit.js
index 80238ab1741f7..ff957b575c7a4 100644
--- a/packages/block-library/src/search/edit.js
+++ b/packages/block-library/src/search/edit.js
@@ -52,7 +52,7 @@ import {
 	PC_WIDTH_DEFAULT,
 	PX_WIDTH_DEFAULT,
 	MIN_WIDTH,
-	MIN_WIDTH_UNIT,
+	isPercentageUnit,
 } from './utils.js';
 
 // Used to calculate border radius adjustment to avoid "fat" corners when
@@ -405,7 +405,13 @@ export default function SearchEdit( {
 					>
 						<UnitControl
 							id={ unitControlInputId }
-							min={ `${ MIN_WIDTH }${ MIN_WIDTH_UNIT }` }
+							min={
+								isPercentageUnit( widthUnit ) ? 0 : MIN_WIDTH
+							}
+							max={
+								isPercentageUnit( widthUnit ) ? 100 : undefined
+							}
+							step={ 1 }
 							onChange={ ( newWidth ) => {
 								const filteredWidth =
 									widthUnit === '%' &&
diff --git a/packages/block-library/src/search/utils.js b/packages/block-library/src/search/utils.js
index b54048b609eb3..8438dd7148a01 100644
--- a/packages/block-library/src/search/utils.js
+++ b/packages/block-library/src/search/utils.js
@@ -4,7 +4,6 @@
 export const PC_WIDTH_DEFAULT = 50;
 export const PX_WIDTH_DEFAULT = 350;
 export const MIN_WIDTH = 220;
-export const MIN_WIDTH_UNIT = 'px';
 
 /**
  * Returns a boolean whether passed unit is percentage

From 67dc1bc2bd1d56f8d8e8fba5bd4b7a388dade920 Mon Sep 17 00:00:00 2001
From: Marco Ciampini <marco.ciampo@gmail.com>
Date: Wed, 30 Aug 2023 15:05:52 +0200
Subject: [PATCH 04/34] Popover: update `@floating-ui` to latest version,
 remove custom fix for iframe positioning and scaling (#46845)

* Update `@floating-ui` to latest version

* Remove custom iframe logic

* Middleware don't need filtering

* Add contextElement fields to virtual elements

* Fix zoom-out mode: don't manually account for scale, update on scroll

* Changelog update

* Disable the autoUpdate.layoutShift option for smoother scrolling

* Remove fake timers from DotTip unit tests. Fixes popover act warnings.

* Remove unneeded update() call: used to be updateFrameOffset()

---------

Co-authored-by: Jarda Snajdr <jsnajdr@gmail.com>
---
 package-lock.json                             |  44 ++--
 .../src/components/block-popover/inbetween.js |   4 +-
 .../src/components/block-popover/index.js     |   2 +-
 .../components/list-view/drop-indicator.js    |   9 +-
 packages/components/CHANGELOG.md              |   1 +
 packages/components/package.json              |   2 +-
 packages/components/src/popover/index.tsx     | 140 ++++--------
 .../components/src/popover/limit-shift.ts     | 205 ------------------
 .../src/popover/overlay-middlewares.tsx       |   4 +-
 packages/components/src/popover/utils.ts      |  61 +-----
 .../dot-tip/test/__snapshots__/index.js.snap  |   2 +-
 .../nux/src/components/dot-tip/test/index.js  |  16 +-
 .../rich-text/src/component/use-anchor.js     |   4 +-
 13 files changed, 83 insertions(+), 411 deletions(-)
 delete mode 100644 packages/components/src/popover/limit-shift.ts

diff --git a/package-lock.json b/package-lock.json
index 059180e5c24ba..4e6c42786efce 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -3813,18 +3813,6 @@
 				"@floating-ui/utils": "^0.1.1"
 			}
 		},
-		"node_modules/@floating-ui/react-dom": {
-			"version": "1.0.0",
-			"resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-1.0.0.tgz",
-			"integrity": "sha512-uiOalFKPG937UCLm42RxjESTWUVpbbatvlphQAU6bsv+ence6IoVG8JOUZcy8eW81NkU+Idiwvx10WFLmR4MIg==",
-			"dependencies": {
-				"@floating-ui/dom": "^1.0.0"
-			},
-			"peerDependencies": {
-				"react": ">=16.8.0",
-				"react-dom": ">=16.8.0"
-			}
-		},
 		"node_modules/@floating-ui/utils": {
 			"version": "0.1.1",
 			"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.1.1.tgz",
@@ -54555,7 +54543,7 @@
 				"@emotion/serialize": "^1.0.2",
 				"@emotion/styled": "^11.6.0",
 				"@emotion/utils": "^1.0.0",
-				"@floating-ui/react-dom": "1.0.0",
+				"@floating-ui/react-dom": "^2.0.1",
 				"@radix-ui/react-dropdown-menu": "2.0.4",
 				"@use-gesture/react": "^10.2.24",
 				"@wordpress/a11y": "file:../a11y",
@@ -54605,6 +54593,18 @@
 				"react-dom": "^18.0.0"
 			}
 		},
+		"packages/components/node_modules/@floating-ui/react-dom": {
+			"version": "2.0.1",
+			"resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.1.tgz",
+			"integrity": "sha512-rZtAmSht4Lry6gdhAJDrCp/6rKN7++JnL1/Anbr/DdeyYXQPxvg/ivrbYvJulbRf4vL8b212suwMM2lxbv+RQA==",
+			"dependencies": {
+				"@floating-ui/dom": "^1.3.0"
+			},
+			"peerDependencies": {
+				"react": ">=16.8.0",
+				"react-dom": ">=16.8.0"
+			}
+		},
 		"packages/components/node_modules/framer-motion": {
 			"version": "10.13.0",
 			"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-10.13.0.tgz",
@@ -58757,14 +58757,6 @@
 				"@floating-ui/utils": "^0.1.1"
 			}
 		},
-		"@floating-ui/react-dom": {
-			"version": "1.0.0",
-			"resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-1.0.0.tgz",
-			"integrity": "sha512-uiOalFKPG937UCLm42RxjESTWUVpbbatvlphQAU6bsv+ence6IoVG8JOUZcy8eW81NkU+Idiwvx10WFLmR4MIg==",
-			"requires": {
-				"@floating-ui/dom": "^1.0.0"
-			}
-		},
 		"@floating-ui/utils": {
 			"version": "0.1.1",
 			"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.1.1.tgz",
@@ -67277,7 +67269,7 @@
 				"@emotion/serialize": "^1.0.2",
 				"@emotion/styled": "^11.6.0",
 				"@emotion/utils": "^1.0.0",
-				"@floating-ui/react-dom": "1.0.0",
+				"@floating-ui/react-dom": "^2.0.1",
 				"@radix-ui/react-dropdown-menu": "2.0.4",
 				"@use-gesture/react": "^10.2.24",
 				"@wordpress/a11y": "file:../a11y",
@@ -67320,6 +67312,14 @@
 				"valtio": "1.7.0"
 			},
 			"dependencies": {
+				"@floating-ui/react-dom": {
+					"version": "2.0.1",
+					"resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.1.tgz",
+					"integrity": "sha512-rZtAmSht4Lry6gdhAJDrCp/6rKN7++JnL1/Anbr/DdeyYXQPxvg/ivrbYvJulbRf4vL8b212suwMM2lxbv+RQA==",
+					"requires": {
+						"@floating-ui/dom": "^1.3.0"
+					}
+				},
 				"framer-motion": {
 					"version": "10.13.0",
 					"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-10.13.0.tgz",
diff --git a/packages/block-editor/src/components/block-popover/inbetween.js b/packages/block-editor/src/components/block-popover/inbetween.js
index cadd49632b2e8..a0175c4d4ae58 100644
--- a/packages/block-editor/src/components/block-popover/inbetween.js
+++ b/packages/block-editor/src/components/block-popover/inbetween.js
@@ -81,10 +81,10 @@ function BlockPopoverInbetween( {
 			return undefined;
 		}
 
-		const { ownerDocument } = previousElement || nextElement;
+		const contextElement = previousElement || nextElement;
 
 		return {
-			ownerDocument,
+			contextElement,
 			getBoundingClientRect() {
 				const previousRect = previousElement
 					? previousElement.getBoundingClientRect()
diff --git a/packages/block-editor/src/components/block-popover/index.js b/packages/block-editor/src/components/block-popover/index.js
index 26042e8c6d6e9..13e6ba4d9e7f8 100644
--- a/packages/block-editor/src/components/block-popover/index.js
+++ b/packages/block-editor/src/components/block-popover/index.js
@@ -142,7 +142,7 @@ function BlockPopover(
 
 				return new window.DOMRect( left, top, width, height );
 			},
-			ownerDocument: selectedElement.ownerDocument,
+			contextElement: selectedElement,
 		};
 	}, [
 		bottomClientId,
diff --git a/packages/block-editor/src/components/list-view/drop-indicator.js b/packages/block-editor/src/components/list-view/drop-indicator.js
index 8178bc92453bc..04ce87b4a0c8d 100644
--- a/packages/block-editor/src/components/list-view/drop-indicator.js
+++ b/packages/block-editor/src/components/list-view/drop-indicator.js
@@ -159,10 +159,8 @@ export default function ListViewDropIndicator( {
 			return undefined;
 		}
 
-		const ownerDocument = targetElement.ownerDocument;
-
 		return {
-			ownerDocument,
+			contextElement: targetElement,
 			getBoundingClientRect() {
 				const rect = targetElement.getBoundingClientRect();
 				const indent = getDropIndicatorIndent( rect );
@@ -189,9 +187,10 @@ export default function ListViewDropIndicator( {
 					'horizontal'
 				);
 
+				const doc = targetElement.ownerDocument;
 				const windowScroll =
-					scrollContainer === ownerDocument.body ||
-					scrollContainer === ownerDocument.documentElement;
+					scrollContainer === doc.body ||
+					scrollContainer === doc.documentElement;
 
 				// If the scroll container is not the window, offset the left position, if need be.
 				if ( scrollContainer && ! windowScroll ) {
diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md
index eb52028535f89..7dce9d2a315f4 100644
--- a/packages/components/CHANGELOG.md
+++ b/packages/components/CHANGELOG.md
@@ -17,6 +17,7 @@
 
 -   `Shortcut`: Add Storybook stories ([#53627](https://github.com/WordPress/gutenberg/pull/53627)).
 -   `SlotFill`: Do not render children when using `<Slot bubblesVirtually />`. ([#53272](https://github.com/WordPress/gutenberg/pull/53272))
+-   Update `@floating-ui/react-dom` to the latest version ([#46845](https://github.com/WordPress/gutenberg/pull/46845)).
 
 ## 25.6.0 (2023-08-16)
 
diff --git a/packages/components/package.json b/packages/components/package.json
index a245281fc5ec9..2a4f7c380ee97 100644
--- a/packages/components/package.json
+++ b/packages/components/package.json
@@ -38,7 +38,7 @@
 		"@emotion/serialize": "^1.0.2",
 		"@emotion/styled": "^11.6.0",
 		"@emotion/utils": "^1.0.0",
-		"@floating-ui/react-dom": "1.0.0",
+		"@floating-ui/react-dom": "^2.0.1",
 		"@radix-ui/react-dropdown-menu": "2.0.4",
 		"@use-gesture/react": "^10.2.24",
 		"@wordpress/a11y": "file:../a11y",
diff --git a/packages/components/src/popover/index.tsx b/packages/components/src/popover/index.tsx
index 21f81e0f09e80..69c107cffda8d 100644
--- a/packages/components/src/popover/index.tsx
+++ b/packages/components/src/popover/index.tsx
@@ -3,11 +3,11 @@
  */
 import type { ForwardedRef, SyntheticEvent, RefCallback } from 'react';
 import classnames from 'classnames';
-import type { Middleware, MiddlewareArguments } from '@floating-ui/react-dom';
 import {
 	useFloating,
 	flip as flipMiddleware,
 	shift as shiftMiddleware,
+	limitShift,
 	autoUpdate,
 	arrow,
 	offset as offsetMiddleware,
@@ -50,8 +50,6 @@ import ScrollLock from '../scroll-lock';
 import { Slot, Fill, useSlot } from '../slot-fill';
 import {
 	computePopoverPosition,
-	getFrameOffset,
-	getFrameScale,
 	positionToPlacement,
 	placementToMotionAnimationProps,
 	getReferenceOwnerDocument,
@@ -64,7 +62,6 @@ import type {
 	PopoverAnchorRefReference,
 	PopoverAnchorRefTopBottom,
 } from './types';
-import { limitShift as customLimitShift } from './limit-shift';
 import { overlayMiddlewares } from './overlay-middlewares';
 
 /**
@@ -262,69 +259,34 @@ const UnforwardedPopover = (
 		? positionToPlacement( position )
 		: placementProp;
 
-	/**
-	 * Offsets the position of the popover when the anchor is inside an iframe.
-	 *
-	 * Store the offset in a ref, due to constraints with floating-ui:
-	 * https://floating-ui.com/docs/react-dom#variables-inside-middleware-functions.
-	 */
-	const frameOffsetRef = useRef( getFrameOffset( referenceOwnerDocument ) );
-
 	const middleware = [
 		...( placementProp === 'overlay' ? overlayMiddlewares() : [] ),
-		// Custom middleware which adjusts the popover's position by taking into
-		// account the offset of the anchor's iframe (if any) compared to the page.
-		{
-			name: 'frameOffset',
-			fn( { x, y }: MiddlewareArguments ) {
-				if ( ! frameOffsetRef.current ) {
-					return {
-						x,
-						y,
-					};
-				}
-
-				return {
-					x: x + frameOffsetRef.current.x,
-					y: y + frameOffsetRef.current.y,
-					data: {
-						// This will be used in the customLimitShift() function.
-						amount: frameOffsetRef.current,
-					},
-				};
-			},
-		},
 		offsetMiddleware( offsetProp ),
-		computedFlipProp ? flipMiddleware() : undefined,
-		computedResizeProp
-			? size( {
-					apply( sizeProps ) {
-						const { firstElementChild } =
-							refs.floating.current ?? {};
-
-						// Only HTMLElement instances have the `style` property.
-						if ( ! ( firstElementChild instanceof HTMLElement ) )
-							return;
-
-						// Reduce the height of the popover to the available space.
-						Object.assign( firstElementChild.style, {
-							maxHeight: `${ sizeProps.availableHeight }px`,
-							overflow: 'auto',
-						} );
-					},
-			  } )
-			: undefined,
-		shift
-			? shiftMiddleware( {
-					crossAxis: true,
-					limiter: customLimitShift(),
-					padding: 1, // Necessary to avoid flickering at the edge of the viewport.
-			  } )
-			: undefined,
+		computedFlipProp && flipMiddleware(),
+		computedResizeProp &&
+			size( {
+				apply( sizeProps ) {
+					const { firstElementChild } = refs.floating.current ?? {};
+
+					// Only HTMLElement instances have the `style` property.
+					if ( ! ( firstElementChild instanceof HTMLElement ) )
+						return;
+
+					// Reduce the height of the popover to the available space.
+					Object.assign( firstElementChild.style, {
+						maxHeight: `${ sizeProps.availableHeight }px`,
+						overflow: 'auto',
+					} );
+				},
+			} ),
+		shift &&
+			shiftMiddleware( {
+				crossAxis: true,
+				limiter: limitShift(),
+				padding: 1, // Necessary to avoid flickering at the edge of the viewport.
+			} ),
 		arrow( { element: arrowRef } ),
-	].filter(
-		( m: Middleware | undefined ): m is Middleware => m !== undefined
-	);
+	];
 	const slotName = useContext( slotNameContext ) || __unstableSlotName;
 	const slot = useSlot( slotName );
 
@@ -353,10 +315,6 @@ const UnforwardedPopover = (
 		// Positioning coordinates
 		x,
 		y,
-		// Callback refs (not regular refs). This allows the position to be updated.
-		// when either elements change.
-		reference: referenceCallbackRef,
-		floating,
 		// Object with "regular" refs to both "reference" and "floating"
 		refs,
 		// Type of CSS position property to use (absolute or fixed)
@@ -372,6 +330,7 @@ const UnforwardedPopover = (
 		middleware,
 		whileElementsMounted: ( referenceParam, floatingParam, updateParam ) =>
 			autoUpdate( referenceParam, floatingParam, updateParam, {
+				layoutShift: false,
 				animationFrame: true,
 			} ),
 	} );
@@ -406,17 +365,16 @@ const UnforwardedPopover = (
 			fallbackReferenceElement,
 			fallbackDocument: document,
 		} );
-		const scale = getFrameScale( resultingReferenceOwnerDoc );
+
 		const resultingReferenceElement = getReferenceElement( {
 			anchor,
 			anchorRef,
 			anchorRect,
 			getAnchorRect,
 			fallbackReferenceElement,
-			scale,
 		} );
 
-		referenceCallbackRef( resultingReferenceElement );
+		refs.setReference( resultingReferenceElement );
 
 		setReferenceOwnerDocument( resultingReferenceOwnerDoc );
 	}, [
@@ -429,23 +387,17 @@ const UnforwardedPopover = (
 		anchorRect,
 		getAnchorRect,
 		fallbackReferenceElement,
-		referenceCallbackRef,
+		refs,
 	] );
 
 	// If the reference element is in a different ownerDocument (e.g. iFrame),
 	// we need to manually update the floating's position as the reference's owner
-	// document scrolls. Also update the frame offset if the view resizes.
+	// document scrolls.
 	useLayoutEffect( () => {
 		if (
-			// Reference and root documents are the same.
-			referenceOwnerDocument === document ||
-			// Reference and floating are in the same document.
-			referenceOwnerDocument === refs.floating.current?.ownerDocument ||
-			// The reference's document has no view (i.e. window)
-			// or frame element (ie. it's not an iframe).
-			! referenceOwnerDocument?.defaultView?.frameElement
+			! referenceOwnerDocument ||
+			! referenceOwnerDocument.defaultView
 		) {
-			frameOffsetRef.current = undefined;
 			return;
 		}
 
@@ -456,23 +408,17 @@ const UnforwardedPopover = (
 			? getScrollContainer( frameElement )
 			: null;
 
-		const updateFrameOffset = () => {
-			frameOffsetRef.current = getFrameOffset( referenceOwnerDocument );
-			update();
-		};
-		defaultView.addEventListener( 'resize', updateFrameOffset );
-		scrollContainer?.addEventListener( 'scroll', updateFrameOffset );
-
-		updateFrameOffset();
+		defaultView.addEventListener( 'resize', update );
+		scrollContainer?.addEventListener( 'scroll', update );
 
 		return () => {
-			defaultView.removeEventListener( 'resize', updateFrameOffset );
-			scrollContainer?.removeEventListener( 'scroll', updateFrameOffset );
+			defaultView.removeEventListener( 'resize', update );
+			scrollContainer?.removeEventListener( 'scroll', update );
 		};
-	}, [ referenceOwnerDocument, update, refs.floating ] );
+	}, [ referenceOwnerDocument, update ] );
 
 	const mergedFloatingRef = useMergeRefs( [
-		floating,
+		refs.setFloating,
 		dialogRef,
 		forwardedRef,
 	] );
@@ -543,18 +489,12 @@ const UnforwardedPopover = (
 						left:
 							typeof arrowData?.x !== 'undefined' &&
 							Number.isFinite( arrowData.x )
-								? `${
-										arrowData.x +
-										( frameOffsetRef.current?.x ?? 0 )
-								  }px`
+								? `${ arrowData.x }px`
 								: '',
 						top:
 							typeof arrowData?.y !== 'undefined' &&
 							Number.isFinite( arrowData.y )
-								? `${
-										arrowData.y +
-										( frameOffsetRef.current?.y ?? 0 )
-								  }px`
+								? `${ arrowData.y }px`
 								: '',
 					} }
 				>
diff --git a/packages/components/src/popover/limit-shift.ts b/packages/components/src/popover/limit-shift.ts
deleted file mode 100644
index 45e65a0b61909..0000000000000
--- a/packages/components/src/popover/limit-shift.ts
+++ /dev/null
@@ -1,205 +0,0 @@
-/**
- * External dependencies
- */
-import type {
-	Axis,
-	Coords,
-	Placement,
-	Side,
-	MiddlewareArguments,
-} from '@floating-ui/react-dom';
-
-/**
- * Parts of this source were derived and modified from `floating-ui`,
- * released under the MIT license.
- *
- * https://github.com/floating-ui/floating-ui
- *
- * Copyright (c) 2021 Floating UI contributors
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-/**
- * Custom limiter function for the `shift` middleware.
- * This function is mostly identical default `limitShift` from ``@floating-ui`;
- * the only difference is that, when computing the min/max shift limits, it
- * also takes into account the iframe offset that is added by the
- * custom "frameOffset" middleware.
- *
- * All unexported types and functions are also from the `@floating-ui` library,
- * and have been copied to this file for convenience.
- */
-
-type LimitShiftOffset =
-	| ( ( args: MiddlewareArguments ) =>
-			| number
-			| {
-					/**
-					 * Offset the limiting of the axis that runs along the alignment of the
-					 * floating element.
-					 */
-					mainAxis?: number;
-					/**
-					 * Offset the limiting of the axis that runs along the side of the
-					 * floating element.
-					 */
-					crossAxis?: number;
-			  } )
-	| number
-	| {
-			/**
-			 * Offset the limiting of the axis that runs along the alignment of the
-			 * floating element.
-			 */
-			mainAxis?: number;
-			/**
-			 * Offset the limiting of the axis that runs along the side of the
-			 * floating element.
-			 */
-			crossAxis?: number;
-	  };
-
-type LimitShiftOptions = {
-	/**
-	 * Offset when limiting starts. `0` will limit when the opposite edges of the
-	 * reference and floating elements are aligned.
-	 * - positive = start limiting earlier
-	 * - negative = start limiting later
-	 */
-	offset: LimitShiftOffset;
-	/**
-	 * Whether to limit the axis that runs along the alignment of the floating
-	 * element.
-	 */
-	mainAxis: boolean;
-	/**
-	 * Whether to limit the axis that runs along the side of the floating element.
-	 */
-	crossAxis: boolean;
-};
-
-function getSide( placement: Placement ): Side {
-	return placement.split( '-' )[ 0 ] as Side;
-}
-
-function getMainAxisFromPlacement( placement: Placement ): Axis {
-	return [ 'top', 'bottom' ].includes( getSide( placement ) ) ? 'x' : 'y';
-}
-
-function getCrossAxis( axis: Axis ): Axis {
-	return axis === 'x' ? 'y' : 'x';
-}
-
-export const limitShift = (
-	options: Partial< LimitShiftOptions > = {}
-): {
-	options: Partial< LimitShiftOffset >;
-	fn: ( middlewareArguments: MiddlewareArguments ) => Coords;
-} => ( {
-	options,
-	fn( middlewareArguments ) {
-		const { x, y, placement, rects, middlewareData } = middlewareArguments;
-		const {
-			offset = 0,
-			mainAxis: checkMainAxis = true,
-			crossAxis: checkCrossAxis = true,
-		} = options;
-
-		const coords = { x, y };
-		const mainAxis = getMainAxisFromPlacement( placement );
-		const crossAxis = getCrossAxis( mainAxis );
-
-		let mainAxisCoord = coords[ mainAxis ];
-		let crossAxisCoord = coords[ crossAxis ];
-
-		const rawOffset =
-			typeof offset === 'function'
-				? offset( middlewareArguments )
-				: offset;
-		const computedOffset =
-			typeof rawOffset === 'number'
-				? { mainAxis: rawOffset, crossAxis: 0 }
-				: { mainAxis: 0, crossAxis: 0, ...rawOffset };
-
-		// At the moment of writing, this is the only difference
-		// with the `limitShift` function from `@floating-ui`.
-		// This offset needs to be added to all min/max limits
-		// in order to make the shift-limiting work as expected.
-		const additionalFrameOffset = {
-			x: 0,
-			y: 0,
-			...middlewareData.frameOffset?.amount,
-		};
-
-		if ( checkMainAxis ) {
-			const len = mainAxis === 'y' ? 'height' : 'width';
-			const limitMin =
-				rects.reference[ mainAxis ] -
-				rects.floating[ len ] +
-				computedOffset.mainAxis +
-				additionalFrameOffset[ mainAxis ];
-			const limitMax =
-				rects.reference[ mainAxis ] +
-				rects.reference[ len ] -
-				computedOffset.mainAxis +
-				additionalFrameOffset[ mainAxis ];
-
-			if ( mainAxisCoord < limitMin ) {
-				mainAxisCoord = limitMin;
-			} else if ( mainAxisCoord > limitMax ) {
-				mainAxisCoord = limitMax;
-			}
-		}
-
-		if ( checkCrossAxis ) {
-			const len = mainAxis === 'y' ? 'width' : 'height';
-			const isOriginSide = [ 'top', 'left' ].includes(
-				getSide( placement )
-			);
-			const limitMin =
-				rects.reference[ crossAxis ] -
-				rects.floating[ len ] +
-				( isOriginSide
-					? middlewareData.offset?.[ crossAxis ] ?? 0
-					: 0 ) +
-				( isOriginSide ? 0 : computedOffset.crossAxis ) +
-				additionalFrameOffset[ crossAxis ];
-			const limitMax =
-				rects.reference[ crossAxis ] +
-				rects.reference[ len ] +
-				( isOriginSide
-					? 0
-					: middlewareData.offset?.[ crossAxis ] ?? 0 ) -
-				( isOriginSide ? computedOffset.crossAxis : 0 ) +
-				additionalFrameOffset[ crossAxis ];
-
-			if ( crossAxisCoord < limitMin ) {
-				crossAxisCoord = limitMin;
-			} else if ( crossAxisCoord > limitMax ) {
-				crossAxisCoord = limitMax;
-			}
-		}
-
-		return {
-			[ mainAxis ]: mainAxisCoord,
-			[ crossAxis ]: crossAxisCoord,
-		} as Coords;
-	},
-} );
diff --git a/packages/components/src/popover/overlay-middlewares.tsx b/packages/components/src/popover/overlay-middlewares.tsx
index 83cc1cd0d21a9..fb64d739dce3b 100644
--- a/packages/components/src/popover/overlay-middlewares.tsx
+++ b/packages/components/src/popover/overlay-middlewares.tsx
@@ -1,14 +1,14 @@
 /**
  * External dependencies
  */
-import type { MiddlewareArguments } from '@floating-ui/react-dom';
+import type { MiddlewareState } from '@floating-ui/react-dom';
 import { size } from '@floating-ui/react-dom';
 
 export function overlayMiddlewares() {
 	return [
 		{
 			name: 'overlay',
-			fn( { rects }: MiddlewareArguments ) {
+			fn( { rects }: MiddlewareState ) {
 				return rects.reference;
 			},
 		},
diff --git a/packages/components/src/popover/utils.ts b/packages/components/src/popover/utils.ts
index 2112b28596982..5833a65816c6d 100644
--- a/packages/components/src/popover/utils.ts
+++ b/packages/components/src/popover/utils.ts
@@ -3,7 +3,7 @@
  */
 // eslint-disable-next-line no-restricted-imports
 import type { MotionProps } from 'framer-motion';
-import type { ReferenceType } from '@floating-ui/react-dom';
+import type { ReferenceType, VirtualElement } from '@floating-ui/react-dom';
 
 /**
  * Internal dependencies
@@ -139,42 +139,6 @@ export const placementToMotionAnimationProps = (
 	};
 };
 
-/**
- * Returns the offset of a document's frame element.
- *
- * @param document The iframe's owner document.
- *
- * @return The offset of the document's frame element, or undefined if the
- * document has no frame element.
- */
-export const getFrameOffset = (
-	document?: Document
-): { x: number; y: number } | undefined => {
-	const frameElement = document?.defaultView?.frameElement;
-	if ( ! frameElement ) {
-		return;
-	}
-	const iframeRect = frameElement.getBoundingClientRect();
-	return { x: iframeRect.left, y: iframeRect.top };
-};
-
-export const getFrameScale = (
-	document?: Document
-): {
-	x: number;
-	y: number;
-} => {
-	const frameElement = document?.defaultView?.frameElement as HTMLElement;
-	if ( ! frameElement ) {
-		return { x: 1, y: 1 };
-	}
-	const rect = frameElement.getBoundingClientRect();
-	return {
-		x: rect.width / frameElement.offsetWidth,
-		y: rect.height / frameElement.offsetHeight,
-	};
-};
-
 export const getReferenceOwnerDocument = ( {
 	anchor,
 	anchorRef,
@@ -197,7 +161,10 @@ export const getReferenceOwnerDocument = ( {
 	// with the `getBoundingClientRect()` function (like real elements).
 	// See https://floating-ui.com/docs/virtual-elements for more info.
 	let resultingReferenceOwnerDoc;
-	if ( anchor ) {
+	if ( ( anchor as VirtualElement )?.contextElement ) {
+		resultingReferenceOwnerDoc = ( anchor as VirtualElement ).contextElement
+			?.ownerDocument;
+	} else if ( anchor ) {
 		resultingReferenceOwnerDoc = anchor.ownerDocument;
 	} else if ( ( anchorRef as PopoverAnchorRefTopBottom | undefined )?.top ) {
 		resultingReferenceOwnerDoc = ( anchorRef as PopoverAnchorRefTopBottom )
@@ -231,13 +198,11 @@ export const getReferenceElement = ( {
 	anchorRect,
 	getAnchorRect,
 	fallbackReferenceElement,
-	scale,
 }: Pick<
 	PopoverProps,
 	'anchorRef' | 'anchorRect' | 'getAnchorRect' | 'anchor'
 > & {
 	fallbackReferenceElement: Element | null;
-	scale: { x: number; y: number };
 } ): ReferenceType | null => {
 	let referenceElement = null;
 
@@ -299,22 +264,6 @@ export const getReferenceElement = ( {
 		referenceElement = fallbackReferenceElement.parentElement;
 	}
 
-	if ( referenceElement && ( scale.x !== 1 || scale.y !== 1 ) ) {
-		// If the popover is inside an iframe, the coordinates of the
-		// reference element need to be scaled to match the iframe's scale.
-		const rect = referenceElement.getBoundingClientRect();
-		referenceElement = {
-			getBoundingClientRect() {
-				return new window.DOMRect(
-					rect.x * scale.x,
-					rect.y * scale.y,
-					rect.width * scale.x,
-					rect.height * scale.y
-				);
-			},
-		};
-	}
-
 	// Convert any `undefined` value to `null`.
 	return referenceElement ?? null;
 };
diff --git a/packages/nux/src/components/dot-tip/test/__snapshots__/index.js.snap b/packages/nux/src/components/dot-tip/test/__snapshots__/index.js.snap
index 29a743a5316de..bfdb68b78cf57 100644
--- a/packages/nux/src/components/dot-tip/test/__snapshots__/index.js.snap
+++ b/packages/nux/src/components/dot-tip/test/__snapshots__/index.js.snap
@@ -5,7 +5,7 @@ exports[`DotTip should render correctly 1`] = `
   aria-label="Editor tips"
   class="components-popover nux-dot-tip is-positioned"
   role="dialog"
-  style="position: absolute; top: 0px; left: 0px; opacity: 1; transform: none; transform-origin: 0% 50% 0;"
+  style="position: absolute; top: 0px; left: 0px; opacity: 0; transform: translateX(0px) translateY(0px) translateX(-2em) scale(0) translateZ(0); transform-origin: 0% 50% 0;"
   tabindex="-1"
 >
   <div
diff --git a/packages/nux/src/components/dot-tip/test/index.js b/packages/nux/src/components/dot-tip/test/index.js
index 9d54e00088625..8f8fba22dd626 100644
--- a/packages/nux/src/components/dot-tip/test/index.js
+++ b/packages/nux/src/components/dot-tip/test/index.js
@@ -10,14 +10,6 @@ import userEvent from '@testing-library/user-event';
 import { DotTip } from '..';
 
 describe( 'DotTip', () => {
-	beforeEach( () => {
-		jest.useFakeTimers();
-	} );
-
-	afterEach( () => {
-		jest.useRealTimers();
-	} );
-
 	it( 'should not render anything if invisible', () => {
 		render(
 			<DotTip>
@@ -43,9 +35,7 @@ describe( 'DotTip', () => {
 	} );
 
 	it( 'should call onDismiss when the dismiss button is clicked', async () => {
-		const user = userEvent.setup( {
-			advanceTimers: jest.advanceTimersByTime,
-		} );
+		const user = userEvent.setup();
 		const onDismiss = jest.fn();
 
 		render(
@@ -64,9 +54,7 @@ describe( 'DotTip', () => {
 	} );
 
 	it( 'should call onDisable when the X button is clicked', async () => {
-		const user = userEvent.setup( {
-			advanceTimers: jest.advanceTimersByTime,
-		} );
+		const user = userEvent.setup();
 		const onDisable = jest.fn();
 
 		render(
diff --git a/packages/rich-text/src/component/use-anchor.js b/packages/rich-text/src/component/use-anchor.js
index aa803df7c7666..5cc62a2de4ffd 100644
--- a/packages/rich-text/src/component/use-anchor.js
+++ b/packages/rich-text/src/component/use-anchor.js
@@ -51,7 +51,7 @@ function getFormatElement( range, editableContentElement, tagName, className ) {
 /**
  * @typedef {Object} VirtualAnchorElement
  * @property {() => DOMRect} getBoundingClientRect A function returning a DOMRect
- * @property {Document}      ownerDocument         The element's ownerDocument
+ * @property {HTMLElement}   contextElement        The actual DOM element
  */
 
 /**
@@ -64,7 +64,7 @@ function getFormatElement( range, editableContentElement, tagName, className ) {
  */
 function createVirtualAnchorElement( range, editableContentElement ) {
 	return {
-		ownerDocument: range.startContainer.ownerDocument,
+		contextElement: editableContentElement,
 		getBoundingClientRect() {
 			return editableContentElement.contains( range.startContainer )
 				? range.getBoundingClientRect()

From bd630b72c3404431f7714478ea71827613c00c36 Mon Sep 17 00:00:00 2001
From: Jarda Snajdr <jsnajdr@gmail.com>
Date: Wed, 30 Aug 2023 15:13:08 +0200
Subject: [PATCH 05/34] Blocks: move bootstrapped block types to Redux state
 (#53807)

* Blocks: move bootstrapped block types to Redux state

* Fix the ADD_UNPROCESSED_BLOCK_TYPE reducer

* Unit test for reapplyBlockFilters, fixing block unregistration

* Fixup unprocessedBlockTypes reducer tests

* Remove the apiVersion and ancestor polyfills

* Improve action/selector documentation

* Add -Type suffix to block registration actions

* Stabilize reapplyBlockTypeFilters

* Revert back to boostrapping metadata only when provided
---
 .../reference-guides/data/data-core-blocks.md |   6 +-
 packages/blocks/src/api/registration.js       |  98 +--------
 packages/blocks/src/api/test/registration.js  | 118 +++-------
 packages/blocks/src/store/actions.js          | 202 +++---------------
 packages/blocks/src/store/index.js            |   2 +
 packages/blocks/src/store/private-actions.js  |  42 ++++
 .../blocks/src/store/private-selectors.js     |  24 +++
 .../blocks/src/store/process-block-type.js    | 159 ++++++++++++++
 packages/blocks/src/store/reducer.js          |  74 ++++++-
 packages/blocks/src/store/selectors.js        |  11 -
 packages/blocks/src/store/test/reducer.js     |  15 +-
 packages/customize-widgets/src/index.js       |   2 +-
 packages/edit-post/src/index.js               |   2 +-
 packages/edit-site/src/index.js               |   2 +-
 packages/edit-widgets/src/index.js            |   2 +-
 15 files changed, 387 insertions(+), 372 deletions(-)
 create mode 100644 packages/blocks/src/store/private-actions.js
 create mode 100644 packages/blocks/src/store/process-block-type.js

diff --git a/docs/reference-guides/data/data-core-blocks.md b/docs/reference-guides/data/data-core-blocks.md
index ba047160f047d..084c9c1d7a5fb 100644
--- a/docs/reference-guides/data/data-core-blocks.md
+++ b/docs/reference-guides/data/data-core-blocks.md
@@ -712,6 +712,10 @@ The actions in this package shouldn't be used directly. Instead, use the functio
 
 <!-- START TOKEN(Autogenerated actions|../../../packages/blocks/src/store/actions.js) -->
 
-Nothing to document.
+### reapplyBlockTypeFilters
+
+Signals that all block types should be computed again. It uses stored unprocessed block types and all the most recent list of registered filters.
+
+It addresses the issue where third party block filters get registered after third party blocks. A sample sequence: 1. Filter A. 2. Block B. 3. Block C. 4. Filter D. 5. Filter E. 6. Block F. 7. Filter G. In this scenario some filters would not get applied for all blocks because they are registered too late.
 
 <!-- END TOKEN(Autogenerated actions|../../../packages/blocks/src/store/actions.js) -->
diff --git a/packages/blocks/src/api/registration.js b/packages/blocks/src/api/registration.js
index 702582ff6489e..72c0a30db0205 100644
--- a/packages/blocks/src/api/registration.js
+++ b/packages/blocks/src/api/registration.js
@@ -1,10 +1,5 @@
 /* eslint no-console: [ 'error', { allow: [ 'error', 'warn' ] } ] */
 
-/**
- * External dependencies
- */
-import { camelCase } from 'change-case';
-
 /**
  * WordPress dependencies
  */
@@ -15,8 +10,8 @@ import { _x } from '@wordpress/i18n';
  * Internal dependencies
  */
 import i18nBlockSchema from './i18n-block.json';
-import { BLOCK_ICON_DEFAULT } from './constants';
 import { store as blocksStore } from '../store';
+import { unlock } from '../lock-unlock';
 
 /**
  * An icon type definition. One of a Dashicon slug, an element,
@@ -129,8 +124,6 @@ import { store as blocksStore } from '../store';
  *                                              then no preview is shown.
  */
 
-const serverSideBlockDefinitions = {};
-
 function isObject( object ) {
 	return object !== null && typeof object === 'object';
 }
@@ -142,65 +135,9 @@ function isObject( object ) {
  */
 // eslint-disable-next-line camelcase
 export function unstable__bootstrapServerSideBlockDefinitions( definitions ) {
-	for ( const blockName of Object.keys( definitions ) ) {
-		// Don't overwrite if already set. It covers the case when metadata
-		// was initialized from the server.
-		if ( serverSideBlockDefinitions[ blockName ] ) {
-			// We still need to polyfill `apiVersion` for WordPress version
-			// lower than 5.7. If it isn't present in the definition shared
-			// from the server, we try to fallback to the definition passed.
-			// @see https://github.com/WordPress/gutenberg/pull/29279
-			if (
-				serverSideBlockDefinitions[ blockName ].apiVersion ===
-					undefined &&
-				definitions[ blockName ].apiVersion
-			) {
-				serverSideBlockDefinitions[ blockName ].apiVersion =
-					definitions[ blockName ].apiVersion;
-			}
-			// The `ancestor` prop is not included in the definitions shared
-			// from the server yet, so it needs to be polyfilled as well.
-			// @see https://github.com/WordPress/gutenberg/pull/39894
-			if (
-				serverSideBlockDefinitions[ blockName ].ancestor ===
-					undefined &&
-				definitions[ blockName ].ancestor
-			) {
-				serverSideBlockDefinitions[ blockName ].ancestor =
-					definitions[ blockName ].ancestor;
-			}
-			// The `selectors` prop is not yet included in the server provided
-			// definitions. Polyfill it as well. This can be removed when the
-			// minimum supported WordPress is >= 6.3.
-			if (
-				serverSideBlockDefinitions[ blockName ].selectors ===
-					undefined &&
-				definitions[ blockName ].selectors
-			) {
-				serverSideBlockDefinitions[ blockName ].selectors =
-					definitions[ blockName ].selectors;
-			}
-
-			if (
-				serverSideBlockDefinitions[ blockName ]
-					.__experimentalAutoInsert === undefined &&
-				definitions[ blockName ].__experimentalAutoInsert
-			) {
-				serverSideBlockDefinitions[
-					blockName
-				].__experimentalAutoInsert =
-					definitions[ blockName ].__experimentalAutoInsert;
-			}
-			continue;
-		}
-
-		serverSideBlockDefinitions[ blockName ] = Object.fromEntries(
-			Object.entries( definitions[ blockName ] )
-				.filter(
-					( [ , value ] ) => value !== null && value !== undefined
-				)
-				.map( ( [ key, value ] ) => [ camelCase( key ), value ] )
-		);
+	const { addBootstrappedBlockType } = unlock( dispatch( blocksStore ) );
+	for ( const [ name, blockType ] of Object.entries( definitions ) ) {
+		addBootstrappedBlockType( name, blockType );
 	}
 }
 
@@ -302,29 +239,16 @@ export function registerBlockType( blockNameOrMetadata, settings ) {
 		return;
 	}
 
+	const { addBootstrappedBlockType, addUnprocessedBlockType } = unlock(
+		dispatch( blocksStore )
+	);
+
 	if ( isObject( blockNameOrMetadata ) ) {
-		unstable__bootstrapServerSideBlockDefinitions( {
-			[ name ]: getBlockSettingsFromMetadata( blockNameOrMetadata ),
-		} );
+		const metadata = getBlockSettingsFromMetadata( blockNameOrMetadata );
+		addBootstrappedBlockType( name, metadata );
 	}
 
-	const blockType = {
-		name,
-		icon: BLOCK_ICON_DEFAULT,
-		keywords: [],
-		attributes: {},
-		providesContext: {},
-		usesContext: [],
-		selectors: {},
-		supports: {},
-		styles: [],
-		variations: [],
-		save: () => null,
-		...serverSideBlockDefinitions?.[ name ],
-		...settings,
-	};
-
-	dispatch( blocksStore ).__experimentalRegisterBlockType( blockType );
+	addUnprocessedBlockType( name, settings );
 
 	return select( blocksStore ).getBlockType( name );
 }
diff --git a/packages/blocks/src/api/test/registration.js b/packages/blocks/src/api/test/registration.js
index 42f4dcfbf0e48..877c9fdc4a038 100644
--- a/packages/blocks/src/api/test/registration.js
+++ b/packages/blocks/src/api/test/registration.js
@@ -5,7 +5,7 @@
  */
 import { addFilter, removeAllFilters, removeFilter } from '@wordpress/hooks';
 import { logged } from '@wordpress/deprecated';
-import { select } from '@wordpress/data';
+import { select, dispatch } from '@wordpress/data';
 
 /**
  * Internal dependencies
@@ -33,6 +33,7 @@ import {
 import { BLOCK_ICON_DEFAULT, DEPRECATED_ENTRY_KEYS } from '../constants';
 import { omit } from '../utils';
 import { store as blocksStore } from '../../store';
+import { unlock } from '../../lock-unlock';
 
 const noop = () => {};
 
@@ -48,19 +49,14 @@ describe( 'blocks', () => {
 		title: 'block title',
 	};
 
-	beforeAll( () => {
-		// Initialize the block store.
-		require( '../../store' );
-	} );
-
 	afterEach( () => {
-		getBlockTypes().forEach( ( block ) => {
-			unregisterBlockType( block.name );
-		} );
+		const registeredNames = Object.keys(
+			unlock( select( blocksStore ) ).getUnprocessedBlockTypes()
+		);
+		dispatch( blocksStore ).removeBlockTypes( registeredNames );
 		setFreeformContentHandlerName( undefined );
 		setUnregisteredTypeHandlerName( undefined );
 		setDefaultBlockName( undefined );
-		unstable__bootstrapServerSideBlockDefinitions( {} );
 
 		// Reset deprecation logging to ensure we properly track warnings.
 		for ( const key in logged ) {
@@ -392,80 +388,6 @@ describe( 'blocks', () => {
 			} );
 		} );
 
-		// This test can be removed once the polyfill for apiVersion gets removed.
-		it( 'should apply apiVersion on the client when not set on the server', () => {
-			const blockName = 'core/test-block-back-compat';
-			unstable__bootstrapServerSideBlockDefinitions( {
-				[ blockName ]: {
-					category: 'widgets',
-				},
-			} );
-			unstable__bootstrapServerSideBlockDefinitions( {
-				[ blockName ]: {
-					apiVersion: 3,
-					category: 'ignored',
-				},
-			} );
-
-			const blockType = {
-				title: 'block title',
-			};
-			registerBlockType( blockName, blockType );
-			expect( getBlockType( blockName ) ).toEqual( {
-				apiVersion: 3,
-				name: blockName,
-				save: expect.any( Function ),
-				title: 'block title',
-				category: 'widgets',
-				icon: { src: BLOCK_ICON_DEFAULT },
-				attributes: {},
-				providesContext: {},
-				usesContext: [],
-				keywords: [],
-				selectors: {},
-				supports: {},
-				styles: [],
-				variations: [],
-			} );
-		} );
-
-		// This test can be removed once the polyfill for ancestor gets removed.
-		it( 'should apply ancestor on the client when not set on the server', () => {
-			const blockName = 'core/test-block-with-ancestor';
-			unstable__bootstrapServerSideBlockDefinitions( {
-				[ blockName ]: {
-					category: 'widgets',
-				},
-			} );
-			unstable__bootstrapServerSideBlockDefinitions( {
-				[ blockName ]: {
-					ancestor: 'core/test-block-ancestor',
-					category: 'ignored',
-				},
-			} );
-
-			const blockType = {
-				title: 'block title',
-			};
-			registerBlockType( blockName, blockType );
-			expect( getBlockType( blockName ) ).toEqual( {
-				ancestor: 'core/test-block-ancestor',
-				name: blockName,
-				save: expect.any( Function ),
-				title: 'block title',
-				category: 'widgets',
-				icon: { src: BLOCK_ICON_DEFAULT },
-				attributes: {},
-				providesContext: {},
-				usesContext: [],
-				keywords: [],
-				selectors: {},
-				supports: {},
-				styles: [],
-				variations: [],
-			} );
-		} );
-
 		// This can be removed once polyfill adding selectors has been removed.
 		it( 'should apply selectors on the client when not set on the server', () => {
 			const blockName = 'core/test-block-with-selectors';
@@ -920,6 +842,34 @@ describe( 'blocks', () => {
 					'Declaring non-string block descriptions is deprecated since version 6.2.'
 				);
 			} );
+
+			it( 're-applies block filters', () => {
+				// register block
+				registerBlockType( 'test/block', defaultBlockSettings );
+
+				// register a filter after registering a block
+				addFilter(
+					'blocks.registerBlockType',
+					'core/blocks/reapply',
+					( settings ) => ( {
+						...settings,
+						title: settings.title + ' filtered',
+					} )
+				);
+
+				// check that block type has unfiltered values
+				expect( getBlockType( 'test/block' ).title ).toBe(
+					'block title'
+				);
+
+				// reapply the block filters
+				dispatch( blocksStore ).reapplyBlockTypeFilters();
+
+				// check that block type has filtered values
+				expect( getBlockType( 'test/block' ).title ).toBe(
+					'block title filtered'
+				);
+			} );
 		} );
 
 		test( 'registers block from metadata', () => {
diff --git a/packages/blocks/src/store/actions.js b/packages/blocks/src/store/actions.js
index 2c02fb73b0352..d3bd71c067ebe 100644
--- a/packages/blocks/src/store/actions.js
+++ b/packages/blocks/src/store/actions.js
@@ -1,154 +1,17 @@
-/**
- * External dependencies
- */
-import { isPlainObject } from 'is-plain-object';
-
 /**
  * WordPress dependencies
  */
 import deprecated from '@wordpress/deprecated';
-import { applyFilters } from '@wordpress/hooks';
 
 /**
  * Internal dependencies
  */
-import { isValidIcon, normalizeIconObject, omit } from '../api/utils';
-import { DEPRECATED_ENTRY_KEYS } from '../api/constants';
+import { processBlockType } from './process-block-type';
 
 /** @typedef {import('../api/registration').WPBlockVariation} WPBlockVariation */
 /** @typedef {import('../api/registration').WPBlockType} WPBlockType */
 /** @typedef {import('./reducer').WPBlockCategory} WPBlockCategory */
 
-const { error, warn } = window.console;
-
-/**
- * Mapping of legacy category slugs to their latest normal values, used to
- * accommodate updates of the default set of block categories.
- *
- * @type {Record<string,string>}
- */
-const LEGACY_CATEGORY_MAPPING = {
-	common: 'text',
-	formatting: 'text',
-	layout: 'design',
-};
-
-/**
- * Whether the argument is a function.
- *
- * @param {*} maybeFunc The argument to check.
- * @return {boolean} True if the argument is a function, false otherwise.
- */
-function isFunction( maybeFunc ) {
-	return typeof maybeFunc === 'function';
-}
-
-/**
- * Takes the unprocessed block type data and applies all the existing filters for the registered block type.
- * Next, it validates all the settings and performs additional processing to the block type definition.
- *
- * @param {WPBlockType} blockType        Unprocessed block type settings.
- * @param {Object}      thunkArgs        Argument object for the thunk middleware.
- * @param {Function}    thunkArgs.select Function to select from the store.
- *
- * @return {WPBlockType | undefined} The block, if it has been successfully registered; otherwise `undefined`.
- */
-const processBlockType = ( blockType, { select } ) => {
-	const { name } = blockType;
-
-	const settings = applyFilters(
-		'blocks.registerBlockType',
-		{ ...blockType },
-		name,
-		null
-	);
-
-	if ( settings.description && typeof settings.description !== 'string' ) {
-		deprecated( 'Declaring non-string block descriptions', {
-			since: '6.2',
-		} );
-	}
-
-	if ( settings.deprecated ) {
-		settings.deprecated = settings.deprecated.map( ( deprecation ) =>
-			Object.fromEntries(
-				Object.entries(
-					// Only keep valid deprecation keys.
-					applyFilters(
-						'blocks.registerBlockType',
-						// Merge deprecation keys with pre-filter settings
-						// so that filters that depend on specific keys being
-						// present don't fail.
-						{
-							// Omit deprecation keys here so that deprecations
-							// can opt out of specific keys like "supports".
-							...omit( blockType, DEPRECATED_ENTRY_KEYS ),
-							...deprecation,
-						},
-						name,
-						deprecation
-					)
-				).filter( ( [ key ] ) => DEPRECATED_ENTRY_KEYS.includes( key ) )
-			)
-		);
-	}
-
-	if ( ! isPlainObject( settings ) ) {
-		error( 'Block settings must be a valid object.' );
-		return;
-	}
-
-	if ( ! isFunction( settings.save ) ) {
-		error( 'The "save" property must be a valid function.' );
-		return;
-	}
-	if ( 'edit' in settings && ! isFunction( settings.edit ) ) {
-		error( 'The "edit" property must be a valid function.' );
-		return;
-	}
-
-	// Canonicalize legacy categories to equivalent fallback.
-	if ( LEGACY_CATEGORY_MAPPING.hasOwnProperty( settings.category ) ) {
-		settings.category = LEGACY_CATEGORY_MAPPING[ settings.category ];
-	}
-
-	if (
-		'category' in settings &&
-		! select
-			.getCategories()
-			.some( ( { slug } ) => slug === settings.category )
-	) {
-		warn(
-			'The block "' +
-				name +
-				'" is registered with an invalid category "' +
-				settings.category +
-				'".'
-		);
-		delete settings.category;
-	}
-
-	if ( ! ( 'title' in settings ) || settings.title === '' ) {
-		error( 'The block "' + name + '" must have a title.' );
-		return;
-	}
-	if ( typeof settings.title !== 'string' ) {
-		error( 'Block titles must be strings.' );
-		return;
-	}
-
-	settings.icon = normalizeIconObject( settings.icon );
-	if ( ! isValidIcon( settings.icon.src ) ) {
-		error(
-			'The icon passed is invalid. ' +
-				'The icon should be a string, an element, a function, or an object following the specifications documented in https://developer.wordpress.org/block-editor/developers/block-api/block-registration/#icon-optional'
-		);
-		return;
-	}
-
-	return settings;
-};
-
 /**
  * Returns an action object used in signalling that block types have been added.
  * Ignored from documentation as the recommended usage for this action through registerBlockType from @wordpress/blocks.
@@ -167,26 +30,6 @@ export function addBlockTypes( blockTypes ) {
 	};
 }
 
-/**
- * Signals that the passed block type's settings should be stored in the state.
- *
- * @param {WPBlockType} blockType Unprocessed block type settings.
- */
-export const __experimentalRegisterBlockType =
-	( blockType ) =>
-	( { dispatch, select } ) => {
-		dispatch( {
-			type: 'ADD_UNPROCESSED_BLOCK_TYPE',
-			blockType,
-		} );
-
-		const processedBlockType = processBlockType( blockType, { select } );
-		if ( ! processedBlockType ) {
-			return;
-		}
-		dispatch.addBlockTypes( processedBlockType );
-	};
-
 /**
  * Signals that all block types should be computed again.
  * It uses stored unprocessed block types and all the most recent list of registered filters.
@@ -201,25 +44,17 @@ export const __experimentalRegisterBlockType =
  *   7. Filter G.
  * In this scenario some filters would not get applied for all blocks because they are registered too late.
  */
-export const __experimentalReapplyBlockTypeFilters =
-	() =>
-	( { dispatch, select } ) => {
-		const unprocessedBlockTypes =
-			select.__experimentalGetUnprocessedBlockTypes();
-
-		const processedBlockTypes = Object.keys( unprocessedBlockTypes ).reduce(
-			( accumulator, blockName ) => {
-				const result = processBlockType(
-					unprocessedBlockTypes[ blockName ],
-					{ select }
-				);
-				if ( result ) {
-					accumulator.push( result );
-				}
-				return accumulator;
-			},
-			[]
-		);
+export function reapplyBlockTypeFilters() {
+	return ( { dispatch, select } ) => {
+		const processedBlockTypes = [];
+		for ( const [ name, settings ] of Object.entries(
+			select.getUnprocessedBlockTypes()
+		) ) {
+			const result = dispatch( processBlockType( name, settings ) );
+			if ( result ) {
+				processedBlockTypes.push( result );
+			}
+		}
 
 		if ( ! processedBlockTypes.length ) {
 			return;
@@ -227,6 +62,19 @@ export const __experimentalReapplyBlockTypeFilters =
 
 		dispatch.addBlockTypes( processedBlockTypes );
 	};
+}
+
+export function __experimentalReapplyBlockFilters() {
+	deprecated(
+		'wp.data.dispatch( "core/blocks" ).__experimentalReapplyBlockFilters',
+		{
+			since: '6.4',
+			alternative: 'reapplyBlockFilters',
+		}
+	);
+
+	return reapplyBlockTypeFilters();
+}
 
 /**
  * Returns an action object used to remove a registered block type.
diff --git a/packages/blocks/src/store/index.js b/packages/blocks/src/store/index.js
index ce69fd83d4e6c..ffda3ffe00026 100644
--- a/packages/blocks/src/store/index.js
+++ b/packages/blocks/src/store/index.js
@@ -10,6 +10,7 @@ import reducer from './reducer';
 import * as selectors from './selectors';
 import * as privateSelectors from './private-selectors';
 import * as actions from './actions';
+import * as privateActions from './private-actions';
 import { STORE_NAME } from './constants';
 import { unlock } from '../lock-unlock';
 
@@ -28,3 +29,4 @@ export const store = createReduxStore( STORE_NAME, {
 
 register( store );
 unlock( store ).registerPrivateSelectors( privateSelectors );
+unlock( store ).registerPrivateActions( privateActions );
diff --git a/packages/blocks/src/store/private-actions.js b/packages/blocks/src/store/private-actions.js
new file mode 100644
index 0000000000000..bc06e231b1722
--- /dev/null
+++ b/packages/blocks/src/store/private-actions.js
@@ -0,0 +1,42 @@
+/**
+ * Internal dependencies
+ */
+import { processBlockType } from './process-block-type';
+
+/** @typedef {import('../api/registration').WPBlockType} WPBlockType */
+
+/**
+ * Add bootstrapped block type metadata to the store. These metadata usually come from
+ * the `block.json` file and are either statically boostrapped from the server, or
+ * passed as the `metadata` parameter to the `registerBlockType` function.
+ *
+ * @param {string}      name      Block name.
+ * @param {WPBlockType} blockType Block type metadata.
+ */
+export function addBootstrappedBlockType( name, blockType ) {
+	return {
+		type: 'ADD_BOOTSTRAPPED_BLOCK_TYPE',
+		name,
+		blockType,
+	};
+}
+
+/**
+ * Add unprocessed block type settings to the store. These data are passed as the
+ * `settings` parameter to the client-side `registerBlockType` function.
+ *
+ * @param {string}      name      Block name.
+ * @param {WPBlockType} blockType Unprocessed block type settings.
+ */
+export function addUnprocessedBlockType( name, blockType ) {
+	return ( { dispatch } ) => {
+		dispatch( { type: 'ADD_UNPROCESSED_BLOCK_TYPE', name, blockType } );
+		const processedBlockType = dispatch(
+			processBlockType( name, blockType )
+		);
+		if ( ! processedBlockType ) {
+			return;
+		}
+		dispatch.addBlockTypes( processedBlockType );
+	};
+}
diff --git a/packages/blocks/src/store/private-selectors.js b/packages/blocks/src/store/private-selectors.js
index c9f5002b2219a..7e4311658c869 100644
--- a/packages/blocks/src/store/private-selectors.js
+++ b/packages/blocks/src/store/private-selectors.js
@@ -152,3 +152,27 @@ export const getSupportedStyles = createSelector(
 	},
 	( state, name ) => [ state.blockTypes[ name ] ]
 );
+
+/**
+ * Returns the bootstrapped block type metadata for a give block name.
+ *
+ * @param {Object} state Data state.
+ * @param {string} name  Block name.
+ *
+ * @return {Object} Bootstrapped block type metadata for a block.
+ */
+export function getBootstrappedBlockType( state, name ) {
+	return state.bootstrappedBlockTypes[ name ];
+}
+
+/**
+ * Returns all the unprocessed (before applying the `registerBlockType` filter)
+ * block type settings as passed during block registration.
+ *
+ * @param {Object} state Data state.
+ *
+ * @return {Array} Unprocessed block type settings for all blocks.
+ */
+export function getUnprocessedBlockTypes( state ) {
+	return state.unprocessedBlockTypes;
+}
diff --git a/packages/blocks/src/store/process-block-type.js b/packages/blocks/src/store/process-block-type.js
new file mode 100644
index 0000000000000..aab198af6c66f
--- /dev/null
+++ b/packages/blocks/src/store/process-block-type.js
@@ -0,0 +1,159 @@
+/**
+ * External dependencies
+ */
+import { isPlainObject } from 'is-plain-object';
+
+/**
+ * WordPress dependencies
+ */
+import deprecated from '@wordpress/deprecated';
+import { applyFilters } from '@wordpress/hooks';
+
+/**
+ * Internal dependencies
+ */
+import { isValidIcon, normalizeIconObject, omit } from '../api/utils';
+import { BLOCK_ICON_DEFAULT, DEPRECATED_ENTRY_KEYS } from '../api/constants';
+
+/** @typedef {import('../api/registration').WPBlockType} WPBlockType */
+
+const { error, warn } = window.console;
+
+/**
+ * Mapping of legacy category slugs to their latest normal values, used to
+ * accommodate updates of the default set of block categories.
+ *
+ * @type {Record<string,string>}
+ */
+const LEGACY_CATEGORY_MAPPING = {
+	common: 'text',
+	formatting: 'text',
+	layout: 'design',
+};
+
+/**
+ * Takes the unprocessed block type settings, merges them with block type metadata
+ * and applies all the existing filters for the registered block type.
+ * Next, it validates all the settings and performs additional processing to the block type definition.
+ *
+ * @param {string}      name          Block name.
+ * @param {WPBlockType} blockSettings Unprocessed block type settings.
+ *
+ * @return {WPBlockType | undefined} The block, if it has been processed and can be registered; otherwise `undefined`.
+ */
+export const processBlockType =
+	( name, blockSettings ) =>
+	( { select } ) => {
+		const blockType = {
+			name,
+			icon: BLOCK_ICON_DEFAULT,
+			keywords: [],
+			attributes: {},
+			providesContext: {},
+			usesContext: [],
+			selectors: {},
+			supports: {},
+			styles: [],
+			variations: [],
+			save: () => null,
+			...select.getBootstrappedBlockType( name ),
+			...blockSettings,
+		};
+
+		const settings = applyFilters(
+			'blocks.registerBlockType',
+			blockType,
+			name,
+			null
+		);
+
+		if (
+			settings.description &&
+			typeof settings.description !== 'string'
+		) {
+			deprecated( 'Declaring non-string block descriptions', {
+				since: '6.2',
+			} );
+		}
+
+		if ( settings.deprecated ) {
+			settings.deprecated = settings.deprecated.map( ( deprecation ) =>
+				Object.fromEntries(
+					Object.entries(
+						// Only keep valid deprecation keys.
+						applyFilters(
+							'blocks.registerBlockType',
+							// Merge deprecation keys with pre-filter settings
+							// so that filters that depend on specific keys being
+							// present don't fail.
+							{
+								// Omit deprecation keys here so that deprecations
+								// can opt out of specific keys like "supports".
+								...omit( blockType, DEPRECATED_ENTRY_KEYS ),
+								...deprecation,
+							},
+							blockType.name,
+							deprecation
+						)
+					).filter( ( [ key ] ) =>
+						DEPRECATED_ENTRY_KEYS.includes( key )
+					)
+				)
+			);
+		}
+
+		if ( ! isPlainObject( settings ) ) {
+			error( 'Block settings must be a valid object.' );
+			return;
+		}
+
+		if ( typeof settings.save !== 'function' ) {
+			error( 'The "save" property must be a valid function.' );
+			return;
+		}
+		if ( 'edit' in settings && typeof settings.edit !== 'function' ) {
+			error( 'The "edit" property must be a valid function.' );
+			return;
+		}
+
+		// Canonicalize legacy categories to equivalent fallback.
+		if ( LEGACY_CATEGORY_MAPPING.hasOwnProperty( settings.category ) ) {
+			settings.category = LEGACY_CATEGORY_MAPPING[ settings.category ];
+		}
+
+		if (
+			'category' in settings &&
+			! select
+				.getCategories()
+				.some( ( { slug } ) => slug === settings.category )
+		) {
+			warn(
+				'The block "' +
+					name +
+					'" is registered with an invalid category "' +
+					settings.category +
+					'".'
+			);
+			delete settings.category;
+		}
+
+		if ( ! ( 'title' in settings ) || settings.title === '' ) {
+			error( 'The block "' + name + '" must have a title.' );
+			return;
+		}
+		if ( typeof settings.title !== 'string' ) {
+			error( 'Block titles must be strings.' );
+			return;
+		}
+
+		settings.icon = normalizeIconObject( settings.icon );
+		if ( ! isValidIcon( settings.icon.src ) ) {
+			error(
+				'The icon passed is invalid. ' +
+					'The icon should be a string, an element, a function, or an object following the specifications documented in https://developer.wordpress.org/block-editor/developers/block-api/block-registration/#icon-optional'
+			);
+			return;
+		}
+
+		return settings;
+	};
diff --git a/packages/blocks/src/store/reducer.js b/packages/blocks/src/store/reducer.js
index d8f76e00fc71d..a8f114fea79c7 100644
--- a/packages/blocks/src/store/reducer.js
+++ b/packages/blocks/src/store/reducer.js
@@ -1,3 +1,8 @@
+/**
+ * External dependencies
+ */
+import { camelCase } from 'change-case';
+
 /**
  * WordPress dependencies
  */
@@ -52,6 +57,72 @@ function getUniqueItemsByName( items ) {
 	}, [] );
 }
 
+function bootstrappedBlockTypes( state = {}, action ) {
+	switch ( action.type ) {
+		case 'ADD_BOOTSTRAPPED_BLOCK_TYPE':
+			const { name, blockType } = action;
+			const serverDefinition = state[ name ];
+			let newDefinition;
+			// Don't overwrite if already set. It covers the case when metadata
+			// was initialized from the server.
+			if ( serverDefinition ) {
+				// The `selectors` prop is not yet included in the server provided
+				// definitions and needs to be polyfilled. This can be removed when the
+				// minimum supported WordPress is >= 6.3.
+				if (
+					serverDefinition.selectors === undefined &&
+					blockType.selectors
+				) {
+					newDefinition = {
+						...serverDefinition,
+						selectors: blockType.selectors,
+					};
+				}
+
+				// The `autoInsert` prop is not yet included in the server provided
+				// definitions and needs to be polyfilled. This can be removed when the
+				// minimum supported WordPress is >= 6.4.
+				if (
+					serverDefinition.__experimentalAutoInsert === undefined &&
+					blockType.__experimentalAutoInsert
+				) {
+					newDefinition = {
+						...serverDefinition,
+						...newDefinition,
+						__experimentalAutoInsert:
+							blockType.__experimentalAutoInsert,
+					};
+				}
+			} else {
+				newDefinition = Object.fromEntries(
+					Object.entries( blockType )
+						.filter(
+							( [ , value ] ) =>
+								value !== null && value !== undefined
+						)
+						.map( ( [ key, value ] ) => [
+							camelCase( key ),
+							value,
+						] )
+				);
+				newDefinition.name = name;
+			}
+
+			if ( newDefinition ) {
+				return {
+					...state,
+					[ name ]: newDefinition,
+				};
+			}
+
+			return state;
+		case 'REMOVE_BLOCK_TYPES':
+			return omit( state, action.names );
+	}
+
+	return state;
+}
+
 /**
  * Reducer managing the unprocessed block types in a form passed when registering the by block.
  * It's for internal use only. It allows recomputing the processed block types on-demand after block type filters
@@ -67,7 +138,7 @@ export function unprocessedBlockTypes( state = {}, action ) {
 		case 'ADD_UNPROCESSED_BLOCK_TYPE':
 			return {
 				...state,
-				[ action.blockType.name ]: action.blockType,
+				[ action.name ]: action.blockType,
 			};
 		case 'REMOVE_BLOCK_TYPES':
 			return omit( state, action.names );
@@ -300,6 +371,7 @@ export function collections( state = {}, action ) {
 }
 
 export default combineReducers( {
+	bootstrappedBlockTypes,
 	unprocessedBlockTypes,
 	blockTypes,
 	blockStyles,
diff --git a/packages/blocks/src/store/selectors.js b/packages/blocks/src/store/selectors.js
index cf577c695c9c5..b2b8ab8106f09 100644
--- a/packages/blocks/src/store/selectors.js
+++ b/packages/blocks/src/store/selectors.js
@@ -32,17 +32,6 @@ const getNormalizedBlockType = ( state, nameOrType ) =>
 		? getBlockType( state, nameOrType )
 		: nameOrType;
 
-/**
- * Returns all the unprocessed block types as passed during the registration.
- *
- * @param {Object} state Data state.
- *
- * @return {Array} Unprocessed block types.
- */
-export function __experimentalGetUnprocessedBlockTypes( state ) {
-	return state.unprocessedBlockTypes;
-}
-
 /**
  * Returns all the available block types.
  *
diff --git a/packages/blocks/src/store/test/reducer.js b/packages/blocks/src/store/test/reducer.js
index b4312d0fd7df2..5664f9d876cb6 100644
--- a/packages/blocks/src/store/test/reducer.js
+++ b/packages/blocks/src/store/test/reducer.js
@@ -31,24 +31,25 @@ describe( 'unprocessedBlockTypes', () => {
 
 	it( 'should add a new block type', () => {
 		const original = deepFreeze( {
-			'core/paragraph': { name: 'core/paragraph' },
+			'core/paragraph': { title: 'Paragraph' },
 		} );
 
 		const state = unprocessedBlockTypes( original, {
 			type: 'ADD_UNPROCESSED_BLOCK_TYPE',
-			blockType: { name: 'core/code' },
+			name: 'core/code',
+			blockType: { title: 'Code' },
 		} );
 
 		expect( state ).toEqual( {
-			'core/paragraph': { name: 'core/paragraph' },
-			'core/code': { name: 'core/code' },
+			'core/paragraph': { title: 'Paragraph' },
+			'core/code': { title: 'Code' },
 		} );
 	} );
 
 	it( 'should remove unprocessed block types', () => {
 		const original = deepFreeze( {
-			'core/paragraph': { name: 'core/paragraph' },
-			'core/code': { name: 'core/code' },
+			'core/paragraph': { title: 'Paragraph' },
+			'core/code': { title: 'Code' },
 		} );
 
 		const state = blockTypes( original, {
@@ -57,7 +58,7 @@ describe( 'unprocessedBlockTypes', () => {
 		} );
 
 		expect( state ).toEqual( {
-			'core/paragraph': { name: 'core/paragraph' },
+			'core/paragraph': { title: 'Paragraph' },
 		} );
 	} );
 } );
diff --git a/packages/customize-widgets/src/index.js b/packages/customize-widgets/src/index.js
index 10ca00ff78309..5b438cac86f49 100644
--- a/packages/customize-widgets/src/index.js
+++ b/packages/customize-widgets/src/index.js
@@ -49,7 +49,7 @@ export function initialize( editorName, blockEditorSettings ) {
 		welcomeGuide: true,
 	} );
 
-	dispatch( blocksStore ).__experimentalReapplyBlockTypeFilters();
+	dispatch( blocksStore ).reapplyBlockTypeFilters();
 	const coreBlocks = __experimentalGetCoreBlocks().filter( ( block ) => {
 		return ! (
 			DISABLED_BLOCKS.includes( block.name ) ||
diff --git a/packages/edit-post/src/index.js b/packages/edit-post/src/index.js
index fc5ce8c948e2a..9dec0c6cd5e35 100644
--- a/packages/edit-post/src/index.js
+++ b/packages/edit-post/src/index.js
@@ -62,7 +62,7 @@ export function initializeEditor(
 		welcomeGuideTemplate: true,
 	} );
 
-	dispatch( blocksStore ).__experimentalReapplyBlockTypeFilters();
+	dispatch( blocksStore ).reapplyBlockTypeFilters();
 
 	// Check if the block list view should be open by default.
 	// If `distractionFree` mode is enabled, the block list view should not be open.
diff --git a/packages/edit-site/src/index.js b/packages/edit-site/src/index.js
index ea2899a8abc10..18cb0d5e5db69 100644
--- a/packages/edit-site/src/index.js
+++ b/packages/edit-site/src/index.js
@@ -43,7 +43,7 @@ export function initializeEditor( id, settings ) {
 		fetchLinkSuggestions( search, searchOptions, settings );
 	settings.__experimentalFetchRichUrlData = fetchUrlData;
 
-	dispatch( blocksStore ).__experimentalReapplyBlockTypeFilters();
+	dispatch( blocksStore ).reapplyBlockTypeFilters();
 	const coreBlocks = __experimentalGetCoreBlocks().filter(
 		( { name } ) => name !== 'core/freeform'
 	);
diff --git a/packages/edit-widgets/src/index.js b/packages/edit-widgets/src/index.js
index 56421ebe357ba..eb87d22fefef9 100644
--- a/packages/edit-widgets/src/index.js
+++ b/packages/edit-widgets/src/index.js
@@ -70,7 +70,7 @@ export function initializeEditor( id, settings ) {
 		themeStyles: true,
 	} );
 
-	dispatch( blocksStore ).__experimentalReapplyBlockTypeFilters();
+	dispatch( blocksStore ).reapplyBlockTypeFilters();
 	registerCoreBlocks( coreBlocks );
 	registerLegacyWidgetBlock();
 	if ( process.env.IS_GUTENBERG_PLUGIN ) {

From f706c86f201f325a4108d74fdf1e0315521db705 Mon Sep 17 00:00:00 2001
From: Nik Tsekouras <ntsekouras@outlook.com>
Date: Wed, 30 Aug 2023 16:35:04 +0300
Subject: [PATCH 06/34] Toggle Distraction free mode mode based on
 compatibility (#54030)

* Toggle DFM mode based on compatibility

* rename `dfm` to `distractionFreeMode`

* add tests

* rename to `isDistractionFree`
---
 .../data/data-core-edit-site.md               |  4 +
 .../keyboard-shortcuts/edit-mode.js           | 32 +------
 .../index.js                                  | 41 +--------
 .../src/hooks/commands/use-common-commands.js | 22 -----
 .../hooks/commands/use-edit-mode-commands.js  | 28 +++---
 packages/edit-site/src/store/actions.js       | 77 ++++++++++++++--
 packages/edit-site/src/store/test/actions.js  | 88 +++++++++++++++++++
 packages/edit-site/src/store/test/reducer.js  | 31 +++++--
 8 files changed, 198 insertions(+), 125 deletions(-)

diff --git a/docs/reference-guides/data/data-core-edit-site.md b/docs/reference-guides/data/data-core-edit-site.md
index 83e9b09dfd7d8..0cac2268b2ab2 100644
--- a/docs/reference-guides/data/data-core-edit-site.md
+++ b/docs/reference-guides/data/data-core-edit-site.md
@@ -408,6 +408,10 @@ _Returns_
 
 Undocumented declaration.
 
+### toggleDistractionFree
+
+Action that toggles Distraction free mode. Distraction free mode expects there are no sidebars, as due to the z-index values set, you can't close sidebars.
+
 ### toggleFeature
 
 Dispatches an action that toggles a feature flag.
diff --git a/packages/edit-site/src/components/keyboard-shortcuts/edit-mode.js b/packages/edit-site/src/components/keyboard-shortcuts/edit-mode.js
index 49efa11368a7d..1346041b6a94c 100644
--- a/packages/edit-site/src/components/keyboard-shortcuts/edit-mode.js
+++ b/packages/edit-site/src/components/keyboard-shortcuts/edit-mode.js
@@ -1,15 +1,12 @@
 /**
  * WordPress dependencies
  */
-import { __ } from '@wordpress/i18n';
 import { useShortcut } from '@wordpress/keyboard-shortcuts';
 import { useDispatch, useSelect } from '@wordpress/data';
 import { store as coreStore } from '@wordpress/core-data';
 import { store as blockEditorStore } from '@wordpress/block-editor';
 import { store as interfaceStore } from '@wordpress/interface';
 import { createBlock } from '@wordpress/blocks';
-import { store as preferencesStore } from '@wordpress/preferences';
-import { store as noticesStore } from '@wordpress/notices';
 
 /**
  * Internal dependencies
@@ -32,12 +29,8 @@ function KeyboardShortcutsEditMode() {
 		[]
 	);
 	const { redo, undo } = useDispatch( coreStore );
-	const {
-		setIsListViewOpened,
-		switchEditorMode,
-		setIsInserterOpened,
-		closeGeneralSidebar,
-	} = useDispatch( editSiteStore );
+	const { setIsListViewOpened, switchEditorMode, toggleDistractionFree } =
+		useDispatch( editSiteStore );
 	const { enableComplementaryArea, disableComplementaryArea } =
 		useDispatch( interfaceStore );
 
@@ -45,17 +38,6 @@ function KeyboardShortcutsEditMode() {
 	const { getBlockName, getSelectedBlockClientId, getBlockAttributes } =
 		useSelect( blockEditorStore );
 
-	const { get: getPreference } = useSelect( preferencesStore );
-	const { set: setPreference, toggle } = useDispatch( preferencesStore );
-	const { createInfoNotice } = useDispatch( noticesStore );
-
-	const toggleDistractionFree = () => {
-		setPreference( 'core/edit-site', 'fixedToolbar', false );
-		setIsInserterOpened( false );
-		setIsListViewOpened( false );
-		closeGeneralSidebar();
-	};
-
 	const handleTextLevelShortcut = ( event, level ) => {
 		event.preventDefault();
 		const destinationBlockName =
@@ -134,16 +116,6 @@ function KeyboardShortcutsEditMode() {
 
 	useShortcut( 'core/edit-site/toggle-distraction-free', () => {
 		toggleDistractionFree();
-		toggle( 'core/edit-site', 'distractionFree' );
-		createInfoNotice(
-			getPreference( 'core/edit-site', 'distractionFree' )
-				? __( 'Distraction free mode turned on.' )
-				: __( 'Distraction free mode turned off.' ),
-			{
-				id: 'core/edit-site/distraction-free-mode/notice',
-				type: 'snackbar',
-			}
-		);
 	} );
 
 	return null;
diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-global-styles/index.js b/packages/edit-site/src/components/sidebar-navigation-screen-global-styles/index.js
index a156bd40c2211..5ad7691c5e5a2 100644
--- a/packages/edit-site/src/components/sidebar-navigation-screen-global-styles/index.js
+++ b/packages/edit-site/src/components/sidebar-navigation-screen-global-styles/index.js
@@ -15,8 +15,6 @@ import { useViewportMatch } from '@wordpress/compose';
 import { BlockEditorProvider } from '@wordpress/block-editor';
 import { humanTimeDiff } from '@wordpress/date';
 import { useCallback } from '@wordpress/element';
-import { store as noticesStore } from '@wordpress/notices';
-import { store as preferencesStore } from '@wordpress/preferences';
 
 /**
  * Internal dependencies
@@ -35,24 +33,7 @@ const noop = () => {};
 export function SidebarNavigationItemGlobalStyles( props ) {
 	const { openGeneralSidebar } = useDispatch( editSiteStore );
 	const { setCanvasMode } = unlock( useDispatch( editSiteStore ) );
-	const { createNotice } = useDispatch( noticesStore );
-	const { set: setPreference } = useDispatch( preferencesStore );
-	const { get: getPreference } = useSelect( preferencesStore );
 
-	const turnOffDistractionFreeMode = useCallback( () => {
-		const isDistractionFree = getPreference(
-			editSiteStore.name,
-			'distractionFree'
-		);
-		if ( ! isDistractionFree ) {
-			return;
-		}
-		setPreference( editSiteStore.name, 'distractionFree', false );
-		createNotice( 'info', __( 'Distraction free mode turned off' ), {
-			isDismissible: true,
-			type: 'snackbar',
-		} );
-	}, [ createNotice, setPreference, getPreference ] );
 	const hasGlobalStyleVariations = useSelect(
 		( select ) =>
 			!! select(
@@ -73,7 +54,6 @@ export function SidebarNavigationItemGlobalStyles( props ) {
 		<SidebarNavigationItem
 			{ ...props }
 			onClick={ () => {
-				turnOffDistractionFreeMode();
 				// Switch to edit mode.
 				setCanvasMode( 'edit' );
 				// Open global styles sidebar.
@@ -150,9 +130,6 @@ export default function SidebarNavigationScreenGlobalStyles() {
 	const { setCanvasMode, setEditorCanvasContainerView } = unlock(
 		useDispatch( editSiteStore )
 	);
-	const { createNotice } = useDispatch( noticesStore );
-	const { set: setPreference } = useDispatch( preferencesStore );
-	const { get: getPreference } = useSelect( preferencesStore );
 	const { isViewMode, isStyleBookOpened, revisionsCount } = useSelect(
 		( select ) => {
 			const { getCanvasMode, getEditorCanvasContainerView } = unlock(
@@ -176,28 +153,12 @@ export default function SidebarNavigationScreenGlobalStyles() {
 		[]
 	);
 
-	const turnOffDistractionFreeMode = useCallback( () => {
-		const isDistractionFree = getPreference(
-			editSiteStore.name,
-			'distractionFree'
-		);
-		if ( ! isDistractionFree ) {
-			return;
-		}
-		setPreference( editSiteStore.name, 'distractionFree', false );
-		createNotice( 'info', __( 'Distraction free mode turned off' ), {
-			isDismissible: true,
-			type: 'snackbar',
-		} );
-	}, [ createNotice, setPreference, getPreference ] );
-
 	const openGlobalStyles = useCallback( async () => {
-		turnOffDistractionFreeMode();
 		return Promise.all( [
 			setCanvasMode( 'edit' ),
 			openGeneralSidebar( 'edit-site/global-styles' ),
 		] );
-	}, [ setCanvasMode, openGeneralSidebar, turnOffDistractionFreeMode ] );
+	}, [ setCanvasMode, openGeneralSidebar ] );
 
 	const openStyleBook = useCallback( async () => {
 		await openGlobalStyles();
diff --git a/packages/edit-site/src/hooks/commands/use-common-commands.js b/packages/edit-site/src/hooks/commands/use-common-commands.js
index c1a023d0b1b20..16d07132ad7c7 100644
--- a/packages/edit-site/src/hooks/commands/use-common-commands.js
+++ b/packages/edit-site/src/hooks/commands/use-common-commands.js
@@ -10,7 +10,6 @@ import { privateApis as blockEditorPrivateApis } from '@wordpress/block-editor';
 import { privateApis as routerPrivateApis } from '@wordpress/router';
 import { store as preferencesStore } from '@wordpress/preferences';
 import { store as coreStore } from '@wordpress/core-data';
-import { store as noticesStore } from '@wordpress/notices';
 import { useViewportMatch } from '@wordpress/compose';
 
 /**
@@ -31,16 +30,7 @@ function useGlobalStylesOpenStylesCommands() {
 	const isMobileViewport = useViewportMatch( 'medium', '<' );
 	const isEditorPage = ! getIsListPage( params, isMobileViewport );
 	const { getCanvasMode } = unlock( useSelect( editSiteStore ) );
-	const { set } = useDispatch( preferencesStore );
-	const { createInfoNotice } = useDispatch( noticesStore );
-
 	const history = useHistory();
-	const isDistractionFree = useSelect( ( select ) => {
-		return select( preferencesStore ).get(
-			editSiteStore.name,
-			'distractionFree'
-		);
-	}, [] );
 
 	const isBlockBasedTheme = useSelect( ( select ) => {
 		return select( coreStore ).getCurrentTheme().is_block_theme;
@@ -66,15 +56,6 @@ function useGlobalStylesOpenStylesCommands() {
 					if ( isEditorPage && getCanvasMode() !== 'edit' ) {
 						setCanvasMode( 'edit' );
 					}
-					if ( isDistractionFree ) {
-						set( editSiteStore.name, 'distractionFree', false );
-						createInfoNotice(
-							__( 'Distraction free mode turned off.' ),
-							{
-								type: 'snackbar',
-							}
-						);
-					}
 					openGeneralSidebar( 'edit-site/global-styles' );
 				},
 				icon: styles,
@@ -85,11 +66,8 @@ function useGlobalStylesOpenStylesCommands() {
 		openGeneralSidebar,
 		setCanvasMode,
 		isEditorPage,
-		createInfoNotice,
 		getCanvasMode,
-		isDistractionFree,
 		isBlockBasedTheme,
-		set,
 	] );
 
 	return {
diff --git a/packages/edit-site/src/hooks/commands/use-edit-mode-commands.js b/packages/edit-site/src/hooks/commands/use-edit-mode-commands.js
index 6079c01773be7..76c7eea513743 100644
--- a/packages/edit-site/src/hooks/commands/use-edit-mode-commands.js
+++ b/packages/edit-site/src/hooks/commands/use-edit-mode-commands.js
@@ -195,7 +195,7 @@ function useEditUICommands() {
 	const {
 		openGeneralSidebar,
 		closeGeneralSidebar,
-		setIsInserterOpened,
+		toggleDistractionFree,
 		setIsListViewOpened,
 		switchEditorMode,
 	} = useDispatch( editSiteStore );
@@ -205,6 +205,7 @@ function useEditUICommands() {
 		activeSidebar,
 		showBlockBreadcrumbs,
 		isListViewOpen,
+		isDistractionFree,
 	} = useSelect( ( select ) => {
 		const { isListViewOpened, getEditorMode } = select( editSiteStore );
 		return {
@@ -218,11 +219,14 @@ function useEditUICommands() {
 				'showBlockBreadcrumbs'
 			),
 			isListViewOpen: isListViewOpened(),
+			isDistractionFree: select( preferencesStore ).get(
+				editSiteStore.name,
+				'distractionFree'
+			),
 		};
 	}, [] );
 	const { openModal } = useDispatch( interfaceStore );
-	const { get: getPreference } = useSelect( preferencesStore );
-	const { set: setPreference, toggle } = useDispatch( preferencesStore );
+	const { toggle } = useDispatch( preferencesStore );
 	const { createInfoNotice } = useDispatch( noticesStore );
 
 	if ( canvasMode !== 'edit' ) {
@@ -272,20 +276,7 @@ function useEditUICommands() {
 		name: 'core/toggle-distraction-free',
 		label: __( 'Toggle distraction free' ),
 		callback: ( { close } ) => {
-			setPreference( 'core/edit-site', 'fixedToolbar', false );
-			setIsInserterOpened( false );
-			setIsListViewOpened( false );
-			closeGeneralSidebar();
-			toggle( 'core/edit-site', 'distractionFree' );
-			createInfoNotice(
-				getPreference( 'core/edit-site', 'distractionFree' )
-					? __( 'Distraction free on.' )
-					: __( 'Distraction free off.' ),
-				{
-					id: 'core/edit-site/distraction-free-mode/notice',
-					type: 'snackbar',
-				}
-			);
+			toggleDistractionFree();
 			close();
 		},
 	} );
@@ -295,6 +286,9 @@ function useEditUICommands() {
 		label: __( 'Toggle top toolbar' ),
 		callback: ( { close } ) => {
 			toggle( 'core/edit-site', 'fixedToolbar' );
+			if ( isDistractionFree ) {
+				toggleDistractionFree();
+			}
 			close();
 		},
 	} );
diff --git a/packages/edit-site/src/store/actions.js b/packages/edit-site/src/store/actions.js
index ac07ac5beaa2a..0ad521a9c9a54 100644
--- a/packages/edit-site/src/store/actions.js
+++ b/packages/edit-site/src/store/actions.js
@@ -373,12 +373,20 @@ export function updateSettings( settings ) {
  * @param {boolean} isOpen If true, opens the list view. If false, closes it.
  *                         It does not toggle the state, but sets it directly.
  */
-export function setIsListViewOpened( isOpen ) {
-	return {
-		type: 'SET_IS_LIST_VIEW_OPENED',
-		isOpen,
+export const setIsListViewOpened =
+	( isOpen ) =>
+	( { dispatch, registry } ) => {
+		const isDistractionFree = registry
+			.select( preferencesStore )
+			.get( 'core/edit-site', 'distractionFree' );
+		if ( isDistractionFree && isOpen ) {
+			dispatch.toggleDistractionFree();
+		}
+		dispatch( {
+			type: 'SET_IS_LIST_VIEW_OPENED',
+			isOpen,
+		} );
 	};
-}
 
 /**
  * Sets whether the save view panel should be open.
@@ -533,7 +541,13 @@ export const revertTemplate =
  */
 export const openGeneralSidebar =
 	( name ) =>
-	( { registry } ) => {
+	( { dispatch, registry } ) => {
+		const isDistractionFree = registry
+			.select( preferencesStore )
+			.get( 'core/edit-site', 'distractionFree' );
+		if ( isDistractionFree ) {
+			dispatch.toggleDistractionFree();
+		}
 		registry
 			.dispatch( interfaceStore )
 			.enableComplementaryArea( editSiteStoreName, name );
@@ -552,7 +566,7 @@ export const closeGeneralSidebar =
 
 export const switchEditorMode =
 	( mode ) =>
-	( { registry } ) => {
+	( { dispatch, registry } ) => {
 		registry
 			.dispatch( 'core/preferences' )
 			.set( 'core/edit-site', 'editorMode', mode );
@@ -565,6 +579,12 @@ export const switchEditorMode =
 		if ( mode === 'visual' ) {
 			speak( __( 'Visual editor selected' ), 'assertive' );
 		} else if ( mode === 'text' ) {
+			const isDistractionFree = registry
+				.select( preferencesStore )
+				.get( 'core/edit-site', 'distractionFree' );
+			if ( isDistractionFree ) {
+				dispatch.toggleDistractionFree();
+			}
 			speak( __( 'Code editor selected' ), 'assertive' );
 		}
 	};
@@ -587,3 +607,46 @@ export const setHasPageContentFocus =
 			hasPageContentFocus,
 		} );
 	};
+
+/**
+ * Action that toggles Distraction free mode.
+ * Distraction free mode expects there are no sidebars, as due to the
+ * z-index values set, you can't close sidebars.
+ */
+export const toggleDistractionFree =
+	() =>
+	( { dispatch, registry } ) => {
+		const isDistractionFree = registry
+			.select( preferencesStore )
+			.get( 'core/edit-site', 'distractionFree' );
+		if ( ! isDistractionFree ) {
+			registry.batch( () => {
+				registry
+					.dispatch( preferencesStore )
+					.set( 'core/edit-site', 'fixedToolbar', false );
+				dispatch.setIsInserterOpened( false );
+				dispatch.setIsListViewOpened( false );
+				dispatch.closeGeneralSidebar();
+			} );
+		}
+		registry.batch( () => {
+			registry
+				.dispatch( preferencesStore )
+				.set(
+					'core/edit-site',
+					'distractionFree',
+					! isDistractionFree
+				);
+			registry
+				.dispatch( noticesStore )
+				.createInfoNotice(
+					isDistractionFree
+						? __( 'Distraction free off.' )
+						: __( 'Distraction free on.' ),
+					{
+						id: 'core/edit-site/distraction-free-mode/notice',
+						type: 'snackbar',
+					}
+				);
+		} );
+	};
diff --git a/packages/edit-site/src/store/test/actions.js b/packages/edit-site/src/store/test/actions.js
index 8ce914b00b208..345fcddbbba3b 100644
--- a/packages/edit-site/src/store/test/actions.js
+++ b/packages/edit-site/src/store/test/actions.js
@@ -215,6 +215,94 @@ describe( 'actions', () => {
 				false
 			);
 		} );
+		it( 'should turn off distraction free mode when opening the list view', () => {
+			const registry = createRegistryWithStores();
+			registry
+				.dispatch( preferencesStore )
+				.set( 'core/edit-site', 'distractionFree', true );
+			registry.dispatch( editSiteStore ).setIsListViewOpened( true );
+			expect(
+				registry
+					.select( preferencesStore )
+					.get( 'core/edit-site', 'distractionFree' )
+			).toBe( false );
+		} );
+	} );
+
+	describe( 'openGeneralSidebar', () => {
+		it( 'should turn off distraction free mode when opening a general sidebar', () => {
+			const registry = createRegistryWithStores();
+			registry
+				.dispatch( preferencesStore )
+				.set( 'core/edit-site', 'distractionFree', true );
+
+			registry
+				.dispatch( editSiteStore )
+				.openGeneralSidebar( 'edit-site/global-styles' );
+			expect(
+				registry
+					.select( preferencesStore )
+					.get( 'core/edit-site', 'distractionFree' )
+			).toBe( false );
+		} );
+	} );
+
+	describe( 'switchEditorMode', () => {
+		it( 'should turn off distraction free mode when switching to code editor', () => {
+			const registry = createRegistryWithStores();
+			registry
+				.dispatch( preferencesStore )
+				.set( 'core/edit-site', 'distractionFree', true );
+			registry.dispatch( editSiteStore ).switchEditorMode( 'visual' );
+			expect(
+				registry
+					.select( preferencesStore )
+					.get( 'core/edit-site', 'distractionFree' )
+			).toBe( true );
+			registry.dispatch( editSiteStore ).switchEditorMode( 'text' );
+			expect(
+				registry
+					.select( preferencesStore )
+					.get( 'core/edit-site', 'distractionFree' )
+			).toBe( false );
+		} );
+	} );
+
+	describe( 'toggleDistractionFree', () => {
+		it( 'should properly update settings to prevent layout corruption when enabling distraction free mode', () => {
+			const registry = createRegistryWithStores();
+			// Enable everything that shouldn't be enabled in distraction free mode.
+			registry
+				.dispatch( preferencesStore )
+				.set( 'core/edit-site', 'fixedToolbar', true );
+			registry.dispatch( editSiteStore ).setIsListViewOpened( true );
+			registry
+				.dispatch( editSiteStore )
+				.openGeneralSidebar( 'edit-site/global-styles' );
+			// Initial state is falsy.
+			registry.dispatch( editSiteStore ).toggleDistractionFree();
+			expect(
+				registry
+					.select( preferencesStore )
+					.get( 'core/edit-site', 'fixedToolbar' )
+			).toBe( false );
+			expect( registry.select( editSiteStore ).isListViewOpened() ).toBe(
+				false
+			);
+			expect( registry.select( editSiteStore ).isInserterOpened() ).toBe(
+				false
+			);
+			expect(
+				registry
+					.select( interfaceStore )
+					.getActiveComplementaryArea( editSiteStore.name )
+			).toBeNull();
+			expect(
+				registry
+					.select( preferencesStore )
+					.get( 'core/edit-site', 'distractionFree' )
+			).toBe( true );
+		} );
 	} );
 
 	describe( 'setHasPageContentFocus', () => {
diff --git a/packages/edit-site/src/store/test/reducer.js b/packages/edit-site/src/store/test/reducer.js
index 9ab7f8e3964f8..fc06faf925b38 100644
--- a/packages/edit-site/src/store/test/reducer.js
+++ b/packages/edit-site/src/store/test/reducer.js
@@ -14,7 +14,7 @@ import {
 	hasPageContentFocus,
 } from '../reducer';
 
-import { setIsInserterOpened, setIsListViewOpened } from '../actions';
+import { setIsInserterOpened } from '../actions';
 
 describe( 'state', () => {
 	describe( 'settings()', () => {
@@ -95,13 +95,19 @@ describe( 'state', () => {
 
 		it( 'should close the inserter when opening the list view panel', () => {
 			expect(
-				blockInserterPanel( true, setIsListViewOpened( true ) )
+				blockInserterPanel( true, {
+					type: 'SET_IS_LIST_VIEW_OPENED',
+					isOpen: true,
+				} )
 			).toBe( false );
 		} );
 
 		it( 'should not change the state when closing the list view panel', () => {
 			expect(
-				blockInserterPanel( true, setIsListViewOpened( false ) )
+				blockInserterPanel( true, {
+					type: 'SET_IS_LIST_VIEW_OPENED',
+					isOpen: false,
+				} )
 			).toBe( true );
 		} );
 	} );
@@ -116,12 +122,19 @@ describe( 'state', () => {
 		} );
 
 		it( 'should set the open state of the list view panel', () => {
-			expect( listViewPanel( false, setIsListViewOpened( true ) ) ).toBe(
-				true
-			);
-			expect( listViewPanel( true, setIsListViewOpened( false ) ) ).toBe(
-				false
-			);
+			// registry.dispatch( editSiteStore ).toggleFeature( 'name' );
+			expect(
+				listViewPanel( false, {
+					type: 'SET_IS_LIST_VIEW_OPENED',
+					isOpen: true,
+				} )
+			).toBe( true );
+			expect(
+				listViewPanel( true, {
+					type: 'SET_IS_LIST_VIEW_OPENED',
+					isOpen: false,
+				} )
+			).toBe( false );
 		} );
 
 		it( 'should close the list view when opening the inserter panel', () => {

From 69dabb1e5604e4598c5916b24fc2aa6a0c4099e2 Mon Sep 17 00:00:00 2001
From: Gutenberg Repository Automation <gutenberg@wordpress.org>
Date: Wed, 30 Aug 2023 16:39:01 +0000
Subject: [PATCH 07/34] Bump plugin version to 16.6.0-rc.1

---
 gutenberg.php     | 2 +-
 package-lock.json | 4 ++--
 package.json      | 2 +-
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/gutenberg.php b/gutenberg.php
index d42b0a09017c0..44146b0c57d30 100644
--- a/gutenberg.php
+++ b/gutenberg.php
@@ -5,7 +5,7 @@
  * Description: Printing since 1440. This is the development plugin for the block editor, site editor, and other future WordPress core functionality.
  * Requires at least: 6.1
  * Requires PHP: 7.0
- * Version: 16.5.1
+ * Version: 16.6.0-rc.1
  * Author: Gutenberg Team
  * Text Domain: gutenberg
  *
diff --git a/package-lock.json b/package-lock.json
index 4e6c42786efce..e70301a75ef61 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
 {
 	"name": "gutenberg",
-	"version": "16.5.1",
+	"version": "16.6.0-rc.1",
 	"lockfileVersion": 2,
 	"requires": true,
 	"packages": {
 		"": {
 			"name": "gutenberg",
-			"version": "16.5.1",
+			"version": "16.6.0-rc.1",
 			"hasInstallScript": true,
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
diff --git a/package.json b/package.json
index a4131bca556d9..d8640a128c10f 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "gutenberg",
-	"version": "16.5.1",
+	"version": "16.6.0-rc.1",
 	"private": true,
 	"description": "A new WordPress editor experience.",
 	"author": "The WordPress Contributors",

From 2d4314d754dcec540ac05890cc0d2dbf89ab5d53 Mon Sep 17 00:00:00 2001
From: Gutenberg Repository Automation <gutenberg@wordpress.org>
Date: Wed, 30 Aug 2023 16:49:26 +0000
Subject: [PATCH 08/34] Update Changelog for 16.6.0-rc.1

---
 changelog.txt | 250 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 250 insertions(+)

diff --git a/changelog.txt b/changelog.txt
index 40c6e315feac6..0f4f5bcf9bbcf 100644
--- a/changelog.txt
+++ b/changelog.txt
@@ -1,5 +1,255 @@
 == Changelog ==
 
+= 16.6.0-rc.1 =
+
+## Changelog
+
+### Features
+
+#### Interactivity API
+- Add Slot and Fill directives. ([53958](https://github.com/WordPress/gutenberg/pull/53958))
+- Query block: Client-side pagination. ([53812](https://github.com/WordPress/gutenberg/pull/53812))
+- Update `data-wp-bind` directive logic. ([54003](https://github.com/WordPress/gutenberg/pull/54003))
+
+
+### Enhancements
+
+- Bundle ObserveTyping within the BlockList component. ([53875](https://github.com/WordPress/gutenberg/pull/53875))
+- Default appender: Hide the dashed indicator until ancestor is selected. ([53761](https://github.com/WordPress/gutenberg/pull/53761))
+- Register the block editor keyboard shortcuts automatically when using BlockEditorProvider. ([53910](https://github.com/WordPress/gutenberg/pull/53910))
+- [Commands]: Add toggle list view command in site editor. ([53983](https://github.com/WordPress/gutenberg/pull/53983))
+
+#### Components
+- Bundle SlotFillProvider within BlockEditorProvider. ([53940](https://github.com/WordPress/gutenberg/pull/53940))
+- Make the Popover.Slot optional. ([53889](https://github.com/WordPress/gutenberg/pull/53889))
+- Popover: Update `@floating-ui` to latest version, remove custom fix for iframe positioning and scaling. ([46845](https://github.com/WordPress/gutenberg/pull/46845))
+- `AlignmentMatrixControl`: Replace `act()` with `userEvent`. ([53703](https://github.com/WordPress/gutenberg/pull/53703))
+- `ProgressBar`: Add transition to determinate indicator. ([53877](https://github.com/WordPress/gutenberg/pull/53877))
+
+#### Block Library
+- Blocks: Move bootstrapped block types to Redux state. ([53807](https://github.com/WordPress/gutenberg/pull/53807))
+- Capture toolbars in navigation block. ([53697](https://github.com/WordPress/gutenberg/pull/53697))
+- Content Block: Change placeholder and end-to-end test to refer to Content block. ([53902](https://github.com/WordPress/gutenberg/pull/53902))
+- Make mid size parameter settable for Query Pagination block. ([51216](https://github.com/WordPress/gutenberg/pull/51216))
+
+#### Block Editor
+- Capture toolbars in quote block. ([53699](https://github.com/WordPress/gutenberg/pull/53699))
+- Improve writing flow for lists by capturing list item toolbars. ([53306](https://github.com/WordPress/gutenberg/pull/53306))
+- RichTextValue: Typescript Adjustment. ([54002](https://github.com/WordPress/gutenberg/pull/54002))
+
+#### Typography
+- Font Face: Prepare for merge into Core. ([53858](https://github.com/WordPress/gutenberg/pull/53858))
+- Renames "Fonts Library" to "Font Library". ([53780](https://github.com/WordPress/gutenberg/pull/53780))
+
+#### Post Editor
+- Edit Post: Use hooks instead of HoCs in `TaxonomyPanel`. ([53773](https://github.com/WordPress/gutenberg/pull/53773))
+
+#### List View
+- Add keyboard shortcut for duplicating blocks. ([53559](https://github.com/WordPress/gutenberg/pull/53559))
+
+#### Patterns
+- Add a custom taxonomy for user created patterns. ([53163](https://github.com/WordPress/gutenberg/pull/53163))
+
+
+### New APIs
+
+#### Interactivity API
+- Router with region-based client-side navigation. ([53733](https://github.com/WordPress/gutenberg/pull/53733))
+
+
+### Bug Fixes
+
+- Add missing aria roles for block locking toolbar and menu buttons. ([53734](https://github.com/WordPress/gutenberg/pull/53734))
+- Block Editor: Fix cleanup in the 'useNavModeExit' hook. ([53795](https://github.com/WordPress/gutenberg/pull/53795))
+- Command Palette: Fix crash on block-related commands. ([53923](https://github.com/WordPress/gutenberg/pull/53923))
+- Date: Add relative time translations for moment.js. ([53931](https://github.com/WordPress/gutenberg/pull/53931))
+- Date: Update translation domains for strings to be translatable. ([53995](https://github.com/WordPress/gutenberg/pull/53995))
+- Iframe: Set character encoding to utf-8. ([53519](https://github.com/WordPress/gutenberg/pull/53519))
+- Replace horizontal ellipsis icon with vertical ellipsis icon. ([52731](https://github.com/WordPress/gutenberg/pull/52731))
+- Toggle Distraction free mode mode based on compatibility. ([54030](https://github.com/WordPress/gutenberg/pull/54030))
+- Warning: Introduce `SCRIPT_DEBUG` to make the package compatible with webpack 5. ([50122](https://github.com/WordPress/gutenberg/pull/50122))
+- [Commands]: Fix `move to` command condition for registering. ([54049](https://github.com/WordPress/gutenberg/pull/54049))
+- [Commands]: Fix block editor commands availability. ([53994](https://github.com/WordPress/gutenberg/pull/53994))
+- [Format library]: Fix `language` popover position. ([53841](https://github.com/WordPress/gutenberg/pull/53841))
+
+#### Block Library
+- Add init.js module for the Footnotes block. ([53763](https://github.com/WordPress/gutenberg/pull/53763))
+- Adding center align css for social icon issue. ([43120](https://github.com/WordPress/gutenberg/pull/43120))
+- Cover block: Fix exception when adding video background. ([53961](https://github.com/WordPress/gutenberg/pull/53961))
+- List View: Allow replacing template part when a block isn't selected. ([53757](https://github.com/WordPress/gutenberg/pull/53757))
+- Post Navigation Link: Remove unnecessary space between arrows and label. ([53572](https://github.com/WordPress/gutenberg/pull/53572))
+- Search block: Fix width input field. ([53952](https://github.com/WordPress/gutenberg/pull/53952))
+- Simplify check for no posts in query-no-results block. ([53772](https://github.com/WordPress/gutenberg/pull/53772))
+- Site Logo: Remove line-height for the anchor element. ([53909](https://github.com/WordPress/gutenberg/pull/53909))
+
+#### Components
+- Always render the fallback Popover anchor within the Popover's parent element. ([53982](https://github.com/WordPress/gutenberg/pull/53982))
+- Fix the cleanup method for SandBox. ([53796](https://github.com/WordPress/gutenberg/pull/53796))
+- PaletteEdit: Fix component height. ([54000](https://github.com/WordPress/gutenberg/pull/54000))
+
+#### Post Editor
+- Edit Post: Fix tab border conflicts in the Document Overview panel. ([53711](https://github.com/WordPress/gutenberg/pull/53711))
+- EditPostPreferencesModal: Fix intermittently failing tests. ([53814](https://github.com/WordPress/gutenberg/pull/53814))
+- getInsertionPoint: Fix type check for the state value. ([53793](https://github.com/WordPress/gutenberg/pull/53793))
+
+#### npm Packages
+- Workflow: Run Learn directly from GitHub action when publishing to npm targeting WP core. ([53762](https://github.com/WordPress/gutenberg/pull/53762))
+- Workflows: Fix issues with the npm publishing workflow when using locally. ([53565](https://github.com/WordPress/gutenberg/pull/53565))
+
+#### Themes
+- Command Palette: Proper handling of page/post links in all themes. ([53718](https://github.com/WordPress/gutenberg/pull/53718))
+- Fix query loop bugs by correctly relying on the main query and removing problematic workaround. ([49904](https://github.com/WordPress/gutenberg/pull/49904))
+
+#### Block Editor
+- Fix: Indicator style when block moving mode. ([53972](https://github.com/WordPress/gutenberg/pull/53972))
+
+#### Icons
+- Fix invalid namespaces. ([53955](https://github.com/WordPress/gutenberg/pull/53955))
+
+#### Patterns
+- Disable the preview option button when editing. ([53913](https://github.com/WordPress/gutenberg/pull/53913))
+
+#### Global Styles
+- Gallery: Re-enable block spacing at block level while still hiding in global styles. ([53900](https://github.com/WordPress/gutenberg/pull/53900))
+
+#### Layout
+- BlockList: Ensure element styles (and svg) are always appended at the end of the document. ([53859](https://github.com/WordPress/gutenberg/pull/53859))
+
+#### Interactivity API
+- Add "supports.interactivity" to Image block. ([53850](https://github.com/WordPress/gutenberg/pull/53850))
+
+#### Style Variations
+- Block Styles: Fix misplaced preview popover on RTL site. ([53726](https://github.com/WordPress/gutenberg/pull/53726))
+
+#### List View
+- Recalculate window list when expanded state changes (fix logic for long nested lists). ([53716](https://github.com/WordPress/gutenberg/pull/53716))
+
+#### Widgets Editor
+- Block Widget: Fix content cutoff in the keyboard shortcut modal. ([53638](https://github.com/WordPress/gutenberg/pull/53638))
+
+#### Rich Text
+- Fix cleanup in `useRemoveBrowserShortcuts`. ([52225](https://github.com/WordPress/gutenberg/pull/52225))
+
+
+### Accessibility
+
+- Edit site: Add missing label to post status password protected input field. ([52885](https://github.com/WordPress/gutenberg/pull/52885))
+- [a11y] Fix: Aria-haspop, aria-expanded attributes on the link format button. ([53691](https://github.com/WordPress/gutenberg/pull/53691))
+
+#### Site Editor
+- Add missing aria roles to the 'Create template part' menu item. ([53754](https://github.com/WordPress/gutenberg/pull/53754))
+- Unify the delete button style in the dropdown menu with red. ([52597](https://github.com/WordPress/gutenberg/pull/52597))
+
+#### Block Library
+- Add missing aria roles to the 'Replace template part' menu item. ([53755](https://github.com/WordPress/gutenberg/pull/53755))
+
+#### Patterns
+- Add missing aria roles to the 'Create pattern' menu item. ([53739](https://github.com/WordPress/gutenberg/pull/53739))
+
+#### List View
+- [a11y] Fix: Aria-haspop and aria-expanded attributes on list view button. ([53693](https://github.com/WordPress/gutenberg/pull/53693))
+
+#### Block Editor
+- [a11y] Fix: Aria-haspop and aria-expanded attributes on the inserter button. ([53692](https://github.com/WordPress/gutenberg/pull/53692))
+
+
+### Performance
+
+- Revert "Switch performance tests to Playwright (#52022)". ([53741](https://github.com/WordPress/gutenberg/pull/53741))
+- StartPageOptions: Load and parse patterns only after establishing the need for them. ([53673](https://github.com/WordPress/gutenberg/pull/53673))
+- Switch performance tests to Playwright: Take 2. ([53768](https://github.com/WordPress/gutenberg/pull/53768))
+
+
+### Experiments
+
+#### Block API
+- Auto-inserting blocks: Add block inspector panel. ([52969](https://github.com/WordPress/gutenberg/pull/52969))
+
+
+### Documentation
+
+- Add juanmaguitar as codeowner of /packages/interactivity/docs. ([53845](https://github.com/WordPress/gutenberg/pull/53845))
+- Add new How-to Guide for enqueueing assets in the Editor. ([53828](https://github.com/WordPress/gutenberg/pull/53828))
+- Adds example for useBlockProps hook. ([53646](https://github.com/WordPress/gutenberg/pull/53646))
+- Adds explanatory text to view.js template. ([53870](https://github.com/WordPress/gutenberg/pull/53870))
+- Clarification for `parent` and `ancestor` hierarchical relationships. ([53855](https://github.com/WordPress/gutenberg/pull/53855))
+- Docs: Extend the information about using `render` with `block.json`. ([53973](https://github.com/WordPress/gutenberg/pull/53973))
+- Docs: Remove duplicate sections from FAQ page. ([53830](https://github.com/WordPress/gutenberg/pull/53830))
+- Document the naming convention for `block-library` PHP functions. ([53777](https://github.com/WordPress/gutenberg/pull/53777))
+- Fix 'lerna' links in the release documentation. ([53770](https://github.com/WordPress/gutenberg/pull/53770))
+- Fix typo in code sample for Interactivity API. ([53916](https://github.com/WordPress/gutenberg/pull/53916))
+- MenuItem: Add Storybook stories. ([53613](https://github.com/WordPress/gutenberg/pull/53613))
+- Shortcut: Add Storybook stories. ([53627](https://github.com/WordPress/gutenberg/pull/53627))
+- Storybook: Add back subcomponents to props table. ([53751](https://github.com/WordPress/gutenberg/pull/53751))
+- Storybook: Fix default source visibility. ([53749](https://github.com/WordPress/gutenberg/pull/53749))
+- Storybook: Show main story before description. ([53753](https://github.com/WordPress/gutenberg/pull/53753))
+- Update local instructions on the dev env documentation. ([53924](https://github.com/WordPress/gutenberg/pull/53924))
+- Update the Block Variations API doc. ([53817](https://github.com/WordPress/gutenberg/pull/53817))
+- Update to node 16 and npm 8 in the getting started with code contribution doc. ([53912](https://github.com/WordPress/gutenberg/pull/53912))
+- docs: Fix report-flaky-test link. ([53848](https://github.com/WordPress/gutenberg/pull/53848))
+
+
+### Code Quality
+
+- Components: Update Popover per reviews. ([53907](https://github.com/WordPress/gutenberg/pull/53907))
+- Edit Site: Rename `CanvasSpinner` to `CanvasLoader`. ([53728](https://github.com/WordPress/gutenberg/pull/53728))
+- Enforce valid function names in the packages/block-library/src/*/*.php files. ([53438](https://github.com/WordPress/gutenberg/pull/53438))
+- Fonts Library: Update properties name from snake case to camel case to match the rest of the properties. ([53746](https://github.com/WordPress/gutenberg/pull/53746))
+
+#### Post Editor
+- Editor: Fix the 'useSelect' warning in the 'useIsDirty' hook. ([53759](https://github.com/WordPress/gutenberg/pull/53759))
+- Fix browser console error when changing device preview mode. ([53969](https://github.com/WordPress/gutenberg/pull/53969))
+- Refactor latest content selectors in 'CopyContentMenuItem' components. ([53676](https://github.com/WordPress/gutenberg/pull/53676))
+
+#### Components
+- Remove unnecessary utils. ([53679](https://github.com/WordPress/gutenberg/pull/53679))
+- SlotFill: Refactor `<Slot bubblesVirtually />`. ([53272](https://github.com/WordPress/gutenberg/pull/53272))
+- Storybook: Update TypeScript types. ([53748](https://github.com/WordPress/gutenberg/pull/53748))
+
+#### List View
+- Fix warning error when the gallery block has the same image URLs. ([53809](https://github.com/WordPress/gutenberg/pull/53809))
+
+#### Typography
+- Font Face API: Use `gutenberg_get_global_settings` instead of private API. ([53805](https://github.com/WordPress/gutenberg/pull/53805))
+
+
+### Tools
+
+- Try: Change PR label enforcer automation not to work on draft PRs by default. ([53417](https://github.com/WordPress/gutenberg/pull/53417))
+
+#### Testing
+- Attempt to fix intermittent end-to-end test failure. ([53905](https://github.com/WordPress/gutenberg/pull/53905))
+- Fonts Library: Test improvements. ([53702](https://github.com/WordPress/gutenberg/pull/53702))
+- Make fonts test files use Core approach. ([53856](https://github.com/WordPress/gutenberg/pull/53856))
+- Migrate shortcut help end-to-end tests to Playwright. ([53832](https://github.com/WordPress/gutenberg/pull/53832))
+- Relocates Font Face and Fonts Library PHP files into Core's fonts directory. ([53747](https://github.com/WordPress/gutenberg/pull/53747))
+- `ColorPalette`: Refine test query. ([53704](https://github.com/WordPress/gutenberg/pull/53704))
+- end-to-end Playwright Utils: Automatically detect canvas type. ([53744](https://github.com/WordPress/gutenberg/pull/53744))
+- test: Automate mobile editor tests. ([53991](https://github.com/WordPress/gutenberg/pull/53991))
+
+#### Build Tooling
+- Update Jest to latest version, and use optimized JSDOM. ([53736](https://github.com/WordPress/gutenberg/pull/53736))
+
+#### Plugin
+- Backport themes `is_block_theme` collection param from core. ([53846](https://github.com/WordPress/gutenberg/pull/53846))
+
+
+## First time contributors
+
+The following PRs were merged by first time contributors:
+
+- @JEverhart383: Fix typo in code sample for Interactivity API. ([53916](https://github.com/WordPress/gutenberg/pull/53916))
+- @krokodok: Make mid size parameter settable for Query Pagination block. ([51216](https://github.com/WordPress/gutenberg/pull/51216))
+- @mklute101: Update local instructions on the dev env documentation. ([53924](https://github.com/WordPress/gutenberg/pull/53924))
+
+
+## Contributors
+
+The following contributors merged PRs in this release:
+
+@afercia @andrewserong @anton-vlasenko @bangank36 @brookewp @ciampo @colorful-tones @DAreRodz @dcalhoun @derekblank @ellatrix @felixarntz @geriux @glendaviesnz @gziolo @hellofromtonya @jasmussen @jblz @JEverhart383 @jordesign @jorgefilipecosta @jsnajdr @juanmaguitar @krokodok @luisherranz @Mamaduka @margolisj @matiasbenedetto @mburridge @mirka @mklute101 @mokagio @ndiego @ntsekouras @oandregal @ocean90 @ockham @priethor @ramonjd @richtabor @SiobhyB @Smit2808 @stokesman @t-hamano @torounit @tyxla @walbo @WunderBart @youknowriad
+
+
 = 16.5.1 =
 
 

From 22d24f352df72b7507939f3eb70d7b372547c5c3 Mon Sep 17 00:00:00 2001
From: Gerardo Pacheco <gerardo.pacheco@automattic.com>
Date: Wed, 30 Aug 2023 19:28:15 +0200
Subject: [PATCH 09/34] Mobile - Columns block - Fix transforming into a Group
 block crash (#54035)

* Mobile - Column block - Fix issue when transforming a Columns block that's nested in a container block like Group. By avoid accessing the width value outside of the isSelected and adding a fallback value.

* Mobile - Columns block - Add transform Columns block integration test

* Mobile - Columns Block Tests - Update usage of the screen variable, now its imported from the helpers instead of the initializedEditor

* Mobile - Update changelog
---
 .../block-library/src/column/edit.native.js   |  8 +--
 .../test/__snapshots__/edit.native.js.snap    | 14 ++++
 .../src/columns/test/edit.native.js           | 64 ++++++++++++++-----
 packages/react-native-editor/CHANGELOG.md     |  1 +
 test/native/__mocks__/styleMock.js            |  4 ++
 5 files changed, 70 insertions(+), 21 deletions(-)

diff --git a/packages/block-library/src/column/edit.native.js b/packages/block-library/src/column/edit.native.js
index 46e5012f68a34..7e18e73a9b14a 100644
--- a/packages/block-library/src/column/edit.native.js
+++ b/packages/block-library/src/column/edit.native.js
@@ -111,10 +111,10 @@ function ColumnEdit( {
 	};
 
 	const renderAppender = useCallback( () => {
-		const { width: columnWidth } = contentStyle[ clientId ];
-		const isFullWidth = columnWidth === screenWidth;
-
 		if ( isSelected ) {
+			const { width: columnWidth } = contentStyle[ clientId ] || {};
+			const isFullWidth = columnWidth === screenWidth;
+
 			return (
 				<View
 					style={ [
@@ -133,7 +133,7 @@ function ColumnEdit( {
 			);
 		}
 		return null;
-	}, [ contentStyle[ clientId ], screenWidth, isSelected, hasChildren ] );
+	}, [ contentStyle, clientId, screenWidth, isSelected, hasChildren ] );
 
 	if ( ! isSelected && ! hasChildren ) {
 		return (
diff --git a/packages/block-library/src/columns/test/__snapshots__/edit.native.js.snap b/packages/block-library/src/columns/test/__snapshots__/edit.native.js.snap
index b6fd551f348a8..f7b47026a4ec4 100644
--- a/packages/block-library/src/columns/test/__snapshots__/edit.native.js.snap
+++ b/packages/block-library/src/columns/test/__snapshots__/edit.native.js.snap
@@ -82,6 +82,20 @@ exports[`Columns block sets current vertical alignment on new Columns 1`] = `
 <!-- /wp:columns -->"
 `;
 
+exports[`Columns block transforms a nested Columns block into a Group block 1`] = `
+"<!-- wp:group {"layout":{"type":"constrained"}} -->
+<div class="wp-block-group"><!-- wp:group {"layout":{"type":"constrained"}} -->
+<div class="wp-block-group"><!-- wp:columns -->
+<div class="wp-block-columns"><!-- wp:column {"width":"100%"} -->
+<div class="wp-block-column" style="flex-basis:100%"><!-- wp:paragraph -->
+<p></p>
+<!-- /wp:paragraph --></div>
+<!-- /wp:column --></div>
+<!-- /wp:columns --></div>
+<!-- /wp:group --></div>
+<!-- /wp:group -->"
+`;
+
 exports[`Columns block when using columns percentage mechanism sets custom values correctly 1`] = `
 "<!-- wp:columns -->
 <div class="wp-block-columns"><!-- wp:column {"width":"90%"} -->
diff --git a/packages/block-library/src/columns/test/edit.native.js b/packages/block-library/src/columns/test/edit.native.js
index bb7bfbdafea4b..20430704c8e1e 100644
--- a/packages/block-library/src/columns/test/edit.native.js
+++ b/packages/block-library/src/columns/test/edit.native.js
@@ -4,14 +4,16 @@
 import {
 	act,
 	addBlock,
+	dismissModal,
 	fireEvent,
+	getBlock,
 	getEditorHtml,
 	initializeEditor,
+	openBlockActionsMenu,
 	openBlockSettings,
-	within,
-	getBlock,
-	dismissModal,
+	screen,
 	waitForModalVisible,
+	within,
 } from 'test/helpers';
 
 /**
@@ -43,7 +45,7 @@ afterAll( () => {
 
 describe( 'Columns block', () => {
 	it( 'inserts block', async () => {
-		const screen = await initializeEditor();
+		await initializeEditor();
 
 		// Add block
 		await addBlock( screen, 'Columns' );
@@ -55,7 +57,7 @@ describe( 'Columns block', () => {
 	} );
 
 	it( 'adds a column block using the appender', async () => {
-		const screen = await initializeEditor( {
+		await initializeEditor( {
 			initialHtml: TWO_COLUMNS_BLOCK_HTML,
 		} );
 
@@ -73,7 +75,7 @@ describe( 'Columns block', () => {
 
 	describe( 'when using the number of columns setting', () => {
 		it( 'adds a column block when incrementing the value', async () => {
-			const screen = await initializeEditor( {
+			await initializeEditor( {
 				initialHtml: TWO_COLUMNS_BLOCK_HTML,
 			} );
 			const { getByLabelText } = screen;
@@ -95,7 +97,7 @@ describe( 'Columns block', () => {
 		} );
 
 		it( 'adds at least 15 Column blocks without limitation', async () => {
-			const screen = await initializeEditor( {
+			await initializeEditor( {
 				initialHtml: TWO_COLUMNS_BLOCK_HTML,
 			} );
 			const { getByLabelText } = screen;
@@ -120,7 +122,7 @@ describe( 'Columns block', () => {
 		} );
 
 		it( 'removes a column block when decrementing the value', async () => {
-			const screen = await initializeEditor( {
+			await initializeEditor( {
 				initialHtml: TWO_COLUMNS_BLOCK_HTML,
 			} );
 			const { getByLabelText } = screen;
@@ -142,7 +144,7 @@ describe( 'Columns block', () => {
 		} );
 
 		it( 'reaches the minimum limit of number of column blocks', async () => {
-			const screen = await initializeEditor();
+			await initializeEditor();
 
 			// Add block
 			await addBlock( screen, 'Columns' );
@@ -185,7 +187,7 @@ describe( 'Columns block', () => {
 	} );
 
 	it( 'removes column with the remove button', async () => {
-		const screen = await initializeEditor( {
+		await initializeEditor( {
 			initialHtml: TWO_COLUMNS_BLOCK_HTML,
 		} );
 		const { getByLabelText } = screen;
@@ -210,7 +212,7 @@ describe( 'Columns block', () => {
 	} );
 
 	it( 'removes the only one left Column with the remove button', async () => {
-		const screen = await initializeEditor( {
+		await initializeEditor( {
 			initialHtml: TWO_COLUMNS_BLOCK_HTML,
 		} );
 		const { getByLabelText } = screen;
@@ -247,7 +249,7 @@ describe( 'Columns block', () => {
 	} );
 
 	it( 'changes vertical alignment on Columns', async () => {
-		const screen = await initializeEditor( {
+		await initializeEditor( {
 			initialHtml: TWO_COLUMNS_BLOCK_HTML,
 		} );
 		const { getByLabelText } = screen;
@@ -270,7 +272,7 @@ describe( 'Columns block', () => {
 	} );
 
 	it( 'changes the vertical alignment on individual Column', async () => {
-		const screen = await initializeEditor( {
+		await initializeEditor( {
 			initialHtml: TWO_COLUMNS_BLOCK_HTML,
 		} );
 
@@ -308,7 +310,7 @@ describe( 'Columns block', () => {
 	} );
 
 	it( 'sets current vertical alignment on new Columns', async () => {
-		const screen = await initializeEditor( {
+		await initializeEditor( {
 			initialHtml: TWO_COLUMNS_BLOCK_HTML,
 		} );
 		const { getByLabelText } = screen;
@@ -337,7 +339,7 @@ describe( 'Columns block', () => {
 
 	describe( 'when using columns percentage mechanism', () => {
 		it( "updates the slider's input value", async () => {
-			const screen = await initializeEditor();
+			await initializeEditor();
 
 			// Add block
 			await addBlock( screen, 'Columns' );
@@ -377,7 +379,7 @@ describe( 'Columns block', () => {
 		} );
 
 		it( 'sets custom values correctly', async () => {
-			const screen = await initializeEditor( {
+			await initializeEditor( {
 				initialHtml: TWO_COLUMNS_BLOCK_HTML,
 			} );
 			const { getByLabelText, getByTestId } = screen;
@@ -443,7 +445,7 @@ describe( 'Columns block', () => {
 		test.each( testData )(
 			'sets the predefined percentages for %s',
 			async ( layout ) => {
-				const screen = await initializeEditor();
+				await initializeEditor();
 
 				// Add block
 				await addBlock( screen, 'Columns' );
@@ -463,4 +465,32 @@ describe( 'Columns block', () => {
 			}
 		);
 	} );
+
+	it( 'transforms a nested Columns block into a Group block', async () => {
+		await initializeEditor( {
+			initialHtml: `<!-- wp:group {"layout":{"type":"constrained"}} -->
+			<div class="wp-block-group"><!-- wp:columns -->
+			<div class="wp-block-columns"><!-- wp:column {"width":"100%"} -->
+			<div class="wp-block-column" style="flex-basis:100%"><!-- wp:paragraph -->
+			<p></p>
+			<!-- /wp:paragraph --></div>
+			<!-- /wp:column --></div>
+			<!-- /wp:columns --></div>
+			<!-- /wp:group -->`,
+		} );
+
+		// Get Columns block
+		const columnsBlock = await getBlock( screen, 'Columns' );
+		fireEvent.press( columnsBlock );
+
+		// Open block actions menu
+		await openBlockActionsMenu( screen );
+
+		// Tap on the Transform block button
+		fireEvent.press( screen.getByLabelText( /Transform block…/ ) );
+
+		// Tap on the Group transform button
+		fireEvent.press( screen.getByLabelText( 'Group' ) );
+		expect( getEditorHtml() ).toMatchSnapshot();
+	} );
 } );
diff --git a/packages/react-native-editor/CHANGELOG.md b/packages/react-native-editor/CHANGELOG.md
index 7cb02a4848dd6..a9fa07190f2fc 100644
--- a/packages/react-native-editor/CHANGELOG.md
+++ b/packages/react-native-editor/CHANGELOG.md
@@ -15,6 +15,7 @@ For each user feature we should also add a importance categorization label  to i
 -   [*] Improve horizontal rule styles to avoid invisible lines [#53883]
 -   [*] Fix horizontal rule style extensions [#53917]
 -   [*] Add block outline to all Social Link blocks when selected [#54011]
+-   [*] Columns block - Fix transforming into a Group block crash [#54035]
 
 ## 1.102.1
 - [**] Fix Voice Over and assistive keyboards [#53895]
diff --git a/test/native/__mocks__/styleMock.js b/test/native/__mocks__/styleMock.js
index e0c690b628234..8b682ef005e49 100644
--- a/test/native/__mocks__/styleMock.js
+++ b/test/native/__mocks__/styleMock.js
@@ -204,4 +204,8 @@ module.exports = {
 	},
 	picker: {},
 	pickerPointer: {},
+	columnsContainer: {
+		marginLeft: 16,
+		minWidth: 32,
+	},
 };

From 54103891010f05984e6751b4b1d33f048fd1900c Mon Sep 17 00:00:00 2001
From: merickarabulut <91661972+MericKarabulut@users.noreply.github.com>
Date: Thu, 31 Aug 2023 01:39:28 +0300
Subject: [PATCH 10/34] Improving error messages and codes in jest-console,
 matcher.js (#53743)

* code maintainability and error messages improved

* code maintainability and error messages improved

* Added changelog  entry for recent changes.
---
 packages/jest-console/CHANGELOG.md    |  4 ++
 packages/jest-console/src/matchers.js | 84 +++++++++++++++------------
 2 files changed, 50 insertions(+), 38 deletions(-)

diff --git a/packages/jest-console/CHANGELOG.md b/packages/jest-console/CHANGELOG.md
index caa7f460f6f2b..f5aa959e2542e 100644
--- a/packages/jest-console/CHANGELOG.md
+++ b/packages/jest-console/CHANGELOG.md
@@ -2,6 +2,10 @@
 
 ## Unreleased
 
+### Enhancement
+
+- Improved error messages and codes printed on the console ([#53743](https://github.com/WordPress/gutenberg/pull/53743)).
+
 ## 7.11.0 (2023-08-16)
 
 ## 7.10.0 (2023-08-10)
diff --git a/packages/jest-console/src/matchers.js b/packages/jest-console/src/matchers.js
index 078ef47fb1da5..9793164f532b3 100644
--- a/packages/jest-console/src/matchers.js
+++ b/packages/jest-console/src/matchers.js
@@ -8,58 +8,66 @@ import { matcherHint, printExpected, printReceived } from 'jest-matcher-utils';
  */
 import supportedMatchers from './supported-matchers';
 
+const createErrorMessage = ( spyInfo ) => {
+	const { spy, pass, calls, matcherName, methodName, expected } = spyInfo;
+	const hint = pass ? `.not${ matcherName }` : matcherName;
+	const message = pass
+		? `Expected mock function not to be called but it was called with:\n${ calls.map(
+				printReceived
+		  ) }`
+		: `Expected mock function to be called${
+				expected ? ` with:\n${ printExpected( expected ) }\n` : '.'
+		  }\nbut it was called with:\n${ calls.map( printReceived ) }`;
+
+	return () =>
+		`${ matcherHint( hint, spy.getMockName() ) }` +
+		'\n\n' +
+		message +
+		'\n\n' +
+		`console.${ methodName }() should not be used unless explicitly expected\n` +
+		'See https://www.npmjs.com/package/@wordpress/jest-console for details.';
+};
+
+const createSpyInfo = ( spy, matcherName, methodName, expected ) => {
+	const calls = spy.mock.calls;
+
+	const pass = expected
+		? JSON.stringify( calls ).includes( JSON.stringify( expected ) )
+		: calls.length > 0;
+
+	const message = createErrorMessage( {
+		spy,
+		pass,
+		calls,
+		matcherName,
+		methodName,
+		expected,
+	} );
+
+	return {
+		pass,
+		message,
+	};
+};
+
 const createToHaveBeenCalledMatcher =
 	( matcherName, methodName ) => ( received ) => {
 		const spy = received[ methodName ];
-		const calls = spy.mock.calls;
-		const pass = calls.length > 0;
-		const message = pass
-			? () =>
-					matcherHint( `.not${ matcherName }`, spy.getMockName() ) +
-					'\n\n' +
-					'Expected mock function not to be called but it was called with:\n' +
-					calls.map( printReceived )
-			: () =>
-					matcherHint( matcherName, spy.getMockName() ) +
-					'\n\n' +
-					'Expected mock function to be called.';
+		const spyInfo = createSpyInfo( spy, matcherName, methodName );
 
 		spy.assertionsNumber += 1;
 
-		return {
-			message,
-			pass,
-		};
+		return spyInfo;
 	};
 
 const createToHaveBeenCalledWith = ( matcherName, methodName ) =>
 	function ( received, ...expected ) {
 		const spy = received[ methodName ];
-		const calls = spy.mock.calls;
-		const pass = calls.some( ( objects ) =>
-			this.equals( objects, expected )
-		);
-		const message = pass
-			? () =>
-					matcherHint( `.not${ matcherName }`, spy.getMockName() ) +
-					'\n\n' +
-					'Expected mock function not to be called with:\n' +
-					printExpected( expected )
-			: () =>
-					matcherHint( matcherName, spy.getMockName() ) +
-					'\n\n' +
-					'Expected mock function to be called with:\n' +
-					printExpected( expected ) +
-					'\n' +
-					'but it was called with:\n' +
-					calls.map( printReceived );
+		const spyInfo = createSpyInfo( spy, matcherName, methodName, expected );
 
 		spy.assertionsNumber += 1;
 
-		return {
-			message,
-			pass,
-		};
+		return spyInfo;
 	};
 
 expect.extend(

From dcb4df603308c7737825fb3ba432a69feeca3011 Mon Sep 17 00:00:00 2001
From: annezazu <annezazu@gmail.com>
Date: Wed, 30 Aug 2023 17:12:31 -0700
Subject: [PATCH 11/34] Update versions for 6.3.1 (#54069)

---
 docs/contributors/versions-in-wordpress.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/docs/contributors/versions-in-wordpress.md b/docs/contributors/versions-in-wordpress.md
index 6709d124b242b..c4e25c3b2095e 100644
--- a/docs/contributors/versions-in-wordpress.md
+++ b/docs/contributors/versions-in-wordpress.md
@@ -6,6 +6,7 @@ If anything looks incorrect here, please bring it up in #core-editor in [WordPre
 
 | Gutenberg Versions | WordPress Version |
 | ------------------ | ----------------- |
+| 15.2-16.1          | 6.3.1             |
 | 15.2-16.1          | 6.3               |
 | 14.2-15.1          | 6.2               |
 | 13.1-14.1          | 6.1.1             |

From d67a8d5852918a054bfa99de750bff91ca8f1e28 Mon Sep 17 00:00:00 2001
From: Riad Benguella <benguella@gmail.com>
Date: Thu, 31 Aug 2023 05:30:58 +0100
Subject: [PATCH 12/34] Update the components changelog to mark the popover
 slot removal as a breaking change (#54022)

---
 packages/components/CHANGELOG.md | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md
index 7dce9d2a315f4..a12e300ad4724 100644
--- a/packages/components/CHANGELOG.md
+++ b/packages/components/CHANGELOG.md
@@ -2,9 +2,12 @@
 
 ## Unreleased
 
+### Breaking changes
+
+-   Make the `Popover.Slot` optional and render popovers at the bottom of the document's body by default. ([#53889](https://github.com/WordPress/gutenberg/pull/53889), [#53982](https://github.com/WordPress/gutenberg/pull/53982)).
+
 ### Enhancements
 
--   Make the `Popover.Slot` optional and render popovers at the bottom of the document's body by default. ([#53889](https://github.com/WordPress/gutenberg/pull/53889)).
 -   `ProgressBar`: Add transition to determinate indicator ([#53877](https://github.com/WordPress/gutenberg/pull/53877)).
 -   Prevent nested `SlotFillProvider` from rendering ([#53940](https://github.com/WordPress/gutenberg/pull/53940)).
 

From 3db444b6cbe8e67ee00cc43aca43b83300fa83dd Mon Sep 17 00:00:00 2001
From: Carolina Nymark <myazalea@hotmail.com>
Date: Thu, 31 Aug 2023 07:53:47 +0200
Subject: [PATCH 13/34] Post navigation link: Fix the writing mode setting on
 the front. (#54053)

Add style attributes to the wrapper in the index.php file
---
 .../block-library/src/post-navigation-link/index.php  | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/packages/block-library/src/post-navigation-link/index.php b/packages/block-library/src/post-navigation-link/index.php
index cb066ad69f2c5..8b5d82e745aee 100644
--- a/packages/block-library/src/post-navigation-link/index.php
+++ b/packages/block-library/src/post-navigation-link/index.php
@@ -28,7 +28,16 @@ function render_block_core_post_navigation_link( $attributes, $content ) {
 	if ( isset( $attributes['textAlign'] ) ) {
 		$classes .= " has-text-align-{$attributes['textAlign']}";
 	}
-	$wrapper_attributes = get_block_wrapper_attributes( array( 'class' => $classes ) );
+	$styles = '';
+	if ( isset( $attributes['style']['typography']['writingMode'] ) ) {
+		$styles = "writing-mode:{$attributes['style']['typography']['writingMode']};";
+	}
+	$wrapper_attributes = get_block_wrapper_attributes(
+		array(
+			'class' => $classes,
+			'style' => $styles,
+		)
+	);
 	// Set default values.
 	$format = '%link';
 	$link   = 'next' === $navigation_type ? _x( 'Next', 'label for next post link' ) : _x( 'Previous', 'label for previous post link' );

From a8dbc883c4d68b6d025b91f763596206182bae6f Mon Sep 17 00:00:00 2001
From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com>
Date: Thu, 31 Aug 2023 16:43:41 +1000
Subject: [PATCH 14/34] Design Tools: Add block instance elements support for
 buttons and headings (#53667)

---
 docs/reference-guides/core-blocks.md          |   2 +-
 lib/block-supports/colors.php                 |   6 +-
 lib/block-supports/elements.php               | 176 ++++++++++++++----
 packages/block-editor/src/hooks/style.js      | 120 ++++++++----
 packages/block-editor/src/hooks/utils.js      |   4 +
 packages/block-library/src/group/block.json   |   1 +
 .../style-engine/class-wp-style-engine.php    |   6 +
 7 files changed, 238 insertions(+), 77 deletions(-)

diff --git a/docs/reference-guides/core-blocks.md b/docs/reference-guides/core-blocks.md
index 0f25cead8b07a..7f66c40bb00ba 100644
--- a/docs/reference-guides/core-blocks.md
+++ b/docs/reference-guides/core-blocks.md
@@ -302,7 +302,7 @@ Gather blocks in a layout container. ([Source](https://github.com/WordPress/gute
 
 -	**Name:** core/group
 -	**Category:** design
--	**Supports:** align (full, wide), anchor, ariaLabel, color (background, gradients, heading, link, text), dimensions (minHeight), layout (allowSizingOnChildren), position (sticky), spacing (blockGap, margin, padding), typography (fontSize, lineHeight), ~~html~~
+-	**Supports:** align (full, wide), anchor, ariaLabel, color (background, button, gradients, heading, link, text), dimensions (minHeight), layout (allowSizingOnChildren), position (sticky), spacing (blockGap, margin, padding), typography (fontSize, lineHeight), ~~html~~
 -	**Attributes:** allowedBlocks, tagName, templateLock
 
 ## Heading
diff --git a/lib/block-supports/colors.php b/lib/block-supports/colors.php
index 7ec32bf14d753..88e13abfc9cc9 100644
--- a/lib/block-supports/colors.php
+++ b/lib/block-supports/colors.php
@@ -16,10 +16,14 @@ function gutenberg_register_colors_support( $block_type ) {
 	$has_background_colors_support = true === $color_support || ( is_array( $color_support ) && _wp_array_get( $color_support, array( 'background' ), true ) );
 	$has_gradients_support         = _wp_array_get( $color_support, array( 'gradients' ), false );
 	$has_link_colors_support       = _wp_array_get( $color_support, array( 'link' ), false );
+	$has_button_colors_support     = _wp_array_get( $color_support, array( 'button' ), false );
+	$has_heading_colors_support    = _wp_array_get( $color_support, array( 'heading' ), false );
 	$has_color_support             = $has_text_colors_support ||
 		$has_background_colors_support ||
 		$has_gradients_support ||
-		$has_link_colors_support;
+		$has_link_colors_support ||
+		$has_button_colors_support ||
+		$has_heading_colors_support;
 
 	if ( ! $block_type->attributes ) {
 		$block_type->attributes = array();
diff --git a/lib/block-supports/elements.php b/lib/block-supports/elements.php
index d4502aaa2e478..4029c91aa40a3 100644
--- a/lib/block-supports/elements.php
+++ b/lib/block-supports/elements.php
@@ -23,34 +23,79 @@ function gutenberg_get_elements_class_name( $block ) {
  * @return string                Filtered block content.
  */
 function gutenberg_render_elements_support( $block_content, $block ) {
-	if ( ! $block_content ) {
+	if ( ! $block_content || empty( $block['attrs'] ) ) {
 		return $block_content;
 	}
 
-	$block_type                    = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] );
-	$skip_link_color_serialization = wp_should_skip_block_supports_serialization( $block_type, 'color', 'link' );
+	$block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] );
+
+	$element_color_properties = array(
+		'button'  => array(
+			'skip'  => wp_should_skip_block_supports_serialization( $block_type, 'color', 'button' ),
+			'paths' => array(
+				'style.elements.button.color.text',
+				'style.elements.button.color.background',
+				'style.elements.button.color.gradient',
+			),
+		),
+		'link'    => array(
+			'skip'  => wp_should_skip_block_supports_serialization( $block_type, 'color', 'link' ),
+			'paths' => array(
+				'style.elements.link.color.text',
+				'style.elements.link.:hover.color.text',
+			),
+		),
+		'heading' => array(
+			'skip'  => wp_should_skip_block_supports_serialization( $block_type, 'color', 'heading' ),
+			'paths' => array(
+				'style.elements.heading.color.text',
+				'style.elements.heading.color.background',
+				'style.elements.heading.color.gradient',
+				'style.elements.h1.color.text',
+				'style.elements.h1.color.background',
+				'style.elements.h1.color.gradient',
+				'style.elements.h2.color.text',
+				'style.elements.h2.color.background',
+				'style.elements.h2.color.gradient',
+				'style.elements.h3.color.text',
+				'style.elements.h3.color.background',
+				'style.elements.h3.color.gradient',
+				'style.elements.h4.color.text',
+				'style.elements.h4.color.background',
+				'style.elements.h4.color.gradient',
+				'style.elements.h5.color.text',
+				'style.elements.h5.color.background',
+				'style.elements.h5.color.gradient',
+				'style.elements.h6.color.text',
+				'style.elements.h6.color.background',
+				'style.elements.h6.color.gradient',
+			),
+		),
+	);
+
+	$skip_all_element_color_serialization = $element_color_properties['button']['skip'] &&
+		$element_color_properties['link']['skip'] &&
+		$element_color_properties['heading']['skip'];
 
-	if ( $skip_link_color_serialization ) {
+	if ( $skip_all_element_color_serialization ) {
 		return $block_content;
 	}
 
-	$link_color = null;
-	if ( ! empty( $block['attrs'] ) ) {
-		$link_color = _wp_array_get( $block['attrs'], array( 'style', 'elements', 'link', 'color', 'text' ), null );
-	}
+	$element_colors_set = 0;
+
+	foreach ( $element_color_properties as $element_config ) {
+		if ( $element_config['skip'] ) {
+			continue;
+		}
 
-	$hover_link_color = null;
-	if ( ! empty( $block['attrs'] ) ) {
-		$hover_link_color = _wp_array_get( $block['attrs'], array( 'style', 'elements', 'link', ':hover', 'color', 'text' ), null );
+		foreach ( $element_config['paths'] as $path ) {
+			if ( null !== _wp_array_get( $block['attrs'], explode( '.', $path ), null ) ) {
+				$element_colors_set++;
+			}
+		}
 	}
 
-	/*
-	* For now we only care about link colors.
-	* This code in the future when we have a public API
-	* should take advantage of WP_Theme_JSON_Gutenberg::compute_style_properties
-	* and work for any element and style.
-	*/
-	if ( null === $link_color && null === $hover_link_color ) {
+	if ( ! $element_colors_set ) {
 		return $block_content;
 	}
 
@@ -80,33 +125,84 @@ function gutenberg_render_elements_support_styles( $pre_render, $block ) {
 	$block_type           = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] );
 	$element_block_styles = isset( $block['attrs']['style']['elements'] ) ? $block['attrs']['style']['elements'] : null;
 
-	/*
-	* For now we only care about link color.
-	*/
-	$skip_link_color_serialization = wp_should_skip_block_supports_serialization( $block_type, 'color', 'link' );
+	if ( ! $element_block_styles ) {
+		return null;
+	}
 
-	if ( $skip_link_color_serialization ) {
+	$skip_link_color_serialization         = wp_should_skip_block_supports_serialization( $block_type, 'color', 'link' );
+	$skip_heading_color_serialization      = wp_should_skip_block_supports_serialization( $block_type, 'color', 'heading' );
+	$skip_button_color_serialization       = wp_should_skip_block_supports_serialization( $block_type, 'color', 'button' );
+	$skips_all_element_color_serialization = $skip_link_color_serialization &&
+		$skip_heading_color_serialization &&
+		$skip_button_color_serialization;
+
+	if ( $skips_all_element_color_serialization ) {
 		return null;
 	}
-	$class_name        = gutenberg_get_elements_class_name( $block );
-	$link_block_styles = isset( $element_block_styles['link'] ) ? $element_block_styles['link'] : null;
-
-	gutenberg_style_engine_get_styles(
-		$link_block_styles,
-		array(
-			'selector' => ".$class_name a",
-			'context'  => 'block-supports',
-		)
+
+	$class_name = gutenberg_get_elements_class_name( $block );
+
+	$element_types = array(
+		'button'  => array(
+			'selector' => ".$class_name .wp-element-button, .$class_name .wp-block-button__link",
+			'skip'     => $skip_button_color_serialization,
+		),
+		'link'    => array(
+			'selector'       => ".$class_name a",
+			'hover_selector' => ".$class_name a:hover",
+			'skip'           => $skip_link_color_serialization,
+		),
+		'heading' => array(
+			'selector' => ".$class_name h1, .$class_name h2, .$class_name h3, .$class_name h4, .$class_name h5, .$class_name h6",
+			'skip'     => $skip_heading_color_serialization,
+			'elements' => array( 'h1', 'h2', 'h3', 'h4', 'h5', 'h6' ),
+		),
 	);
 
-	if ( isset( $link_block_styles[':hover'] ) ) {
-		gutenberg_style_engine_get_styles(
-			$link_block_styles[':hover'],
-			array(
-				'selector' => ".$class_name a:hover",
-				'context'  => 'block-supports',
-			)
-		);
+	foreach ( $element_types as $element_type => $element_config ) {
+		if ( $element_config['skip'] ) {
+			continue;
+		}
+
+		$element_style_object = _wp_array_get( $element_block_styles, array( $element_type ), null );
+
+		// Process primary element type styles.
+		if ( $element_style_object ) {
+			gutenberg_style_engine_get_styles(
+				$element_style_object,
+				array(
+					'selector' => $element_config['selector'],
+					'context'  => 'block-supports',
+				)
+			);
+
+			if ( isset( $element_style_object[':hover'] ) ) {
+				gutenberg_style_engine_get_styles(
+					$element_style_object[':hover'],
+					array(
+						'selector' => $element_config['hover_selector'],
+						'context'  => 'block-supports',
+					)
+				);
+			}
+		}
+
+		// Process related elements e.g. h1-h6 for headings.
+		if ( isset( $element_config['elements'] ) ) {
+			foreach ( $element_config['elements'] as $element ) {
+				$element_style_object = _wp_array_get( $element_block_styles, array( $element ), null );
+
+				if ( $element_style_object ) {
+					gutenberg_style_engine_get_styles(
+						$element_style_object,
+						array(
+							'selector' => ".$class_name $element",
+							'context'  => 'block-supports',
+						)
+					);
+				}
+			}
+		}
 	}
 
 	return null;
diff --git a/packages/block-editor/src/hooks/style.js b/packages/block-editor/src/hooks/style.js
index b416c86405e51..e165a88882294 100644
--- a/packages/block-editor/src/hooks/style.js
+++ b/packages/block-editor/src/hooks/style.js
@@ -34,6 +34,7 @@ import {
 } from './dimensions';
 import useDisplayBlockControls from '../components/use-display-block-controls';
 import { shouldSkipSerialization } from './utils';
+import { scopeSelector } from '../components/global-styles/utils';
 import { useBlockEditingMode } from '../components/block-editing-mode';
 
 const styleSupportKeys = [
@@ -371,6 +372,18 @@ export const withBlockControls = createHigherOrderComponent(
 	'withToolbarControls'
 );
 
+// Defines which element types are supported, including their hover styles or
+// any other elements that have been included under a single element type
+// e.g. heading and h1-h6.
+const elementTypes = [
+	{ elementType: 'button' },
+	{ elementType: 'link', pseudo: [ ':hover' ] },
+	{
+		elementType: 'heading',
+		elements: [ 'h1', 'h2', 'h3', 'h4', 'h5', 'h6' ],
+	},
+];
+
 /**
  * Override the default block element to include elements styles.
  *
@@ -383,47 +396,84 @@ const withElementsStyles = createHigherOrderComponent(
 			BlockListBlock
 		) }`;
 
-		const skipLinkColorSerialization = shouldSkipSerialization(
-			props.name,
-			COLOR_SUPPORT_KEY,
-			'link'
-		);
+		// The .editor-styles-wrapper selector is required on elements styles. As it is
+		// added to all other editor styles, not providing it causes reset and global
+		// styles to override element styles because of higher specificity.
+		const baseElementSelector = `.editor-styles-wrapper .${ blockElementsContainerIdentifier }`;
+		const blockElementStyles = props.attributes.style?.elements;
 
 		const styles = useMemo( () => {
-			// The .editor-styles-wrapper selector is required on elements styles. As it is
-			// added to all other editor styles, not providing it causes reset and global
-			// styles to override element styles because of higher specificity.
-			const elements = [
-				{
-					styles: ! skipLinkColorSerialization
-						? props.attributes.style?.elements?.link
-						: undefined,
-					selector: `.editor-styles-wrapper .${ blockElementsContainerIdentifier } ${ ELEMENTS.link }`,
-				},
-				{
-					styles: ! skipLinkColorSerialization
-						? props.attributes.style?.elements?.link?.[ ':hover' ]
-						: undefined,
-					selector: `.editor-styles-wrapper .${ blockElementsContainerIdentifier } ${ ELEMENTS.link }:hover`,
-				},
-			];
-			const elementCssRules = [];
-			for ( const { styles: elementStyles, selector } of elements ) {
+			if ( ! blockElementStyles ) {
+				return;
+			}
+
+			const elementCSSRules = [];
+
+			elementTypes.forEach( ( { elementType, pseudo, elements } ) => {
+				const skipSerialization = shouldSkipSerialization(
+					props.name,
+					COLOR_SUPPORT_KEY,
+					elementType
+				);
+
+				if ( skipSerialization ) {
+					return;
+				}
+
+				const elementStyles = blockElementStyles?.[ elementType ];
+
+				// Process primary element type styles.
 				if ( elementStyles ) {
-					const cssRule = compileCSS( elementStyles, {
-						selector,
+					const selector = scopeSelector(
+						baseElementSelector,
+						ELEMENTS[ elementType ]
+					);
+
+					elementCSSRules.push(
+						compileCSS( elementStyles, { selector } )
+					);
+
+					// Process any interactive states for the element type.
+					if ( pseudo ) {
+						pseudo.forEach( ( pseudoSelector ) => {
+							if ( elementStyles[ pseudoSelector ] ) {
+								elementCSSRules.push(
+									compileCSS(
+										elementStyles[ pseudoSelector ],
+										{
+											selector: scopeSelector(
+												baseElementSelector,
+												`${ ELEMENTS[ elementType ] }${ pseudoSelector }`
+											),
+										}
+									)
+								);
+							}
+						} );
+					}
+				}
+
+				// Process related elements e.g. h1-h6 for headings
+				if ( elements ) {
+					elements.forEach( ( element ) => {
+						if ( blockElementStyles[ element ] ) {
+							elementCSSRules.push(
+								compileCSS( blockElementStyles[ element ], {
+									selector: scopeSelector(
+										baseElementSelector,
+										ELEMENTS[ element ]
+									),
+								} )
+							);
+						}
 					} );
-					elementCssRules.push( cssRule );
 				}
-			}
-			return elementCssRules.length > 0
-				? elementCssRules.join( '' )
+			} );
+
+			return elementCSSRules.length > 0
+				? elementCSSRules.join( '' )
 				: undefined;
-		}, [
-			props.attributes.style?.elements,
-			blockElementsContainerIdentifier,
-			skipLinkColorSerialization,
-		] );
+		}, [ baseElementSelector, blockElementStyles, props.name ] );
 
 		const element = useContext( BlockList.__unstableElementContext );
 
diff --git a/packages/block-editor/src/hooks/utils.js b/packages/block-editor/src/hooks/utils.js
index 9c6bf957d61c5..f81fc118ea84b 100644
--- a/packages/block-editor/src/hooks/utils.js
+++ b/packages/block-editor/src/hooks/utils.js
@@ -166,6 +166,8 @@ export function useBlockSettings( name, parentLayout ) {
 	const isBackgroundEnabled = useSetting( 'color.background' );
 	const isLinkEnabled = useSetting( 'color.link' );
 	const isTextEnabled = useSetting( 'color.text' );
+	const isHeadingEnabled = useSetting( 'color.heading' );
+	const isButtonEnabled = useSetting( 'color.button' );
 
 	const rawSettings = useMemo( () => {
 		return {
@@ -193,6 +195,8 @@ export function useBlockSettings( name, parentLayout ) {
 				customDuotone,
 				background: isBackgroundEnabled,
 				link: isLinkEnabled,
+				heading: isHeadingEnabled,
+				button: isButtonEnabled,
 				text: isTextEnabled,
 			},
 			typography: {
diff --git a/packages/block-library/src/group/block.json b/packages/block-library/src/group/block.json
index 4f8de8802ea70..9c2d012620c3c 100644
--- a/packages/block-library/src/group/block.json
+++ b/packages/block-library/src/group/block.json
@@ -31,6 +31,7 @@
 		"color": {
 			"gradients": true,
 			"heading": true,
+			"button": true,
 			"link": true,
 			"__experimentalDefaultControls": {
 				"background": true,
diff --git a/packages/style-engine/class-wp-style-engine.php b/packages/style-engine/class-wp-style-engine.php
index 7304b57ec5612..283601e551c98 100644
--- a/packages/style-engine/class-wp-style-engine.php
+++ b/packages/style-engine/class-wp-style-engine.php
@@ -60,6 +60,9 @@ final class WP_Style_Engine {
 					'default' => 'background-color',
 				),
 				'path'          => array( 'color', 'background' ),
+				'css_vars'      => array(
+					'color' => '--wp--preset--color--$slug',
+				),
 				'classnames'    => array(
 					'has-background'             => true,
 					'has-$slug-background-color' => 'color',
@@ -69,6 +72,9 @@ final class WP_Style_Engine {
 				'property_keys' => array(
 					'default' => 'background',
 				),
+				'css_vars'      => array(
+					'gradient' => '--wp--preset--gradient--$slug',
+				),
 				'path'          => array( 'color', 'gradient' ),
 				'classnames'    => array(
 					'has-background'                => true,

From e691d320e2a13ba4c514ca01d1a1400009d9ee2a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Grzegorz=20Zi=C3=B3=C5=82kowski?= <grzegorz@gziolo.pl>
Date: Thu, 31 Aug 2023 11:09:01 +0200
Subject: [PATCH 15/34] Packages: Ensure that the release branch is fetched
 from origin during publishing Fixes the issue detected during the last
 attempt logged at
 https://github.com/WordPress/gutenberg/actions/runs/6027639373/job/16374862576.
 The exact message printed: "GitError: fatal: invalid reference:
 origin/release/16.6".

---
 bin/plugin/commands/packages.js | 1 +
 1 file changed, 1 insertion(+)

diff --git a/bin/plugin/commands/packages.js b/bin/plugin/commands/packages.js
index 87ecc6eb89cfc..4cf509764436c 100644
--- a/bin/plugin/commands/packages.js
+++ b/bin/plugin/commands/packages.js
@@ -150,6 +150,7 @@ async function runNpmReleaseBranchSyncStep( pluginReleaseBranch, config ) {
 		 */
 		await repo
 			.raw( 'rm', '-r', '.' )
+			.fetch( 'origin', pluginReleaseBranch, [ '--depth=1' ] )
 			.raw( 'checkout', `origin/${ pluginReleaseBranch }`, '--', '.' );
 
 		const { commit: commitHash } = await repo.commit(

From 5b42dda991a756b3a5f354ac58f200bc54759c75 Mon Sep 17 00:00:00 2001
From: Gutenberg Repository Automation <gutenberg@wordpress.org>
Date: Thu, 31 Aug 2023 09:12:27 +0000
Subject: [PATCH 16/34] Update changelog files

---
 packages/a11y/CHANGELOG.md                                 | 2 ++
 packages/a11y/package.json                                 | 2 +-
 packages/annotations/CHANGELOG.md                          | 2 ++
 packages/annotations/package.json                          | 2 +-
 packages/api-fetch/CHANGELOG.md                            | 2 ++
 packages/api-fetch/package.json                            | 2 +-
 packages/autop/CHANGELOG.md                                | 2 ++
 packages/autop/package.json                                | 2 +-
 packages/babel-plugin-import-jsx-pragma/CHANGELOG.md       | 2 ++
 packages/babel-plugin-import-jsx-pragma/package.json       | 2 +-
 packages/babel-plugin-makepot/CHANGELOG.md                 | 2 ++
 packages/babel-plugin-makepot/package.json                 | 2 +-
 packages/babel-preset-default/CHANGELOG.md                 | 2 ++
 packages/babel-preset-default/package.json                 | 2 +-
 packages/base-styles/CHANGELOG.md                          | 2 ++
 packages/base-styles/package.json                          | 2 +-
 packages/blob/CHANGELOG.md                                 | 2 ++
 packages/blob/package.json                                 | 2 +-
 packages/block-directory/CHANGELOG.md                      | 2 ++
 packages/block-directory/package.json                      | 2 +-
 packages/block-editor/CHANGELOG.md                         | 2 ++
 packages/block-editor/package.json                         | 2 +-
 packages/block-library/CHANGELOG.md                        | 2 ++
 packages/block-library/package.json                        | 2 +-
 packages/block-serialization-default-parser/CHANGELOG.md   | 2 ++
 packages/block-serialization-default-parser/package.json   | 2 +-
 packages/block-serialization-spec-parser/CHANGELOG.md      | 2 ++
 packages/block-serialization-spec-parser/package.json      | 2 +-
 packages/blocks/CHANGELOG.md                               | 2 ++
 packages/blocks/package.json                               | 2 +-
 packages/browserslist-config/CHANGELOG.md                  | 2 ++
 packages/browserslist-config/package.json                  | 2 +-
 packages/commands/CHANGELOG.md                             | 2 ++
 packages/commands/package.json                             | 2 +-
 packages/components/CHANGELOG.md                           | 2 ++
 packages/components/package.json                           | 2 +-
 packages/compose/CHANGELOG.md                              | 2 ++
 packages/compose/package.json                              | 2 +-
 packages/core-commands/CHANGELOG.md                        | 2 ++
 packages/core-commands/package.json                        | 2 +-
 packages/core-data/CHANGELOG.md                            | 2 ++
 packages/core-data/package.json                            | 2 +-
 packages/create-block-interactive-template/CHANGELOG.md    | 2 ++
 packages/create-block-interactive-template/package.json    | 2 +-
 packages/create-block-tutorial-template/CHANGELOG.md       | 2 ++
 packages/create-block-tutorial-template/package.json       | 2 +-
 packages/create-block/CHANGELOG.md                         | 2 ++
 packages/create-block/package.json                         | 2 +-
 packages/customize-widgets/CHANGELOG.md                    | 2 ++
 packages/customize-widgets/package.json                    | 2 +-
 packages/data-controls/CHANGELOG.md                        | 2 ++
 packages/data-controls/package.json                        | 2 +-
 packages/data/CHANGELOG.md                                 | 2 ++
 packages/data/package.json                                 | 2 +-
 packages/date/CHANGELOG.md                                 | 2 ++
 packages/date/package.json                                 | 2 +-
 packages/dependency-extraction-webpack-plugin/CHANGELOG.md | 2 ++
 packages/dependency-extraction-webpack-plugin/package.json | 2 +-
 packages/deprecated/CHANGELOG.md                           | 2 ++
 packages/deprecated/package.json                           | 2 +-
 packages/docgen/CHANGELOG.md                               | 2 ++
 packages/docgen/package.json                               | 2 +-
 packages/dom-ready/CHANGELOG.md                            | 2 ++
 packages/dom-ready/package.json                            | 2 +-
 packages/dom/CHANGELOG.md                                  | 2 ++
 packages/dom/package.json                                  | 2 +-
 packages/e2e-test-utils-playwright/CHANGELOG.md            | 2 ++
 packages/e2e-test-utils-playwright/package.json            | 2 +-
 packages/e2e-test-utils/CHANGELOG.md                       | 2 ++
 packages/e2e-test-utils/package.json                       | 2 +-
 packages/e2e-tests/CHANGELOG.md                            | 2 ++
 packages/e2e-tests/package.json                            | 2 +-
 packages/edit-post/CHANGELOG.md                            | 2 ++
 packages/edit-post/package.json                            | 2 +-
 packages/edit-site/CHANGELOG.md                            | 2 ++
 packages/edit-site/package.json                            | 2 +-
 packages/edit-widgets/CHANGELOG.md                         | 2 ++
 packages/edit-widgets/package.json                         | 2 +-
 packages/editor/CHANGELOG.md                               | 2 ++
 packages/editor/package.json                               | 2 +-
 packages/element/CHANGELOG.md                              | 2 ++
 packages/element/package.json                              | 2 +-
 packages/env/CHANGELOG.md                                  | 2 ++
 packages/env/package.json                                  | 2 +-
 packages/escape-html/CHANGELOG.md                          | 2 ++
 packages/escape-html/package.json                          | 2 +-
 packages/eslint-plugin/CHANGELOG.md                        | 2 ++
 packages/eslint-plugin/package.json                        | 2 +-
 packages/format-library/CHANGELOG.md                       | 2 ++
 packages/format-library/package.json                       | 2 +-
 packages/hooks/CHANGELOG.md                                | 2 ++
 packages/hooks/package.json                                | 2 +-
 packages/html-entities/CHANGELOG.md                        | 2 ++
 packages/html-entities/package.json                        | 2 +-
 packages/i18n/CHANGELOG.md                                 | 2 ++
 packages/i18n/package.json                                 | 2 +-
 packages/icons/CHANGELOG.md                                | 2 ++
 packages/icons/package.json                                | 2 +-
 packages/interactivity/CHANGELOG.md                        | 2 ++
 packages/interactivity/package.json                        | 2 +-
 packages/interface/CHANGELOG.md                            | 2 ++
 packages/interface/package.json                            | 2 +-
 packages/is-shallow-equal/CHANGELOG.md                     | 2 ++
 packages/is-shallow-equal/package.json                     | 2 +-
 packages/jest-console/CHANGELOG.md                         | 6 ++++--
 packages/jest-console/package.json                         | 2 +-
 packages/jest-preset-default/CHANGELOG.md                  | 2 ++
 packages/jest-preset-default/package.json                  | 2 +-
 packages/jest-puppeteer-axe/CHANGELOG.md                   | 2 ++
 packages/jest-puppeteer-axe/package.json                   | 2 +-
 packages/keyboard-shortcuts/CHANGELOG.md                   | 2 ++
 packages/keyboard-shortcuts/package.json                   | 2 +-
 packages/keycodes/CHANGELOG.md                             | 2 ++
 packages/keycodes/package.json                             | 2 +-
 packages/lazy-import/CHANGELOG.md                          | 2 ++
 packages/lazy-import/package.json                          | 2 +-
 packages/list-reusable-blocks/CHANGELOG.md                 | 2 ++
 packages/list-reusable-blocks/package.json                 | 2 +-
 packages/media-utils/CHANGELOG.md                          | 2 ++
 packages/media-utils/package.json                          | 2 +-
 packages/notices/CHANGELOG.md                              | 2 ++
 packages/notices/package.json                              | 2 +-
 packages/npm-package-json-lint-config/CHANGELOG.md         | 2 ++
 packages/npm-package-json-lint-config/package.json         | 2 +-
 packages/nux/CHANGELOG.md                                  | 2 ++
 packages/nux/package.json                                  | 2 +-
 packages/patterns/CHANGELOG.md                             | 2 ++
 packages/patterns/package.json                             | 2 +-
 packages/plugins/CHANGELOG.md                              | 2 ++
 packages/plugins/package.json                              | 2 +-
 packages/postcss-plugins-preset/CHANGELOG.md               | 2 ++
 packages/postcss-plugins-preset/package.json               | 2 +-
 packages/postcss-themes/CHANGELOG.md                       | 2 ++
 packages/postcss-themes/package.json                       | 2 +-
 packages/preferences-persistence/CHANGELOG.md              | 2 ++
 packages/preferences-persistence/package.json              | 2 +-
 packages/preferences/CHANGELOG.md                          | 2 ++
 packages/preferences/package.json                          | 2 +-
 packages/prettier-config/CHANGELOG.md                      | 2 ++
 packages/prettier-config/package.json                      | 2 +-
 packages/primitives/CHANGELOG.md                           | 2 ++
 packages/primitives/package.json                           | 2 +-
 packages/priority-queue/CHANGELOG.md                       | 2 ++
 packages/priority-queue/package.json                       | 2 +-
 packages/private-apis/CHANGELOG.md                         | 2 ++
 packages/private-apis/package.json                         | 2 +-
 packages/project-management-automation/CHANGELOG.md        | 2 ++
 packages/project-management-automation/package.json        | 2 +-
 packages/react-i18n/CHANGELOG.md                           | 2 ++
 packages/react-i18n/package.json                           | 2 +-
 packages/readable-js-assets-webpack-plugin/CHANGELOG.md    | 2 ++
 packages/readable-js-assets-webpack-plugin/package.json    | 2 +-
 packages/redux-routine/CHANGELOG.md                        | 2 ++
 packages/redux-routine/package.json                        | 2 +-
 packages/reusable-blocks/CHANGELOG.md                      | 2 ++
 packages/reusable-blocks/package.json                      | 2 +-
 packages/rich-text/CHANGELOG.md                            | 2 ++
 packages/rich-text/package.json                            | 2 +-
 packages/router/CHANGELOG.md                               | 2 ++
 packages/router/package.json                               | 2 +-
 packages/scripts/CHANGELOG.md                              | 2 ++
 packages/scripts/package.json                              | 2 +-
 packages/server-side-render/CHANGELOG.md                   | 2 ++
 packages/server-side-render/package.json                   | 2 +-
 packages/shortcode/CHANGELOG.md                            | 2 ++
 packages/shortcode/package.json                            | 2 +-
 packages/style-engine/CHANGELOG.md                         | 2 ++
 packages/style-engine/package.json                         | 2 +-
 packages/stylelint-config/CHANGELOG.md                     | 2 ++
 packages/stylelint-config/package.json                     | 2 +-
 packages/sync/CHANGELOG.md                                 | 2 ++
 packages/sync/package.json                                 | 2 +-
 packages/token-list/CHANGELOG.md                           | 2 ++
 packages/token-list/package.json                           | 2 +-
 packages/url/CHANGELOG.md                                  | 2 ++
 packages/url/package.json                                  | 2 +-
 packages/viewport/CHANGELOG.md                             | 2 ++
 packages/viewport/package.json                             | 2 +-
 packages/warning/CHANGELOG.md                              | 2 ++
 packages/warning/package.json                              | 2 +-
 packages/widgets/CHANGELOG.md                              | 2 ++
 packages/widgets/package.json                              | 2 +-
 packages/wordcount/CHANGELOG.md                            | 2 ++
 packages/wordcount/package.json                            | 2 +-
 184 files changed, 278 insertions(+), 94 deletions(-)

diff --git a/packages/a11y/CHANGELOG.md b/packages/a11y/CHANGELOG.md
index dbfbf49152203..011948dfe9f34 100644
--- a/packages/a11y/CHANGELOG.md
+++ b/packages/a11y/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 3.41.0 (2023-08-31)
+
 ## 3.40.0 (2023-08-16)
 
 ## 3.39.0 (2023-08-10)
diff --git a/packages/a11y/package.json b/packages/a11y/package.json
index d37f77eddb36a..837633b0a97ea 100644
--- a/packages/a11y/package.json
+++ b/packages/a11y/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/a11y",
-	"version": "3.40.0",
+	"version": "3.41.0-prerelease",
 	"description": "Accessibility (a11y) utilities for WordPress.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/annotations/CHANGELOG.md b/packages/annotations/CHANGELOG.md
index c57f7caff11a3..6bd4cdaa1ee8f 100644
--- a/packages/annotations/CHANGELOG.md
+++ b/packages/annotations/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 2.41.0 (2023-08-31)
+
 ## 2.40.0 (2023-08-16)
 
 ## 2.39.0 (2023-08-10)
diff --git a/packages/annotations/package.json b/packages/annotations/package.json
index 2cadc82cb2f4e..b3e444e85499f 100644
--- a/packages/annotations/package.json
+++ b/packages/annotations/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/annotations",
-	"version": "2.40.0",
+	"version": "2.41.0-prerelease",
 	"description": "Annotate content in the Gutenberg editor.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/api-fetch/CHANGELOG.md b/packages/api-fetch/CHANGELOG.md
index 6bcbbbc76bc02..8deb000c264f8 100644
--- a/packages/api-fetch/CHANGELOG.md
+++ b/packages/api-fetch/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 6.38.0 (2023-08-31)
+
 ## 6.37.0 (2023-08-16)
 
 ## 6.36.0 (2023-08-10)
diff --git a/packages/api-fetch/package.json b/packages/api-fetch/package.json
index b4f51ad893046..7d9425fe0a968 100644
--- a/packages/api-fetch/package.json
+++ b/packages/api-fetch/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/api-fetch",
-	"version": "6.37.0",
+	"version": "6.38.0-prerelease",
 	"description": "Utility to make WordPress REST API requests.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/autop/CHANGELOG.md b/packages/autop/CHANGELOG.md
index 4cd68bcfd4bb2..ebe2a449d26b8 100644
--- a/packages/autop/CHANGELOG.md
+++ b/packages/autop/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 3.41.0 (2023-08-31)
+
 ## 3.40.0 (2023-08-16)
 
 ## 3.39.0 (2023-08-10)
diff --git a/packages/autop/package.json b/packages/autop/package.json
index b60fcc15cec5d..c9534a7ea36fc 100644
--- a/packages/autop/package.json
+++ b/packages/autop/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/autop",
-	"version": "3.40.0",
+	"version": "3.41.0-prerelease",
 	"description": "WordPress's automatic paragraph functions `autop` and `removep`.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/babel-plugin-import-jsx-pragma/CHANGELOG.md b/packages/babel-plugin-import-jsx-pragma/CHANGELOG.md
index 5f1bfd8bfde9d..003b6f4e215a5 100644
--- a/packages/babel-plugin-import-jsx-pragma/CHANGELOG.md
+++ b/packages/babel-plugin-import-jsx-pragma/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 4.24.0 (2023-08-31)
+
 ## 4.23.0 (2023-08-16)
 
 ## 4.22.0 (2023-08-10)
diff --git a/packages/babel-plugin-import-jsx-pragma/package.json b/packages/babel-plugin-import-jsx-pragma/package.json
index 478bbc3856e24..dfb6a5ce61558 100644
--- a/packages/babel-plugin-import-jsx-pragma/package.json
+++ b/packages/babel-plugin-import-jsx-pragma/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/babel-plugin-import-jsx-pragma",
-	"version": "4.23.0",
+	"version": "4.24.0-prerelease",
 	"description": "Babel transform plugin for automatically injecting an import to be used as the pragma for the React JSX Transform plugin.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/babel-plugin-makepot/CHANGELOG.md b/packages/babel-plugin-makepot/CHANGELOG.md
index 5c2ce02f63579..b8f66bb633d2b 100644
--- a/packages/babel-plugin-makepot/CHANGELOG.md
+++ b/packages/babel-plugin-makepot/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 5.25.0 (2023-08-31)
+
 ## 5.24.0 (2023-08-16)
 
 ## 5.23.0 (2023-08-10)
diff --git a/packages/babel-plugin-makepot/package.json b/packages/babel-plugin-makepot/package.json
index d3912b587721f..5315010b8c442 100644
--- a/packages/babel-plugin-makepot/package.json
+++ b/packages/babel-plugin-makepot/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/babel-plugin-makepot",
-	"version": "5.24.0",
+	"version": "5.25.0-prerelease",
 	"description": "WordPress Babel internationalization (i18n) plugin.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/babel-preset-default/CHANGELOG.md b/packages/babel-preset-default/CHANGELOG.md
index f66a844f8f641..dbf4cdc7eafa9 100644
--- a/packages/babel-preset-default/CHANGELOG.md
+++ b/packages/babel-preset-default/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 7.25.0 (2023-08-31)
+
 ## 7.24.0 (2023-08-16)
 
 ## 7.23.0 (2023-08-10)
diff --git a/packages/babel-preset-default/package.json b/packages/babel-preset-default/package.json
index ee62386610e72..949d0495a0fbc 100644
--- a/packages/babel-preset-default/package.json
+++ b/packages/babel-preset-default/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/babel-preset-default",
-	"version": "7.24.0",
+	"version": "7.25.0-prerelease",
 	"description": "Default Babel preset for WordPress development.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/base-styles/CHANGELOG.md b/packages/base-styles/CHANGELOG.md
index 59dba998089a8..525dd6f7713ca 100644
--- a/packages/base-styles/CHANGELOG.md
+++ b/packages/base-styles/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 4.32.0 (2023-08-31)
+
 ## 4.31.0 (2023-08-16)
 
 ## 4.30.0 (2023-08-10)
diff --git a/packages/base-styles/package.json b/packages/base-styles/package.json
index c1639194eb4e9..6bfa5e3305635 100644
--- a/packages/base-styles/package.json
+++ b/packages/base-styles/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/base-styles",
-	"version": "4.31.0",
+	"version": "4.32.0-prerelease",
 	"description": "Base SCSS utilities and variables for WordPress.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/blob/CHANGELOG.md b/packages/blob/CHANGELOG.md
index 1c73ea77fbd6d..ea3944697d7f3 100644
--- a/packages/blob/CHANGELOG.md
+++ b/packages/blob/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 3.41.0 (2023-08-31)
+
 ## 3.40.0 (2023-08-16)
 
 ## 3.39.0 (2023-08-10)
diff --git a/packages/blob/package.json b/packages/blob/package.json
index b3a398881a528..705fe60013633 100644
--- a/packages/blob/package.json
+++ b/packages/blob/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/blob",
-	"version": "3.40.0",
+	"version": "3.41.0-prerelease",
 	"description": "Blob utilities for WordPress.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/block-directory/CHANGELOG.md b/packages/block-directory/CHANGELOG.md
index ad6cba079850b..800ee15468213 100644
--- a/packages/block-directory/CHANGELOG.md
+++ b/packages/block-directory/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 4.18.0 (2023-08-31)
+
 ## 4.17.0 (2023-08-16)
 
 ## 4.16.0 (2023-08-10)
diff --git a/packages/block-directory/package.json b/packages/block-directory/package.json
index b90a0a38e1c4e..fc14b4ce524cf 100644
--- a/packages/block-directory/package.json
+++ b/packages/block-directory/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/block-directory",
-	"version": "4.17.0",
+	"version": "4.18.0-prerelease",
 	"description": "Extend editor with block directory features to search, download and install blocks.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/block-editor/CHANGELOG.md b/packages/block-editor/CHANGELOG.md
index c74c20d6f21ae..63a6281f78b42 100644
--- a/packages/block-editor/CHANGELOG.md
+++ b/packages/block-editor/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 12.9.0 (2023-08-31)
+
 ### Enhancements
 
 - Embed the `ObserveTyping` behavior within the `BlockList` component making to simplify instanciations of third-party block editors.
diff --git a/packages/block-editor/package.json b/packages/block-editor/package.json
index 8e25e6f649654..4b70c30e946eb 100644
--- a/packages/block-editor/package.json
+++ b/packages/block-editor/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/block-editor",
-	"version": "12.8.0",
+	"version": "12.9.0-prerelease",
 	"description": "Generic block editor.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/block-library/CHANGELOG.md b/packages/block-library/CHANGELOG.md
index a7fe0bd99bd4a..e31c8ba509518 100644
--- a/packages/block-library/CHANGELOG.md
+++ b/packages/block-library/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 8.18.0 (2023-08-31)
+
 ## 8.17.0 (2023-08-16)
 
 ## 8.16.0 (2023-08-10)
diff --git a/packages/block-library/package.json b/packages/block-library/package.json
index 5fe213651f54a..c9031f8f2d6cf 100644
--- a/packages/block-library/package.json
+++ b/packages/block-library/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/block-library",
-	"version": "8.17.0",
+	"version": "8.18.0-prerelease",
 	"description": "Block library for the WordPress editor.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/block-serialization-default-parser/CHANGELOG.md b/packages/block-serialization-default-parser/CHANGELOG.md
index c99b60550849e..3a235ffe77b2c 100644
--- a/packages/block-serialization-default-parser/CHANGELOG.md
+++ b/packages/block-serialization-default-parser/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 4.41.0 (2023-08-31)
+
 ## 4.40.0 (2023-08-16)
 
 ## 4.39.0 (2023-08-10)
diff --git a/packages/block-serialization-default-parser/package.json b/packages/block-serialization-default-parser/package.json
index 0cda27b554e2e..4727c4249d140 100644
--- a/packages/block-serialization-default-parser/package.json
+++ b/packages/block-serialization-default-parser/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/block-serialization-default-parser",
-	"version": "4.40.0",
+	"version": "4.41.0-prerelease",
 	"description": "Block serialization specification parser for WordPress posts.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/block-serialization-spec-parser/CHANGELOG.md b/packages/block-serialization-spec-parser/CHANGELOG.md
index 799f24ec66366..c760b228dc9f5 100644
--- a/packages/block-serialization-spec-parser/CHANGELOG.md
+++ b/packages/block-serialization-spec-parser/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 4.41.0 (2023-08-31)
+
 ## 4.40.0 (2023-08-16)
 
 ## 4.39.0 (2023-08-10)
diff --git a/packages/block-serialization-spec-parser/package.json b/packages/block-serialization-spec-parser/package.json
index 7e39e035848be..ea3adfe911d52 100644
--- a/packages/block-serialization-spec-parser/package.json
+++ b/packages/block-serialization-spec-parser/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/block-serialization-spec-parser",
-	"version": "4.40.0",
+	"version": "4.41.0-prerelease",
 	"description": "Block serialization specification parser for WordPress posts.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/blocks/CHANGELOG.md b/packages/blocks/CHANGELOG.md
index 34c7f2c7ceb50..d843396d51504 100644
--- a/packages/blocks/CHANGELOG.md
+++ b/packages/blocks/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 12.18.0 (2023-08-31)
+
 ## 12.17.0 (2023-08-16)
 
 ## 12.16.0 (2023-08-10)
diff --git a/packages/blocks/package.json b/packages/blocks/package.json
index 1dee1f1d4ae00..470b71c92de9f 100644
--- a/packages/blocks/package.json
+++ b/packages/blocks/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/blocks",
-	"version": "12.17.0",
+	"version": "12.18.0-prerelease",
 	"description": "Block API for WordPress.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/browserslist-config/CHANGELOG.md b/packages/browserslist-config/CHANGELOG.md
index cd3e675cb577d..a78c54cf9a662 100644
--- a/packages/browserslist-config/CHANGELOG.md
+++ b/packages/browserslist-config/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 5.24.0 (2023-08-31)
+
 ## 5.23.0 (2023-08-16)
 
 ## 5.22.0 (2023-08-10)
diff --git a/packages/browserslist-config/package.json b/packages/browserslist-config/package.json
index 9136613a01dc2..0283bd39a1e60 100644
--- a/packages/browserslist-config/package.json
+++ b/packages/browserslist-config/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/browserslist-config",
-	"version": "5.23.0",
+	"version": "5.24.0-prerelease",
 	"description": "WordPress Browserslist shared configuration.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/commands/CHANGELOG.md b/packages/commands/CHANGELOG.md
index 7eaf493e7ce98..e3af451de196a 100644
--- a/packages/commands/CHANGELOG.md
+++ b/packages/commands/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 0.12.0 (2023-08-31)
+
 ## 0.11.0 (2023-08-16)
 
 ## 0.10.0 (2023-08-10)
diff --git a/packages/commands/package.json b/packages/commands/package.json
index ea89a529dc4c4..52812c3297e9f 100644
--- a/packages/commands/package.json
+++ b/packages/commands/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/commands",
-	"version": "0.11.0",
+	"version": "0.12.0-prerelease",
 	"description": "Handles the commands menu.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md
index a12e300ad4724..be5608c72fc06 100644
--- a/packages/components/CHANGELOG.md
+++ b/packages/components/CHANGELOG.md
@@ -6,6 +6,8 @@
 
 -   Make the `Popover.Slot` optional and render popovers at the bottom of the document's body by default. ([#53889](https://github.com/WordPress/gutenberg/pull/53889), [#53982](https://github.com/WordPress/gutenberg/pull/53982)).
 
+## 25.7.0 (2023-08-31)
+
 ### Enhancements
 
 -   `ProgressBar`: Add transition to determinate indicator ([#53877](https://github.com/WordPress/gutenberg/pull/53877)).
diff --git a/packages/components/package.json b/packages/components/package.json
index 2a4f7c380ee97..a06d94e70e886 100644
--- a/packages/components/package.json
+++ b/packages/components/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/components",
-	"version": "25.6.0",
+	"version": "25.7.0-prerelease",
 	"description": "UI components for WordPress.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/compose/CHANGELOG.md b/packages/compose/CHANGELOG.md
index c8db63936b656..ab2f98424481d 100644
--- a/packages/compose/CHANGELOG.md
+++ b/packages/compose/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 6.18.0 (2023-08-31)
+
 ## 6.17.0 (2023-08-16)
 
 ## 6.16.0 (2023-08-10)
diff --git a/packages/compose/package.json b/packages/compose/package.json
index 0ae05b1473939..e862e30ea1c17 100644
--- a/packages/compose/package.json
+++ b/packages/compose/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/compose",
-	"version": "6.17.0",
+	"version": "6.18.0-prerelease",
 	"description": "WordPress higher-order components (HOCs).",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/core-commands/CHANGELOG.md b/packages/core-commands/CHANGELOG.md
index 9a5cabcae8392..abf22fac3e2ca 100644
--- a/packages/core-commands/CHANGELOG.md
+++ b/packages/core-commands/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 0.10.0 (2023-08-31)
+
 ## 0.9.0 (2023-08-16)
 
 ## 0.8.0 (2023-08-10)
diff --git a/packages/core-commands/package.json b/packages/core-commands/package.json
index a802e4d384c50..8d408785583d8 100644
--- a/packages/core-commands/package.json
+++ b/packages/core-commands/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/core-commands",
-	"version": "0.9.0",
+	"version": "0.10.0-prerelease",
 	"description": "WordPress core reusable commands.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/core-data/CHANGELOG.md b/packages/core-data/CHANGELOG.md
index 830aed362938c..6e47bd2b3dd75 100644
--- a/packages/core-data/CHANGELOG.md
+++ b/packages/core-data/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 6.18.0 (2023-08-31)
+
 ## 6.17.0 (2023-08-16)
 
 ## 6.16.0 (2023-08-10)
diff --git a/packages/core-data/package.json b/packages/core-data/package.json
index 8bb83bfdbbf24..d7501a4c25e23 100644
--- a/packages/core-data/package.json
+++ b/packages/core-data/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/core-data",
-	"version": "6.17.0",
+	"version": "6.18.0-prerelease",
 	"description": "Access to and manipulation of core WordPress entities.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/create-block-interactive-template/CHANGELOG.md b/packages/create-block-interactive-template/CHANGELOG.md
index e5c32350167c9..c423f364d62a4 100644
--- a/packages/create-block-interactive-template/CHANGELOG.md
+++ b/packages/create-block-interactive-template/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 1.4.0 (2023-08-31)
+
 ## 1.3.0 (2023-08-16)
 
 ## 1.2.0 (2023-08-10)
diff --git a/packages/create-block-interactive-template/package.json b/packages/create-block-interactive-template/package.json
index 8022c86ecb8a1..92ea21667d39b 100644
--- a/packages/create-block-interactive-template/package.json
+++ b/packages/create-block-interactive-template/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/create-block-interactive-template",
-	"version": "1.3.0",
+	"version": "1.4.0-prerelease",
 	"description": "Template for @wordpress/create-block to create interactive blocks with the Interactivity API.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/create-block-tutorial-template/CHANGELOG.md b/packages/create-block-tutorial-template/CHANGELOG.md
index 8fc38debebc19..77d47333db059 100644
--- a/packages/create-block-tutorial-template/CHANGELOG.md
+++ b/packages/create-block-tutorial-template/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 2.29.0 (2023-08-31)
+
 ## 2.28.0 (2023-08-16)
 
 ## 2.27.0 (2023-08-10)
diff --git a/packages/create-block-tutorial-template/package.json b/packages/create-block-tutorial-template/package.json
index cb48abc1d4650..1c26630d1555d 100644
--- a/packages/create-block-tutorial-template/package.json
+++ b/packages/create-block-tutorial-template/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/create-block-tutorial-template",
-	"version": "2.28.0",
+	"version": "2.29.0-prerelease",
 	"description": "Template for @wordpress/create-block used in the official WordPress tutorial.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/create-block/CHANGELOG.md b/packages/create-block/CHANGELOG.md
index af94e7a1400d3..202d1f774ac2b 100644
--- a/packages/create-block/CHANGELOG.md
+++ b/packages/create-block/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 4.25.0 (2023-08-31)
+
 ## 4.24.0 (2023-08-16)
 
 ## 4.23.0 (2023-08-10)
diff --git a/packages/create-block/package.json b/packages/create-block/package.json
index 4dc0c99fd51c9..c005d4c27440c 100644
--- a/packages/create-block/package.json
+++ b/packages/create-block/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/create-block",
-	"version": "4.24.0",
+	"version": "4.25.0-prerelease",
 	"description": "Generates PHP, JS and CSS code for registering a block for a WordPress plugin.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/customize-widgets/CHANGELOG.md b/packages/customize-widgets/CHANGELOG.md
index cd20fea831205..d9ce39cbb6846 100644
--- a/packages/customize-widgets/CHANGELOG.md
+++ b/packages/customize-widgets/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 4.18.0 (2023-08-31)
+
 ## 4.17.0 (2023-08-16)
 
 ## 4.16.0 (2023-08-10)
diff --git a/packages/customize-widgets/package.json b/packages/customize-widgets/package.json
index 497879afbf94e..42cd5d209b783 100644
--- a/packages/customize-widgets/package.json
+++ b/packages/customize-widgets/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/customize-widgets",
-	"version": "4.17.0",
+	"version": "4.18.0-prerelease",
 	"description": "Widgets blocks in Customizer Module for WordPress.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/data-controls/CHANGELOG.md b/packages/data-controls/CHANGELOG.md
index 5cefd056155b9..f5ed8ea0e7da4 100644
--- a/packages/data-controls/CHANGELOG.md
+++ b/packages/data-controls/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 3.10.0 (2023-08-31)
+
 ## 3.9.0 (2023-08-16)
 
 ## 3.8.0 (2023-08-10)
diff --git a/packages/data-controls/package.json b/packages/data-controls/package.json
index 71601bb4e0848..4911949ac90d7 100644
--- a/packages/data-controls/package.json
+++ b/packages/data-controls/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/data-controls",
-	"version": "3.9.0",
+	"version": "3.10.0-prerelease",
 	"description": "A set of common controls for the @wordpress/data api.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/data/CHANGELOG.md b/packages/data/CHANGELOG.md
index ba2abd614681f..4d970465b7b9d 100644
--- a/packages/data/CHANGELOG.md
+++ b/packages/data/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 9.11.0 (2023-08-31)
+
 ## 9.10.0 (2023-08-16)
 
 ### Enhancements
diff --git a/packages/data/package.json b/packages/data/package.json
index f6050a3abee34..5917feb3143cf 100644
--- a/packages/data/package.json
+++ b/packages/data/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/data",
-	"version": "9.10.0",
+	"version": "9.11.0-prerelease",
 	"description": "Data module for WordPress.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/date/CHANGELOG.md b/packages/date/CHANGELOG.md
index 1ef50cd50cdee..b12bf6a06a09e 100644
--- a/packages/date/CHANGELOG.md
+++ b/packages/date/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 4.41.0 (2023-08-31)
+
 ## 4.40.0 (2023-08-16)
 
 ## 4.39.0 (2023-08-10)
diff --git a/packages/date/package.json b/packages/date/package.json
index 64f21a4b286fa..a3e1e721857d4 100644
--- a/packages/date/package.json
+++ b/packages/date/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/date",
-	"version": "4.40.0",
+	"version": "4.41.0-prerelease",
 	"description": "Date module for WordPress.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/dependency-extraction-webpack-plugin/CHANGELOG.md b/packages/dependency-extraction-webpack-plugin/CHANGELOG.md
index b5d33757b0e29..cdc42b5696a3d 100644
--- a/packages/dependency-extraction-webpack-plugin/CHANGELOG.md
+++ b/packages/dependency-extraction-webpack-plugin/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 4.24.0 (2023-08-31)
+
 ## 4.23.0 (2023-08-16)
 
 ## 4.22.0 (2023-08-10)
diff --git a/packages/dependency-extraction-webpack-plugin/package.json b/packages/dependency-extraction-webpack-plugin/package.json
index 874c28165c56f..51e911fc11090 100644
--- a/packages/dependency-extraction-webpack-plugin/package.json
+++ b/packages/dependency-extraction-webpack-plugin/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/dependency-extraction-webpack-plugin",
-	"version": "4.23.0",
+	"version": "4.24.0-prerelease",
 	"description": "Extract WordPress script dependencies from webpack bundles.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/deprecated/CHANGELOG.md b/packages/deprecated/CHANGELOG.md
index 00ec41b9cb5e7..7df9106e59418 100644
--- a/packages/deprecated/CHANGELOG.md
+++ b/packages/deprecated/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 3.41.0 (2023-08-31)
+
 ## 3.40.0 (2023-08-16)
 
 ## 3.39.0 (2023-08-10)
diff --git a/packages/deprecated/package.json b/packages/deprecated/package.json
index f5401e350629f..d14453ac5b427 100644
--- a/packages/deprecated/package.json
+++ b/packages/deprecated/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/deprecated",
-	"version": "3.40.0",
+	"version": "3.41.0-prerelease",
 	"description": "Deprecation utility for WordPress.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/docgen/CHANGELOG.md b/packages/docgen/CHANGELOG.md
index 43e411ec15cde..7a1c50c38231c 100644
--- a/packages/docgen/CHANGELOG.md
+++ b/packages/docgen/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 1.50.0 (2023-08-31)
+
 ## 1.49.0 (2023-08-16)
 
 ## 1.48.0 (2023-08-10)
diff --git a/packages/docgen/package.json b/packages/docgen/package.json
index c3333e5adad4f..4bfba69df01d5 100644
--- a/packages/docgen/package.json
+++ b/packages/docgen/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/docgen",
-	"version": "1.49.0",
+	"version": "1.50.0-prerelease",
 	"description": "Autogenerate public API documentation from exports and JSDoc comments.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/dom-ready/CHANGELOG.md b/packages/dom-ready/CHANGELOG.md
index 7adb8755b8cc1..5713c42d33ecc 100644
--- a/packages/dom-ready/CHANGELOG.md
+++ b/packages/dom-ready/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 3.41.0 (2023-08-31)
+
 ## 3.40.0 (2023-08-16)
 
 ## 3.39.0 (2023-08-10)
diff --git a/packages/dom-ready/package.json b/packages/dom-ready/package.json
index 0dd190caf2ad5..6220897a8cf5c 100644
--- a/packages/dom-ready/package.json
+++ b/packages/dom-ready/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/dom-ready",
-	"version": "3.40.0",
+	"version": "3.41.0-prerelease",
 	"description": "Execute callback after the DOM is loaded.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/dom/CHANGELOG.md b/packages/dom/CHANGELOG.md
index dc51858e17ab2..33b7597519dff 100644
--- a/packages/dom/CHANGELOG.md
+++ b/packages/dom/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 3.41.0 (2023-08-31)
+
 ## 3.40.0 (2023-08-16)
 
 ## 3.39.0 (2023-08-10)
diff --git a/packages/dom/package.json b/packages/dom/package.json
index a7fec1b3e8b15..428004ba39615 100644
--- a/packages/dom/package.json
+++ b/packages/dom/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/dom",
-	"version": "3.40.0",
+	"version": "3.41.0-prerelease",
 	"description": "DOM utilities module for WordPress.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/e2e-test-utils-playwright/CHANGELOG.md b/packages/e2e-test-utils-playwright/CHANGELOG.md
index d889a1751a2a8..e698734ec7d3f 100644
--- a/packages/e2e-test-utils-playwright/CHANGELOG.md
+++ b/packages/e2e-test-utils-playwright/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 0.9.0 (2023-08-31)
+
 ## 0.8.0 (2023-08-16)
 
 ## 0.7.0 (2023-08-10)
diff --git a/packages/e2e-test-utils-playwright/package.json b/packages/e2e-test-utils-playwright/package.json
index 584e90df0342e..82579c877a9d0 100644
--- a/packages/e2e-test-utils-playwright/package.json
+++ b/packages/e2e-test-utils-playwright/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/e2e-test-utils-playwright",
-	"version": "0.8.0",
+	"version": "0.9.0-prerelease",
 	"description": "End-To-End (E2E) test utils for WordPress.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/e2e-test-utils/CHANGELOG.md b/packages/e2e-test-utils/CHANGELOG.md
index ab52a15b3f7aa..f75ecf987eb7a 100644
--- a/packages/e2e-test-utils/CHANGELOG.md
+++ b/packages/e2e-test-utils/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 10.12.0 (2023-08-31)
+
 ## 10.11.0 (2023-08-16)
 
 ## 10.10.0 (2023-08-10)
diff --git a/packages/e2e-test-utils/package.json b/packages/e2e-test-utils/package.json
index 135e25517b59f..e8058cb780e2c 100644
--- a/packages/e2e-test-utils/package.json
+++ b/packages/e2e-test-utils/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/e2e-test-utils",
-	"version": "10.11.0",
+	"version": "10.12.0-prerelease",
 	"description": "End-To-End (E2E) test utils for WordPress.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/e2e-tests/CHANGELOG.md b/packages/e2e-tests/CHANGELOG.md
index 4dc2746182f9f..6d5daa8944719 100644
--- a/packages/e2e-tests/CHANGELOG.md
+++ b/packages/e2e-tests/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 7.12.0 (2023-08-31)
+
 ## 7.11.0 (2023-08-16)
 
 ## 7.10.0 (2023-08-10)
diff --git a/packages/e2e-tests/package.json b/packages/e2e-tests/package.json
index f9ad6efcf0e2c..9fc4ccf4ba769 100644
--- a/packages/e2e-tests/package.json
+++ b/packages/e2e-tests/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/e2e-tests",
-	"version": "7.11.0",
+	"version": "7.12.0-prerelease",
 	"description": "End-To-End (E2E) tests for WordPress.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/edit-post/CHANGELOG.md b/packages/edit-post/CHANGELOG.md
index dc5f6267945b7..c56c3b968764a 100644
--- a/packages/edit-post/CHANGELOG.md
+++ b/packages/edit-post/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 7.18.0 (2023-08-31)
+
 ## 7.17.0 (2023-08-16)
 
 ## 7.16.0 (2023-08-10)
diff --git a/packages/edit-post/package.json b/packages/edit-post/package.json
index d3ac35d5012d9..e7d2c22af2dc2 100644
--- a/packages/edit-post/package.json
+++ b/packages/edit-post/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/edit-post",
-	"version": "7.17.0",
+	"version": "7.18.0-prerelease",
 	"description": "Edit Post module for WordPress.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/edit-site/CHANGELOG.md b/packages/edit-site/CHANGELOG.md
index 2a90482062517..452755b4557c7 100644
--- a/packages/edit-site/CHANGELOG.md
+++ b/packages/edit-site/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 5.18.0 (2023-08-31)
+
 ## 5.17.0 (2023-08-16)
 
 ## 5.16.0 (2023-08-10)
diff --git a/packages/edit-site/package.json b/packages/edit-site/package.json
index e0f9ac3dfcb5c..ae0ca530a288b 100644
--- a/packages/edit-site/package.json
+++ b/packages/edit-site/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/edit-site",
-	"version": "5.17.0",
+	"version": "5.18.0-prerelease",
 	"description": "Edit Site Page module for WordPress.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/edit-widgets/CHANGELOG.md b/packages/edit-widgets/CHANGELOG.md
index 995ba74ef66ee..13d457c8fa457 100644
--- a/packages/edit-widgets/CHANGELOG.md
+++ b/packages/edit-widgets/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 5.18.0 (2023-08-31)
+
 ## 5.17.0 (2023-08-16)
 
 ## 5.16.0 (2023-08-10)
diff --git a/packages/edit-widgets/package.json b/packages/edit-widgets/package.json
index 0e4fb70d95f2c..61545a5f40ce3 100644
--- a/packages/edit-widgets/package.json
+++ b/packages/edit-widgets/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/edit-widgets",
-	"version": "5.17.0",
+	"version": "5.18.0-prerelease",
 	"description": "Widgets Page module for WordPress..",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/editor/CHANGELOG.md b/packages/editor/CHANGELOG.md
index 2a7d933ad5c66..30c51d93f23b3 100644
--- a/packages/editor/CHANGELOG.md
+++ b/packages/editor/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 13.18.0 (2023-08-31)
+
 ## 13.17.0 (2023-08-16)
 
 ## 13.16.0 (2023-08-10)
diff --git a/packages/editor/package.json b/packages/editor/package.json
index ba7cba3fa03e5..bb39cfdec2d08 100644
--- a/packages/editor/package.json
+++ b/packages/editor/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/editor",
-	"version": "13.17.0",
+	"version": "13.18.0-prerelease",
 	"description": "Enhanced block editor for WordPress posts.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/element/CHANGELOG.md b/packages/element/CHANGELOG.md
index 6e1c9539740b5..e49a949134f37 100644
--- a/packages/element/CHANGELOG.md
+++ b/packages/element/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 5.18.0 (2023-08-31)
+
 ## 5.17.0 (2023-08-16)
 
 ## 5.16.0 (2023-08-10)
diff --git a/packages/element/package.json b/packages/element/package.json
index 102874371d4cf..89cf2c0d996c2 100644
--- a/packages/element/package.json
+++ b/packages/element/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/element",
-	"version": "5.17.0",
+	"version": "5.18.0-prerelease",
 	"description": "Element React module for WordPress.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/env/CHANGELOG.md b/packages/env/CHANGELOG.md
index c909b2654acaa..52b7f97b82ac9 100644
--- a/packages/env/CHANGELOG.md
+++ b/packages/env/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 8.7.0 (2023-08-31)
+
 ## 8.6.0 (2023-08-16)
 
 ## 8.5.0 (2023-08-10)
diff --git a/packages/env/package.json b/packages/env/package.json
index ba83d8c5b6ef1..33a81f2568f45 100644
--- a/packages/env/package.json
+++ b/packages/env/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/env",
-	"version": "8.6.0",
+	"version": "8.7.0-prerelease",
 	"description": "A zero-config, self contained local WordPress environment for development and testing.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/escape-html/CHANGELOG.md b/packages/escape-html/CHANGELOG.md
index 86320c1dd505d..fdbdc1d6ec4e5 100644
--- a/packages/escape-html/CHANGELOG.md
+++ b/packages/escape-html/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 2.41.0 (2023-08-31)
+
 ## 2.40.0 (2023-08-16)
 
 ## 2.39.0 (2023-08-10)
diff --git a/packages/escape-html/package.json b/packages/escape-html/package.json
index c20e43f8fb32e..0baf569edb896 100644
--- a/packages/escape-html/package.json
+++ b/packages/escape-html/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/escape-html",
-	"version": "2.40.0",
+	"version": "2.41.0-prerelease",
 	"description": "Escape HTML utils.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/eslint-plugin/CHANGELOG.md b/packages/eslint-plugin/CHANGELOG.md
index dc1ca8f9b3da2..d8d39769a279b 100644
--- a/packages/eslint-plugin/CHANGELOG.md
+++ b/packages/eslint-plugin/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 15.1.0 (2023-08-31)
+
 ## 15.0.0 (2023-08-16)
 
 ### Breaking Changes
diff --git a/packages/eslint-plugin/package.json b/packages/eslint-plugin/package.json
index 6abf721e12010..71abe7e887e0a 100644
--- a/packages/eslint-plugin/package.json
+++ b/packages/eslint-plugin/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/eslint-plugin",
-	"version": "15.0.0",
+	"version": "15.1.0-prerelease",
 	"description": "ESLint plugin for WordPress development.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/format-library/CHANGELOG.md b/packages/format-library/CHANGELOG.md
index d2aa5c66405ff..d46aa5959d1a6 100644
--- a/packages/format-library/CHANGELOG.md
+++ b/packages/format-library/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 4.18.0 (2023-08-31)
+
 ## 4.17.0 (2023-08-16)
 
 ## 4.16.0 (2023-08-10)
diff --git a/packages/format-library/package.json b/packages/format-library/package.json
index 793ca7cbd9599..88c10386e5a48 100644
--- a/packages/format-library/package.json
+++ b/packages/format-library/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/format-library",
-	"version": "4.17.0",
+	"version": "4.18.0-prerelease",
 	"description": "Format library for the WordPress editor.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/hooks/CHANGELOG.md b/packages/hooks/CHANGELOG.md
index 49c9320fd63ad..8ad1d109473e2 100644
--- a/packages/hooks/CHANGELOG.md
+++ b/packages/hooks/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 3.41.0 (2023-08-31)
+
 ## 3.40.0 (2023-08-16)
 
 ## 3.39.0 (2023-08-10)
diff --git a/packages/hooks/package.json b/packages/hooks/package.json
index 35218c60f5ff8..e404ee6773806 100644
--- a/packages/hooks/package.json
+++ b/packages/hooks/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/hooks",
-	"version": "3.40.0",
+	"version": "3.41.0-prerelease",
 	"description": "WordPress hooks library.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/html-entities/CHANGELOG.md b/packages/html-entities/CHANGELOG.md
index ca3b32d6dd6b0..878c95e28be92 100644
--- a/packages/html-entities/CHANGELOG.md
+++ b/packages/html-entities/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 3.41.0 (2023-08-31)
+
 ## 3.40.0 (2023-08-16)
 
 ## 3.39.0 (2023-08-10)
diff --git a/packages/html-entities/package.json b/packages/html-entities/package.json
index 2484954ca5210..63aad915a10f5 100644
--- a/packages/html-entities/package.json
+++ b/packages/html-entities/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/html-entities",
-	"version": "3.40.0",
+	"version": "3.41.0-prerelease",
 	"description": "HTML entity utilities for WordPress.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/i18n/CHANGELOG.md b/packages/i18n/CHANGELOG.md
index 43676a827be9a..a38f7ecd49edc 100644
--- a/packages/i18n/CHANGELOG.md
+++ b/packages/i18n/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 4.41.0 (2023-08-31)
+
 ## 4.40.0 (2023-08-16)
 
 ## 4.39.0 (2023-08-10)
diff --git a/packages/i18n/package.json b/packages/i18n/package.json
index 1b25f9a9fe2de..30236afa6f83b 100644
--- a/packages/i18n/package.json
+++ b/packages/i18n/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/i18n",
-	"version": "4.40.0",
+	"version": "4.41.0-prerelease",
 	"description": "WordPress internationalization (i18n) library.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/icons/CHANGELOG.md b/packages/icons/CHANGELOG.md
index 7ce0c1bbfa3ff..3c1715455ad49 100644
--- a/packages/icons/CHANGELOG.md
+++ b/packages/icons/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 9.32.0 (2023-08-31)
+
 ### Bug Fix
 
 -   Fixed invalid XML namespace on `alignJustify`, `customLink`, `mapMarker`, `postContent` and `title` ([#53955](https://github.com/WordPress/gutenberg/pull/53955)).
diff --git a/packages/icons/package.json b/packages/icons/package.json
index 8aa25cba5cb22..7199d55bdcd8a 100644
--- a/packages/icons/package.json
+++ b/packages/icons/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/icons",
-	"version": "9.31.0",
+	"version": "9.32.0-prerelease",
 	"description": "WordPress Icons package, based on dashicon.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/interactivity/CHANGELOG.md b/packages/interactivity/CHANGELOG.md
index 6a4565f3ad183..636f78d3357cf 100644
--- a/packages/interactivity/CHANGELOG.md
+++ b/packages/interactivity/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 2.2.0 (2023-08-31)
+
 ### Enhancements
 
 -   Support keys using `data-wp-key`. ([#53844](https://github.com/WordPress/gutenberg/pull/53844))
diff --git a/packages/interactivity/package.json b/packages/interactivity/package.json
index 4903126004fc7..68a159473ace2 100644
--- a/packages/interactivity/package.json
+++ b/packages/interactivity/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/interactivity",
-	"version": "2.1.0",
+	"version": "2.2.0-prerelease",
 	"description": "Package that provides a standard and simple way to handle the frontend interactivity of Gutenberg blocks.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/interface/CHANGELOG.md b/packages/interface/CHANGELOG.md
index d378dfa6c7ca8..1dc013ea4d497 100644
--- a/packages/interface/CHANGELOG.md
+++ b/packages/interface/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 5.18.0 (2023-08-31)
+
 ## 5.17.0 (2023-08-16)
 
 ## 5.16.0 (2023-08-10)
diff --git a/packages/interface/package.json b/packages/interface/package.json
index 55de7645dff67..5f4591f77f075 100644
--- a/packages/interface/package.json
+++ b/packages/interface/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/interface",
-	"version": "5.17.0",
+	"version": "5.18.0-prerelease",
 	"description": "Interface module for WordPress. The package contains shared functionality across the modern JavaScript-based WordPress screens.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/is-shallow-equal/CHANGELOG.md b/packages/is-shallow-equal/CHANGELOG.md
index 057275132433e..8ce603c82a3b5 100644
--- a/packages/is-shallow-equal/CHANGELOG.md
+++ b/packages/is-shallow-equal/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 4.41.0 (2023-08-31)
+
 ## 4.40.0 (2023-08-16)
 
 ## 4.39.0 (2023-08-10)
diff --git a/packages/is-shallow-equal/package.json b/packages/is-shallow-equal/package.json
index 17c3f85398a02..fdc8de459ef90 100644
--- a/packages/is-shallow-equal/package.json
+++ b/packages/is-shallow-equal/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/is-shallow-equal",
-	"version": "4.40.0",
+	"version": "4.41.0-prerelease",
 	"description": "Test for shallow equality between two objects or arrays.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/jest-console/CHANGELOG.md b/packages/jest-console/CHANGELOG.md
index f5aa959e2542e..6a97368b82762 100644
--- a/packages/jest-console/CHANGELOG.md
+++ b/packages/jest-console/CHANGELOG.md
@@ -4,7 +4,9 @@
 
 ### Enhancement
 
-- Improved error messages and codes printed on the console ([#53743](https://github.com/WordPress/gutenberg/pull/53743)).
+-   Improved error messages and codes printed on the console ([#53743](https://github.com/WordPress/gutenberg/pull/53743)).
+
+## 7.12.0 (2023-08-31)
 
 ## 7.11.0 (2023-08-16)
 
@@ -32,7 +34,7 @@
 
 ### Breaking Changes
 
--  Started requiring Jest v29 instead of v27 as a peer dependency. See [breaking changes in Jest 28](https://jestjs.io/blog/2022/04/25/jest-28) and [in jest 29](https://jestjs.io/blog/2022/08/25/jest-29) ([#47388](https://github.com/WordPress/gutenberg/pull/47388))
+-   Started requiring Jest v29 instead of v27 as a peer dependency. See [breaking changes in Jest 28](https://jestjs.io/blog/2022/04/25/jest-28) and [in jest 29](https://jestjs.io/blog/2022/08/25/jest-29) ([#47388](https://github.com/WordPress/gutenberg/pull/47388))
 
 ## 6.11.0 (2023-03-01)
 
diff --git a/packages/jest-console/package.json b/packages/jest-console/package.json
index 9c5433c632173..24c264f812325 100644
--- a/packages/jest-console/package.json
+++ b/packages/jest-console/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/jest-console",
-	"version": "7.11.0",
+	"version": "7.12.0-prerelease",
 	"description": "Custom Jest matchers for the Console object.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/jest-preset-default/CHANGELOG.md b/packages/jest-preset-default/CHANGELOG.md
index 77f8976807d96..e206afd899930 100644
--- a/packages/jest-preset-default/CHANGELOG.md
+++ b/packages/jest-preset-default/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 11.12.0 (2023-08-31)
+
 ## 11.11.0 (2023-08-16)
 
 ## 11.10.0 (2023-08-10)
diff --git a/packages/jest-preset-default/package.json b/packages/jest-preset-default/package.json
index 61f5e3590ce85..2c53457515d98 100644
--- a/packages/jest-preset-default/package.json
+++ b/packages/jest-preset-default/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/jest-preset-default",
-	"version": "11.11.0",
+	"version": "11.12.0-prerelease",
 	"description": "Default Jest preset for WordPress development.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/jest-puppeteer-axe/CHANGELOG.md b/packages/jest-puppeteer-axe/CHANGELOG.md
index aa77aa43d5403..22ca105f55dd2 100644
--- a/packages/jest-puppeteer-axe/CHANGELOG.md
+++ b/packages/jest-puppeteer-axe/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 6.12.0 (2023-08-31)
+
 ## 6.11.0 (2023-08-16)
 
 ## 6.10.0 (2023-08-10)
diff --git a/packages/jest-puppeteer-axe/package.json b/packages/jest-puppeteer-axe/package.json
index 0145bf3c1e3ab..717d4279571f5 100644
--- a/packages/jest-puppeteer-axe/package.json
+++ b/packages/jest-puppeteer-axe/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/jest-puppeteer-axe",
-	"version": "6.11.0",
+	"version": "6.12.0-prerelease",
 	"description": "Axe API integration with Jest and Puppeteer.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/keyboard-shortcuts/CHANGELOG.md b/packages/keyboard-shortcuts/CHANGELOG.md
index ef50c5f3c1594..858c80ffce79a 100644
--- a/packages/keyboard-shortcuts/CHANGELOG.md
+++ b/packages/keyboard-shortcuts/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 4.18.0 (2023-08-31)
+
 ## 4.17.0 (2023-08-16)
 
 ## 4.16.0 (2023-08-10)
diff --git a/packages/keyboard-shortcuts/package.json b/packages/keyboard-shortcuts/package.json
index 1c87e9b72eac9..3de49133c9e13 100644
--- a/packages/keyboard-shortcuts/package.json
+++ b/packages/keyboard-shortcuts/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/keyboard-shortcuts",
-	"version": "4.17.0",
+	"version": "4.18.0-prerelease",
 	"description": "Handling keyboard shortcuts.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/keycodes/CHANGELOG.md b/packages/keycodes/CHANGELOG.md
index 8b166452f814a..7c8f372643b73 100644
--- a/packages/keycodes/CHANGELOG.md
+++ b/packages/keycodes/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 3.41.0 (2023-08-31)
+
 ## 3.40.0 (2023-08-16)
 
 ## 3.39.0 (2023-08-10)
diff --git a/packages/keycodes/package.json b/packages/keycodes/package.json
index e3923149b7b3b..e2c206c33bfc2 100644
--- a/packages/keycodes/package.json
+++ b/packages/keycodes/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/keycodes",
-	"version": "3.40.0",
+	"version": "3.41.0-prerelease",
 	"description": "Keycodes utilities for WordPress. Used to check for keyboard events across browsers/operating systems.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/lazy-import/CHANGELOG.md b/packages/lazy-import/CHANGELOG.md
index 8960e4cf55f09..65b4dca4ab160 100644
--- a/packages/lazy-import/CHANGELOG.md
+++ b/packages/lazy-import/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 1.28.0 (2023-08-31)
+
 ## 1.27.0 (2023-08-16)
 
 ## 1.26.0 (2023-08-10)
diff --git a/packages/lazy-import/package.json b/packages/lazy-import/package.json
index 28e2366de9841..4725970c1fdac 100644
--- a/packages/lazy-import/package.json
+++ b/packages/lazy-import/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/lazy-import",
-	"version": "1.27.0",
+	"version": "1.28.0-prerelease",
 	"description": "Lazily import a module, installing it automatically if missing.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/list-reusable-blocks/CHANGELOG.md b/packages/list-reusable-blocks/CHANGELOG.md
index 9db39b32d6b10..b1812c42dbc00 100644
--- a/packages/list-reusable-blocks/CHANGELOG.md
+++ b/packages/list-reusable-blocks/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 4.18.0 (2023-08-31)
+
 ## 4.17.0 (2023-08-16)
 
 ## 4.16.0 (2023-08-10)
diff --git a/packages/list-reusable-blocks/package.json b/packages/list-reusable-blocks/package.json
index f55344e7838d1..532f929eecbbc 100644
--- a/packages/list-reusable-blocks/package.json
+++ b/packages/list-reusable-blocks/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/list-reusable-blocks",
-	"version": "4.17.0",
+	"version": "4.18.0-prerelease",
 	"description": "Adding Export/Import support to the reusable blocks listing.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/media-utils/CHANGELOG.md b/packages/media-utils/CHANGELOG.md
index 6421e4238cdd9..435a971621d90 100644
--- a/packages/media-utils/CHANGELOG.md
+++ b/packages/media-utils/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 4.32.0 (2023-08-31)
+
 ## 4.31.0 (2023-08-16)
 
 ## 4.30.0 (2023-08-10)
diff --git a/packages/media-utils/package.json b/packages/media-utils/package.json
index fcfbd1e9edff8..0869be440f1b9 100644
--- a/packages/media-utils/package.json
+++ b/packages/media-utils/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/media-utils",
-	"version": "4.31.0",
+	"version": "4.32.0-prerelease",
 	"description": "WordPress Media Upload Utils.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/notices/CHANGELOG.md b/packages/notices/CHANGELOG.md
index 51ac0b28d9644..a4b42df832dcb 100644
--- a/packages/notices/CHANGELOG.md
+++ b/packages/notices/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 4.9.0 (2023-08-31)
+
 ## 4.8.0 (2023-08-16)
 
 ## 4.7.0 (2023-08-10)
diff --git a/packages/notices/package.json b/packages/notices/package.json
index 67a1dc161add6..f129e65344c99 100644
--- a/packages/notices/package.json
+++ b/packages/notices/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/notices",
-	"version": "4.8.0",
+	"version": "4.9.0-prerelease",
 	"description": "State management for notices.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/npm-package-json-lint-config/CHANGELOG.md b/packages/npm-package-json-lint-config/CHANGELOG.md
index cb8971f86de4f..63e6266d9bbfb 100644
--- a/packages/npm-package-json-lint-config/CHANGELOG.md
+++ b/packages/npm-package-json-lint-config/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 4.26.0 (2023-08-31)
+
 ## 4.25.0 (2023-08-16)
 
 ### Enhancement
diff --git a/packages/npm-package-json-lint-config/package.json b/packages/npm-package-json-lint-config/package.json
index 8a5e2993ee2bd..7747193e29771 100644
--- a/packages/npm-package-json-lint-config/package.json
+++ b/packages/npm-package-json-lint-config/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/npm-package-json-lint-config",
-	"version": "4.25.0",
+	"version": "4.26.0-prerelease",
 	"description": "WordPress npm-package-json-lint shareable configuration.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/nux/CHANGELOG.md b/packages/nux/CHANGELOG.md
index 5fda6a3c01ac0..1514e24433eef 100644
--- a/packages/nux/CHANGELOG.md
+++ b/packages/nux/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 8.3.0 (2023-08-31)
+
 ## 8.2.0 (2023-08-16)
 
 ## 8.1.0 (2023-08-10)
diff --git a/packages/nux/package.json b/packages/nux/package.json
index 64d56e80ac0ec..d1bda5c1452cd 100644
--- a/packages/nux/package.json
+++ b/packages/nux/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/nux",
-	"version": "8.2.0",
+	"version": "8.3.0-prerelease",
 	"description": "NUX (New User eXperience) module for WordPress.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/patterns/CHANGELOG.md b/packages/patterns/CHANGELOG.md
index 694450fd27090..19a81007d0056 100644
--- a/packages/patterns/CHANGELOG.md
+++ b/packages/patterns/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 1.2.0 (2023-08-31)
+
 ## 1.1.0 (2023-08-16)
 
 Initial release.
diff --git a/packages/patterns/package.json b/packages/patterns/package.json
index 59a783cc436b5..52f61e45e8ef9 100644
--- a/packages/patterns/package.json
+++ b/packages/patterns/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/patterns",
-	"version": "1.1.0",
+	"version": "1.2.0-prerelease",
 	"description": "Management of user pattern editing.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/plugins/CHANGELOG.md b/packages/plugins/CHANGELOG.md
index 2fe5c6cca77b8..8a1dc3dcd8c23 100644
--- a/packages/plugins/CHANGELOG.md
+++ b/packages/plugins/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 6.9.0 (2023-08-31)
+
 ## 6.8.0 (2023-08-16)
 
 ## 6.7.0 (2023-08-10)
diff --git a/packages/plugins/package.json b/packages/plugins/package.json
index 83a3cf295447a..f3effbc508679 100644
--- a/packages/plugins/package.json
+++ b/packages/plugins/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/plugins",
-	"version": "6.8.0",
+	"version": "6.9.0-prerelease",
 	"description": "Plugins module for WordPress.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/postcss-plugins-preset/CHANGELOG.md b/packages/postcss-plugins-preset/CHANGELOG.md
index b9e20bdb6fc0e..a482f56dc5341 100644
--- a/packages/postcss-plugins-preset/CHANGELOG.md
+++ b/packages/postcss-plugins-preset/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 4.25.0 (2023-08-31)
+
 ## 4.24.0 (2023-08-16)
 
 ## 4.23.0 (2023-08-10)
diff --git a/packages/postcss-plugins-preset/package.json b/packages/postcss-plugins-preset/package.json
index 642fd74ff72e9..14ed00e50cf6f 100644
--- a/packages/postcss-plugins-preset/package.json
+++ b/packages/postcss-plugins-preset/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/postcss-plugins-preset",
-	"version": "4.24.0",
+	"version": "4.25.0-prerelease",
 	"description": "PostCSS sharable plugins preset for WordPress development.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/postcss-themes/CHANGELOG.md b/packages/postcss-themes/CHANGELOG.md
index 4bde8d7a96fe2..eb571d1a74fa2 100644
--- a/packages/postcss-themes/CHANGELOG.md
+++ b/packages/postcss-themes/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 5.24.0 (2023-08-31)
+
 ## 5.23.0 (2023-08-16)
 
 ## 5.22.0 (2023-08-10)
diff --git a/packages/postcss-themes/package.json b/packages/postcss-themes/package.json
index 278a50a6c14d2..bcdd0613e4639 100644
--- a/packages/postcss-themes/package.json
+++ b/packages/postcss-themes/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/postcss-themes",
-	"version": "5.23.0",
+	"version": "5.24.0-prerelease",
 	"description": "PostCSS plugin to generate theme colors.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/preferences-persistence/CHANGELOG.md b/packages/preferences-persistence/CHANGELOG.md
index 2ce6d991ba5ad..5718f3d30dcf0 100644
--- a/packages/preferences-persistence/CHANGELOG.md
+++ b/packages/preferences-persistence/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 1.33.0 (2023-08-31)
+
 ## 1.32.0 (2023-08-16)
 
 ## 1.31.0 (2023-08-10)
diff --git a/packages/preferences-persistence/package.json b/packages/preferences-persistence/package.json
index d43098c3908c4..2c9ab6789998d 100644
--- a/packages/preferences-persistence/package.json
+++ b/packages/preferences-persistence/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/preferences-persistence",
-	"version": "1.32.0",
+	"version": "1.33.0-prerelease",
 	"description": "Persistence utilities for `wordpress/preferences`.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/preferences/CHANGELOG.md b/packages/preferences/CHANGELOG.md
index cf151618f2e35..053ad37366324 100644
--- a/packages/preferences/CHANGELOG.md
+++ b/packages/preferences/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 3.18.0 (2023-08-31)
+
 ## 3.17.0 (2023-08-16)
 
 ## 3.16.0 (2023-08-10)
diff --git a/packages/preferences/package.json b/packages/preferences/package.json
index a452fea6f113b..e7bbeeb246b05 100644
--- a/packages/preferences/package.json
+++ b/packages/preferences/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/preferences",
-	"version": "3.17.0",
+	"version": "3.18.0-prerelease",
 	"description": "Utilities for managing WordPress preferences.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/prettier-config/CHANGELOG.md b/packages/prettier-config/CHANGELOG.md
index 96744789fa9e5..ca25b1f957347 100644
--- a/packages/prettier-config/CHANGELOG.md
+++ b/packages/prettier-config/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 2.24.0 (2023-08-31)
+
 ## 2.23.0 (2023-08-16)
 
 ## 2.22.0 (2023-08-10)
diff --git a/packages/prettier-config/package.json b/packages/prettier-config/package.json
index 32d0d1347f62c..56b7a522df2be 100644
--- a/packages/prettier-config/package.json
+++ b/packages/prettier-config/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/prettier-config",
-	"version": "2.23.0",
+	"version": "2.24.0-prerelease",
 	"description": "WordPress Prettier shared configuration.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/primitives/CHANGELOG.md b/packages/primitives/CHANGELOG.md
index 9fa6f6f9dc02b..6beb37c1b9dd8 100644
--- a/packages/primitives/CHANGELOG.md
+++ b/packages/primitives/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 3.39.0 (2023-08-31)
+
 ## 3.38.0 (2023-08-16)
 
 ## 3.37.0 (2023-08-10)
diff --git a/packages/primitives/package.json b/packages/primitives/package.json
index 05167c4427759..c6c4f997df19d 100644
--- a/packages/primitives/package.json
+++ b/packages/primitives/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/primitives",
-	"version": "3.38.0",
+	"version": "3.39.0-prerelease",
 	"description": "WordPress cross-platform primitives.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/priority-queue/CHANGELOG.md b/packages/priority-queue/CHANGELOG.md
index 7b256496708d6..40fc696323955 100644
--- a/packages/priority-queue/CHANGELOG.md
+++ b/packages/priority-queue/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 2.41.0 (2023-08-31)
+
 ## 2.40.0 (2023-08-16)
 
 ## 2.39.0 (2023-08-10)
diff --git a/packages/priority-queue/package.json b/packages/priority-queue/package.json
index 94e68e7c7ce1b..777a0ffe4e401 100644
--- a/packages/priority-queue/package.json
+++ b/packages/priority-queue/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/priority-queue",
-	"version": "2.40.0",
+	"version": "2.41.0-prerelease",
 	"description": "Generic browser priority queue.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/private-apis/CHANGELOG.md b/packages/private-apis/CHANGELOG.md
index 59966f0881d65..e21054feb4bde 100644
--- a/packages/private-apis/CHANGELOG.md
+++ b/packages/private-apis/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 0.23.0 (2023-08-31)
+
 ## 0.22.0 (2023-08-16)
 
 ## 0.21.0 (2023-08-10)
diff --git a/packages/private-apis/package.json b/packages/private-apis/package.json
index 78afafd14a619..2a4ea80bf268c 100644
--- a/packages/private-apis/package.json
+++ b/packages/private-apis/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/private-apis",
-	"version": "0.22.0",
+	"version": "0.23.0-prerelease",
 	"description": "Internal experimental APIs for WordPress core.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/project-management-automation/CHANGELOG.md b/packages/project-management-automation/CHANGELOG.md
index bdb4343242f42..bbee02666f64e 100644
--- a/packages/project-management-automation/CHANGELOG.md
+++ b/packages/project-management-automation/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 1.40.0 (2023-08-31)
+
 ## 1.39.0 (2023-08-16)
 
 ## 1.38.0 (2023-08-10)
diff --git a/packages/project-management-automation/package.json b/packages/project-management-automation/package.json
index 0d426fab0dc28..4848ed96f40e0 100644
--- a/packages/project-management-automation/package.json
+++ b/packages/project-management-automation/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/project-management-automation",
-	"version": "1.39.0",
+	"version": "1.40.0-prerelease",
 	"description": "GitHub Action that implements various automation to assist with managing the Gutenberg GitHub repository.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/react-i18n/CHANGELOG.md b/packages/react-i18n/CHANGELOG.md
index 9da0ce01a7dea..f9a4be68245c3 100644
--- a/packages/react-i18n/CHANGELOG.md
+++ b/packages/react-i18n/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 3.39.0 (2023-08-31)
+
 ## 3.38.0 (2023-08-16)
 
 ## 3.37.0 (2023-08-10)
diff --git a/packages/react-i18n/package.json b/packages/react-i18n/package.json
index 185d009525530..d4269c65259da 100644
--- a/packages/react-i18n/package.json
+++ b/packages/react-i18n/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/react-i18n",
-	"version": "3.38.0",
+	"version": "3.39.0-prerelease",
 	"description": "React bindings for @wordpress/i18n.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/readable-js-assets-webpack-plugin/CHANGELOG.md b/packages/readable-js-assets-webpack-plugin/CHANGELOG.md
index d762a1ee4aff2..213e4269dfb09 100644
--- a/packages/readable-js-assets-webpack-plugin/CHANGELOG.md
+++ b/packages/readable-js-assets-webpack-plugin/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 2.24.0 (2023-08-31)
+
 ## 2.23.0 (2023-08-16)
 
 ## 2.22.0 (2023-08-10)
diff --git a/packages/readable-js-assets-webpack-plugin/package.json b/packages/readable-js-assets-webpack-plugin/package.json
index 7099596310c4d..3b85ff6731f02 100644
--- a/packages/readable-js-assets-webpack-plugin/package.json
+++ b/packages/readable-js-assets-webpack-plugin/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/readable-js-assets-webpack-plugin",
-	"version": "2.23.0",
+	"version": "2.24.0-prerelease",
 	"description": "Generate a readable JS file for each JS asset.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/redux-routine/CHANGELOG.md b/packages/redux-routine/CHANGELOG.md
index 050b18e2d0fa4..d48813aeaf577 100644
--- a/packages/redux-routine/CHANGELOG.md
+++ b/packages/redux-routine/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 4.41.0 (2023-08-31)
+
 ## 4.40.0 (2023-08-16)
 
 ## 4.39.0 (2023-08-10)
diff --git a/packages/redux-routine/package.json b/packages/redux-routine/package.json
index 66e8d2406cbad..d32bd95ee5ab2 100644
--- a/packages/redux-routine/package.json
+++ b/packages/redux-routine/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/redux-routine",
-	"version": "4.40.0",
+	"version": "4.41.0-prerelease",
 	"description": "Redux middleware for generator coroutines.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/reusable-blocks/CHANGELOG.md b/packages/reusable-blocks/CHANGELOG.md
index 1960e2b9987c0..724be83d370e8 100644
--- a/packages/reusable-blocks/CHANGELOG.md
+++ b/packages/reusable-blocks/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 4.18.0 (2023-08-31)
+
 ## 4.17.0 (2023-08-16)
 
 ## 4.16.0 (2023-08-10)
diff --git a/packages/reusable-blocks/package.json b/packages/reusable-blocks/package.json
index 3ef73b83a49f2..1bd14c5b30116 100644
--- a/packages/reusable-blocks/package.json
+++ b/packages/reusable-blocks/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/reusable-blocks",
-	"version": "4.17.0",
+	"version": "4.18.0-prerelease",
 	"description": "Reusable blocks utilities.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/rich-text/CHANGELOG.md b/packages/rich-text/CHANGELOG.md
index db497e6bc6f40..15294e759c6ee 100644
--- a/packages/rich-text/CHANGELOG.md
+++ b/packages/rich-text/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 6.18.0 (2023-08-31)
+
 ## 6.17.0 (2023-08-16)
 
 ## 6.16.0 (2023-08-10)
diff --git a/packages/rich-text/package.json b/packages/rich-text/package.json
index 6a92d30e008b3..23affc1b33919 100644
--- a/packages/rich-text/package.json
+++ b/packages/rich-text/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/rich-text",
-	"version": "6.17.0",
+	"version": "6.18.0-prerelease",
 	"description": "Rich text value and manipulation API.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/router/CHANGELOG.md b/packages/router/CHANGELOG.md
index 228169a90570d..435d0af046315 100644
--- a/packages/router/CHANGELOG.md
+++ b/packages/router/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 0.10.0 (2023-08-31)
+
 ## 0.9.0 (2023-08-16)
 
 ## 0.8.0 (2023-08-10)
diff --git a/packages/router/package.json b/packages/router/package.json
index 2906fef9cd8a9..9055184b97d32 100644
--- a/packages/router/package.json
+++ b/packages/router/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/router",
-	"version": "0.9.0",
+	"version": "0.10.0-prerelease",
 	"description": "Router API for WordPress pages.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/scripts/CHANGELOG.md b/packages/scripts/CHANGELOG.md
index 48bd891edc2dd..b7dce7e73cf58 100644
--- a/packages/scripts/CHANGELOG.md
+++ b/packages/scripts/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 26.12.0 (2023-08-31)
+
 ## 26.11.0 (2023-08-16)
 
 ### Enhancement
diff --git a/packages/scripts/package.json b/packages/scripts/package.json
index 3410c6e4d9e65..1a8688b28417e 100644
--- a/packages/scripts/package.json
+++ b/packages/scripts/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/scripts",
-	"version": "26.11.0",
+	"version": "26.12.0-prerelease",
 	"description": "Collection of reusable scripts for WordPress development.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/server-side-render/CHANGELOG.md b/packages/server-side-render/CHANGELOG.md
index c6d734e936760..bee32324689bb 100644
--- a/packages/server-side-render/CHANGELOG.md
+++ b/packages/server-side-render/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 4.18.0 (2023-08-31)
+
 ## 4.17.0 (2023-08-16)
 
 ## 4.16.0 (2023-08-10)
diff --git a/packages/server-side-render/package.json b/packages/server-side-render/package.json
index a2d1f596cbb78..9da663ea3c1ff 100644
--- a/packages/server-side-render/package.json
+++ b/packages/server-side-render/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/server-side-render",
-	"version": "4.17.0",
+	"version": "4.18.0-prerelease",
 	"description": "The component used with WordPress to server-side render a preview of dynamic blocks to display in the editor.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/shortcode/CHANGELOG.md b/packages/shortcode/CHANGELOG.md
index 75fda3827e6e0..44164e747e75b 100644
--- a/packages/shortcode/CHANGELOG.md
+++ b/packages/shortcode/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 3.41.0 (2023-08-31)
+
 ## 3.40.0 (2023-08-16)
 
 ## 3.39.0 (2023-08-10)
diff --git a/packages/shortcode/package.json b/packages/shortcode/package.json
index 940eba49cd28a..f2caf9c90f3f1 100644
--- a/packages/shortcode/package.json
+++ b/packages/shortcode/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/shortcode",
-	"version": "3.40.0",
+	"version": "3.41.0-prerelease",
 	"description": "Shortcode module for WordPress.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/style-engine/CHANGELOG.md b/packages/style-engine/CHANGELOG.md
index fbf1aa1eb6ba9..48030434c1949 100644
--- a/packages/style-engine/CHANGELOG.md
+++ b/packages/style-engine/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 1.24.0 (2023-08-31)
+
 ## 1.23.0 (2023-08-16)
 
 ## 1.22.0 (2023-08-10)
diff --git a/packages/style-engine/package.json b/packages/style-engine/package.json
index 30b1c2a1c8036..e0489f43380a0 100644
--- a/packages/style-engine/package.json
+++ b/packages/style-engine/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/style-engine",
-	"version": "1.23.0",
+	"version": "1.24.0-prerelease",
 	"description": "A suite of parsers and compilers for WordPress styles.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/stylelint-config/CHANGELOG.md b/packages/stylelint-config/CHANGELOG.md
index 697007028d26d..2c15a87836121 100644
--- a/packages/stylelint-config/CHANGELOG.md
+++ b/packages/stylelint-config/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 21.24.0 (2023-08-31)
+
 ## 21.23.0 (2023-08-16)
 
 ## 21.22.0 (2023-08-10)
diff --git a/packages/stylelint-config/package.json b/packages/stylelint-config/package.json
index 9255a5eae9f5e..586ecb152c40e 100644
--- a/packages/stylelint-config/package.json
+++ b/packages/stylelint-config/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/stylelint-config",
-	"version": "21.23.0",
+	"version": "21.24.0-prerelease",
 	"description": "stylelint config for WordPress development.",
 	"author": "The WordPress Contributors",
 	"license": "MIT",
diff --git a/packages/sync/CHANGELOG.md b/packages/sync/CHANGELOG.md
index 4cae82da1cac8..cca705e0ec86d 100644
--- a/packages/sync/CHANGELOG.md
+++ b/packages/sync/CHANGELOG.md
@@ -2,4 +2,6 @@
 
 ## Unreleased
 
+## 0.3.0 (2023-08-31)
+
 ## 0.2.0 (2023-08-16)
diff --git a/packages/sync/package.json b/packages/sync/package.json
index 04e7355f14ead..8469d6703fe1f 100644
--- a/packages/sync/package.json
+++ b/packages/sync/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/sync",
-	"version": "0.2.0",
+	"version": "0.3.0-prerelease",
 	"description": "Sync Data.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/token-list/CHANGELOG.md b/packages/token-list/CHANGELOG.md
index 741560797cac3..a3709066a1e24 100644
--- a/packages/token-list/CHANGELOG.md
+++ b/packages/token-list/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 2.41.0 (2023-08-31)
+
 ## 2.40.0 (2023-08-16)
 
 ## 2.39.0 (2023-08-10)
diff --git a/packages/token-list/package.json b/packages/token-list/package.json
index 978a381023314..36b2d9a76612a 100644
--- a/packages/token-list/package.json
+++ b/packages/token-list/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/token-list",
-	"version": "2.40.0",
+	"version": "2.41.0-prerelease",
 	"description": "Constructable, plain JavaScript DOMTokenList implementation, supporting non-browser runtimes.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/url/CHANGELOG.md b/packages/url/CHANGELOG.md
index e51bf2521f7d2..cfeede0f27853 100644
--- a/packages/url/CHANGELOG.md
+++ b/packages/url/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 3.42.0 (2023-08-31)
+
 ## 3.41.0 (2023-08-16)
 
 ## 3.40.0 (2023-08-10)
diff --git a/packages/url/package.json b/packages/url/package.json
index e0aee8699528d..8e56d357a34d6 100644
--- a/packages/url/package.json
+++ b/packages/url/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/url",
-	"version": "3.41.0",
+	"version": "3.42.0-prerelease",
 	"description": "WordPress URL utilities.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/viewport/CHANGELOG.md b/packages/viewport/CHANGELOG.md
index eb1fb65de3bb4..812b195c768f5 100644
--- a/packages/viewport/CHANGELOG.md
+++ b/packages/viewport/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 5.18.0 (2023-08-31)
+
 ## 5.17.0 (2023-08-16)
 
 ## 5.16.0 (2023-08-10)
diff --git a/packages/viewport/package.json b/packages/viewport/package.json
index 981e39c3527ff..ba1b78792b391 100644
--- a/packages/viewport/package.json
+++ b/packages/viewport/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/viewport",
-	"version": "5.17.0",
+	"version": "5.18.0-prerelease",
 	"description": "Viewport module for WordPress.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/warning/CHANGELOG.md b/packages/warning/CHANGELOG.md
index 93d9395d68c25..6a7154ccdbdc3 100644
--- a/packages/warning/CHANGELOG.md
+++ b/packages/warning/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 2.41.0 (2023-08-31)
+
 ## 2.40.0 (2023-08-16)
 
 ## 2.39.0 (2023-08-10)
diff --git a/packages/warning/package.json b/packages/warning/package.json
index 09a83537ec8c6..58c50aed2da90 100644
--- a/packages/warning/package.json
+++ b/packages/warning/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/warning",
-	"version": "2.40.0",
+	"version": "2.41.0-prerelease",
 	"description": "Warning utility for WordPress.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/widgets/CHANGELOG.md b/packages/widgets/CHANGELOG.md
index 856dc7555bad0..9307d83e42d22 100644
--- a/packages/widgets/CHANGELOG.md
+++ b/packages/widgets/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 3.18.0 (2023-08-31)
+
 ## 3.17.0 (2023-08-16)
 
 ## 3.16.0 (2023-08-10)
diff --git a/packages/widgets/package.json b/packages/widgets/package.json
index 4650479b71b47..f63cb4cd38dd3 100644
--- a/packages/widgets/package.json
+++ b/packages/widgets/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/widgets",
-	"version": "3.17.0",
+	"version": "3.18.0-prerelease",
 	"description": "Functionality used by the widgets block editor in the Widgets screen and the Customizer.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/wordcount/CHANGELOG.md b/packages/wordcount/CHANGELOG.md
index d0b54b70a81d4..24fec1ce33086 100644
--- a/packages/wordcount/CHANGELOG.md
+++ b/packages/wordcount/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 3.41.0 (2023-08-31)
+
 ## 3.40.0 (2023-08-16)
 
 ## 3.39.0 (2023-08-10)
diff --git a/packages/wordcount/package.json b/packages/wordcount/package.json
index f0f5cf017f7e1..2579bdf7f63ac 100644
--- a/packages/wordcount/package.json
+++ b/packages/wordcount/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/wordcount",
-	"version": "3.40.0",
+	"version": "3.41.0-prerelease",
 	"description": "WordPress word count utility.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",

From cc85f9b0055ab5d746d555d13de5d8ae627cad13 Mon Sep 17 00:00:00 2001
From: Gutenberg Repository Automation <gutenberg@wordpress.org>
Date: Thu, 31 Aug 2023 09:14:51 +0000
Subject: [PATCH 17/34] chore(release): publish

 - @wordpress/a11y@3.41.0
 - @wordpress/annotations@2.41.0
 - @wordpress/api-fetch@6.38.0
 - @wordpress/autop@3.41.0
 - @wordpress/babel-plugin-import-jsx-pragma@4.24.0
 - @wordpress/babel-plugin-makepot@5.25.0
 - @wordpress/babel-preset-default@7.25.0
 - @wordpress/base-styles@4.32.0
 - @wordpress/blob@3.41.0
 - @wordpress/block-directory@4.18.0
 - @wordpress/block-editor@12.9.0
 - @wordpress/block-library@8.18.0
 - @wordpress/block-serialization-default-parser@4.41.0
 - @wordpress/block-serialization-spec-parser@4.41.0
 - @wordpress/blocks@12.18.0
 - @wordpress/browserslist-config@5.24.0
 - @wordpress/commands@0.12.0
 - @wordpress/components@25.7.0
 - @wordpress/compose@6.18.0
 - @wordpress/core-commands@0.10.0
 - @wordpress/core-data@6.18.0
 - @wordpress/create-block@4.25.0
 - @wordpress/create-block-interactive-template@1.4.0
 - @wordpress/create-block-tutorial-template@2.29.0
 - @wordpress/customize-widgets@4.18.0
 - @wordpress/data@9.11.0
 - @wordpress/data-controls@3.10.0
 - @wordpress/date@4.41.0
 - @wordpress/dependency-extraction-webpack-plugin@4.24.0
 - @wordpress/deprecated@3.41.0
 - @wordpress/docgen@1.50.0
 - @wordpress/dom@3.41.0
 - @wordpress/dom-ready@3.41.0
 - @wordpress/e2e-test-utils@10.12.0
 - @wordpress/e2e-test-utils-playwright@0.9.0
 - @wordpress/e2e-tests@7.12.0
 - @wordpress/edit-post@7.18.0
 - @wordpress/edit-site@5.18.0
 - @wordpress/edit-widgets@5.18.0
 - @wordpress/editor@13.18.0
 - @wordpress/element@5.18.0
 - @wordpress/env@8.7.0
 - @wordpress/escape-html@2.41.0
 - @wordpress/eslint-plugin@15.1.0
 - @wordpress/format-library@4.18.0
 - @wordpress/hooks@3.41.0
 - @wordpress/html-entities@3.41.0
 - @wordpress/i18n@4.41.0
 - @wordpress/icons@9.32.0
 - @wordpress/interactivity@2.2.0
 - @wordpress/interface@5.18.0
 - @wordpress/is-shallow-equal@4.41.0
 - @wordpress/jest-console@7.12.0
 - @wordpress/jest-preset-default@11.12.0
 - @wordpress/jest-puppeteer-axe@6.12.0
 - @wordpress/keyboard-shortcuts@4.18.0
 - @wordpress/keycodes@3.41.0
 - @wordpress/lazy-import@1.28.0
 - @wordpress/list-reusable-blocks@4.18.0
 - @wordpress/media-utils@4.32.0
 - @wordpress/notices@4.9.0
 - @wordpress/npm-package-json-lint-config@4.26.0
 - @wordpress/nux@8.3.0
 - @wordpress/patterns@1.2.0
 - @wordpress/plugins@6.9.0
 - @wordpress/postcss-plugins-preset@4.25.0
 - @wordpress/postcss-themes@5.24.0
 - @wordpress/preferences@3.18.0
 - @wordpress/preferences-persistence@1.33.0
 - @wordpress/prettier-config@2.24.0
 - @wordpress/primitives@3.39.0
 - @wordpress/priority-queue@2.41.0
 - @wordpress/private-apis@0.23.0
 - @wordpress/project-management-automation@1.40.0
 - @wordpress/react-i18n@3.39.0
 - @wordpress/readable-js-assets-webpack-plugin@2.24.0
 - @wordpress/redux-routine@4.41.0
 - @wordpress/reusable-blocks@4.18.0
 - @wordpress/rich-text@6.18.0
 - @wordpress/router@0.10.0
 - @wordpress/scripts@26.12.0
 - @wordpress/server-side-render@4.18.0
 - @wordpress/shortcode@3.41.0
 - @wordpress/style-engine@1.24.0
 - @wordpress/stylelint-config@21.24.0
 - @wordpress/sync@0.3.0
 - @wordpress/token-list@2.41.0
 - @wordpress/url@3.42.0
 - @wordpress/viewport@5.18.0
 - @wordpress/warning@2.41.0
 - @wordpress/widgets@3.18.0
 - @wordpress/wordcount@3.41.0
---
 package-lock.json                             | 182 +++++++++---------
 packages/a11y/package.json                    |   2 +-
 packages/annotations/package.json             |   2 +-
 packages/api-fetch/package.json               |   2 +-
 packages/autop/package.json                   |   2 +-
 .../package.json                              |   2 +-
 packages/babel-plugin-makepot/package.json    |   2 +-
 packages/babel-preset-default/package.json    |   2 +-
 packages/base-styles/package.json             |   2 +-
 packages/blob/package.json                    |   2 +-
 packages/block-directory/package.json         |   2 +-
 packages/block-editor/package.json            |   2 +-
 packages/block-library/package.json           |   2 +-
 .../package.json                              |   2 +-
 .../package.json                              |   2 +-
 packages/blocks/package.json                  |   2 +-
 packages/browserslist-config/package.json     |   2 +-
 packages/commands/package.json                |   2 +-
 packages/components/package.json              |   2 +-
 packages/compose/package.json                 |   2 +-
 packages/core-commands/package.json           |   2 +-
 packages/core-data/package.json               |   2 +-
 .../package.json                              |   2 +-
 .../package.json                              |   2 +-
 packages/create-block/package.json            |   2 +-
 packages/customize-widgets/package.json       |   2 +-
 packages/data-controls/package.json           |   2 +-
 packages/data/package.json                    |   2 +-
 packages/date/package.json                    |   2 +-
 .../package.json                              |   2 +-
 packages/deprecated/package.json              |   2 +-
 packages/docgen/package.json                  |   2 +-
 packages/dom-ready/package.json               |   2 +-
 packages/dom/package.json                     |   2 +-
 .../e2e-test-utils-playwright/package.json    |   2 +-
 packages/e2e-test-utils/package.json          |   2 +-
 packages/e2e-tests/package.json               |   2 +-
 packages/edit-post/package.json               |   2 +-
 packages/edit-site/package.json               |   2 +-
 packages/edit-widgets/package.json            |   2 +-
 packages/editor/package.json                  |   2 +-
 packages/element/package.json                 |   2 +-
 packages/env/package.json                     |   2 +-
 packages/escape-html/package.json             |   2 +-
 packages/eslint-plugin/package.json           |   2 +-
 packages/format-library/package.json          |   2 +-
 packages/hooks/package.json                   |   2 +-
 packages/html-entities/package.json           |   2 +-
 packages/i18n/package.json                    |   2 +-
 packages/icons/package.json                   |   2 +-
 packages/interactivity/package.json           |   2 +-
 packages/interface/package.json               |   2 +-
 packages/is-shallow-equal/package.json        |   2 +-
 packages/jest-console/package.json            |   2 +-
 packages/jest-preset-default/package.json     |   2 +-
 packages/jest-puppeteer-axe/package.json      |   2 +-
 packages/keyboard-shortcuts/package.json      |   2 +-
 packages/keycodes/package.json                |   2 +-
 packages/lazy-import/package.json             |   2 +-
 packages/list-reusable-blocks/package.json    |   2 +-
 packages/media-utils/package.json             |   2 +-
 packages/notices/package.json                 |   2 +-
 .../npm-package-json-lint-config/package.json |   2 +-
 packages/nux/package.json                     |   2 +-
 packages/patterns/package.json                |   2 +-
 packages/plugins/package.json                 |   2 +-
 packages/postcss-plugins-preset/package.json  |   2 +-
 packages/postcss-themes/package.json          |   2 +-
 packages/preferences-persistence/package.json |   2 +-
 packages/preferences/package.json             |   2 +-
 packages/prettier-config/package.json         |   2 +-
 packages/primitives/package.json              |   2 +-
 packages/priority-queue/package.json          |   2 +-
 packages/private-apis/package.json            |   2 +-
 .../package.json                              |   2 +-
 packages/react-i18n/package.json              |   2 +-
 .../package.json                              |   2 +-
 packages/redux-routine/package.json           |   2 +-
 packages/reusable-blocks/package.json         |   2 +-
 packages/rich-text/package.json               |   2 +-
 packages/router/package.json                  |   2 +-
 packages/scripts/package.json                 |   2 +-
 packages/server-side-render/package.json      |   2 +-
 packages/shortcode/package.json               |   2 +-
 packages/style-engine/package.json            |   2 +-
 packages/stylelint-config/package.json        |   2 +-
 packages/sync/package.json                    |   2 +-
 packages/token-list/package.json              |   2 +-
 packages/url/package.json                     |   2 +-
 packages/viewport/package.json                |   2 +-
 packages/warning/package.json                 |   2 +-
 packages/widgets/package.json                 |   2 +-
 packages/wordcount/package.json               |   2 +-
 93 files changed, 183 insertions(+), 183 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index e70301a75ef61..cabb3c8c18155 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -54159,7 +54159,7 @@
 		},
 		"packages/a11y": {
 			"name": "@wordpress/a11y",
-			"version": "3.40.0",
+			"version": "3.41.0",
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
 				"@babel/runtime": "^7.16.0",
@@ -54172,7 +54172,7 @@
 		},
 		"packages/annotations": {
 			"name": "@wordpress/annotations",
-			"version": "2.40.0",
+			"version": "2.41.0",
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
 				"@babel/runtime": "^7.16.0",
@@ -54192,7 +54192,7 @@
 		},
 		"packages/api-fetch": {
 			"name": "@wordpress/api-fetch",
-			"version": "6.37.0",
+			"version": "6.38.0",
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
 				"@babel/runtime": "^7.16.0",
@@ -54205,7 +54205,7 @@
 		},
 		"packages/autop": {
 			"name": "@wordpress/autop",
-			"version": "3.40.0",
+			"version": "3.41.0",
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
 				"@babel/runtime": "^7.16.0"
@@ -54216,7 +54216,7 @@
 		},
 		"packages/babel-plugin-import-jsx-pragma": {
 			"name": "@wordpress/babel-plugin-import-jsx-pragma",
-			"version": "4.23.0",
+			"version": "4.24.0",
 			"dev": true,
 			"license": "GPL-2.0-or-later",
 			"engines": {
@@ -54228,7 +54228,7 @@
 		},
 		"packages/babel-plugin-makepot": {
 			"name": "@wordpress/babel-plugin-makepot",
-			"version": "5.24.0",
+			"version": "5.25.0",
 			"dev": true,
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
@@ -54245,7 +54245,7 @@
 		},
 		"packages/babel-preset-default": {
 			"name": "@wordpress/babel-preset-default",
-			"version": "7.24.0",
+			"version": "7.25.0",
 			"dev": true,
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
@@ -54268,13 +54268,13 @@
 		},
 		"packages/base-styles": {
 			"name": "@wordpress/base-styles",
-			"version": "4.31.0",
+			"version": "4.32.0",
 			"dev": true,
 			"license": "GPL-2.0-or-later"
 		},
 		"packages/blob": {
 			"name": "@wordpress/blob",
-			"version": "3.40.0",
+			"version": "3.41.0",
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
 				"@babel/runtime": "^7.16.0"
@@ -54285,7 +54285,7 @@
 		},
 		"packages/block-directory": {
 			"name": "@wordpress/block-directory",
-			"version": "4.17.0",
+			"version": "4.18.0",
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
 				"@babel/runtime": "^7.16.0",
@@ -54319,7 +54319,7 @@
 		},
 		"packages/block-editor": {
 			"name": "@wordpress/block-editor",
-			"version": "12.8.0",
+			"version": "12.9.0",
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
 				"@babel/runtime": "^7.16.0",
@@ -54380,7 +54380,7 @@
 		},
 		"packages/block-library": {
 			"name": "@wordpress/block-library",
-			"version": "8.17.0",
+			"version": "8.18.0",
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
 				"@babel/runtime": "^7.16.0",
@@ -54435,7 +54435,7 @@
 		},
 		"packages/block-serialization-default-parser": {
 			"name": "@wordpress/block-serialization-default-parser",
-			"version": "4.40.0",
+			"version": "4.41.0",
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
 				"@babel/runtime": "^7.16.0"
@@ -54446,7 +54446,7 @@
 		},
 		"packages/block-serialization-spec-parser": {
 			"name": "@wordpress/block-serialization-spec-parser",
-			"version": "4.40.0",
+			"version": "4.41.0",
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
 				"pegjs": "^0.10.0",
@@ -54458,7 +54458,7 @@
 		},
 		"packages/blocks": {
 			"name": "@wordpress/blocks",
-			"version": "12.17.0",
+			"version": "12.18.0",
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
 				"@babel/runtime": "^7.16.0",
@@ -54498,7 +54498,7 @@
 		},
 		"packages/browserslist-config": {
 			"name": "@wordpress/browserslist-config",
-			"version": "5.23.0",
+			"version": "5.24.0",
 			"dev": true,
 			"license": "GPL-2.0-or-later",
 			"engines": {
@@ -54507,7 +54507,7 @@
 		},
 		"packages/commands": {
 			"name": "@wordpress/commands",
-			"version": "0.11.0",
+			"version": "0.12.0",
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
 				"@babel/runtime": "^7.16.0",
@@ -54532,7 +54532,7 @@
 		},
 		"packages/components": {
 			"name": "@wordpress/components",
-			"version": "25.6.0",
+			"version": "25.7.0",
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
 				"@ariakit/react": "^0.2.12",
@@ -54623,7 +54623,7 @@
 		},
 		"packages/compose": {
 			"name": "@wordpress/compose",
-			"version": "6.17.0",
+			"version": "6.18.0",
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
 				"@babel/runtime": "^7.16.0",
@@ -54648,7 +54648,7 @@
 		},
 		"packages/core-commands": {
 			"name": "@wordpress/core-commands",
-			"version": "0.9.0",
+			"version": "0.10.0",
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
 				"@babel/runtime": "^7.16.0",
@@ -54673,7 +54673,7 @@
 		},
 		"packages/core-data": {
 			"name": "@wordpress/core-data",
-			"version": "6.17.0",
+			"version": "6.18.0",
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
 				"@babel/runtime": "^7.16.0",
@@ -54707,7 +54707,7 @@
 		},
 		"packages/create-block": {
 			"name": "@wordpress/create-block",
-			"version": "4.24.0",
+			"version": "4.25.0",
 			"dev": true,
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
@@ -54735,13 +54735,13 @@
 		},
 		"packages/create-block-tutorial-template": {
 			"name": "@wordpress/create-block-tutorial-template",
-			"version": "2.28.0",
+			"version": "2.29.0",
 			"dev": true,
 			"license": "GPL-2.0-or-later"
 		},
 		"packages/customize-widgets": {
 			"name": "@wordpress/customize-widgets",
-			"version": "4.17.0",
+			"version": "4.18.0",
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
 				"@babel/runtime": "^7.16.0",
@@ -54778,7 +54778,7 @@
 		},
 		"packages/data": {
 			"name": "@wordpress/data",
-			"version": "9.10.0",
+			"version": "9.11.0",
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
 				"@babel/runtime": "^7.16.0",
@@ -54806,7 +54806,7 @@
 		},
 		"packages/data-controls": {
 			"name": "@wordpress/data-controls",
-			"version": "3.9.0",
+			"version": "3.10.0",
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
 				"@babel/runtime": "^7.16.0",
@@ -54823,7 +54823,7 @@
 		},
 		"packages/date": {
 			"name": "@wordpress/date",
-			"version": "4.40.0",
+			"version": "4.41.0",
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
 				"@babel/runtime": "^7.16.0",
@@ -54837,7 +54837,7 @@
 		},
 		"packages/dependency-extraction-webpack-plugin": {
 			"name": "@wordpress/dependency-extraction-webpack-plugin",
-			"version": "4.23.0",
+			"version": "4.24.0",
 			"dev": true,
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
@@ -54853,7 +54853,7 @@
 		},
 		"packages/deprecated": {
 			"name": "@wordpress/deprecated",
-			"version": "3.40.0",
+			"version": "3.41.0",
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
 				"@babel/runtime": "^7.16.0",
@@ -54865,7 +54865,7 @@
 		},
 		"packages/docgen": {
 			"name": "@wordpress/docgen",
-			"version": "1.49.0",
+			"version": "1.50.0",
 			"dev": true,
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
@@ -54883,7 +54883,7 @@
 		},
 		"packages/dom": {
 			"name": "@wordpress/dom",
-			"version": "3.40.0",
+			"version": "3.41.0",
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
 				"@babel/runtime": "^7.16.0",
@@ -54895,7 +54895,7 @@
 		},
 		"packages/dom-ready": {
 			"name": "@wordpress/dom-ready",
-			"version": "3.40.0",
+			"version": "3.41.0",
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
 				"@babel/runtime": "^7.16.0"
@@ -54906,7 +54906,7 @@
 		},
 		"packages/e2e-test-utils": {
 			"name": "@wordpress/e2e-test-utils",
-			"version": "10.11.0",
+			"version": "10.12.0",
 			"dev": true,
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
@@ -54928,7 +54928,7 @@
 		},
 		"packages/e2e-test-utils-playwright": {
 			"name": "@wordpress/e2e-test-utils-playwright",
-			"version": "0.8.0",
+			"version": "0.9.0",
 			"dev": true,
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
@@ -54948,7 +54948,7 @@
 		},
 		"packages/e2e-tests": {
 			"name": "@wordpress/e2e-tests",
-			"version": "7.11.0",
+			"version": "7.12.0",
 			"dev": true,
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
@@ -54977,7 +54977,7 @@
 		},
 		"packages/edit-post": {
 			"name": "@wordpress/edit-post",
-			"version": "7.17.0",
+			"version": "7.18.0",
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
 				"@babel/runtime": "^7.16.0",
@@ -55025,7 +55025,7 @@
 		},
 		"packages/edit-site": {
 			"name": "@wordpress/edit-site",
-			"version": "5.17.0",
+			"version": "5.18.0",
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
 				"@babel/runtime": "^7.16.0",
@@ -55089,7 +55089,7 @@
 		},
 		"packages/edit-widgets": {
 			"name": "@wordpress/edit-widgets",
-			"version": "5.17.0",
+			"version": "5.18.0",
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
 				"@babel/runtime": "^7.16.0",
@@ -55131,7 +55131,7 @@
 		},
 		"packages/editor": {
 			"name": "@wordpress/editor",
-			"version": "13.17.0",
+			"version": "13.18.0",
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
 				"@babel/runtime": "^7.16.0",
@@ -55181,7 +55181,7 @@
 		},
 		"packages/element": {
 			"name": "@wordpress/element",
-			"version": "5.17.0",
+			"version": "5.18.0",
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
 				"@babel/runtime": "^7.16.0",
@@ -55199,7 +55199,7 @@
 		},
 		"packages/env": {
 			"name": "@wordpress/env",
-			"version": "8.6.0",
+			"version": "8.7.0",
 			"dev": true,
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
@@ -55312,7 +55312,7 @@
 		},
 		"packages/escape-html": {
 			"name": "@wordpress/escape-html",
-			"version": "2.40.0",
+			"version": "2.41.0",
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
 				"@babel/runtime": "^7.16.0"
@@ -55323,7 +55323,7 @@
 		},
 		"packages/eslint-plugin": {
 			"name": "@wordpress/eslint-plugin",
-			"version": "15.0.0",
+			"version": "15.1.0",
 			"dev": true,
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
@@ -55365,7 +55365,7 @@
 		},
 		"packages/format-library": {
 			"name": "@wordpress/format-library",
-			"version": "4.17.0",
+			"version": "4.18.0",
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
 				"@babel/runtime": "^7.16.0",
@@ -55391,7 +55391,7 @@
 		},
 		"packages/hooks": {
 			"name": "@wordpress/hooks",
-			"version": "3.40.0",
+			"version": "3.41.0",
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
 				"@babel/runtime": "^7.16.0"
@@ -55402,7 +55402,7 @@
 		},
 		"packages/html-entities": {
 			"name": "@wordpress/html-entities",
-			"version": "3.40.0",
+			"version": "3.41.0",
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
 				"@babel/runtime": "^7.16.0"
@@ -55413,7 +55413,7 @@
 		},
 		"packages/i18n": {
 			"name": "@wordpress/i18n",
-			"version": "4.40.0",
+			"version": "4.41.0",
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
 				"@babel/runtime": "^7.16.0",
@@ -55432,7 +55432,7 @@
 		},
 		"packages/icons": {
 			"name": "@wordpress/icons",
-			"version": "9.31.0",
+			"version": "9.32.0",
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
 				"@babel/runtime": "^7.16.0",
@@ -55445,7 +55445,7 @@
 		},
 		"packages/interactivity": {
 			"name": "@wordpress/interactivity",
-			"version": "2.1.0",
+			"version": "2.2.0",
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
 				"@preact/signals": "^1.1.3",
@@ -55458,7 +55458,7 @@
 		},
 		"packages/interface": {
 			"name": "@wordpress/interface",
-			"version": "5.17.0",
+			"version": "5.18.0",
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
 				"@babel/runtime": "^7.16.0",
@@ -55485,7 +55485,7 @@
 		},
 		"packages/is-shallow-equal": {
 			"name": "@wordpress/is-shallow-equal",
-			"version": "4.40.0",
+			"version": "4.41.0",
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
 				"@babel/runtime": "^7.16.0"
@@ -55496,7 +55496,7 @@
 		},
 		"packages/jest-console": {
 			"name": "@wordpress/jest-console",
-			"version": "7.11.0",
+			"version": "7.12.0",
 			"dev": true,
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
@@ -55512,7 +55512,7 @@
 		},
 		"packages/jest-preset-default": {
 			"name": "@wordpress/jest-preset-default",
-			"version": "11.11.0",
+			"version": "11.12.0",
 			"dev": true,
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
@@ -55529,7 +55529,7 @@
 		},
 		"packages/jest-puppeteer-axe": {
 			"name": "@wordpress/jest-puppeteer-axe",
-			"version": "6.11.0",
+			"version": "6.12.0",
 			"dev": true,
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
@@ -55551,7 +55551,7 @@
 		},
 		"packages/keyboard-shortcuts": {
 			"name": "@wordpress/keyboard-shortcuts",
-			"version": "4.17.0",
+			"version": "4.18.0",
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
 				"@babel/runtime": "^7.16.0",
@@ -55569,7 +55569,7 @@
 		},
 		"packages/keycodes": {
 			"name": "@wordpress/keycodes",
-			"version": "3.40.0",
+			"version": "3.41.0",
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
 				"@babel/runtime": "^7.16.0",
@@ -55582,7 +55582,7 @@
 		},
 		"packages/lazy-import": {
 			"name": "@wordpress/lazy-import",
-			"version": "1.27.0",
+			"version": "1.28.0",
 			"dev": true,
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
@@ -55596,7 +55596,7 @@
 		},
 		"packages/list-reusable-blocks": {
 			"name": "@wordpress/list-reusable-blocks",
-			"version": "4.17.0",
+			"version": "4.18.0",
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
 				"@babel/runtime": "^7.16.0",
@@ -55617,7 +55617,7 @@
 		},
 		"packages/media-utils": {
 			"name": "@wordpress/media-utils",
-			"version": "4.31.0",
+			"version": "4.32.0",
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
 				"@babel/runtime": "^7.16.0",
@@ -55632,7 +55632,7 @@
 		},
 		"packages/notices": {
 			"name": "@wordpress/notices",
-			"version": "4.8.0",
+			"version": "4.9.0",
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
 				"@babel/runtime": "^7.16.0",
@@ -55648,7 +55648,7 @@
 		},
 		"packages/npm-package-json-lint-config": {
 			"name": "@wordpress/npm-package-json-lint-config",
-			"version": "4.25.0",
+			"version": "4.26.0",
 			"dev": true,
 			"license": "GPL-2.0-or-later",
 			"engines": {
@@ -55660,7 +55660,7 @@
 		},
 		"packages/nux": {
 			"name": "@wordpress/nux",
-			"version": "8.2.0",
+			"version": "8.3.0",
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
 				"@babel/runtime": "^7.16.0",
@@ -55683,7 +55683,7 @@
 		},
 		"packages/patterns": {
 			"name": "@wordpress/patterns",
-			"version": "1.1.0",
+			"version": "1.2.0",
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
 				"@wordpress/block-editor": "file:../block-editor",
@@ -55710,7 +55710,7 @@
 		},
 		"packages/plugins": {
 			"name": "@wordpress/plugins",
-			"version": "6.8.0",
+			"version": "6.9.0",
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
 				"@babel/runtime": "^7.16.0",
@@ -55732,7 +55732,7 @@
 		},
 		"packages/postcss-plugins-preset": {
 			"name": "@wordpress/postcss-plugins-preset",
-			"version": "4.24.0",
+			"version": "4.25.0",
 			"dev": true,
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
@@ -55748,7 +55748,7 @@
 		},
 		"packages/postcss-themes": {
 			"name": "@wordpress/postcss-themes",
-			"version": "5.23.0",
+			"version": "5.24.0",
 			"dev": true,
 			"license": "GPL-2.0-or-later",
 			"engines": {
@@ -55760,7 +55760,7 @@
 		},
 		"packages/preferences": {
 			"name": "@wordpress/preferences",
-			"version": "3.17.0",
+			"version": "3.18.0",
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
 				"@babel/runtime": "^7.16.0",
@@ -55782,7 +55782,7 @@
 		},
 		"packages/preferences-persistence": {
 			"name": "@wordpress/preferences-persistence",
-			"version": "1.32.0",
+			"version": "1.33.0",
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
 				"@babel/runtime": "^7.16.0",
@@ -55794,7 +55794,7 @@
 		},
 		"packages/prettier-config": {
 			"name": "@wordpress/prettier-config",
-			"version": "2.23.0",
+			"version": "2.24.0",
 			"dev": true,
 			"license": "GPL-2.0-or-later",
 			"engines": {
@@ -55806,7 +55806,7 @@
 		},
 		"packages/primitives": {
 			"name": "@wordpress/primitives",
-			"version": "3.38.0",
+			"version": "3.39.0",
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
 				"@babel/runtime": "^7.16.0",
@@ -55819,7 +55819,7 @@
 		},
 		"packages/priority-queue": {
 			"name": "@wordpress/priority-queue",
-			"version": "2.40.0",
+			"version": "2.41.0",
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
 				"@babel/runtime": "^7.16.0",
@@ -55831,7 +55831,7 @@
 		},
 		"packages/private-apis": {
 			"name": "@wordpress/private-apis",
-			"version": "0.22.0",
+			"version": "0.23.0",
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
 				"@babel/runtime": "^7.16.0"
@@ -55842,7 +55842,7 @@
 		},
 		"packages/project-management-automation": {
 			"name": "@wordpress/project-management-automation",
-			"version": "1.39.0",
+			"version": "1.40.0",
 			"dev": true,
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
@@ -55855,7 +55855,7 @@
 		},
 		"packages/react-i18n": {
 			"name": "@wordpress/react-i18n",
-			"version": "3.38.0",
+			"version": "3.39.0",
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
 				"@babel/runtime": "^7.16.0",
@@ -55949,7 +55949,7 @@
 		},
 		"packages/readable-js-assets-webpack-plugin": {
 			"name": "@wordpress/readable-js-assets-webpack-plugin",
-			"version": "2.23.0",
+			"version": "2.24.0",
 			"dev": true,
 			"license": "GPL-2.0-or-later",
 			"engines": {
@@ -55961,7 +55961,7 @@
 		},
 		"packages/redux-routine": {
 			"name": "@wordpress/redux-routine",
-			"version": "4.40.0",
+			"version": "4.41.0",
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
 				"@babel/runtime": "^7.16.0",
@@ -56005,7 +56005,7 @@
 		},
 		"packages/reusable-blocks": {
 			"name": "@wordpress/reusable-blocks",
-			"version": "4.17.0",
+			"version": "4.18.0",
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
 				"@babel/runtime": "^7.16.0",
@@ -56031,7 +56031,7 @@
 		},
 		"packages/rich-text": {
 			"name": "@wordpress/rich-text",
-			"version": "6.17.0",
+			"version": "6.18.0",
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
 				"@babel/runtime": "^7.16.0",
@@ -56055,7 +56055,7 @@
 		},
 		"packages/router": {
 			"name": "@wordpress/router",
-			"version": "0.9.0",
+			"version": "0.10.0",
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
 				"@babel/runtime": "^7.16.0",
@@ -56073,7 +56073,7 @@
 		},
 		"packages/scripts": {
 			"name": "@wordpress/scripts",
-			"version": "26.11.0",
+			"version": "26.12.0",
 			"dev": true,
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
@@ -56148,7 +56148,7 @@
 		},
 		"packages/server-side-render": {
 			"name": "@wordpress/server-side-render",
-			"version": "4.17.0",
+			"version": "4.18.0",
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
 				"@babel/runtime": "^7.16.0",
@@ -56173,7 +56173,7 @@
 		},
 		"packages/shortcode": {
 			"name": "@wordpress/shortcode",
-			"version": "3.40.0",
+			"version": "3.41.0",
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
 				"@babel/runtime": "^7.16.0",
@@ -56185,7 +56185,7 @@
 		},
 		"packages/style-engine": {
 			"name": "@wordpress/style-engine",
-			"version": "1.23.0",
+			"version": "1.24.0",
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
 				"@babel/runtime": "^7.16.0",
@@ -56197,7 +56197,7 @@
 		},
 		"packages/stylelint-config": {
 			"name": "@wordpress/stylelint-config",
-			"version": "21.23.0",
+			"version": "21.24.0",
 			"dev": true,
 			"license": "MIT",
 			"dependencies": {
@@ -56213,7 +56213,7 @@
 		},
 		"packages/sync": {
 			"name": "@wordpress/sync",
-			"version": "0.2.0",
+			"version": "0.3.0",
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
 				"@babel/runtime": "^7.16.0",
@@ -56227,7 +56227,7 @@
 		},
 		"packages/token-list": {
 			"name": "@wordpress/token-list",
-			"version": "2.40.0",
+			"version": "2.41.0",
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
 				"@babel/runtime": "^7.16.0"
@@ -56238,7 +56238,7 @@
 		},
 		"packages/url": {
 			"name": "@wordpress/url",
-			"version": "3.41.0",
+			"version": "3.42.0",
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
 				"@babel/runtime": "^7.16.0",
@@ -56250,7 +56250,7 @@
 		},
 		"packages/viewport": {
 			"name": "@wordpress/viewport",
-			"version": "5.17.0",
+			"version": "5.18.0",
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
 				"@babel/runtime": "^7.16.0",
@@ -56267,7 +56267,7 @@
 		},
 		"packages/warning": {
 			"name": "@wordpress/warning",
-			"version": "2.40.0",
+			"version": "2.41.0",
 			"license": "GPL-2.0-or-later",
 			"engines": {
 				"node": ">=12"
@@ -56275,7 +56275,7 @@
 		},
 		"packages/widgets": {
 			"name": "@wordpress/widgets",
-			"version": "3.17.0",
+			"version": "3.18.0",
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
 				"@babel/runtime": "^7.16.0",
@@ -56299,7 +56299,7 @@
 		},
 		"packages/wordcount": {
 			"name": "@wordpress/wordcount",
-			"version": "3.40.0",
+			"version": "3.41.0",
 			"license": "GPL-2.0-or-later",
 			"dependencies": {
 				"@babel/runtime": "^7.16.0"
diff --git a/packages/a11y/package.json b/packages/a11y/package.json
index 837633b0a97ea..6f2e9ca3d8e75 100644
--- a/packages/a11y/package.json
+++ b/packages/a11y/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/a11y",
-	"version": "3.41.0-prerelease",
+	"version": "3.41.0",
 	"description": "Accessibility (a11y) utilities for WordPress.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/annotations/package.json b/packages/annotations/package.json
index b3e444e85499f..faf9e4acf78e0 100644
--- a/packages/annotations/package.json
+++ b/packages/annotations/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/annotations",
-	"version": "2.41.0-prerelease",
+	"version": "2.41.0",
 	"description": "Annotate content in the Gutenberg editor.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/api-fetch/package.json b/packages/api-fetch/package.json
index 7d9425fe0a968..12396223a7ec3 100644
--- a/packages/api-fetch/package.json
+++ b/packages/api-fetch/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/api-fetch",
-	"version": "6.38.0-prerelease",
+	"version": "6.38.0",
 	"description": "Utility to make WordPress REST API requests.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/autop/package.json b/packages/autop/package.json
index c9534a7ea36fc..501b163c4341d 100644
--- a/packages/autop/package.json
+++ b/packages/autop/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/autop",
-	"version": "3.41.0-prerelease",
+	"version": "3.41.0",
 	"description": "WordPress's automatic paragraph functions `autop` and `removep`.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/babel-plugin-import-jsx-pragma/package.json b/packages/babel-plugin-import-jsx-pragma/package.json
index dfb6a5ce61558..4a7e892fe52f0 100644
--- a/packages/babel-plugin-import-jsx-pragma/package.json
+++ b/packages/babel-plugin-import-jsx-pragma/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/babel-plugin-import-jsx-pragma",
-	"version": "4.24.0-prerelease",
+	"version": "4.24.0",
 	"description": "Babel transform plugin for automatically injecting an import to be used as the pragma for the React JSX Transform plugin.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/babel-plugin-makepot/package.json b/packages/babel-plugin-makepot/package.json
index 5315010b8c442..aae54179b186e 100644
--- a/packages/babel-plugin-makepot/package.json
+++ b/packages/babel-plugin-makepot/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/babel-plugin-makepot",
-	"version": "5.25.0-prerelease",
+	"version": "5.25.0",
 	"description": "WordPress Babel internationalization (i18n) plugin.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/babel-preset-default/package.json b/packages/babel-preset-default/package.json
index 949d0495a0fbc..748c219e2a7e1 100644
--- a/packages/babel-preset-default/package.json
+++ b/packages/babel-preset-default/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/babel-preset-default",
-	"version": "7.25.0-prerelease",
+	"version": "7.25.0",
 	"description": "Default Babel preset for WordPress development.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/base-styles/package.json b/packages/base-styles/package.json
index 6bfa5e3305635..1ff2b3a34e825 100644
--- a/packages/base-styles/package.json
+++ b/packages/base-styles/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/base-styles",
-	"version": "4.32.0-prerelease",
+	"version": "4.32.0",
 	"description": "Base SCSS utilities and variables for WordPress.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/blob/package.json b/packages/blob/package.json
index 705fe60013633..7a317ee29affa 100644
--- a/packages/blob/package.json
+++ b/packages/blob/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/blob",
-	"version": "3.41.0-prerelease",
+	"version": "3.41.0",
 	"description": "Blob utilities for WordPress.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/block-directory/package.json b/packages/block-directory/package.json
index fc14b4ce524cf..d8bf8bc0f3acb 100644
--- a/packages/block-directory/package.json
+++ b/packages/block-directory/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/block-directory",
-	"version": "4.18.0-prerelease",
+	"version": "4.18.0",
 	"description": "Extend editor with block directory features to search, download and install blocks.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/block-editor/package.json b/packages/block-editor/package.json
index 4b70c30e946eb..50b58568b861d 100644
--- a/packages/block-editor/package.json
+++ b/packages/block-editor/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/block-editor",
-	"version": "12.9.0-prerelease",
+	"version": "12.9.0",
 	"description": "Generic block editor.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/block-library/package.json b/packages/block-library/package.json
index c9031f8f2d6cf..443c48f8bf4ef 100644
--- a/packages/block-library/package.json
+++ b/packages/block-library/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/block-library",
-	"version": "8.18.0-prerelease",
+	"version": "8.18.0",
 	"description": "Block library for the WordPress editor.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/block-serialization-default-parser/package.json b/packages/block-serialization-default-parser/package.json
index 4727c4249d140..61741c4ca0412 100644
--- a/packages/block-serialization-default-parser/package.json
+++ b/packages/block-serialization-default-parser/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/block-serialization-default-parser",
-	"version": "4.41.0-prerelease",
+	"version": "4.41.0",
 	"description": "Block serialization specification parser for WordPress posts.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/block-serialization-spec-parser/package.json b/packages/block-serialization-spec-parser/package.json
index ea3adfe911d52..ccdeee4fff5c3 100644
--- a/packages/block-serialization-spec-parser/package.json
+++ b/packages/block-serialization-spec-parser/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/block-serialization-spec-parser",
-	"version": "4.41.0-prerelease",
+	"version": "4.41.0",
 	"description": "Block serialization specification parser for WordPress posts.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/blocks/package.json b/packages/blocks/package.json
index 470b71c92de9f..71b560154223e 100644
--- a/packages/blocks/package.json
+++ b/packages/blocks/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/blocks",
-	"version": "12.18.0-prerelease",
+	"version": "12.18.0",
 	"description": "Block API for WordPress.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/browserslist-config/package.json b/packages/browserslist-config/package.json
index 0283bd39a1e60..4490a5a949526 100644
--- a/packages/browserslist-config/package.json
+++ b/packages/browserslist-config/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/browserslist-config",
-	"version": "5.24.0-prerelease",
+	"version": "5.24.0",
 	"description": "WordPress Browserslist shared configuration.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/commands/package.json b/packages/commands/package.json
index 52812c3297e9f..5c33f43deb2e9 100644
--- a/packages/commands/package.json
+++ b/packages/commands/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/commands",
-	"version": "0.12.0-prerelease",
+	"version": "0.12.0",
 	"description": "Handles the commands menu.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/components/package.json b/packages/components/package.json
index a06d94e70e886..cc4db850438b8 100644
--- a/packages/components/package.json
+++ b/packages/components/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/components",
-	"version": "25.7.0-prerelease",
+	"version": "25.7.0",
 	"description": "UI components for WordPress.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/compose/package.json b/packages/compose/package.json
index e862e30ea1c17..e8d98651f768b 100644
--- a/packages/compose/package.json
+++ b/packages/compose/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/compose",
-	"version": "6.18.0-prerelease",
+	"version": "6.18.0",
 	"description": "WordPress higher-order components (HOCs).",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/core-commands/package.json b/packages/core-commands/package.json
index 8d408785583d8..58698d98ce87a 100644
--- a/packages/core-commands/package.json
+++ b/packages/core-commands/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/core-commands",
-	"version": "0.10.0-prerelease",
+	"version": "0.10.0",
 	"description": "WordPress core reusable commands.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/core-data/package.json b/packages/core-data/package.json
index d7501a4c25e23..80bc41ff0a5af 100644
--- a/packages/core-data/package.json
+++ b/packages/core-data/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/core-data",
-	"version": "6.18.0-prerelease",
+	"version": "6.18.0",
 	"description": "Access to and manipulation of core WordPress entities.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/create-block-interactive-template/package.json b/packages/create-block-interactive-template/package.json
index 92ea21667d39b..f91db15ecbf95 100644
--- a/packages/create-block-interactive-template/package.json
+++ b/packages/create-block-interactive-template/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/create-block-interactive-template",
-	"version": "1.4.0-prerelease",
+	"version": "1.4.0",
 	"description": "Template for @wordpress/create-block to create interactive blocks with the Interactivity API.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/create-block-tutorial-template/package.json b/packages/create-block-tutorial-template/package.json
index 1c26630d1555d..a92a21fbc66cc 100644
--- a/packages/create-block-tutorial-template/package.json
+++ b/packages/create-block-tutorial-template/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/create-block-tutorial-template",
-	"version": "2.29.0-prerelease",
+	"version": "2.29.0",
 	"description": "Template for @wordpress/create-block used in the official WordPress tutorial.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/create-block/package.json b/packages/create-block/package.json
index c005d4c27440c..0c5cf181700e0 100644
--- a/packages/create-block/package.json
+++ b/packages/create-block/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/create-block",
-	"version": "4.25.0-prerelease",
+	"version": "4.25.0",
 	"description": "Generates PHP, JS and CSS code for registering a block for a WordPress plugin.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/customize-widgets/package.json b/packages/customize-widgets/package.json
index 42cd5d209b783..ae71dcb1d8069 100644
--- a/packages/customize-widgets/package.json
+++ b/packages/customize-widgets/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/customize-widgets",
-	"version": "4.18.0-prerelease",
+	"version": "4.18.0",
 	"description": "Widgets blocks in Customizer Module for WordPress.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/data-controls/package.json b/packages/data-controls/package.json
index 4911949ac90d7..591c5113c56b1 100644
--- a/packages/data-controls/package.json
+++ b/packages/data-controls/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/data-controls",
-	"version": "3.10.0-prerelease",
+	"version": "3.10.0",
 	"description": "A set of common controls for the @wordpress/data api.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/data/package.json b/packages/data/package.json
index 5917feb3143cf..b5c7737c2ba76 100644
--- a/packages/data/package.json
+++ b/packages/data/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/data",
-	"version": "9.11.0-prerelease",
+	"version": "9.11.0",
 	"description": "Data module for WordPress.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/date/package.json b/packages/date/package.json
index a3e1e721857d4..d3f812ea68d36 100644
--- a/packages/date/package.json
+++ b/packages/date/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/date",
-	"version": "4.41.0-prerelease",
+	"version": "4.41.0",
 	"description": "Date module for WordPress.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/dependency-extraction-webpack-plugin/package.json b/packages/dependency-extraction-webpack-plugin/package.json
index 51e911fc11090..abf0c8182b9e8 100644
--- a/packages/dependency-extraction-webpack-plugin/package.json
+++ b/packages/dependency-extraction-webpack-plugin/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/dependency-extraction-webpack-plugin",
-	"version": "4.24.0-prerelease",
+	"version": "4.24.0",
 	"description": "Extract WordPress script dependencies from webpack bundles.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/deprecated/package.json b/packages/deprecated/package.json
index d14453ac5b427..89568c0a58dfb 100644
--- a/packages/deprecated/package.json
+++ b/packages/deprecated/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/deprecated",
-	"version": "3.41.0-prerelease",
+	"version": "3.41.0",
 	"description": "Deprecation utility for WordPress.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/docgen/package.json b/packages/docgen/package.json
index 4bfba69df01d5..786e4e30102ad 100644
--- a/packages/docgen/package.json
+++ b/packages/docgen/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/docgen",
-	"version": "1.50.0-prerelease",
+	"version": "1.50.0",
 	"description": "Autogenerate public API documentation from exports and JSDoc comments.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/dom-ready/package.json b/packages/dom-ready/package.json
index 6220897a8cf5c..d8ebbd484eca3 100644
--- a/packages/dom-ready/package.json
+++ b/packages/dom-ready/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/dom-ready",
-	"version": "3.41.0-prerelease",
+	"version": "3.41.0",
 	"description": "Execute callback after the DOM is loaded.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/dom/package.json b/packages/dom/package.json
index 428004ba39615..c50aae35a53ff 100644
--- a/packages/dom/package.json
+++ b/packages/dom/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/dom",
-	"version": "3.41.0-prerelease",
+	"version": "3.41.0",
 	"description": "DOM utilities module for WordPress.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/e2e-test-utils-playwright/package.json b/packages/e2e-test-utils-playwright/package.json
index 82579c877a9d0..cb2789369d979 100644
--- a/packages/e2e-test-utils-playwright/package.json
+++ b/packages/e2e-test-utils-playwright/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/e2e-test-utils-playwright",
-	"version": "0.9.0-prerelease",
+	"version": "0.9.0",
 	"description": "End-To-End (E2E) test utils for WordPress.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/e2e-test-utils/package.json b/packages/e2e-test-utils/package.json
index e8058cb780e2c..bdb31b9ef62b0 100644
--- a/packages/e2e-test-utils/package.json
+++ b/packages/e2e-test-utils/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/e2e-test-utils",
-	"version": "10.12.0-prerelease",
+	"version": "10.12.0",
 	"description": "End-To-End (E2E) test utils for WordPress.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/e2e-tests/package.json b/packages/e2e-tests/package.json
index 9fc4ccf4ba769..d3efad955d360 100644
--- a/packages/e2e-tests/package.json
+++ b/packages/e2e-tests/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/e2e-tests",
-	"version": "7.12.0-prerelease",
+	"version": "7.12.0",
 	"description": "End-To-End (E2E) tests for WordPress.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/edit-post/package.json b/packages/edit-post/package.json
index e7d2c22af2dc2..3f1625891960a 100644
--- a/packages/edit-post/package.json
+++ b/packages/edit-post/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/edit-post",
-	"version": "7.18.0-prerelease",
+	"version": "7.18.0",
 	"description": "Edit Post module for WordPress.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/edit-site/package.json b/packages/edit-site/package.json
index ae0ca530a288b..a74742def329c 100644
--- a/packages/edit-site/package.json
+++ b/packages/edit-site/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/edit-site",
-	"version": "5.18.0-prerelease",
+	"version": "5.18.0",
 	"description": "Edit Site Page module for WordPress.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/edit-widgets/package.json b/packages/edit-widgets/package.json
index 61545a5f40ce3..d05022730beae 100644
--- a/packages/edit-widgets/package.json
+++ b/packages/edit-widgets/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/edit-widgets",
-	"version": "5.18.0-prerelease",
+	"version": "5.18.0",
 	"description": "Widgets Page module for WordPress..",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/editor/package.json b/packages/editor/package.json
index bb39cfdec2d08..e0ab76755e547 100644
--- a/packages/editor/package.json
+++ b/packages/editor/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/editor",
-	"version": "13.18.0-prerelease",
+	"version": "13.18.0",
 	"description": "Enhanced block editor for WordPress posts.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/element/package.json b/packages/element/package.json
index 89cf2c0d996c2..0b999f7c539ae 100644
--- a/packages/element/package.json
+++ b/packages/element/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/element",
-	"version": "5.18.0-prerelease",
+	"version": "5.18.0",
 	"description": "Element React module for WordPress.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/env/package.json b/packages/env/package.json
index 33a81f2568f45..573f253487036 100644
--- a/packages/env/package.json
+++ b/packages/env/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/env",
-	"version": "8.7.0-prerelease",
+	"version": "8.7.0",
 	"description": "A zero-config, self contained local WordPress environment for development and testing.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/escape-html/package.json b/packages/escape-html/package.json
index 0baf569edb896..0be6771907951 100644
--- a/packages/escape-html/package.json
+++ b/packages/escape-html/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/escape-html",
-	"version": "2.41.0-prerelease",
+	"version": "2.41.0",
 	"description": "Escape HTML utils.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/eslint-plugin/package.json b/packages/eslint-plugin/package.json
index 71abe7e887e0a..9fada24d1782d 100644
--- a/packages/eslint-plugin/package.json
+++ b/packages/eslint-plugin/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/eslint-plugin",
-	"version": "15.1.0-prerelease",
+	"version": "15.1.0",
 	"description": "ESLint plugin for WordPress development.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/format-library/package.json b/packages/format-library/package.json
index 88c10386e5a48..31b89eedf797c 100644
--- a/packages/format-library/package.json
+++ b/packages/format-library/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/format-library",
-	"version": "4.18.0-prerelease",
+	"version": "4.18.0",
 	"description": "Format library for the WordPress editor.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/hooks/package.json b/packages/hooks/package.json
index e404ee6773806..800c89ea490d8 100644
--- a/packages/hooks/package.json
+++ b/packages/hooks/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/hooks",
-	"version": "3.41.0-prerelease",
+	"version": "3.41.0",
 	"description": "WordPress hooks library.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/html-entities/package.json b/packages/html-entities/package.json
index 63aad915a10f5..bed86a9fe4004 100644
--- a/packages/html-entities/package.json
+++ b/packages/html-entities/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/html-entities",
-	"version": "3.41.0-prerelease",
+	"version": "3.41.0",
 	"description": "HTML entity utilities for WordPress.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/i18n/package.json b/packages/i18n/package.json
index 30236afa6f83b..9a980843c907a 100644
--- a/packages/i18n/package.json
+++ b/packages/i18n/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/i18n",
-	"version": "4.41.0-prerelease",
+	"version": "4.41.0",
 	"description": "WordPress internationalization (i18n) library.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/icons/package.json b/packages/icons/package.json
index 7199d55bdcd8a..375b3c327f4c4 100644
--- a/packages/icons/package.json
+++ b/packages/icons/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/icons",
-	"version": "9.32.0-prerelease",
+	"version": "9.32.0",
 	"description": "WordPress Icons package, based on dashicon.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/interactivity/package.json b/packages/interactivity/package.json
index 68a159473ace2..96254c822d511 100644
--- a/packages/interactivity/package.json
+++ b/packages/interactivity/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/interactivity",
-	"version": "2.2.0-prerelease",
+	"version": "2.2.0",
 	"description": "Package that provides a standard and simple way to handle the frontend interactivity of Gutenberg blocks.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/interface/package.json b/packages/interface/package.json
index 5f4591f77f075..ec5f49f64c8b1 100644
--- a/packages/interface/package.json
+++ b/packages/interface/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/interface",
-	"version": "5.18.0-prerelease",
+	"version": "5.18.0",
 	"description": "Interface module for WordPress. The package contains shared functionality across the modern JavaScript-based WordPress screens.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/is-shallow-equal/package.json b/packages/is-shallow-equal/package.json
index fdc8de459ef90..69a750e180e8f 100644
--- a/packages/is-shallow-equal/package.json
+++ b/packages/is-shallow-equal/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/is-shallow-equal",
-	"version": "4.41.0-prerelease",
+	"version": "4.41.0",
 	"description": "Test for shallow equality between two objects or arrays.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/jest-console/package.json b/packages/jest-console/package.json
index 24c264f812325..668b7e3f25e4e 100644
--- a/packages/jest-console/package.json
+++ b/packages/jest-console/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/jest-console",
-	"version": "7.12.0-prerelease",
+	"version": "7.12.0",
 	"description": "Custom Jest matchers for the Console object.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/jest-preset-default/package.json b/packages/jest-preset-default/package.json
index 2c53457515d98..7fc4186291ca6 100644
--- a/packages/jest-preset-default/package.json
+++ b/packages/jest-preset-default/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/jest-preset-default",
-	"version": "11.12.0-prerelease",
+	"version": "11.12.0",
 	"description": "Default Jest preset for WordPress development.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/jest-puppeteer-axe/package.json b/packages/jest-puppeteer-axe/package.json
index 717d4279571f5..93aad7655dc6a 100644
--- a/packages/jest-puppeteer-axe/package.json
+++ b/packages/jest-puppeteer-axe/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/jest-puppeteer-axe",
-	"version": "6.12.0-prerelease",
+	"version": "6.12.0",
 	"description": "Axe API integration with Jest and Puppeteer.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/keyboard-shortcuts/package.json b/packages/keyboard-shortcuts/package.json
index 3de49133c9e13..9e973e51a67ee 100644
--- a/packages/keyboard-shortcuts/package.json
+++ b/packages/keyboard-shortcuts/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/keyboard-shortcuts",
-	"version": "4.18.0-prerelease",
+	"version": "4.18.0",
 	"description": "Handling keyboard shortcuts.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/keycodes/package.json b/packages/keycodes/package.json
index e2c206c33bfc2..e5eade2431bbb 100644
--- a/packages/keycodes/package.json
+++ b/packages/keycodes/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/keycodes",
-	"version": "3.41.0-prerelease",
+	"version": "3.41.0",
 	"description": "Keycodes utilities for WordPress. Used to check for keyboard events across browsers/operating systems.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/lazy-import/package.json b/packages/lazy-import/package.json
index 4725970c1fdac..4137ddcc4d44a 100644
--- a/packages/lazy-import/package.json
+++ b/packages/lazy-import/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/lazy-import",
-	"version": "1.28.0-prerelease",
+	"version": "1.28.0",
 	"description": "Lazily import a module, installing it automatically if missing.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/list-reusable-blocks/package.json b/packages/list-reusable-blocks/package.json
index 532f929eecbbc..5de3a23f416ae 100644
--- a/packages/list-reusable-blocks/package.json
+++ b/packages/list-reusable-blocks/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/list-reusable-blocks",
-	"version": "4.18.0-prerelease",
+	"version": "4.18.0",
 	"description": "Adding Export/Import support to the reusable blocks listing.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/media-utils/package.json b/packages/media-utils/package.json
index 0869be440f1b9..930b1e108364d 100644
--- a/packages/media-utils/package.json
+++ b/packages/media-utils/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/media-utils",
-	"version": "4.32.0-prerelease",
+	"version": "4.32.0",
 	"description": "WordPress Media Upload Utils.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/notices/package.json b/packages/notices/package.json
index f129e65344c99..cca709be07c55 100644
--- a/packages/notices/package.json
+++ b/packages/notices/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/notices",
-	"version": "4.9.0-prerelease",
+	"version": "4.9.0",
 	"description": "State management for notices.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/npm-package-json-lint-config/package.json b/packages/npm-package-json-lint-config/package.json
index 7747193e29771..db060226e1131 100644
--- a/packages/npm-package-json-lint-config/package.json
+++ b/packages/npm-package-json-lint-config/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/npm-package-json-lint-config",
-	"version": "4.26.0-prerelease",
+	"version": "4.26.0",
 	"description": "WordPress npm-package-json-lint shareable configuration.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/nux/package.json b/packages/nux/package.json
index d1bda5c1452cd..7af3c790d5254 100644
--- a/packages/nux/package.json
+++ b/packages/nux/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/nux",
-	"version": "8.3.0-prerelease",
+	"version": "8.3.0",
 	"description": "NUX (New User eXperience) module for WordPress.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/patterns/package.json b/packages/patterns/package.json
index 52f61e45e8ef9..ea796ce9ec867 100644
--- a/packages/patterns/package.json
+++ b/packages/patterns/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/patterns",
-	"version": "1.2.0-prerelease",
+	"version": "1.2.0",
 	"description": "Management of user pattern editing.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/plugins/package.json b/packages/plugins/package.json
index f3effbc508679..7e6f6994aadbd 100644
--- a/packages/plugins/package.json
+++ b/packages/plugins/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/plugins",
-	"version": "6.9.0-prerelease",
+	"version": "6.9.0",
 	"description": "Plugins module for WordPress.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/postcss-plugins-preset/package.json b/packages/postcss-plugins-preset/package.json
index 14ed00e50cf6f..7063303be190c 100644
--- a/packages/postcss-plugins-preset/package.json
+++ b/packages/postcss-plugins-preset/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/postcss-plugins-preset",
-	"version": "4.25.0-prerelease",
+	"version": "4.25.0",
 	"description": "PostCSS sharable plugins preset for WordPress development.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/postcss-themes/package.json b/packages/postcss-themes/package.json
index bcdd0613e4639..96293c2548c5c 100644
--- a/packages/postcss-themes/package.json
+++ b/packages/postcss-themes/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/postcss-themes",
-	"version": "5.24.0-prerelease",
+	"version": "5.24.0",
 	"description": "PostCSS plugin to generate theme colors.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/preferences-persistence/package.json b/packages/preferences-persistence/package.json
index 2c9ab6789998d..34a1ca2038515 100644
--- a/packages/preferences-persistence/package.json
+++ b/packages/preferences-persistence/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/preferences-persistence",
-	"version": "1.33.0-prerelease",
+	"version": "1.33.0",
 	"description": "Persistence utilities for `wordpress/preferences`.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/preferences/package.json b/packages/preferences/package.json
index e7bbeeb246b05..78dc5e93db525 100644
--- a/packages/preferences/package.json
+++ b/packages/preferences/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/preferences",
-	"version": "3.18.0-prerelease",
+	"version": "3.18.0",
 	"description": "Utilities for managing WordPress preferences.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/prettier-config/package.json b/packages/prettier-config/package.json
index 56b7a522df2be..6775d9360138a 100644
--- a/packages/prettier-config/package.json
+++ b/packages/prettier-config/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/prettier-config",
-	"version": "2.24.0-prerelease",
+	"version": "2.24.0",
 	"description": "WordPress Prettier shared configuration.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/primitives/package.json b/packages/primitives/package.json
index c6c4f997df19d..673051f2d4a16 100644
--- a/packages/primitives/package.json
+++ b/packages/primitives/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/primitives",
-	"version": "3.39.0-prerelease",
+	"version": "3.39.0",
 	"description": "WordPress cross-platform primitives.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/priority-queue/package.json b/packages/priority-queue/package.json
index 777a0ffe4e401..2c0b141e13106 100644
--- a/packages/priority-queue/package.json
+++ b/packages/priority-queue/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/priority-queue",
-	"version": "2.41.0-prerelease",
+	"version": "2.41.0",
 	"description": "Generic browser priority queue.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/private-apis/package.json b/packages/private-apis/package.json
index 2a4ea80bf268c..1e227c47ef940 100644
--- a/packages/private-apis/package.json
+++ b/packages/private-apis/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/private-apis",
-	"version": "0.23.0-prerelease",
+	"version": "0.23.0",
 	"description": "Internal experimental APIs for WordPress core.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/project-management-automation/package.json b/packages/project-management-automation/package.json
index 4848ed96f40e0..a74a7ecaa1fcc 100644
--- a/packages/project-management-automation/package.json
+++ b/packages/project-management-automation/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/project-management-automation",
-	"version": "1.40.0-prerelease",
+	"version": "1.40.0",
 	"description": "GitHub Action that implements various automation to assist with managing the Gutenberg GitHub repository.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/react-i18n/package.json b/packages/react-i18n/package.json
index d4269c65259da..6bab711027e36 100644
--- a/packages/react-i18n/package.json
+++ b/packages/react-i18n/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/react-i18n",
-	"version": "3.39.0-prerelease",
+	"version": "3.39.0",
 	"description": "React bindings for @wordpress/i18n.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/readable-js-assets-webpack-plugin/package.json b/packages/readable-js-assets-webpack-plugin/package.json
index 3b85ff6731f02..51a92af7e282b 100644
--- a/packages/readable-js-assets-webpack-plugin/package.json
+++ b/packages/readable-js-assets-webpack-plugin/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/readable-js-assets-webpack-plugin",
-	"version": "2.24.0-prerelease",
+	"version": "2.24.0",
 	"description": "Generate a readable JS file for each JS asset.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/redux-routine/package.json b/packages/redux-routine/package.json
index d32bd95ee5ab2..024ed111cba64 100644
--- a/packages/redux-routine/package.json
+++ b/packages/redux-routine/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/redux-routine",
-	"version": "4.41.0-prerelease",
+	"version": "4.41.0",
 	"description": "Redux middleware for generator coroutines.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/reusable-blocks/package.json b/packages/reusable-blocks/package.json
index 1bd14c5b30116..94addf36b7ccc 100644
--- a/packages/reusable-blocks/package.json
+++ b/packages/reusable-blocks/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/reusable-blocks",
-	"version": "4.18.0-prerelease",
+	"version": "4.18.0",
 	"description": "Reusable blocks utilities.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/rich-text/package.json b/packages/rich-text/package.json
index 23affc1b33919..806fe721e16af 100644
--- a/packages/rich-text/package.json
+++ b/packages/rich-text/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/rich-text",
-	"version": "6.18.0-prerelease",
+	"version": "6.18.0",
 	"description": "Rich text value and manipulation API.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/router/package.json b/packages/router/package.json
index 9055184b97d32..ce23d3cdd35b2 100644
--- a/packages/router/package.json
+++ b/packages/router/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/router",
-	"version": "0.10.0-prerelease",
+	"version": "0.10.0",
 	"description": "Router API for WordPress pages.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/scripts/package.json b/packages/scripts/package.json
index 1a8688b28417e..faad42de3585a 100644
--- a/packages/scripts/package.json
+++ b/packages/scripts/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/scripts",
-	"version": "26.12.0-prerelease",
+	"version": "26.12.0",
 	"description": "Collection of reusable scripts for WordPress development.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/server-side-render/package.json b/packages/server-side-render/package.json
index 9da663ea3c1ff..bff3db88d4393 100644
--- a/packages/server-side-render/package.json
+++ b/packages/server-side-render/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/server-side-render",
-	"version": "4.18.0-prerelease",
+	"version": "4.18.0",
 	"description": "The component used with WordPress to server-side render a preview of dynamic blocks to display in the editor.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/shortcode/package.json b/packages/shortcode/package.json
index f2caf9c90f3f1..5eb37c6c2bdfb 100644
--- a/packages/shortcode/package.json
+++ b/packages/shortcode/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/shortcode",
-	"version": "3.41.0-prerelease",
+	"version": "3.41.0",
 	"description": "Shortcode module for WordPress.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/style-engine/package.json b/packages/style-engine/package.json
index e0489f43380a0..be455df5a0159 100644
--- a/packages/style-engine/package.json
+++ b/packages/style-engine/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/style-engine",
-	"version": "1.24.0-prerelease",
+	"version": "1.24.0",
 	"description": "A suite of parsers and compilers for WordPress styles.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/stylelint-config/package.json b/packages/stylelint-config/package.json
index 586ecb152c40e..a07d01f8f2737 100644
--- a/packages/stylelint-config/package.json
+++ b/packages/stylelint-config/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/stylelint-config",
-	"version": "21.24.0-prerelease",
+	"version": "21.24.0",
 	"description": "stylelint config for WordPress development.",
 	"author": "The WordPress Contributors",
 	"license": "MIT",
diff --git a/packages/sync/package.json b/packages/sync/package.json
index 8469d6703fe1f..7631a826ec554 100644
--- a/packages/sync/package.json
+++ b/packages/sync/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/sync",
-	"version": "0.3.0-prerelease",
+	"version": "0.3.0",
 	"description": "Sync Data.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/token-list/package.json b/packages/token-list/package.json
index 36b2d9a76612a..a14987b5b182c 100644
--- a/packages/token-list/package.json
+++ b/packages/token-list/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/token-list",
-	"version": "2.41.0-prerelease",
+	"version": "2.41.0",
 	"description": "Constructable, plain JavaScript DOMTokenList implementation, supporting non-browser runtimes.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/url/package.json b/packages/url/package.json
index 8e56d357a34d6..c7b6e2f572a77 100644
--- a/packages/url/package.json
+++ b/packages/url/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/url",
-	"version": "3.42.0-prerelease",
+	"version": "3.42.0",
 	"description": "WordPress URL utilities.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/viewport/package.json b/packages/viewport/package.json
index ba1b78792b391..2148327bb87ba 100644
--- a/packages/viewport/package.json
+++ b/packages/viewport/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/viewport",
-	"version": "5.18.0-prerelease",
+	"version": "5.18.0",
 	"description": "Viewport module for WordPress.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/warning/package.json b/packages/warning/package.json
index 58c50aed2da90..3f85c55d131a7 100644
--- a/packages/warning/package.json
+++ b/packages/warning/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/warning",
-	"version": "2.41.0-prerelease",
+	"version": "2.41.0",
 	"description": "Warning utility for WordPress.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/widgets/package.json b/packages/widgets/package.json
index f63cb4cd38dd3..5192ab611e663 100644
--- a/packages/widgets/package.json
+++ b/packages/widgets/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/widgets",
-	"version": "3.18.0-prerelease",
+	"version": "3.18.0",
 	"description": "Functionality used by the widgets block editor in the Widgets screen and the Customizer.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",
diff --git a/packages/wordcount/package.json b/packages/wordcount/package.json
index 2579bdf7f63ac..9be22933f2e43 100644
--- a/packages/wordcount/package.json
+++ b/packages/wordcount/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@wordpress/wordcount",
-	"version": "3.41.0-prerelease",
+	"version": "3.41.0",
 	"description": "WordPress word count utility.",
 	"author": "The WordPress Contributors",
 	"license": "GPL-2.0-or-later",

From ad410d8fc6fb901eb1d930a782c7852ddd159226 Mon Sep 17 00:00:00 2001
From: Nik Tsekouras <ntsekouras@outlook.com>
Date: Thu, 31 Aug 2023 13:17:16 +0300
Subject: [PATCH 18/34] [Post editor]: Toggle Distraction free mode mode based
 on compatibility (#54073)

---
 .../data/data-core-edit-post.md               |  4 +
 .../components/keyboard-shortcuts/index.js    | 33 +-------
 .../src/hooks/commands/use-common-commands.js | 11 ++-
 packages/edit-post/src/store/actions.js       | 81 +++++++++++++++++--
 packages/edit-post/src/store/test/actions.js  | 75 +++++++++++++++++
 packages/edit-post/src/store/test/reducer.js  | 30 ++++---
 packages/edit-site/src/store/test/actions.js  |  1 -
 packages/edit-site/src/store/test/reducer.js  |  1 -
 8 files changed, 187 insertions(+), 49 deletions(-)

diff --git a/docs/reference-guides/data/data-core-edit-post.md b/docs/reference-guides/data/data-core-edit-post.md
index b579e7e658007..7d6a1deed455b 100644
--- a/docs/reference-guides/data/data-core-edit-post.md
+++ b/docs/reference-guides/data/data-core-edit-post.md
@@ -490,6 +490,10 @@ _Parameters_
 
 -   _mode_ `string`: The editor mode.
 
+### toggleDistractionFree
+
+Action that toggles Distraction free mode. Distraction free mode expects there are no sidebars, as due to the z-index values set, you can't close sidebars.
+
 ### toggleEditorPanelEnabled
 
 Returns an action object used to enable or disable a panel in the editor.
diff --git a/packages/edit-post/src/components/keyboard-shortcuts/index.js b/packages/edit-post/src/components/keyboard-shortcuts/index.js
index 1760edd8d3fc0..31db06794e027 100644
--- a/packages/edit-post/src/components/keyboard-shortcuts/index.js
+++ b/packages/edit-post/src/components/keyboard-shortcuts/index.js
@@ -10,8 +10,6 @@ import {
 import { __ } from '@wordpress/i18n';
 import { store as editorStore } from '@wordpress/editor';
 import { store as blockEditorStore } from '@wordpress/block-editor';
-import { store as noticesStore } from '@wordpress/notices';
-import { store as preferencesStore } from '@wordpress/preferences';
 import { createBlock } from '@wordpress/blocks';
 
 /**
@@ -21,39 +19,24 @@ import { store as editPostStore } from '../../store';
 
 function KeyboardShortcuts() {
 	const { getBlockSelectionStart } = useSelect( blockEditorStore );
-	const {
-		getEditorMode,
-		isEditorSidebarOpened,
-		isListViewOpened,
-		isFeatureActive,
-	} = useSelect( editPostStore );
+	const { getEditorMode, isEditorSidebarOpened, isListViewOpened } =
+		useSelect( editPostStore );
 	const isModeToggleDisabled = useSelect( ( select ) => {
 		const { richEditingEnabled, codeEditingEnabled } =
 			select( editorStore ).getEditorSettings();
 		return ! richEditingEnabled || ! codeEditingEnabled;
 	}, [] );
 
-	const { createInfoNotice } = useDispatch( noticesStore );
-
 	const {
 		switchEditorMode,
 		openGeneralSidebar,
 		closeGeneralSidebar,
 		toggleFeature,
 		setIsListViewOpened,
-		setIsInserterOpened,
+		toggleDistractionFree,
 	} = useDispatch( editPostStore );
 	const { registerShortcut } = useDispatch( keyboardShortcutsStore );
 
-	const { set: setPreference } = useDispatch( preferencesStore );
-
-	const toggleDistractionFree = () => {
-		setPreference( 'core/edit-post', 'fixedToolbar', false );
-		setIsInserterOpened( false );
-		setIsListViewOpened( false );
-		closeGeneralSidebar();
-	};
-
 	const { replaceBlocks } = useDispatch( blockEditorStore );
 	const { getBlockName, getSelectedBlockClientId, getBlockAttributes } =
 		useSelect( blockEditorStore );
@@ -224,16 +207,6 @@ function KeyboardShortcuts() {
 
 	useShortcut( 'core/edit-post/toggle-distraction-free', () => {
 		toggleDistractionFree();
-		toggleFeature( 'distractionFree' );
-		createInfoNotice(
-			isFeatureActive( 'distractionFree' )
-				? __( 'Distraction free on.' )
-				: __( 'Distraction free off.' ),
-			{
-				id: 'core/edit-post/distraction-free-mode/notice',
-				type: 'snackbar',
-			}
-		);
 	} );
 
 	useShortcut( 'core/edit-post/toggle-sidebar', ( event ) => {
diff --git a/packages/edit-post/src/hooks/commands/use-common-commands.js b/packages/edit-post/src/hooks/commands/use-common-commands.js
index dfb7c82a1029c..e9534b190416a 100644
--- a/packages/edit-post/src/hooks/commands/use-common-commands.js
+++ b/packages/edit-post/src/hooks/commands/use-common-commands.js
@@ -33,6 +33,7 @@ export default function useCommonCommands() {
 		closeGeneralSidebar,
 		switchEditorMode,
 		setIsListViewOpened,
+		toggleDistractionFree,
 	} = useDispatch( editPostStore );
 	const { openModal } = useDispatch( interfaceStore );
 	const {
@@ -41,6 +42,7 @@ export default function useCommonCommands() {
 		isListViewOpen,
 		isPublishSidebarEnabled,
 		showBlockBreadcrumbs,
+		isDistractionFree,
 	} = useSelect( ( select ) => {
 		const { getEditorMode, isListViewOpened, isFeatureActive } =
 			select( editPostStore );
@@ -53,6 +55,10 @@ export default function useCommonCommands() {
 			isPublishSidebarEnabled:
 				select( editorStore ).isPublishSidebarEnabled(),
 			showBlockBreadcrumbs: isFeatureActive( 'showBlockBreadcrumbs' ),
+			isDistractionFree: select( preferencesStore ).get(
+				editPostStore.name,
+				'distractionFree'
+			),
 		};
 	}, [] );
 	const { toggle } = useDispatch( preferencesStore );
@@ -92,7 +98,7 @@ export default function useCommonCommands() {
 		name: 'core/toggle-distraction-free',
 		label: __( 'Toggle distraction free' ),
 		callback: ( { close } ) => {
-			toggle( 'core/edit-post', 'distractionFree' );
+			toggleDistractionFree();
 			close();
 		},
 	} );
@@ -131,6 +137,9 @@ export default function useCommonCommands() {
 		label: __( 'Toggle top toolbar' ),
 		callback: ( { close } ) => {
 			toggle( 'core/edit-post', 'fixedToolbar' );
+			if ( isDistractionFree ) {
+				toggleDistractionFree();
+			}
 			close();
 		},
 	} );
diff --git a/packages/edit-post/src/store/actions.js b/packages/edit-post/src/store/actions.js
index 0ee1efb62b02e..f7a5e211fd769 100644
--- a/packages/edit-post/src/store/actions.js
+++ b/packages/edit-post/src/store/actions.js
@@ -26,10 +26,17 @@ import { store as editPostStore } from '.';
  */
 export const openGeneralSidebar =
 	( name ) =>
-	( { registry } ) =>
+	( { dispatch, registry } ) => {
+		const isDistractionFree = registry
+			.select( preferencesStore )
+			.get( 'core/edit-post', 'distractionFree' );
+		if ( isDistractionFree ) {
+			dispatch.toggleDistractionFree();
+		}
 		registry
 			.dispatch( interfaceStore )
 			.enableComplementaryArea( editPostStore.name, name );
+	};
 
 /**
  * Returns an action object signalling that the user closed the sidebar.
@@ -210,7 +217,7 @@ export const toggleFeature =
  */
 export const switchEditorMode =
 	( mode ) =>
-	( { registry } ) => {
+	( { dispatch, registry } ) => {
 		registry
 			.dispatch( preferencesStore )
 			.set( 'core/edit-post', 'editorMode', mode );
@@ -220,6 +227,15 @@ export const switchEditorMode =
 			registry.dispatch( blockEditorStore ).clearSelectedBlock();
 		}
 
+		if (
+			mode === 'text' &&
+			registry
+				.select( preferencesStore )
+				.get( 'core/edit-post', 'distractionFree' )
+		) {
+			dispatch.toggleDistractionFree();
+		}
+
 		const message =
 			mode === 'visual'
 				? __( 'Visual editor selected' )
@@ -479,12 +495,20 @@ export function setIsInserterOpened( value ) {
  * @param {boolean} isOpen A boolean representing whether the list view should be opened or closed.
  * @return {Object} Action object.
  */
-export function setIsListViewOpened( isOpen ) {
-	return {
-		type: 'SET_IS_LIST_VIEW_OPENED',
-		isOpen,
+export const setIsListViewOpened =
+	( isOpen ) =>
+	( { dispatch, registry } ) => {
+		const isDistractionFree = registry
+			.select( preferencesStore )
+			.get( 'core/edit-post', 'distractionFree' );
+		if ( isDistractionFree && isOpen ) {
+			dispatch.toggleDistractionFree();
+		}
+		dispatch( {
+			type: 'SET_IS_LIST_VIEW_OPENED',
+			isOpen,
+		} );
 	};
-}
 
 /**
  * Returns an action object used to switch to template editing.
@@ -590,3 +614,46 @@ export const initializeMetaBoxes =
 			type: 'META_BOXES_INITIALIZED',
 		} );
 	};
+
+/**
+ * Action that toggles Distraction free mode.
+ * Distraction free mode expects there are no sidebars, as due to the
+ * z-index values set, you can't close sidebars.
+ */
+export const toggleDistractionFree =
+	() =>
+	( { dispatch, registry } ) => {
+		const isDistractionFree = registry
+			.select( preferencesStore )
+			.get( 'core/edit-post', 'distractionFree' );
+		if ( ! isDistractionFree ) {
+			registry.batch( () => {
+				registry
+					.dispatch( preferencesStore )
+					.set( 'core/edit-post', 'fixedToolbar', false );
+				dispatch.setIsInserterOpened( false );
+				dispatch.setIsListViewOpened( false );
+				dispatch.closeGeneralSidebar();
+			} );
+		}
+		registry.batch( () => {
+			registry
+				.dispatch( preferencesStore )
+				.set(
+					'core/edit-post',
+					'distractionFree',
+					! isDistractionFree
+				);
+			registry
+				.dispatch( noticesStore )
+				.createInfoNotice(
+					isDistractionFree
+						? __( 'Distraction free off.' )
+						: __( 'Distraction free on.' ),
+					{
+						id: 'core/edit-post/distraction-free-mode/notice',
+						type: 'snackbar',
+					}
+				);
+		} );
+	};
diff --git a/packages/edit-post/src/store/test/actions.js b/packages/edit-post/src/store/test/actions.js
index 97494e03afca6..cfd3eecb39e5b 100644
--- a/packages/edit-post/src/store/test/actions.js
+++ b/packages/edit-post/src/store/test/actions.js
@@ -53,6 +53,20 @@ describe( 'actions', () => {
 		).toBeNull();
 	} );
 
+	it( 'openGeneralSidebar - should turn off distraction free mode when opening a general sidebar', () => {
+		registry
+			.dispatch( preferencesStore )
+			.set( 'core/edit-post', 'distractionFree', true );
+		registry
+			.dispatch( editPostStore )
+			.openGeneralSidebar( 'edit-post/block' );
+		expect(
+			registry
+				.select( preferencesStore )
+				.get( 'core/edit-post', 'distractionFree' )
+		).toBe( false );
+	} );
+
 	it( 'toggleFeature', () => {
 		registry.dispatch( editPostStore ).toggleFeature( 'welcomeGuide' );
 		expect(
@@ -102,6 +116,17 @@ describe( 'actions', () => {
 				'text'
 			);
 		} );
+		it( 'should turn off distraction free mode when switching to code editor', () => {
+			registry
+				.dispatch( preferencesStore )
+				.set( 'core/edit-post', 'distractionFree', true );
+			registry.dispatch( editPostStore ).switchEditorMode( 'text' );
+			expect(
+				registry
+					.select( preferencesStore )
+					.get( 'core/edit-post', 'distractionFree' )
+			).toBe( false );
+		} );
 	} );
 
 	it( 'togglePinnedPluginItem', () => {
@@ -333,4 +358,54 @@ describe( 'actions', () => {
 			} );
 		} );
 	} );
+
+	describe( 'toggleDistractionFree', () => {
+		it( 'should properly update settings to prevent layout corruption when enabling distraction free mode', () => {
+			// Enable everything that shouldn't be enabled in distraction free mode.
+			registry
+				.dispatch( preferencesStore )
+				.set( 'core/edit-post', 'fixedToolbar', true );
+			registry.dispatch( editPostStore ).setIsListViewOpened( true );
+			registry
+				.dispatch( editPostStore )
+				.openGeneralSidebar( 'edit-post/block' );
+			// Initial state is falsy.
+			registry.dispatch( editPostStore ).toggleDistractionFree();
+			expect(
+				registry
+					.select( preferencesStore )
+					.get( 'core/edit-post', 'fixedToolbar' )
+			).toBe( false );
+			expect( registry.select( editPostStore ).isListViewOpened() ).toBe(
+				false
+			);
+			expect( registry.select( editPostStore ).isInserterOpened() ).toBe(
+				false
+			);
+			expect(
+				registry
+					.select( interfaceStore )
+					.getActiveComplementaryArea( editPostStore.name )
+			).toBeNull();
+			expect(
+				registry
+					.select( preferencesStore )
+					.get( 'core/edit-post', 'distractionFree' )
+			).toBe( true );
+		} );
+	} );
+
+	describe( 'setIsListViewOpened', () => {
+		it( 'should turn off distraction free mode when opening the list view', () => {
+			registry
+				.dispatch( preferencesStore )
+				.set( 'core/edit-post', 'distractionFree', true );
+			registry.dispatch( editPostStore ).setIsListViewOpened( true );
+			expect(
+				registry
+					.select( preferencesStore )
+					.get( 'core/edit-post', 'distractionFree' )
+			).toBe( false );
+		} );
+	} );
 } );
diff --git a/packages/edit-post/src/store/test/reducer.js b/packages/edit-post/src/store/test/reducer.js
index a083de9c67286..2e8d923d022f3 100644
--- a/packages/edit-post/src/store/test/reducer.js
+++ b/packages/edit-post/src/store/test/reducer.js
@@ -14,7 +14,7 @@ import {
 	listViewPanel,
 } from '../reducer';
 
-import { setIsInserterOpened, setIsListViewOpened } from '../actions';
+import { setIsInserterOpened } from '../actions';
 
 describe( 'state', () => {
 	describe( 'isSavingMetaBoxes', () => {
@@ -135,13 +135,19 @@ describe( 'state', () => {
 
 		it( 'should close the inserter when opening the list view panel', () => {
 			expect(
-				blockInserterPanel( true, setIsListViewOpened( true ) )
+				blockInserterPanel( true, {
+					type: 'SET_IS_LIST_VIEW_OPENED',
+					isOpen: true,
+				} )
 			).toBe( false );
 		} );
 
 		it( 'should not change the state when closing the list view panel', () => {
 			expect(
-				blockInserterPanel( true, setIsListViewOpened( false ) )
+				blockInserterPanel( true, {
+					type: 'SET_IS_LIST_VIEW_OPENED',
+					isOpen: false,
+				} )
 			).toBe( true );
 		} );
 	} );
@@ -156,12 +162,18 @@ describe( 'state', () => {
 		} );
 
 		it( 'should set the open state of the list view panel', () => {
-			expect( listViewPanel( false, setIsListViewOpened( true ) ) ).toBe(
-				true
-			);
-			expect( listViewPanel( true, setIsListViewOpened( false ) ) ).toBe(
-				false
-			);
+			expect(
+				listViewPanel( false, {
+					type: 'SET_IS_LIST_VIEW_OPENED',
+					isOpen: true,
+				} )
+			).toBe( true );
+			expect(
+				listViewPanel( true, {
+					type: 'SET_IS_LIST_VIEW_OPENED',
+					isOpen: false,
+				} )
+			).toBe( false );
 		} );
 
 		it( 'should close the list view when opening the inserter panel', () => {
diff --git a/packages/edit-site/src/store/test/actions.js b/packages/edit-site/src/store/test/actions.js
index 345fcddbbba3b..5fbbf62f369e8 100644
--- a/packages/edit-site/src/store/test/actions.js
+++ b/packages/edit-site/src/store/test/actions.js
@@ -235,7 +235,6 @@ describe( 'actions', () => {
 			registry
 				.dispatch( preferencesStore )
 				.set( 'core/edit-site', 'distractionFree', true );
-
 			registry
 				.dispatch( editSiteStore )
 				.openGeneralSidebar( 'edit-site/global-styles' );
diff --git a/packages/edit-site/src/store/test/reducer.js b/packages/edit-site/src/store/test/reducer.js
index fc06faf925b38..d3816f6ac0ac2 100644
--- a/packages/edit-site/src/store/test/reducer.js
+++ b/packages/edit-site/src/store/test/reducer.js
@@ -122,7 +122,6 @@ describe( 'state', () => {
 		} );
 
 		it( 'should set the open state of the list view panel', () => {
-			// registry.dispatch( editSiteStore ).toggleFeature( 'name' );
 			expect(
 				listViewPanel( false, {
 					type: 'SET_IS_LIST_VIEW_OPENED',

From 0d7548f8f4ffa6ccba241d154377dacd4a45e837 Mon Sep 17 00:00:00 2001
From: Gio Lodi <gio@mokacoding.com>
Date: Thu, 31 Aug 2023 20:17:47 +1000
Subject: [PATCH 19/34] Update editor pckg config to address "multiple Podfile"
 warn (#54077)

---
 packages/react-native-editor/.gitignore             | 1 -
 packages/react-native-editor/ios/.bundle/config     | 2 +-
 packages/react-native-editor/react-native.config.js | 5 +++++
 3 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/packages/react-native-editor/.gitignore b/packages/react-native-editor/.gitignore
index a57f050efe86c..f9d5282d7c2a4 100644
--- a/packages/react-native-editor/.gitignore
+++ b/packages/react-native-editor/.gitignore
@@ -120,5 +120,4 @@ bin/wp-cli.phar
 # Ruby / CocoaPods
 
 ios/Pods
-ios/vendor
 /vendor/bundle/
diff --git a/packages/react-native-editor/ios/.bundle/config b/packages/react-native-editor/ios/.bundle/config
index 2369228816d46..3e22e4d08d33a 100644
--- a/packages/react-native-editor/ios/.bundle/config
+++ b/packages/react-native-editor/ios/.bundle/config
@@ -1,2 +1,2 @@
 ---
-BUNDLE_PATH: "vendor/bundle"
+BUNDLE_PATH: "../vendor/bundle"
diff --git a/packages/react-native-editor/react-native.config.js b/packages/react-native-editor/react-native.config.js
index 1dfc03662f7b5..eafa0b41ca955 100644
--- a/packages/react-native-editor/react-native.config.js
+++ b/packages/react-native-editor/react-native.config.js
@@ -12,4 +12,9 @@ module.exports = {
 			root: path.resolve( __dirname, '../react-native-aztec' ),
 		},
 	},
+	project: {
+		ios: {
+			sourceDir: './ios/',
+		},
+	},
 };

From 9ce50480a72e62668c861a0715f5e7df84ecfe31 Mon Sep 17 00:00:00 2001
From: George Mamadashvili <georgemamadashvili@gmail.com>
Date: Thu, 31 Aug 2023 15:53:28 +0400
Subject: [PATCH 20/34] Post Terms: Fix the 'useSelect' warning in the
 'usePostTerms' hook (#54068)

---
 packages/block-library/src/post-terms/use-post-terms.js | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/packages/block-library/src/post-terms/use-post-terms.js b/packages/block-library/src/post-terms/use-post-terms.js
index 2d0aca51db162..ed22df9a9728b 100644
--- a/packages/block-library/src/post-terms/use-post-terms.js
+++ b/packages/block-library/src/post-terms/use-post-terms.js
@@ -4,6 +4,8 @@
 import { store as coreStore } from '@wordpress/core-data';
 import { useSelect } from '@wordpress/data';
 
+const EMPTY_ARRAY = [];
+
 export default function usePostTerms( { postId, term } ) {
 	const { slug } = term;
 
@@ -12,8 +14,8 @@ export default function usePostTerms( { postId, term } ) {
 			const visible = term?.visibility?.publicly_queryable;
 			if ( ! visible ) {
 				return {
-					postTerms: [],
-					_isLoading: false,
+					postTerms: EMPTY_ARRAY,
+					isLoading: false,
 					hasPostTerms: false,
 				};
 			}

From ea3c23d58f4caf036dd73f89cbf262a7323ac3db Mon Sep 17 00:00:00 2001
From: Bart Kalisz <bartlomiej.kalisz@gmail.com>
Date: Thu, 31 Aug 2023 12:03:05 +0000
Subject: [PATCH 21/34] E2E: Do not run page actions in beforeEach hook when
 using fixme (#54065)

This fixes the flaky tests from inserting-blocks.spec.js
---
 .../e2e-test-utils-playwright/src/test.ts     | 12 +++--
 test/e2e/playwright.config.ts                 |  2 +-
 .../editor/various/inserting-blocks.spec.js   | 52 +++++++++++++------
 3 files changed, 47 insertions(+), 19 deletions(-)

diff --git a/packages/e2e-test-utils-playwright/src/test.ts b/packages/e2e-test-utils-playwright/src/test.ts
index eec8e4e279c0f..d65174f44695e 100644
--- a/packages/e2e-test-utils-playwright/src/test.ts
+++ b/packages/e2e-test-utils-playwright/src/test.ts
@@ -120,9 +120,15 @@ const test = base.extend<
 		await use( page );
 
 		// Clear local storage after each test.
-		await page.evaluate( () => {
-			window.localStorage.clear();
-		} );
+		// This needs to be wrapped with a try/catch because it can fail when
+		// the test is skipped (e.g. via fixme).
+		try {
+			await page.evaluate( () => {
+				window.localStorage.clear();
+			} );
+		} catch ( error ) {
+			// noop.
+		}
 
 		await page.close();
 	},
diff --git a/test/e2e/playwright.config.ts b/test/e2e/playwright.config.ts
index 2e117b9745840..e1724a61d6126 100644
--- a/test/e2e/playwright.config.ts
+++ b/test/e2e/playwright.config.ts
@@ -17,7 +17,7 @@ const config = defineConfig( {
 	forbidOnly: !! process.env.CI,
 	workers: 1,
 	retries: process.env.CI ? 2 : 0,
-	timeout: parseInt( process.env.TIMEOUT || '', 10 ) || 200_000, // Defaults to 200 seconds.
+	timeout: parseInt( process.env.TIMEOUT || '', 10 ) || 100_000, // Defaults to 100 seconds.
 	// Don't report slow test "files", as we will be running our tests in serial.
 	reportSlowTests: null,
 	testDir: fileURLToPath( new URL( './specs', 'file:' + __filename ).href ),
diff --git a/test/e2e/specs/editor/various/inserting-blocks.spec.js b/test/e2e/specs/editor/various/inserting-blocks.spec.js
index 39c159b00b75c..822257e67deff 100644
--- a/test/e2e/specs/editor/various/inserting-blocks.spec.js
+++ b/test/e2e/specs/editor/various/inserting-blocks.spec.js
@@ -15,23 +15,12 @@ test.use( {
 } );
 
 test.describe( 'Inserting blocks (@firefox, @webkit)', () => {
-	test.beforeEach( async ( { admin, page } ) => {
-		await admin.createNewPost();
-		// To do: some drag an drop tests are failing, so run them without
-		// iframe for now.
-		await page.evaluate( () => {
-			window.wp.blocks.registerBlockType( 'test/v2', {
-				apiVersion: '2',
-				title: 'test',
-			} );
-		} );
-	} );
-
 	test.afterAll( async ( { requestUtils } ) => {
 		await requestUtils.deleteAllPosts();
 	} );
 
 	test( 'inserts blocks by dragging and dropping from the global inserter', async ( {
+		admin,
 		page,
 		editor,
 		insertingBlocksUtils,
@@ -41,6 +30,9 @@ test.describe( 'Inserting blocks (@firefox, @webkit)', () => {
 			'The clientX value is always 0 in firefox, see https://github.com/microsoft/playwright/issues/17761 for more info.'
 		);
 
+		await admin.createNewPost();
+		await insertingBlocksUtils.runWithoutIframe();
+
 		// We need a dummy block in place to display the drop indicator due to a bug.
 		// @see https://github.com/WordPress/gutenberg/issues/44064
 		await editor.insertBlock( {
@@ -111,10 +103,14 @@ test.describe( 'Inserting blocks (@firefox, @webkit)', () => {
 	} );
 
 	test( 'cancels dragging blocks from the global inserter by pressing Escape', async ( {
+		admin,
 		page,
 		editor,
 		insertingBlocksUtils,
 	} ) => {
+		await admin.createNewPost();
+		await insertingBlocksUtils.runWithoutIframe();
+
 		// We need a dummy block in place to display the drop indicator due to a bug.
 		// @see https://github.com/WordPress/gutenberg/issues/44064
 		await editor.insertBlock( {
@@ -168,6 +164,7 @@ test.describe( 'Inserting blocks (@firefox, @webkit)', () => {
 	} );
 
 	test( 'inserts patterns by dragging and dropping from the global inserter', async ( {
+		admin,
 		page,
 		editor,
 		insertingBlocksUtils,
@@ -177,6 +174,9 @@ test.describe( 'Inserting blocks (@firefox, @webkit)', () => {
 			'The clientX value is always 0 in firefox, see https://github.com/microsoft/playwright/issues/17761 for more info.'
 		);
 
+		await admin.createNewPost();
+		await insertingBlocksUtils.runWithoutIframe();
+
 		// We need a dummy block in place to display the drop indicator due to a bug.
 		// @see https://github.com/WordPress/gutenberg/issues/44064
 		await editor.insertBlock( {
@@ -239,10 +239,14 @@ test.describe( 'Inserting blocks (@firefox, @webkit)', () => {
 	} );
 
 	test( 'cancels dragging patterns from the global inserter by pressing Escape', async ( {
+		admin,
 		page,
 		editor,
 		insertingBlocksUtils,
 	} ) => {
+		await admin.createNewPost();
+		await insertingBlocksUtils.runWithoutIframe();
+
 		// We need a dummy block in place to display the drop indicator due to a bug.
 		// @see https://github.com/WordPress/gutenberg/issues/44064
 		await editor.insertBlock( {
@@ -300,9 +304,14 @@ test.describe( 'Inserting blocks (@firefox, @webkit)', () => {
 
 	// A test for https://github.com/WordPress/gutenberg/issues/43090.
 	test( 'should close the inserter when clicking on the toggle button', async ( {
+		admin,
 		page,
 		editor,
+		insertingBlocksUtils,
 	} ) => {
+		await admin.createNewPost();
+		await insertingBlocksUtils.runWithoutIframe();
+
 		const inserterButton = page.getByRole( 'button', {
 			name: 'Toggle block inserter',
 		} );
@@ -341,13 +350,14 @@ test.describe( 'insert media from inserter', () => {
 			requestUtils.deleteAllPosts(),
 		] );
 	} );
-	test.beforeEach( async ( { admin } ) => {
-		await admin.createNewPost();
-	} );
+
 	test( 'insert media from the global inserter', async ( {
+		admin,
 		page,
 		editor,
 	} ) => {
+		await admin.createNewPost();
+
 		await page.click(
 			'role=region[name="Editor top bar"i] >> role=button[name="Toggle block inserter"i]'
 		);
@@ -380,4 +390,16 @@ class InsertingBlocksUtils {
 			'data-testid=block-draggable-chip >> visible=true'
 		);
 	}
+
+	async runWithoutIframe() {
+		/**
+		 * @todo Some drag an drop tests are failing, so run them without iframe for now.
+		 */
+		return await this.page.evaluate( () => {
+			window.wp.blocks.registerBlockType( 'test/v2', {
+				apiVersion: '2',
+				title: 'test',
+			} );
+		} );
+	}
 }

From 6d77fd28f50adb39040d27cb797b9fc3a1393ef7 Mon Sep 17 00:00:00 2001
From: Dave Smith <getdavemail@gmail.com>
Date: Thu, 31 Aug 2023 13:18:35 +0100
Subject: [PATCH 22/34] Adds 'nofollow' setting to inline links (rich text
 only) (#53945)

* Add ability for inline link format to parse and handle rel no follow

* Add no follow to Link Control

* Add support for optional help text on setting controls

* Only apply new setting to inline links for now

* Expose default link settings for use by component consumers

* Extend default settings in inline link implementation

* Use nofollow lowercase

* Make help text the title

* Adds test coverage

* Use help text to reduce size of label

* Correct text to qualify statement

Search engines are not compelled to obey this rule

* Remove help text
---
 .../src/components/link-control/index.js      |  1 +
 .../src/components/link-control/settings.js   |  1 +
 .../src/components/link-control/style.scss    | 10 +--
 packages/format-library/src/link/index.js     |  1 +
 packages/format-library/src/link/inline.js    | 15 +++-
 packages/format-library/src/link/utils.js     | 20 ++++-
 test/e2e/specs/editor/blocks/links.spec.js    | 82 +++++++++++++++++++
 7 files changed, 121 insertions(+), 9 deletions(-)

diff --git a/packages/block-editor/src/components/link-control/index.js b/packages/block-editor/src/components/link-control/index.js
index ac1ec660934a3..4e0a1f2291b02 100644
--- a/packages/block-editor/src/components/link-control/index.js
+++ b/packages/block-editor/src/components/link-control/index.js
@@ -475,5 +475,6 @@ function LinkControl( {
 }
 
 LinkControl.ViewerFill = ViewerFill;
+LinkControl.DEFAULT_LINK_SETTINGS = DEFAULT_LINK_SETTINGS;
 
 export default LinkControl;
diff --git a/packages/block-editor/src/components/link-control/settings.js b/packages/block-editor/src/components/link-control/settings.js
index 1d70cc97dff41..e63ef926358fe 100644
--- a/packages/block-editor/src/components/link-control/settings.js
+++ b/packages/block-editor/src/components/link-control/settings.js
@@ -26,6 +26,7 @@ const LinkControlSettings = ( { value, onChange = noop, settings } ) => {
 			label={ setting.title }
 			onChange={ handleSettingChange( setting ) }
 			checked={ value ? !! value[ setting.id ] : false }
+			help={ setting?.help }
 		/>
 	) );
 
diff --git a/packages/block-editor/src/components/link-control/style.scss b/packages/block-editor/src/components/link-control/style.scss
index 0ca13fa916777..2b37d7fccdfe4 100644
--- a/packages/block-editor/src/components/link-control/style.scss
+++ b/packages/block-editor/src/components/link-control/style.scss
@@ -401,19 +401,19 @@ $preview-image-height: 140px;
 }
 
 .block-editor-link-control__setting {
-	margin-bottom: $grid-unit-20;
+	margin-bottom: 0;
 	flex: 1;
 	padding: $grid-unit-10 0 $grid-unit-10 $grid-unit-30;
 
+	.components-base-control__field {
+		display: flex; // don't allow label to wrap under checkbox.
+	}
+
 	// Cancel left margin inherited from WP Admin Forms CSS.
 	input {
 		margin-left: 0;
 	}
 
-	&.block-editor-link-control__setting:last-child {
-		margin-bottom: 0;
-	}
-
 	.is-preview & {
 		padding: 20px $grid-unit-10 $grid-unit-10 0;
 	}
diff --git a/packages/format-library/src/link/index.js b/packages/format-library/src/link/index.js
index 03e25a37b9b8e..50019be4cc958 100644
--- a/packages/format-library/src/link/index.js
+++ b/packages/format-library/src/link/index.js
@@ -131,6 +131,7 @@ export const link = {
 		type: 'data-type',
 		id: 'data-id',
 		target: 'target',
+		rel: 'rel',
 	},
 	__unstablePasteRule( value, { html, plainText } ) {
 		if ( isCollapsed( value ) ) {
diff --git a/packages/format-library/src/link/inline.js b/packages/format-library/src/link/inline.js
index 29636a8d8b94b..62dc4ee322942 100644
--- a/packages/format-library/src/link/inline.js
+++ b/packages/format-library/src/link/inline.js
@@ -31,6 +31,17 @@ import { createLinkFormat, isValidHref, getFormatBoundary } from './utils';
 import { link as settings } from './index';
 import useLinkInstanceKey from './use-link-instance-key';
 
+const LINK_SETTINGS = [
+	...LinkControl.DEFAULT_LINK_SETTINGS,
+	{
+		id: 'nofollow',
+		title: createInterpolateElement(
+			__( 'Mark as <code>nofollow</code>' ),
+			{ code: <code /> }
+		),
+	},
+];
+
 function InlineLinkUI( {
 	isActive,
 	activeAttributes,
@@ -60,6 +71,7 @@ function InlineLinkUI( {
 		type: activeAttributes.type,
 		id: activeAttributes.id,
 		opensInNewTab: activeAttributes.target === '_blank',
+		nofollow: activeAttributes.rel?.includes( 'nofollow' ),
 		title: richTextText,
 	};
 
@@ -77,7 +89,6 @@ function InlineLinkUI( {
 		const didToggleSetting =
 			linkValue.opensInNewTab !== nextValue.opensInNewTab &&
 			nextValue.url === undefined;
-
 		// Merge the next value with the current link value.
 		nextValue = {
 			...linkValue,
@@ -93,6 +104,7 @@ function InlineLinkUI( {
 					? String( nextValue.id )
 					: undefined,
 			opensInNewWindow: nextValue.opensInNewTab,
+			nofollow: nextValue.nofollow,
 		} );
 
 		const newText = nextValue.title || newUrl;
@@ -247,6 +259,7 @@ function InlineLinkUI( {
 				withCreateSuggestion={ userCanCreatePages }
 				createSuggestionButtonText={ createButtonText }
 				hasTextControl
+				settings={ LINK_SETTINGS }
 			/>
 		</Popover>
 	);
diff --git a/packages/format-library/src/link/utils.js b/packages/format-library/src/link/utils.js
index 4c7a582a2e8f2..12fc56f242031 100644
--- a/packages/format-library/src/link/utils.js
+++ b/packages/format-library/src/link/utils.js
@@ -85,10 +85,16 @@ export function isValidHref( href ) {
  * @param {string}  options.type             The type of the link.
  * @param {string}  options.id               The ID of the link.
  * @param {boolean} options.opensInNewWindow Whether this link will open in a new window.
- *
+ * @param {boolean} options.nofollow         Whether this link is marked as no follow relationship.
  * @return {Object} The final format object.
  */
-export function createLinkFormat( { url, type, id, opensInNewWindow } ) {
+export function createLinkFormat( {
+	url,
+	type,
+	id,
+	opensInNewWindow,
+	nofollow,
+} ) {
 	const format = {
 		type: 'core/link',
 		attributes: {
@@ -101,7 +107,15 @@ export function createLinkFormat( { url, type, id, opensInNewWindow } ) {
 
 	if ( opensInNewWindow ) {
 		format.attributes.target = '_blank';
-		format.attributes.rel = 'noreferrer noopener';
+		format.attributes.rel = format.attributes.rel
+			? format.attributes.rel + ' noreferrer noopener'
+			: 'noreferrer noopener';
+	}
+
+	if ( nofollow ) {
+		format.attributes.rel = format.attributes.rel
+			? format.attributes.rel + ' nofollow'
+			: 'nofollow';
 	}
 
 	return format;
diff --git a/test/e2e/specs/editor/blocks/links.spec.js b/test/e2e/specs/editor/blocks/links.spec.js
index b84f954566fd5..83efef0bce2f0 100644
--- a/test/e2e/specs/editor/blocks/links.spec.js
+++ b/test/e2e/specs/editor/blocks/links.spec.js
@@ -233,4 +233,86 @@ test.describe( 'Links', () => {
 		// This verifies that the editor preference was persisted.
 		await expect( page.getByLabel( 'Open in new tab' ) ).not.toBeVisible();
 	} );
+
+	test( 'can toggle link settings and save', async ( {
+		page,
+		editor,
+		pageUtils,
+	} ) => {
+		await editor.insertBlock( {
+			name: 'core/paragraph',
+			attributes: {
+				content:
+					'<a href="https://wordpress.org/gutenberg">Gutenberg</a>',
+			},
+		} );
+
+		// Move caret into the link.
+		await pageUtils.pressKeys( 'ArrowRight' );
+
+		// Switch Link UI to "edit" mode.
+		await page.getByRole( 'button', { name: 'Edit' } ).click();
+
+		// Open Advanced Settings
+		await page
+			.getByRole( 'region', {
+				name: 'Editor content',
+			} )
+			.getByRole( 'button', {
+				name: 'Advanced',
+			} )
+			.click();
+
+		// expect settings for `Open in new tab` and `No follow`
+		await expect( page.getByLabel( 'Open in new tab' ) ).not.toBeChecked();
+		await expect( page.getByLabel( 'nofollow' ) ).not.toBeChecked();
+
+		// Toggle both of the settings
+		await page.getByLabel( 'Open in new tab' ).click();
+		await page.getByLabel( 'nofollow' ).click();
+
+		// Save the link
+		await page
+			.locator( '.block-editor-link-control' )
+			.getByRole( 'button', { name: 'Save' } )
+			.click();
+
+		// Expect correct attributes to be set on the underlying link.
+		await expect.poll( editor.getBlocks ).toMatchObject( [
+			{
+				name: 'core/paragraph',
+				attributes: {
+					content: `<a href="https://wordpress.org/gutenberg" target="_blank" rel="noreferrer noopener nofollow">Gutenberg</a>`,
+				},
+			},
+		] );
+
+		// Move caret back into the link.
+		await page.keyboard.press( 'ArrowRight' );
+		await page.keyboard.press( 'ArrowRight' );
+
+		// Edit the link
+		await page.getByRole( 'button', { name: 'Edit' } ).click();
+
+		// Toggle both the settings to be off.
+		// Note: no need to toggle settings again because the open setting should be persisted.
+		await page.getByLabel( 'Open in new tab' ).click();
+		await page.getByLabel( 'nofollow' ).click();
+
+		// Save the link
+		await page
+			.locator( '.block-editor-link-control' )
+			.getByRole( 'button', { name: 'Save' } )
+			.click();
+
+		// Expect correct attributes to be set on the underlying link.
+		await expect.poll( editor.getBlocks ).toMatchObject( [
+			{
+				name: 'core/paragraph',
+				attributes: {
+					content: `<a href="https://wordpress.org/gutenberg">Gutenberg</a>`,
+				},
+			},
+		] );
+	} );
 } );

From 4df5573ea926474d1c50d83cff54d362e7036aec Mon Sep 17 00:00:00 2001
From: George Mamadashvili <georgemamadashvili@gmail.com>
Date: Thu, 31 Aug 2023 19:53:31 +0400
Subject: [PATCH 23/34] useMediaQuery: Avoid crashing on Safari < 14 (#54023)

* useMediaQuery: Avoid crashing on Safari < 14

* Add comment
---
 packages/compose/src/hooks/use-media-query/index.js | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/packages/compose/src/hooks/use-media-query/index.js b/packages/compose/src/hooks/use-media-query/index.js
index bd8a0e724a40b..59e6e5d99a4af 100644
--- a/packages/compose/src/hooks/use-media-query/index.js
+++ b/packages/compose/src/hooks/use-media-query/index.js
@@ -38,9 +38,10 @@ export default function useMediaQuery( query ) {
 					return () => {};
 				}
 
-				mediaQueryList.addEventListener( 'change', onStoreChange );
+				// Avoid a fatal error when browsers don't support `addEventListener` on MediaQueryList.
+				mediaQueryList.addEventListener?.( 'change', onStoreChange );
 				return () => {
-					mediaQueryList.removeEventListener(
+					mediaQueryList.removeEventListener?.(
 						'change',
 						onStoreChange
 					);

From 3c27042b0ec65125ef0bfc0dc36d2386d9545c10 Mon Sep 17 00:00:00 2001
From: Riad Benguella <benguella@gmail.com>
Date: Thu, 31 Aug 2023 17:20:47 +0100
Subject: [PATCH 24/34] Prevent the list view shortcut from typing unexpected
 characters (#54078)

---
 packages/edit-post/src/components/keyboard-shortcuts/index.js | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/packages/edit-post/src/components/keyboard-shortcuts/index.js b/packages/edit-post/src/components/keyboard-shortcuts/index.js
index 31db06794e027..18f6acc9de89f 100644
--- a/packages/edit-post/src/components/keyboard-shortcuts/index.js
+++ b/packages/edit-post/src/components/keyboard-shortcuts/index.js
@@ -225,8 +225,9 @@ function KeyboardShortcuts() {
 	} );
 
 	// Only opens the list view. Other functionality for this shortcut happens in the rendered sidebar.
-	useShortcut( 'core/edit-post/toggle-list-view', () => {
+	useShortcut( 'core/edit-post/toggle-list-view', ( event ) => {
 		if ( ! isListViewOpened() ) {
+			event.preventDefault();
 			setIsListViewOpened( true );
 		}
 	} );

From 0ecfa2b74b819e0873e838cacc199526143b6429 Mon Sep 17 00:00:00 2001
From: George Mamadashvili <georgemamadashvili@gmail.com>
Date: Thu, 31 Aug 2023 23:59:48 +0400
Subject: [PATCH 25/34] Footnotes: Add missing placeholder instructions text
 (#54056)

---
 packages/block-library/src/footnotes/edit.js | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/packages/block-library/src/footnotes/edit.js b/packages/block-library/src/footnotes/edit.js
index b8b92170fe217..d8777b77f09e2 100644
--- a/packages/block-library/src/footnotes/edit.js
+++ b/packages/block-library/src/footnotes/edit.js
@@ -23,7 +23,9 @@ export default function FootnotesEdit( { context: { postType, postId } } ) {
 				<Placeholder
 					icon={ <BlockIcon icon={ icon } /> }
 					label={ __( 'Footnotes' ) }
-					// To do: add instructions. We can't add new string in RC.
+					instructions={ __(
+						'Footnotes are not supported here. Add this block to post or page content.'
+					) }
 				/>
 			</div>
 		);

From 1170a382e4faee63b9a246cfce170888c2a22391 Mon Sep 17 00:00:00 2001
From: Ella <4710635+ellatrix@users.noreply.github.com>
Date: Thu, 31 Aug 2023 22:11:10 +0200
Subject: [PATCH 26/34] Migrate RichText e2e tests to Playwright (#53493)

---
 .../src/page-utils/press-keys.ts              |  30 +-
 .../__snapshots__/rich-text.test.js.snap      | 231 -----
 .../specs/editor/various/rich-text.test.js    | 570 ------------
 .../performance/post-editor.test.results.json |  19 +
 .../performance/site-editor.test.results.json |  60 ++
 .../specs/editor/various/rich-text.spec.js    | 826 ++++++++++++++++++
 6 files changed, 924 insertions(+), 812 deletions(-)
 delete mode 100644 packages/e2e-tests/specs/editor/various/__snapshots__/rich-text.test.js.snap
 delete mode 100644 packages/e2e-tests/specs/editor/various/rich-text.test.js
 create mode 100644 packages/e2e-tests/specs/performance/post-editor.test.results.json
 create mode 100644 packages/e2e-tests/specs/performance/site-editor.test.results.json
 create mode 100644 test/e2e/specs/editor/various/rich-text.spec.js

diff --git a/packages/e2e-test-utils-playwright/src/page-utils/press-keys.ts b/packages/e2e-test-utils-playwright/src/page-utils/press-keys.ts
index 9929ebf19d01a..3b187625fd47c 100644
--- a/packages/e2e-test-utils-playwright/src/page-utils/press-keys.ts
+++ b/packages/e2e-test-utils-playwright/src/page-utils/press-keys.ts
@@ -20,11 +20,13 @@ import {
 } from '@wordpress/keycodes';
 
 let clipboardDataHolder: {
-	plainText: string;
-	html: string;
+	'text/plain': string;
+	'text/html': string;
+	'rich-text': string;
 } = {
-	plainText: '',
-	html: '',
+	'text/plain': '',
+	'text/html': '',
+	'rich-text': '',
 };
 
 /**
@@ -38,11 +40,12 @@ let clipboardDataHolder: {
  */
 export function setClipboardData(
 	this: PageUtils,
-	{ plainText = '', html = '' }: typeof clipboardDataHolder
+	{ plainText = '', html = '' }
 ) {
 	clipboardDataHolder = {
-		plainText,
-		html,
+		'text/plain': plainText,
+		'text/html': html,
+		'rich-text': '',
 	};
 }
 
@@ -57,11 +60,15 @@ async function emulateClipboard( page: Page, type: 'copy' | 'cut' | 'paste' ) {
 			if ( _type === 'paste' ) {
 				clipboardDataTransfer.setData(
 					'text/plain',
-					_clipboardData.plainText
+					_clipboardData[ 'text/plain' ]
 				);
 				clipboardDataTransfer.setData(
 					'text/html',
-					_clipboardData.html
+					_clipboardData[ 'text/html' ]
+				);
+				clipboardDataTransfer.setData(
+					'rich-text',
+					_clipboardData[ 'rich-text' ]
 				);
 			} else {
 				const selection = canvasDoc.defaultView.getSelection()!;
@@ -91,8 +98,9 @@ async function emulateClipboard( page: Page, type: 'copy' | 'cut' | 'paste' ) {
 			);
 
 			return {
-				plainText: clipboardDataTransfer.getData( 'text/plain' ),
-				html: clipboardDataTransfer.getData( 'text/html' ),
+				'text/plain': clipboardDataTransfer.getData( 'text/plain' ),
+				'text/html': clipboardDataTransfer.getData( 'text/html' ),
+				'rich-text': clipboardDataTransfer.getData( 'rich-text' ),
 			};
 		},
 		[ type, clipboardDataHolder ] as const
diff --git a/packages/e2e-tests/specs/editor/various/__snapshots__/rich-text.test.js.snap b/packages/e2e-tests/specs/editor/various/__snapshots__/rich-text.test.js.snap
deleted file mode 100644
index 7705ff11cbff9..0000000000000
--- a/packages/e2e-tests/specs/editor/various/__snapshots__/rich-text.test.js.snap
+++ /dev/null
@@ -1,231 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`RichText should apply active formatting for inline paste 1`] = `
-"<!-- wp:paragraph -->
-<p><strong>132</strong>3</p>
-<!-- /wp:paragraph -->"
-`;
-
-exports[`RichText should apply formatting when selection is collapsed 1`] = `
-"<!-- wp:paragraph -->
-<p>Some <strong>bold</strong>.</p>
-<!-- /wp:paragraph -->"
-`;
-
-exports[`RichText should apply formatting with primary shortcut 1`] = `
-"<!-- wp:paragraph -->
-<p><strong>test</strong></p>
-<!-- /wp:paragraph -->"
-`;
-
-exports[`RichText should apply multiple formats when selection is collapsed 1`] = `
-"<!-- wp:paragraph -->
-<p><strong><em>1</em></strong>.</p>
-<!-- /wp:paragraph -->"
-`;
-
-exports[`RichText should copy/paste heading 1`] = `
-"<!-- wp:heading -->
-<h2 class="wp-block-heading">Heading</h2>
-<!-- /wp:heading -->
-
-<!-- wp:heading -->
-<h2 class="wp-block-heading">Heading</h2>
-<!-- /wp:heading -->"
-`;
-
-exports[`RichText should handle Home and End keys 1`] = `
-"<!-- wp:paragraph -->
-<p>-<strong>12</strong>+</p>
-<!-- /wp:paragraph -->"
-`;
-
-exports[`RichText should handle change in tag name gracefully 1`] = `
-"<!-- wp:heading {"level":3} -->
-<h3 class="wp-block-heading"></h3>
-<!-- /wp:heading -->"
-`;
-
-exports[`RichText should keep internal selection after blur 1`] = `
-"<!-- wp:paragraph -->
-<p>1<strong>2</strong></p>
-<!-- /wp:paragraph -->"
-`;
-
-exports[`RichText should make bold after split and merge 1`] = `
-"<!-- wp:paragraph -->
-<p>1<strong>2</strong></p>
-<!-- /wp:paragraph -->"
-`;
-
-exports[`RichText should navigate arround emoji 1`] = `
-"<!-- wp:paragraph -->
-<p>1🍓</p>
-<!-- /wp:paragraph -->"
-`;
-
-exports[`RichText should navigate consecutive format boundaries 1`] = `
-"<!-- wp:paragraph -->
-<p><strong>1</strong><em>2</em></p>
-<!-- /wp:paragraph -->"
-`;
-
-exports[`RichText should navigate consecutive format boundaries 2`] = `
-"<!-- wp:paragraph -->
-<p><strong>1</strong>-<em>2</em></p>
-<!-- /wp:paragraph -->"
-`;
-
-exports[`RichText should not format text after code backtick 1`] = `
-"<!-- wp:paragraph -->
-<p>A <code>backtick</code> and more.</p>
-<!-- /wp:paragraph -->"
-`;
-
-exports[`RichText should not lose selection direction 1`] = `
-"<!-- wp:paragraph -->
-<p><strong>1</strong>2-3</p>
-<!-- /wp:paragraph -->"
-`;
-
-exports[`RichText should not split rich text on inline paste 1`] = `
-"<!-- wp:paragraph -->
-<p>123</p>
-<!-- /wp:paragraph -->"
-`;
-
-exports[`RichText should not split rich text on inline paste with formatting 1`] = `
-"<!-- wp:paragraph -->
-<p>a1<strong>2</strong>3b</p>
-<!-- /wp:paragraph -->"
-`;
-
-exports[`RichText should not undo backtick transform with backspace after selection change 1`] = `""`;
-
-exports[`RichText should not undo backtick transform with backspace after typing 1`] = `""`;
-
-exports[`RichText should only mutate text data on input 1`] = `
-"<!-- wp:paragraph -->
-<p>1<strong>2</strong>34</p>
-<!-- /wp:paragraph -->"
-`;
-
-exports[`RichText should paste list contents into paragraph 1`] = `
-"<!-- wp:list -->
-<ul><!-- wp:list-item -->
-<li>1<!-- wp:list -->
-<ul><!-- wp:list-item -->
-<li>2</li>
-<!-- /wp:list-item --></ul>
-<!-- /wp:list --></li>
-<!-- /wp:list-item --></ul>
-<!-- /wp:list -->
-
-<!-- wp:list -->
-<ul><!-- wp:list-item -->
-<li>1<!-- wp:list -->
-<ul><!-- wp:list-item -->
-<li>2</li>
-<!-- /wp:list-item --></ul>
-<!-- /wp:list --></li>
-<!-- /wp:list-item --></ul>
-<!-- /wp:list -->"
-`;
-
-exports[`RichText should paste paragraph contents into list 1`] = `
-"<!-- wp:paragraph -->
-<p>1<br>2</p>
-<!-- /wp:paragraph -->
-
-<!-- wp:list -->
-<ul><!-- wp:list-item -->
-<li>1</li>
-<!-- /wp:list-item -->
-
-<!-- wp:list-item -->
-<li>2</li>
-<!-- /wp:list-item --></ul>
-<!-- /wp:list -->"
-`;
-
-exports[`RichText should preserve internal formatting 1`] = `
-"<!-- wp:paragraph -->
-<p><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-cyan-bluish-gray-color">1</mark></p>
-<!-- /wp:paragraph -->"
-`;
-
-exports[`RichText should preserve internal formatting 2`] = `
-"<!-- wp:paragraph -->
-<p><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-cyan-bluish-gray-color">1</mark></p>
-<!-- /wp:paragraph -->
-
-<!-- wp:paragraph -->
-<p><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-cyan-bluish-gray-color">1</mark></p>
-<!-- /wp:paragraph -->"
-`;
-
-exports[`RichText should return focus when pressing formatting button 1`] = `
-"<!-- wp:paragraph -->
-<p>Some <strong>bold</strong>.</p>
-<!-- /wp:paragraph -->"
-`;
-
-exports[`RichText should run input rules after composition end 1`] = `
-"<!-- wp:paragraph -->
-<p><code>a</code></p>
-<!-- /wp:paragraph -->"
-`;
-
-exports[`RichText should split rich text on paste 1`] = `
-"<!-- wp:paragraph -->
-<p>a</p>
-<!-- /wp:paragraph -->
-
-<!-- wp:paragraph -->
-<p>1</p>
-<!-- /wp:paragraph -->
-
-<!-- wp:paragraph -->
-<p>2</p>
-<!-- /wp:paragraph -->
-
-<!-- wp:paragraph -->
-<p>b</p>
-<!-- /wp:paragraph -->"
-`;
-
-exports[`RichText should transform backtick to code 1`] = `
-"<!-- wp:paragraph -->
-<p>A <code>backtick</code></p>
-<!-- /wp:paragraph -->"
-`;
-
-exports[`RichText should transform backtick to code 2`] = `
-"<!-- wp:paragraph -->
-<p>A \`backtick\`</p>
-<!-- /wp:paragraph -->"
-`;
-
-exports[`RichText should transform when typing backtick over selection 1`] = `
-"<!-- wp:paragraph -->
-<p>A <code>selection</code> test.</p>
-<!-- /wp:paragraph -->"
-`;
-
-exports[`RichText should transform when typing backtick over selection 2`] = `
-"<!-- wp:paragraph -->
-<p>A \`selection\` test.</p>
-<!-- /wp:paragraph -->"
-`;
-
-exports[`RichText should undo backtick transform with backspace 1`] = `
-"<!-- wp:paragraph -->
-<p>\`a\`</p>
-<!-- /wp:paragraph -->"
-`;
-
-exports[`RichText should update internal selection after fresh focus 1`] = `
-"<!-- wp:paragraph -->
-<p>1<strong>2</strong></p>
-<!-- /wp:paragraph -->"
-`;
diff --git a/packages/e2e-tests/specs/editor/various/rich-text.test.js b/packages/e2e-tests/specs/editor/various/rich-text.test.js
deleted file mode 100644
index ff651e61d52ea..0000000000000
--- a/packages/e2e-tests/specs/editor/various/rich-text.test.js
+++ /dev/null
@@ -1,570 +0,0 @@
-/**
- * WordPress dependencies
- */
-import {
-	createNewPost,
-	getEditedPostContent,
-	insertBlock,
-	clickBlockAppender,
-	pressKeyWithModifier,
-	showBlockToolbar,
-	clickBlockToolbarButton,
-	canvas,
-} from '@wordpress/e2e-test-utils';
-
-describe( 'RichText', () => {
-	beforeEach( async () => {
-		await createNewPost();
-	} );
-
-	it( 'should handle change in tag name gracefully', async () => {
-		// Regression test: The heading block changes the tag name of its
-		// RichText element. Historically this has been prone to breakage,
-		// because the Editable component prevents rerenders, so React cannot
-		// update the element by itself.
-		//
-		// See: https://github.com/WordPress/gutenberg/issues/3091
-		await insertBlock( 'Heading' );
-		await page.waitForSelector( '[aria-label="Change level"]' );
-		await page.click( '[aria-label="Change level"]' );
-		await page.click( '[aria-label="Heading 3"]' );
-
-		expect( await getEditedPostContent() ).toMatchSnapshot();
-	} );
-
-	it( 'should apply formatting with primary shortcut', async () => {
-		await clickBlockAppender();
-		await page.keyboard.type( 'test' );
-		await pressKeyWithModifier( 'primary', 'a' );
-		await pressKeyWithModifier( 'primary', 'b' );
-
-		expect( await getEditedPostContent() ).toMatchSnapshot();
-	} );
-
-	it( 'should apply formatting when selection is collapsed', async () => {
-		await clickBlockAppender();
-		await page.keyboard.type( 'Some ' );
-		// All following characters should now be bold.
-		await pressKeyWithModifier( 'primary', 'b' );
-		await page.keyboard.type( 'bold' );
-		// All following characters should no longer be bold.
-		await pressKeyWithModifier( 'primary', 'b' );
-		await page.keyboard.type( '.' );
-
-		expect( await getEditedPostContent() ).toMatchSnapshot();
-	} );
-
-	it( 'should apply multiple formats when selection is collapsed', async () => {
-		await clickBlockAppender();
-		await pressKeyWithModifier( 'primary', 'b' );
-		await pressKeyWithModifier( 'primary', 'i' );
-		await page.keyboard.type( '1' );
-		await pressKeyWithModifier( 'primary', 'i' );
-		await pressKeyWithModifier( 'primary', 'b' );
-		await page.keyboard.type( '.' );
-
-		expect( await getEditedPostContent() ).toMatchSnapshot();
-	} );
-
-	it( 'should not highlight more than one format', async () => {
-		await clickBlockAppender();
-		await pressKeyWithModifier( 'primary', 'b' );
-		await page.keyboard.type( '1' );
-		await pressKeyWithModifier( 'primary', 'b' );
-		await page.keyboard.type( ' 2' );
-		await pressKeyWithModifier( 'shift', 'ArrowLeft' );
-		await pressKeyWithModifier( 'primary', 'b' );
-
-		const count = await canvas().evaluate(
-			() =>
-				document.querySelectorAll( '*[data-rich-text-format-boundary]' )
-					.length
-		);
-
-		expect( count ).toBe( 1 );
-	} );
-
-	it( 'should return focus when pressing formatting button', async () => {
-		await clickBlockAppender();
-		await page.keyboard.type( 'Some ' );
-		await showBlockToolbar();
-		await page.click( '[aria-label="Bold"]' );
-		await page.keyboard.type( 'bold' );
-		await showBlockToolbar();
-		await page.click( '[aria-label="Bold"]' );
-		await page.keyboard.type( '.' );
-
-		expect( await getEditedPostContent() ).toMatchSnapshot();
-	} );
-
-	it( 'should transform backtick to code', async () => {
-		await clickBlockAppender();
-		await page.keyboard.type( 'A `backtick`' );
-
-		expect( await getEditedPostContent() ).toMatchSnapshot();
-
-		await pressKeyWithModifier( 'primary', 'z' );
-
-		expect( await getEditedPostContent() ).toMatchSnapshot();
-	} );
-
-	it( 'should undo backtick transform with backspace', async () => {
-		await clickBlockAppender();
-		await page.keyboard.type( '`a`' );
-		await page.keyboard.press( 'Backspace' );
-
-		// Expect "`a`" to be restored.
-		expect( await getEditedPostContent() ).toMatchSnapshot();
-	} );
-
-	it( 'should not undo backtick transform with backspace after typing', async () => {
-		await clickBlockAppender();
-		await page.keyboard.type( '`a`' );
-		await page.keyboard.type( 'b' );
-		await page.keyboard.press( 'Backspace' );
-		await page.keyboard.press( 'Backspace' );
-
-		// Expect "a" to be deleted.
-		expect( await getEditedPostContent() ).toMatchSnapshot();
-	} );
-
-	it( 'should not undo backtick transform with backspace after selection change', async () => {
-		await clickBlockAppender();
-		await page.keyboard.type( '`a`' );
-		await page.evaluate( () => new Promise( window.requestIdleCallback ) );
-		// Move inside format boundary.
-		await page.keyboard.press( 'ArrowLeft' );
-		await page.keyboard.press( 'ArrowLeft' );
-		await page.keyboard.press( 'ArrowRight' );
-		await page.keyboard.press( 'Backspace' );
-
-		// Expect "a" to be deleted.
-		expect( await getEditedPostContent() ).toMatchSnapshot();
-	} );
-
-	it( 'should not format text after code backtick', async () => {
-		await clickBlockAppender();
-		await page.keyboard.type( 'A `backtick` and more.' );
-
-		expect( await getEditedPostContent() ).toMatchSnapshot();
-	} );
-
-	it( 'should transform when typing backtick over selection', async () => {
-		await clickBlockAppender();
-		await page.keyboard.type( 'A selection test.' );
-		await page.keyboard.press( 'Home' );
-		await page.keyboard.press( 'ArrowRight' );
-		await page.keyboard.press( 'ArrowRight' );
-		await pressKeyWithModifier( 'shiftAlt', 'ArrowRight' );
-		await page.keyboard.type( '`' );
-
-		expect( await getEditedPostContent() ).toMatchSnapshot();
-
-		// Should undo the transform.
-		await pressKeyWithModifier( 'primary', 'z' );
-
-		expect( await getEditedPostContent() ).toMatchSnapshot();
-	} );
-
-	it( 'should only mutate text data on input', async () => {
-		await clickBlockAppender();
-		await page.keyboard.type( '1' );
-		await pressKeyWithModifier( 'primary', 'b' );
-		await page.keyboard.type( '2' );
-		await pressKeyWithModifier( 'primary', 'b' );
-		await page.keyboard.type( '3' );
-
-		await canvas().evaluate( () => {
-			let called;
-			const { body } = document;
-			const config = {
-				attributes: true,
-				childList: true,
-				characterData: true,
-				subtree: true,
-			};
-
-			const mutationObserver = new MutationObserver( ( records ) => {
-				if ( called || records.length > 1 ) {
-					throw new Error( 'Typing should only mutate once.' );
-				}
-
-				records.forEach( ( record ) => {
-					if ( record.type !== 'characterData' ) {
-						throw new Error(
-							`Typing mutated more than character data: ${ record.type }`
-						);
-					}
-				} );
-
-				called = true;
-			} );
-
-			mutationObserver.observe( body, config );
-
-			window.unsubscribes = [ () => mutationObserver.disconnect() ];
-
-			document.addEventListener(
-				'selectionchange',
-				() => {
-					function throwMultipleSelectionChange() {
-						throw new Error(
-							'Typing should only emit one selection change event.'
-						);
-					}
-
-					document.addEventListener(
-						'selectionchange',
-						throwMultipleSelectionChange,
-						{
-							once: true,
-						}
-					);
-
-					window.unsubscribes.push( () => {
-						document.removeEventListener(
-							'selectionchange',
-							throwMultipleSelectionChange
-						);
-					} );
-				},
-				{ once: true }
-			);
-		} );
-
-		await page.keyboard.type( '4' );
-
-		await canvas().evaluate( () => {
-			// The selection change event should be called once. If there's only
-			// one item in `window.unsubscribes`, it means that only one
-			// function is present to disconnect the `mutationObserver`.
-			if ( window.unsubscribes.length === 1 ) {
-				throw new Error(
-					'The selection change event listener was never called.'
-				);
-			}
-
-			window.unsubscribes.forEach( ( unsubscribe ) => unsubscribe() );
-		} );
-
-		expect( await getEditedPostContent() ).toMatchSnapshot();
-	} );
-
-	it( 'should not lose selection direction', async () => {
-		await clickBlockAppender();
-		await pressKeyWithModifier( 'primary', 'b' );
-		await page.keyboard.type( '1' );
-		await pressKeyWithModifier( 'primary', 'b' );
-		await page.keyboard.type( '23' );
-		await page.keyboard.press( 'ArrowLeft' );
-		await page.keyboard.down( 'Shift' );
-		await page.keyboard.press( 'ArrowLeft' );
-		await page.keyboard.press( 'ArrowLeft' );
-		await page.keyboard.press( 'ArrowRight' );
-		await page.keyboard.press( 'ArrowRight' );
-		await page.keyboard.up( 'Shift' );
-
-		// There should be no selection. The following should insert "-" without
-		// deleting the numbers.
-		await page.keyboard.type( '-' );
-
-		expect( await getEditedPostContent() ).toMatchSnapshot();
-	} );
-
-	it( 'should handle Home and End keys', async () => {
-		await page.keyboard.press( 'Enter' );
-
-		// Wait for rich text editor to load.
-		await canvas().waitForSelector( '.block-editor-rich-text__editable' );
-
-		await pressKeyWithModifier( 'primary', 'b' );
-		await page.keyboard.type( '12' );
-		await pressKeyWithModifier( 'primary', 'b' );
-
-		await page.keyboard.press( 'Home' );
-		await page.keyboard.type( '-' );
-		await page.keyboard.press( 'End' );
-		await page.keyboard.type( '+' );
-
-		expect( await getEditedPostContent() ).toMatchSnapshot();
-	} );
-
-	it( 'should update internal selection after fresh focus', async () => {
-		await page.keyboard.press( 'Enter' );
-		await page.keyboard.type( '1' );
-		await page.keyboard.press( 'Tab' );
-		await pressKeyWithModifier( 'shift', 'Tab' );
-		await pressKeyWithModifier( 'primary', 'b' );
-		await page.keyboard.type( '2' );
-		await pressKeyWithModifier( 'primary', 'b' );
-
-		expect( await getEditedPostContent() ).toMatchSnapshot();
-	} );
-
-	it( 'should keep internal selection after blur', async () => {
-		await page.keyboard.press( 'Enter' );
-		await page.keyboard.type( '1' );
-		// Simulate moving focus to a different app, then moving focus back,
-		// without selection being changed.
-		await canvas().evaluate( () => {
-			const activeElement = document.activeElement;
-			activeElement.blur();
-			activeElement.focus();
-		} );
-		// Wait for the next animation frame, see the focus event listener in
-		// RichText.
-		await page.evaluate(
-			() => new Promise( window.requestAnimationFrame )
-		);
-		await pressKeyWithModifier( 'primary', 'b' );
-		await page.keyboard.type( '2' );
-		await pressKeyWithModifier( 'primary', 'b' );
-
-		expect( await getEditedPostContent() ).toMatchSnapshot();
-	} );
-
-	it( 'should split rich text on paste', async () => {
-		await clickBlockAppender();
-		await page.keyboard.type( '1' );
-		await page.keyboard.press( 'Enter' );
-		await page.keyboard.type( '2' );
-		await pressKeyWithModifier( 'primary', 'a' );
-		await pressKeyWithModifier( 'primary', 'a' );
-		await pressKeyWithModifier( 'primary', 'x' );
-		await page.keyboard.type( 'ab' );
-		await page.keyboard.press( 'ArrowLeft' );
-		await pressKeyWithModifier( 'primary', 'v' );
-
-		expect( await getEditedPostContent() ).toMatchSnapshot();
-	} );
-
-	it( 'should not split rich text on inline paste', async () => {
-		await clickBlockAppender();
-		await page.keyboard.type( '2' );
-		await pressKeyWithModifier( 'primary', 'a' );
-		await pressKeyWithModifier( 'primary', 'x' );
-		await page.keyboard.type( '13' );
-		await page.keyboard.press( 'ArrowLeft' );
-		await pressKeyWithModifier( 'primary', 'v' );
-
-		expect( await getEditedPostContent() ).toMatchSnapshot();
-	} );
-
-	it( 'should not split rich text on inline paste with formatting', async () => {
-		await clickBlockAppender();
-		await page.keyboard.type( '1' );
-		await pressKeyWithModifier( 'primary', 'b' );
-		await page.keyboard.type( '2' );
-		await pressKeyWithModifier( 'primary', 'b' );
-		await page.keyboard.type( '3' );
-		await pressKeyWithModifier( 'primary', 'a' );
-		await pressKeyWithModifier( 'primary', 'x' );
-		await page.keyboard.type( 'ab' );
-		await page.keyboard.press( 'ArrowLeft' );
-		await pressKeyWithModifier( 'primary', 'v' );
-
-		expect( await getEditedPostContent() ).toMatchSnapshot();
-	} );
-
-	it( 'should make bold after split and merge', async () => {
-		await clickBlockAppender();
-		await page.keyboard.type( '1' );
-		await page.keyboard.press( 'Enter' );
-		await page.keyboard.press( 'Backspace' );
-		await pressKeyWithModifier( 'primary', 'b' );
-		await page.keyboard.type( '2' );
-
-		expect( await getEditedPostContent() ).toMatchSnapshot();
-	} );
-
-	it( 'should apply active formatting for inline paste', async () => {
-		await clickBlockAppender();
-		await pressKeyWithModifier( 'primary', 'b' );
-		await page.keyboard.type( '1' );
-		await page.keyboard.type( '2' );
-		await pressKeyWithModifier( 'primary', 'b' );
-		await page.keyboard.type( '3' );
-		await pressKeyWithModifier( 'shift', 'ArrowLeft' );
-		await pressKeyWithModifier( 'primary', 'c' );
-		await page.keyboard.press( 'ArrowLeft' );
-		await page.keyboard.press( 'ArrowLeft' );
-		await page.keyboard.press( 'ArrowLeft' );
-		await pressKeyWithModifier( 'primary', 'v' );
-
-		expect( await getEditedPostContent() ).toMatchSnapshot();
-	} );
-
-	it( 'should preserve internal formatting', async () => {
-		await clickBlockAppender();
-
-		// Add text and select to color.
-		await page.keyboard.type( '1' );
-		await pressKeyWithModifier( 'primary', 'a' );
-		await clickBlockToolbarButton( 'More' );
-
-		const button = await page.waitForXPath(
-			`//button[text()='Highlight']`
-		);
-		// Clicks may fail if the button is out of view. Assure it is before click.
-		await button.evaluate( ( element ) => element.scrollIntoView() );
-		await button.click();
-
-		// Wait for the popover with "Text" tab to appear.
-		await page.waitForXPath(
-			'//button[@role="tab"][@aria-selected="true"][text()="Text"]'
-		);
-		// Initial focus is on the "Text" tab.
-		// Tab to the "Custom color picker".
-		await page.keyboard.press( 'Tab' );
-		// Tab to black.
-		await page.keyboard.press( 'Tab' );
-		// Select color other than black.
-		await page.keyboard.press( 'Tab' );
-		await page.keyboard.press( 'Enter' );
-
-		expect( await getEditedPostContent() ).toMatchSnapshot();
-
-		// Dismiss color picker popover.
-		await page.keyboard.press( 'Escape' );
-
-		// Navigate to the block.
-		await page.keyboard.press( 'Tab' );
-
-		// Copy the colored text.
-		await pressKeyWithModifier( 'primary', 'c' );
-
-		// Collapse the selection to the end.
-		await page.keyboard.press( 'ArrowRight' );
-
-		// Create a new paragraph.
-		await page.keyboard.press( 'Enter' );
-
-		// Paste the colored text.
-		await pressKeyWithModifier( 'primary', 'v' );
-
-		expect( await getEditedPostContent() ).toMatchSnapshot();
-	} );
-
-	it( 'should paste paragraph contents into list', async () => {
-		await clickBlockAppender();
-
-		// Create two lines of text in a paragraph.
-		await page.keyboard.type( '1' );
-		await pressKeyWithModifier( 'shift', 'Enter' );
-		await page.keyboard.type( '2' );
-
-		// Select all and copy.
-		await pressKeyWithModifier( 'primary', 'a' );
-		await pressKeyWithModifier( 'primary', 'c' );
-
-		// Collapse the selection to the end.
-		await page.keyboard.press( 'ArrowRight' );
-
-		// Create a list.
-		await page.keyboard.press( 'Enter' );
-		await page.keyboard.type( '* ' );
-
-		// Paste paragraph contents.
-		await pressKeyWithModifier( 'primary', 'v' );
-
-		expect( await getEditedPostContent() ).toMatchSnapshot();
-	} );
-
-	it( 'should paste list contents into paragraph', async () => {
-		await clickBlockAppender();
-
-		// Create an indented list of two lines.
-		await page.keyboard.type( '* 1' );
-		await page.keyboard.press( 'Enter' );
-		await page.keyboard.type( ' 2' );
-
-		// Select all text.
-		await pressKeyWithModifier( 'primary', 'a' );
-		// Select the nested list.
-		await pressKeyWithModifier( 'primary', 'a' );
-		// Select the parent list item.
-		await pressKeyWithModifier( 'primary', 'a' );
-		// Select all the parent list item text.
-		await pressKeyWithModifier( 'primary', 'a' );
-		// Select the entire list.
-		await pressKeyWithModifier( 'primary', 'a' );
-		await pressKeyWithModifier( 'primary', 'c' );
-
-		await page.keyboard.press( 'Enter' );
-
-		// Paste paragraph contents.
-		await pressKeyWithModifier( 'primary', 'v' );
-
-		expect( await getEditedPostContent() ).toMatchSnapshot();
-	} );
-
-	it( 'should navigate arround emoji', async () => {
-		await clickBlockAppender();
-		await page.keyboard.type( '🍓' );
-		// Only one press on arrow left should be required to move in front of
-		// the emoji.
-		await page.keyboard.press( 'ArrowLeft' );
-		await page.keyboard.type( '1' );
-
-		// Expect '1🍓'.
-		expect( await getEditedPostContent() ).toMatchSnapshot();
-	} );
-
-	it( 'should run input rules after composition end', async () => {
-		await clickBlockAppender();
-		// Puppeteer doesn't support composition, so emulate it by inserting
-		// text in the DOM directly, setting selection in the right place, and
-		// firing `compositionend`.
-		// See https://github.com/puppeteer/puppeteer/issues/4981.
-		await canvas().evaluate( async () => {
-			document.activeElement.textContent = '`a`';
-			const selection = window.getSelection();
-			// The `selectionchange` and `compositionend` events should run in separate event
-			// loop ticks to process all data store updates in time. Native events would be
-			// scheduled the same way.
-			selection.selectAllChildren( document.activeElement );
-			selection.collapseToEnd();
-			await new Promise( ( r ) => setTimeout( r, 0 ) );
-			document.activeElement.dispatchEvent(
-				new CompositionEvent( 'compositionend' )
-			);
-		} );
-
-		expect( await getEditedPostContent() ).toMatchSnapshot();
-	} );
-
-	it( 'should navigate consecutive format boundaries', async () => {
-		await clickBlockAppender();
-		await pressKeyWithModifier( 'primary', 'b' );
-		await page.keyboard.type( '1' );
-		await pressKeyWithModifier( 'primary', 'b' );
-		await pressKeyWithModifier( 'primary', 'i' );
-		await page.keyboard.type( '2' );
-		await pressKeyWithModifier( 'primary', 'i' );
-
-		expect( await getEditedPostContent() ).toMatchSnapshot();
-
-		// Should move into the second format.
-		await page.keyboard.press( 'ArrowLeft' );
-		// Should move to the start of the second format.
-		await page.keyboard.press( 'ArrowLeft' );
-		// Should move between the first and second format.
-		await page.keyboard.press( 'ArrowLeft' );
-
-		await page.keyboard.type( '-' );
-
-		// Expect: <strong>1</strong>-<em>2</em>
-		expect( await getEditedPostContent() ).toMatchSnapshot();
-	} );
-
-	test( 'should copy/paste heading', async () => {
-		await insertBlock( 'Heading' );
-		await page.keyboard.type( 'Heading' );
-		await pressKeyWithModifier( 'primary', 'a' );
-		await pressKeyWithModifier( 'primary', 'c' );
-		await page.keyboard.press( 'ArrowRight' );
-		await page.keyboard.press( 'Enter' );
-		await pressKeyWithModifier( 'primary', 'v' );
-		expect( await getEditedPostContent() ).toMatchSnapshot();
-	} );
-} );
diff --git a/packages/e2e-tests/specs/performance/post-editor.test.results.json b/packages/e2e-tests/specs/performance/post-editor.test.results.json
new file mode 100644
index 0000000000000..4317d3ef68807
--- /dev/null
+++ b/packages/e2e-tests/specs/performance/post-editor.test.results.json
@@ -0,0 +1,19 @@
+{
+	"serverResponse": [],
+	"firstPaint": [],
+	"domContentLoaded": [],
+	"loaded": [],
+	"firstContentfulPaint": [],
+	"firstBlock": [],
+	"type": [
+		75.483, 96.376, 82.063, 101.192, 87.573, 56.599000000000004,
+		63.778999999999996, 93.079, 98.277, 95.365, 94.48599999999999, 55.739,
+		81.715, 66.875, 60.897, 54.249, 52.537, 58.745, 49.615
+	],
+	"typeContainer": [],
+	"focus": [],
+	"listViewOpen": [],
+	"inserterOpen": [],
+	"inserterHover": [],
+	"inserterSearch": []
+}
diff --git a/packages/e2e-tests/specs/performance/site-editor.test.results.json b/packages/e2e-tests/specs/performance/site-editor.test.results.json
new file mode 100644
index 0000000000000..a043c552f12d3
--- /dev/null
+++ b/packages/e2e-tests/specs/performance/site-editor.test.results.json
@@ -0,0 +1,60 @@
+{
+	"serverResponse": [
+		409.40000009536743, 405.59999990463257, 410.09999990463257
+	],
+	"firstPaint": [ 438.59999990463257, 447.3999996185303, 449.59999990463257 ],
+	"domContentLoaded": [ 676, 690.0999999046326, 693 ],
+	"loaded": [ 1405.6999998092651, 1400.3999996185303, 1425.9000000953674 ],
+	"firstContentfulPaint": [
+		903.2999997138977, 921.0999999046326, 925.2999997138977
+	],
+	"firstBlock": [
+		3166.7999997138977, 3206.5999999046326, 3238.4000000953674
+	],
+	"type": [
+		81.45900000000002, 37.088, 36.051, 38.596000000000004, 49.931, 40.322,
+		38.99999999999999, 34.235, 33.608999999999995, 32.88399999999999, 30.44,
+		37.113, 31.534999999999997, 33.792, 36.942, 35.251000000000005, 33.722,
+		33.471999999999994, 35.26499999999999, 29.682, 30.173,
+		30.674999999999997, 35.668000000000006, 38.278, 37.62, 37.562, 38.091,
+		32.237, 28.119999999999997, 31.342, 39.89, 37.443, 37.761, 40.262,
+		37.922, 30.727, 30.955000000000002, 36.53000000000001, 32.293, 37.299,
+		38.55800000000001, 39.85699999999999, 33.721999999999994, 30.139,
+		29.294, 31.016, 35.7, 36.839, 31.061000000000003, 29.540000000000003,
+		48.998999999999995, 35.423, 33.650000000000006, 29.404999999999998,
+		32.744, 30.584999999999997, 30.705, 31.873, 28.907, 30.516, 30.882,
+		29.257, 29.794, 31.150000000000002, 32.095, 31.066000000000003, 32.872,
+		31.894, 31.331, 31.796, 31.675, 30.427999999999997, 30.872, 30.974,
+		32.707, 31.849999999999998, 28.935, 28.441000000000003,
+		30.566000000000003, 29.014, 33.158, 32.272, 28.990000000000002, 28.76,
+		28.967000000000002, 29.418, 28.503, 31.255000000000003, 28.703,
+		30.369000000000003, 34.910000000000004, 31.03, 28.523,
+		32.361999999999995, 33.870000000000005, 30.11, 30.944000000000003,
+		28.601, 30.572999999999997, 33.216, 30.822, 28.892000000000003,
+		32.95099999999999, 31.228, 28.251, 34.89, 30.131000000000004, 29.395,
+		31.557000000000002, 28.137, 32.051, 38.242, 36.382999999999996, 35.037,
+		36.2, 31.717999999999996, 28.927999999999997, 32.540000000000006,
+		35.448, 28.292, 35.059999999999995, 31.345000000000002, 36.122, 31.69,
+		28.492, 29.308, 30.793000000000003, 28.784000000000002,
+		28.275999999999996, 36.577999999999996, 30.220000000000002, 35.832,
+		31.192, 36.102999999999994, 30.733999999999998, 30.574,
+		35.455999999999996, 29.963, 37.967, 29.323999999999998, 36.643,
+		31.200000000000003, 36.864999999999995, 32.344, 30.321, 29.214, 28.627,
+		29.71, 29.006, 36.067, 29.583, 29.562, 37.795, 30.166999999999998,
+		30.811999999999998, 33.319, 32.939, 39.233999999999995, 28.856,
+		34.81700000000001, 30.324, 33.611000000000004, 33.707,
+		30.191000000000003, 29.191, 29.23, 30.715, 29.281, 28.168,
+		33.449000000000005, 36.36600000000001, 29.086, 30.589, 29.13, 28.789,
+		29.156000000000002, 43.327, 34.439, 28.777, 30.586, 28.973000000000003,
+		30.026, 40.023, 30.203, 28.328000000000003, 30.825000000000003, 29.739,
+		31.504, 43.708000000000006, 29.296999999999997, 32.294, 31.733, 30.44,
+		28.879, 30.349999999999998, 29.466, 29.302999999999997, 30,
+		29.468999999999998, 28.740000000000002
+	],
+	"typeContainer": [],
+	"focus": [],
+	"inserterOpen": [],
+	"inserterHover": [],
+	"inserterSearch": [],
+	"listViewOpen": []
+}
diff --git a/test/e2e/specs/editor/various/rich-text.spec.js b/test/e2e/specs/editor/various/rich-text.spec.js
new file mode 100644
index 0000000000000..1644bf50252c9
--- /dev/null
+++ b/test/e2e/specs/editor/various/rich-text.spec.js
@@ -0,0 +1,826 @@
+/**
+ * WordPress dependencies
+ */
+const { test, expect } = require( '@wordpress/e2e-test-utils-playwright' );
+
+test.describe( 'RichText', () => {
+	test.beforeEach( async ( { admin } ) => {
+		await admin.createNewPost();
+	} );
+
+	test( 'should handle change in tag name gracefully', async ( {
+		page,
+		editor,
+	} ) => {
+		// Regression test: The heading block changes the tag name of its
+		// RichText element. Historically this has been prone to breakage,
+		// because the Editable component prevents rerenders, so React cannot
+		// update the element by itself.
+		//
+		// See: https://github.com/WordPress/gutenberg/issues/3091
+		await editor.insertBlock( { name: 'core/heading' } );
+		await editor.clickBlockToolbarButton( 'Change level' );
+		await page.locator( 'button[aria-label="Heading 3"]' ).click();
+
+		expect( await editor.getBlocks() ).toMatchObject( [
+			{
+				name: 'core/heading',
+				attributes: { level: 3 },
+			},
+		] );
+	} );
+
+	test( 'should apply formatting with primary shortcut', async ( {
+		page,
+		editor,
+		pageUtils,
+	} ) => {
+		await editor.canvas.click( 'role=button[name="Add default block"i]' );
+		await page.keyboard.type( 'test' );
+		await pageUtils.pressKeys( 'primary+a' );
+		await pageUtils.pressKeys( 'primary+b' );
+
+		expect( await editor.getBlocks() ).toMatchObject( [
+			{
+				name: 'core/paragraph',
+				attributes: { content: '<strong>test</strong>' },
+			},
+		] );
+	} );
+
+	test( 'should apply formatting when selection is collapsed', async ( {
+		page,
+		editor,
+		pageUtils,
+	} ) => {
+		await editor.canvas.click( 'role=button[name="Add default block"i]' );
+		await page.keyboard.type( 'Some ' );
+		await pageUtils.pressKeys( 'primary+b' );
+		await page.keyboard.type( 'bold' );
+		await pageUtils.pressKeys( 'primary+b' );
+		await page.keyboard.type( '.' );
+
+		expect( await editor.getBlocks() ).toMatchObject( [
+			{
+				name: 'core/paragraph',
+				attributes: { content: 'Some <strong>bold</strong>.' },
+			},
+		] );
+	} );
+
+	test( 'should apply multiple formats when selection is collapsed', async ( {
+		page,
+		editor,
+		pageUtils,
+	} ) => {
+		await editor.canvas.click( 'role=button[name="Add default block"i]' );
+		await pageUtils.pressKeys( 'primary+b' );
+		await pageUtils.pressKeys( 'primary+i' );
+		await page.keyboard.type( '1' );
+		await pageUtils.pressKeys( 'primary+i' );
+		await pageUtils.pressKeys( 'primary+b' );
+		await page.keyboard.type( '.' );
+
+		expect( await editor.getBlocks() ).toMatchObject( [
+			{
+				name: 'core/paragraph',
+				attributes: { content: '<strong><em>1</em></strong>.' },
+			},
+		] );
+	} );
+
+	test( 'should not highlight more than one format', async ( {
+		page,
+		editor,
+		pageUtils,
+	} ) => {
+		await page.keyboard.press( 'Enter' );
+		await pageUtils.pressKeys( 'primary+b' );
+		await page.keyboard.type( '1' );
+		await pageUtils.pressKeys( 'primary+b' );
+		await page.keyboard.type( ' 2' );
+		await pageUtils.pressKeys( 'shift+ArrowLeft' );
+		await pageUtils.pressKeys( 'primary+b' );
+
+		const count = await editor.canvas.evaluate(
+			() =>
+				document.querySelectorAll( '*[data-rich-text-format-boundary]' )
+					.length
+		);
+		expect( count ).toBe( 1 );
+	} );
+
+	test( 'should return focus when pressing formatting button', async ( {
+		page,
+		editor,
+	} ) => {
+		await editor.canvas.click( 'role=button[name="Add default block"i]' );
+		await page.keyboard.type( 'Some ' );
+		await editor.clickBlockToolbarButton( 'Bold' );
+		await page.keyboard.type( 'bold' );
+		await editor.clickBlockToolbarButton( 'Bold' );
+		await page.keyboard.type( '.' );
+
+		expect( await editor.getBlocks() ).toMatchObject( [
+			{
+				name: 'core/paragraph',
+				attributes: { content: 'Some <strong>bold</strong>.' },
+			},
+		] );
+	} );
+
+	test( 'should transform backtick to code', async ( {
+		page,
+		editor,
+		pageUtils,
+	} ) => {
+		await editor.canvas.click( 'role=button[name="Add default block"i]' );
+		await page.keyboard.type( 'A `backtick`' );
+
+		expect( await editor.getBlocks() ).toMatchObject( [
+			{
+				name: 'core/paragraph',
+				attributes: { content: 'A <code>backtick</code>' },
+			},
+		] );
+
+		await pageUtils.pressKeys( 'primary+z' );
+
+		expect( await editor.getBlocks() ).toMatchObject( [
+			{ name: 'core/paragraph' },
+		] );
+	} );
+
+	test( 'should undo backtick transform with backspace', async ( {
+		page,
+		editor,
+	} ) => {
+		await editor.canvas.click( 'role=button[name="Add default block"i]' );
+		await page.keyboard.type( '`a`' );
+		await page.keyboard.press( 'Backspace' );
+
+		expect( await editor.getBlocks() ).toMatchObject( [
+			{
+				name: 'core/paragraph',
+				attributes: { content: '`a`' },
+			},
+		] );
+	} );
+
+	test( 'should not undo backtick transform with backspace after typing', async ( {
+		page,
+		editor,
+	} ) => {
+		await editor.canvas.click( 'role=button[name="Add default block"i]' );
+		await page.keyboard.type( '`a`' );
+		await page.keyboard.type( 'b' );
+		await page.keyboard.press( 'Backspace' );
+		await page.keyboard.press( 'Backspace' );
+
+		expect( await editor.getBlocks() ).toMatchObject( [] );
+	} );
+
+	test( 'should not undo backtick transform with backspace after selection change', async ( {
+		page,
+		editor,
+	} ) => {
+		await editor.canvas.click( 'role=button[name="Add default block"i]' );
+		await page.keyboard.type( '`a`' );
+		await page.evaluate( () => new Promise( window.requestIdleCallback ) );
+		// Move inside format boundary.
+		await page.keyboard.press( 'ArrowLeft' );
+		await page.keyboard.press( 'ArrowLeft' );
+		await page.keyboard.press( 'ArrowRight' );
+		await page.keyboard.press( 'Backspace' );
+
+		expect( await editor.getBlocks() ).toMatchObject( [] );
+	} );
+
+	test( 'should not format text after code backtick', async ( {
+		page,
+		editor,
+	} ) => {
+		await editor.canvas.click( 'role=button[name="Add default block"i]' );
+		await page.keyboard.type( 'A `backtick` and more.' );
+
+		expect( await editor.getBlocks() ).toMatchObject( [
+			{
+				name: 'core/paragraph',
+				attributes: { content: 'A <code>backtick</code> and more.' },
+			},
+		] );
+	} );
+
+	test( 'should transform when typing backtick over selection', async ( {
+		page,
+		editor,
+		pageUtils,
+	} ) => {
+		await editor.canvas.click( 'role=button[name="Add default block"i]' );
+		await page.keyboard.type( 'A selection test.' );
+		await page.keyboard.press( 'Home' );
+		await page.keyboard.press( 'ArrowRight' );
+		await page.keyboard.press( 'ArrowRight' );
+		await pageUtils.pressKeys( 'shiftAlt+ArrowRight' );
+		await page.keyboard.type( '`' );
+
+		expect( await editor.getBlocks() ).toMatchObject( [
+			{
+				name: 'core/paragraph',
+				attributes: { content: 'A <code>selection</code> test.' },
+			},
+		] );
+
+		await pageUtils.pressKeys( 'primary+z' );
+
+		expect( await editor.getBlocks() ).toMatchObject( [
+			{
+				name: 'core/paragraph',
+				attributes: { content: 'A `selection` test.' },
+			},
+		] );
+	} );
+
+	test( 'should only mutate text data on input', async ( {
+		page,
+		editor,
+		pageUtils,
+	} ) => {
+		await editor.canvas.click( 'role=button[name="Add default block"i]' );
+		await page.keyboard.type( '1' );
+		await pageUtils.pressKeys( 'primary+b' );
+		await page.keyboard.type( '2' );
+		await pageUtils.pressKeys( 'primary+b' );
+		await page.keyboard.type( '3' );
+
+		await editor.canvas.evaluate( () => {
+			let called;
+			const { body } = document;
+			const config = {
+				attributes: true,
+				childList: true,
+				characterData: true,
+				subtree: true,
+			};
+
+			const mutationObserver = new MutationObserver( ( records ) => {
+				if ( called || records.length > 1 ) {
+					throw new Error( 'Typing should only mutate once.' );
+				}
+
+				records.forEach( ( record ) => {
+					if ( record.type !== 'characterData' ) {
+						throw new Error(
+							`Typing mutated more than character data: ${ record.type }`
+						);
+					}
+				} );
+
+				called = true;
+			} );
+
+			mutationObserver.observe( body, config );
+
+			window.unsubscribes = [ () => mutationObserver.disconnect() ];
+
+			document.addEventListener(
+				'selectionchange',
+				() => {
+					function throwMultipleSelectionChange() {
+						throw new Error(
+							'Typing should only emit one selection change event.'
+						);
+					}
+
+					document.addEventListener(
+						'selectionchange',
+						throwMultipleSelectionChange,
+						{
+							once: true,
+						}
+					);
+
+					window.unsubscribes.push( () => {
+						document.removeEventListener(
+							'selectionchange',
+							throwMultipleSelectionChange
+						);
+					} );
+				},
+				{ once: true }
+			);
+		} );
+
+		await page.keyboard.type( '4' );
+
+		await editor.canvas.evaluate( () => {
+			// The selection change event should be called once. If there's only
+			// one item in `window.unsubscribes`, it means that only one
+			// function is present to disconnect the `mutationObserver`.
+			if ( window.unsubscribes.length === 1 ) {
+				throw new Error(
+					'The selection change event listener was never called.'
+				);
+			}
+
+			window.unsubscribes.forEach( ( unsubscribe ) => unsubscribe() );
+		} );
+
+		expect( await editor.getBlocks() ).toMatchObject( [
+			{
+				name: 'core/paragraph',
+				attributes: { content: '1<strong>2</strong>34' },
+			},
+		] );
+	} );
+
+	test( 'should not lose selection direction', async ( {
+		page,
+		editor,
+		pageUtils,
+	} ) => {
+		await editor.canvas.click( 'role=button[name="Add default block"i]' );
+		await pageUtils.pressKeys( 'primary+b' );
+		await page.keyboard.type( '1' );
+		await pageUtils.pressKeys( 'primary+b' );
+		await page.keyboard.type( '23' );
+		await page.keyboard.press( 'ArrowLeft' );
+		await page.keyboard.down( 'Shift' );
+		await page.keyboard.press( 'ArrowLeft' );
+		await page.keyboard.press( 'ArrowLeft' );
+		await page.keyboard.press( 'ArrowRight' );
+		await page.keyboard.press( 'ArrowRight' );
+		await page.keyboard.up( 'Shift' );
+
+		// There should be no selection. The following should insert "-" without
+		// deleting the numbers.
+		await page.keyboard.type( '-' );
+
+		expect( await editor.getBlocks() ).toMatchObject( [
+			{
+				name: 'core/paragraph',
+				attributes: { content: '<strong>1</strong>2-3' },
+			},
+		] );
+	} );
+
+	test( 'should handle Home and End keys', async ( {
+		page,
+		editor,
+		pageUtils,
+	} ) => {
+		await editor.canvas.click( 'role=button[name="Add default block"i]' );
+		await pageUtils.pressKeys( 'primary+b' );
+		await page.keyboard.type( '12' );
+		await pageUtils.pressKeys( 'primary+b' );
+		await page.keyboard.press( 'Home' );
+		await page.keyboard.type( '-' );
+		await page.keyboard.press( 'End' );
+		await page.keyboard.type( '+' );
+
+		expect( await editor.getBlocks() ).toMatchObject( [
+			{
+				name: 'core/paragraph',
+				attributes: { content: '-<strong>12</strong>+' },
+			},
+		] );
+	} );
+
+	test( 'should update internal selection after fresh focus', async ( {
+		page,
+		editor,
+		pageUtils,
+	} ) => {
+		await editor.canvas.click( 'role=button[name="Add default block"i]' );
+		await page.keyboard.type( '1' );
+		await page.keyboard.press( 'Tab' );
+		await pageUtils.pressKeys( 'shift+Tab' );
+		await pageUtils.pressKeys( 'primary+b' );
+		await page.keyboard.type( '2' );
+		await pageUtils.pressKeys( 'primary+b' );
+
+		expect( await editor.getBlocks() ).toMatchObject( [
+			{
+				name: 'core/paragraph',
+				attributes: { content: '1<strong>2</strong>' },
+			},
+		] );
+	} );
+
+	test( 'should keep internal selection after blur', async ( {
+		page,
+		editor,
+		pageUtils,
+	} ) => {
+		await editor.canvas.click( 'role=button[name="Add default block"i]' );
+		await page.keyboard.type( '1' );
+		// Simulate moving focus to a different app, then moving focus back,
+		// without selection being changed.
+		await editor.canvas.evaluate( () => {
+			const activeElement = document.activeElement;
+			activeElement.blur();
+			activeElement.focus();
+		} );
+		// Wait for the next animation frame, see the focus event listener in
+		// RichText.
+		await page.evaluate(
+			() => new Promise( window.requestAnimationFrame )
+		);
+		await pageUtils.pressKeys( 'primary+b' );
+		await page.keyboard.type( '2' );
+		await pageUtils.pressKeys( 'primary+b' );
+
+		expect( await editor.getBlocks() ).toMatchObject( [
+			{
+				name: 'core/paragraph',
+				attributes: { content: '1<strong>2</strong>' },
+			},
+		] );
+	} );
+
+	test( 'should split rich text on paste', async ( {
+		page,
+		editor,
+		pageUtils,
+	} ) => {
+		await editor.canvas.click( 'role=button[name="Add default block"i]' );
+		await page.keyboard.type( '1' );
+		await page.keyboard.press( 'Enter' );
+		await page.keyboard.type( '2' );
+		await pageUtils.pressKeys( 'primary+a' );
+		await pageUtils.pressKeys( 'primary+a' );
+		await pageUtils.pressKeys( 'primary+x' );
+		await page.keyboard.type( 'ab' );
+		await page.keyboard.press( 'ArrowLeft' );
+		await pageUtils.pressKeys( 'primary+v' );
+
+		expect( await editor.getBlocks() ).toMatchObject( [
+			{
+				name: 'core/paragraph',
+				attributes: { content: 'a' },
+			},
+			{
+				name: 'core/paragraph',
+				attributes: { content: '1' },
+			},
+			{
+				name: 'core/paragraph',
+				attributes: { content: '2' },
+			},
+			{
+				name: 'core/paragraph',
+				attributes: { content: 'b' },
+			},
+		] );
+	} );
+
+	test( 'should not split rich text on inline paste', async ( {
+		page,
+		editor,
+		pageUtils,
+	} ) => {
+		await editor.canvas.click( 'role=button[name="Add default block"i]' );
+		await page.keyboard.type( '2' );
+		await pageUtils.pressKeys( 'primary+a' );
+		await pageUtils.pressKeys( 'primary+x' );
+		await page.keyboard.type( '13' );
+		await page.keyboard.press( 'ArrowLeft' );
+		await pageUtils.pressKeys( 'primary+v' );
+
+		expect( await editor.getBlocks() ).toMatchObject( [
+			{
+				name: 'core/paragraph',
+				attributes: { content: '123' },
+			},
+		] );
+	} );
+
+	test( 'should not split rich text on inline paste with formatting', async ( {
+		page,
+		editor,
+		pageUtils,
+	} ) => {
+		await editor.canvas.click( 'role=button[name="Add default block"i]' );
+		await page.keyboard.type( '1' );
+		await pageUtils.pressKeys( 'primary+b' );
+		await page.keyboard.type( '2' );
+		await pageUtils.pressKeys( 'primary+b' );
+		await page.keyboard.type( '3' );
+		await pageUtils.pressKeys( 'primary+a' );
+		await pageUtils.pressKeys( 'primary+x' );
+		await page.keyboard.type( 'ab' );
+		await page.keyboard.press( 'ArrowLeft' );
+		await pageUtils.pressKeys( 'primary+v' );
+
+		expect( await editor.getBlocks() ).toMatchObject( [
+			{
+				name: 'core/paragraph',
+				attributes: { content: 'a1<strong>2</strong>3b' },
+			},
+		] );
+	} );
+
+	test( 'should make bold after split and merge', async ( {
+		page,
+		editor,
+		pageUtils,
+	} ) => {
+		await editor.canvas.click( 'role=button[name="Add default block"i]' );
+		await page.keyboard.type( '1' );
+		await page.keyboard.press( 'Enter' );
+		await page.keyboard.press( 'Backspace' );
+		await pageUtils.pressKeys( 'primary+b' );
+		await page.keyboard.type( '2' );
+
+		expect( await editor.getBlocks() ).toMatchObject( [
+			{
+				name: 'core/paragraph',
+				attributes: { content: '1<strong>2</strong>' },
+			},
+		] );
+	} );
+
+	test( 'should apply active formatting for inline paste', async ( {
+		page,
+		editor,
+		pageUtils,
+	} ) => {
+		await editor.canvas.click( 'role=button[name="Add default block"i]' );
+		await pageUtils.pressKeys( 'primary+b' );
+		await page.keyboard.type( '1' );
+		await page.keyboard.type( '2' );
+		await pageUtils.pressKeys( 'primary+b' );
+		await page.keyboard.type( '3' );
+		await pageUtils.pressKeys( 'shift+ArrowLeft' );
+		await pageUtils.pressKeys( 'primary+c' );
+		await page.keyboard.press( 'ArrowLeft' );
+		await page.keyboard.press( 'ArrowLeft' );
+		await page.keyboard.press( 'ArrowLeft' );
+		await pageUtils.pressKeys( 'primary+v' );
+
+		expect( await editor.getBlocks() ).toMatchObject( [
+			{
+				name: 'core/paragraph',
+				attributes: { content: '<strong>132</strong>3' },
+			},
+		] );
+	} );
+
+	test( 'should preserve internal formatting', async ( {
+		page,
+		editor,
+		pageUtils,
+	} ) => {
+		await editor.canvas.click( 'role=button[name="Add default block"i]' );
+
+		// Add text and select to color.
+		await page.keyboard.type( '1' );
+		await pageUtils.pressKeys( 'primary+a' );
+		await editor.clickBlockToolbarButton( 'More' );
+		await page.locator( 'button:text("Highlight")' ).click();
+
+		// Initial focus is on the "Text" tab.
+		// Tab to the "Custom color picker".
+		await page.keyboard.press( 'Tab' );
+		// Tab to black.
+		await page.keyboard.press( 'Tab' );
+		// Select color other than black.
+		await page.keyboard.press( 'Tab' );
+		await page.keyboard.press( 'Enter' );
+
+		const result = {
+			name: 'core/paragraph',
+			attributes: {
+				content:
+					'<mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-cyan-bluish-gray-color">1</mark>',
+			},
+		};
+
+		expect( await editor.getBlocks() ).toMatchObject( [ result ] );
+
+		// Dismiss color picker popover.
+		await page.keyboard.press( 'Escape' );
+
+		// Navigate to the block.
+		await page.keyboard.press( 'Tab' );
+
+		// Copy the colored text.
+		await pageUtils.pressKeys( 'primary+c' );
+
+		// Collapse the selection to the end.
+		await page.keyboard.press( 'ArrowRight' );
+
+		// Create a new paragraph.
+		await page.keyboard.press( 'Enter' );
+
+		// Paste the colored text.
+		await pageUtils.pressKeys( 'primary+v' );
+
+		expect( await editor.getBlocks() ).toMatchObject(
+			Array( 2 ).fill( result )
+		);
+	} );
+
+	test( 'should paste paragraph contents into list', async ( {
+		page,
+		editor,
+		pageUtils,
+	} ) => {
+		await editor.canvas.click( 'role=button[name="Add default block"i]' );
+		// Create two lines of text in a paragraph.
+		await page.keyboard.type( '1' );
+		await pageUtils.pressKeys( 'shift+Enter' );
+		await page.keyboard.type( '2' );
+
+		// Select all and copy.
+		await pageUtils.pressKeys( 'primary+a' );
+		await pageUtils.pressKeys( 'primary+c' );
+
+		// Collapse the selection to the end.
+		await page.keyboard.press( 'ArrowRight' );
+
+		// Create a list.
+		await page.keyboard.press( 'Enter' );
+		await page.keyboard.type( '* ' );
+
+		// Paste paragraph contents.
+		await pageUtils.pressKeys( 'primary+v' );
+
+		expect( await editor.getBlocks() ).toMatchObject( [
+			{
+				name: 'core/paragraph',
+				attributes: { content: '1<br>2' },
+			},
+			{
+				name: 'core/list',
+				innerBlocks: [
+					{
+						name: 'core/list-item',
+						attributes: { content: '1' },
+					},
+					{
+						name: 'core/list-item',
+						attributes: { content: '2' },
+					},
+				],
+			},
+		] );
+	} );
+
+	test( 'should paste list contents into paragraph', async ( {
+		page,
+		editor,
+		pageUtils,
+	} ) => {
+		await editor.canvas.click( 'role=button[name="Add default block"i]' );
+
+		// Create an indented list of two lines.
+		await page.keyboard.type( '* 1' );
+		await page.keyboard.press( 'Enter' );
+		await page.keyboard.type( ' 2' );
+
+		// Select all text.
+		await pageUtils.pressKeys( 'primary+a' );
+		// Select the nested list.
+		await pageUtils.pressKeys( 'primary+a' );
+		// Select the parent list item.
+		await pageUtils.pressKeys( 'primary+a' );
+		// Select all the parent list item text.
+		await pageUtils.pressKeys( 'primary+a' );
+		// Select the entire list.
+		await pageUtils.pressKeys( 'primary+a' );
+		await pageUtils.pressKeys( 'primary+c' );
+
+		await page.keyboard.press( 'Enter' );
+
+		// Paste paragraph contents.
+		await pageUtils.pressKeys( 'primary+v' );
+
+		expect( await editor.getBlocks() ).toMatchObject(
+			Array( 2 ).fill( {
+				name: 'core/list',
+				innerBlocks: [
+					{
+						name: 'core/list-item',
+						attributes: { content: '1' },
+						innerBlocks: [
+							{
+								name: 'core/list',
+								innerBlocks: [
+									{
+										name: 'core/list-item',
+										attributes: { content: '2' },
+									},
+								],
+							},
+						],
+					},
+				],
+			} )
+		);
+	} );
+
+	test( 'should navigate arround emoji', async ( { page, editor } ) => {
+		await editor.canvas.click( 'role=button[name="Add default block"i]' );
+		await page.keyboard.type( '🍓' );
+		// Only one press on arrow left should be required to move in front of
+		// the emoji.
+		await page.keyboard.press( 'ArrowLeft' );
+		await page.keyboard.type( '1' );
+
+		expect( await editor.getBlocks() ).toMatchObject( [
+			{
+				name: 'core/paragraph',
+				attributes: { content: '1🍓' },
+			},
+		] );
+	} );
+
+	test( 'should run input rules after composition end', async ( {
+		editor,
+	} ) => {
+		await editor.canvas.click( 'role=button[name="Add default block"i]' );
+		// Playwright doesn't support composition, so emulate it by inserting
+		// text in the DOM directly, setting selection in the right place, and
+		// firing `compositionend`.
+		// See https://github.com/puppeteer/puppeteer/issues/4981.
+		await editor.canvas.evaluate( async () => {
+			document.activeElement.textContent = '`a`';
+			const selection = window.getSelection();
+			// The `selectionchange` and `compositionend` events should run in separate event
+			// loop ticks to process all data store updates in time. Native events would be
+			// scheduled the same way.
+			selection.selectAllChildren( document.activeElement );
+			selection.collapseToEnd();
+			await new Promise( ( r ) => setTimeout( r, 0 ) );
+			document.activeElement.dispatchEvent(
+				new CompositionEvent( 'compositionend' )
+			);
+		} );
+
+		expect( await editor.getBlocks() ).toMatchObject( [
+			{
+				name: 'core/paragraph',
+				attributes: { content: '<code>a</code>' },
+			},
+		] );
+	} );
+
+	test( 'should navigate consecutive format boundaries', async ( {
+		page,
+		editor,
+		pageUtils,
+	} ) => {
+		await editor.canvas.click( 'role=button[name="Add default block"i]' );
+		await pageUtils.pressKeys( 'primary+b' );
+		await page.keyboard.type( '1' );
+		await pageUtils.pressKeys( 'primary+b' );
+		await pageUtils.pressKeys( 'primary+i' );
+		await page.keyboard.type( '2' );
+		await pageUtils.pressKeys( 'primary+i' );
+
+		expect( await editor.getBlocks() ).toMatchObject( [
+			{
+				name: 'core/paragraph',
+				attributes: { content: '<strong>1</strong><em>2</em>' },
+			},
+		] );
+
+		// Should move into the second format.
+		await page.keyboard.press( 'ArrowLeft' );
+		// Should move to the start of the second format.
+		await page.keyboard.press( 'ArrowLeft' );
+		// Should move between the first and second format.
+		await page.keyboard.press( 'ArrowLeft' );
+
+		await page.keyboard.type( '-' );
+
+		expect( await editor.getBlocks() ).toMatchObject( [
+			{
+				name: 'core/paragraph',
+				attributes: { content: '<strong>1</strong>-<em>2</em>' },
+			},
+		] );
+	} );
+
+	test( 'should copy/paste heading', async ( {
+		page,
+		editor,
+		pageUtils,
+	} ) => {
+		await editor.insertBlock( { name: 'core/heading' } );
+		await page.keyboard.type( 'Heading' );
+		await pageUtils.pressKeys( 'primary+a' );
+		await pageUtils.pressKeys( 'primary+c' );
+		await page.keyboard.press( 'ArrowRight' );
+		await page.keyboard.press( 'Enter' );
+		await pageUtils.pressKeys( 'primary+v' );
+
+		expect( await editor.getBlocks() ).toMatchObject(
+			Array( 2 ).fill( {
+				name: 'core/heading',
+				attributes: { content: 'Heading' },
+			} )
+		);
+	} );
+} );

From 9f4aaea47122a4212b0e1fcd99da097363ad37b3 Mon Sep 17 00:00:00 2001
From: Ella <4710635+ellatrix@users.noreply.github.com>
Date: Thu, 31 Aug 2023 22:46:07 +0200
Subject: [PATCH 27/34] Rich text: fix useAnchor (remove nextElementSibling)
 (#54013)

---
 packages/rich-text/src/component/use-anchor.js | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)

diff --git a/packages/rich-text/src/component/use-anchor.js b/packages/rich-text/src/component/use-anchor.js
index 5cc62a2de4ffd..af9e0e4d87dc3 100644
--- a/packages/rich-text/src/component/use-anchor.js
+++ b/packages/rich-text/src/component/use-anchor.js
@@ -20,8 +20,21 @@ import { useState, useLayoutEffect } from '@wordpress/element';
 function getFormatElement( range, editableContentElement, tagName, className ) {
 	let element = range.startContainer;
 
-	// If the caret is right before the element, select the next element.
-	element = element.nextElementSibling || element;
+	// Even if the active format is defined, the actualy DOM range's start
+	// container may be outside of the format's DOM element:
+	// `a‸<strong>b</strong>` (DOM) while visually it's `a<strong>‸b</strong>`.
+	// So at a given selection index, start with the deepest format DOM element.
+	if (
+		element.nodeType === element.TEXT_NODE &&
+		range.startOffset === element.length &&
+		element.nextSibling
+	) {
+		element = element.nextSibling;
+
+		while ( element.firstChild ) {
+			element = element.firstChild;
+		}
+	}
 
 	if ( element.nodeType !== element.ELEMENT_NODE ) {
 		element = element.parentElement;

From 8cd5bff820fb8537cbe7cda3d038e831fdd3f75a Mon Sep 17 00:00:00 2001
From: Birgit Pauli-Haack <birgit.pauli@gmail.com>
Date: Thu, 31 Aug 2023 23:07:09 +0200
Subject: [PATCH 28/34] Update readme.txt "tested up to 6.3" (#54072)

---
 readme.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/readme.txt b/readme.txt
index 09a9d2b49e6f2..668ccca48a5dc 100644
--- a/readme.txt
+++ b/readme.txt
@@ -1,6 +1,6 @@
 === Gutenberg ===
 Contributors: matveb, joen, karmatosed
-Tested up to: 6.2
+Tested up to: 6.3
 Stable tag: V.V.V
 License: GPLv2 or later
 License URI: http://www.gnu.org/licenses/gpl-2.0.html

From 607ef9092bfaffdca1f4f7e6e13d63c1ba9a167e Mon Sep 17 00:00:00 2001
From: Matias Benedetto <matias.benedetto@gmail.com>
Date: Thu, 31 Aug 2023 18:12:43 -0300
Subject: [PATCH 29/34] Font Library: Font Collection backend (#54098)

Add extensibility capabilities to the Font Library.
Provides a way to provide collections of typographic fonts by code.

---------

Co-authored-by: Tonya Mork <tonya.mork@automattic.com>
---
 .../font-library/class-wp-font-collection.php |  95 +++++++++++++++++
 .../font-library/class-wp-font-library.php    |  55 ++++++++++
 .../class-wp-rest-font-library-controller.php |  60 +++++++++++
 .../fonts/font-library/font-library.php       |  24 ++++-
 lib/load.php                                  |   1 +
 .../wpFontCollection/__construct.php          |  92 ++++++++++++++++
 .../font-library/wpFontCollection/getData.php |  52 +++++++++
 .../fonts/font-library/wpFontFamily/base.php  |   1 +
 .../wpFontLibrary/getFontCollection.php       |  36 +++++++
 .../wpFontLibrary/getFontCollections.php      |  45 ++++++++
 .../wpFontLibrary/registerFontCollection.php  |  77 ++++++++++++++
 .../wpRestFontLibraryController/base.php      |  34 ++++++
 .../getFontCollection.php                     |  48 +++++++++
 .../getFontCollections.php                    |  46 ++++++++
 .../installFonts.php}                         |  87 +++------------
 .../registerRoutes.php                        |  29 +++++
 .../uninstallFonts.php                        | 100 ++++++++++++++++++
 17 files changed, 806 insertions(+), 76 deletions(-)
 create mode 100644 lib/experimental/fonts/font-library/class-wp-font-collection.php
 create mode 100644 phpunit/tests/fonts/font-library/wpFontCollection/__construct.php
 create mode 100644 phpunit/tests/fonts/font-library/wpFontCollection/getData.php
 create mode 100644 phpunit/tests/fonts/font-library/wpFontLibrary/getFontCollection.php
 create mode 100644 phpunit/tests/fonts/font-library/wpFontLibrary/getFontCollections.php
 create mode 100644 phpunit/tests/fonts/font-library/wpFontLibrary/registerFontCollection.php
 create mode 100644 phpunit/tests/fonts/font-library/wpRestFontLibraryController/base.php
 create mode 100644 phpunit/tests/fonts/font-library/wpRestFontLibraryController/getFontCollection.php
 create mode 100644 phpunit/tests/fonts/font-library/wpRestFontLibraryController/getFontCollections.php
 rename phpunit/tests/fonts/font-library/{class-wp-rest-font-library-controller.php => wpRestFontLibraryController/installFonts.php} (82%)
 create mode 100644 phpunit/tests/fonts/font-library/wpRestFontLibraryController/registerRoutes.php
 create mode 100644 phpunit/tests/fonts/font-library/wpRestFontLibraryController/uninstallFonts.php

diff --git a/lib/experimental/fonts/font-library/class-wp-font-collection.php b/lib/experimental/fonts/font-library/class-wp-font-collection.php
new file mode 100644
index 0000000000000..1620467d36dfa
--- /dev/null
+++ b/lib/experimental/fonts/font-library/class-wp-font-collection.php
@@ -0,0 +1,95 @@
+<?php
+/**
+ * Font Collection class.
+ *
+ * This file contains the Font Collection class definition.
+ *
+ * @package    WordPress
+ * @subpackage Font Library
+ * @since      6.4.0
+ */
+
+if ( class_exists( 'WP_Font_Collection' ) ) {
+	return;
+}
+
+/**
+ * Font Collection class.
+ *
+ * @since 6.4.0
+ */
+class WP_Font_Collection {
+
+	/**
+	 * Font collection configuration.
+	 *
+	 * @since 6.4.0
+	 *
+	 * @var array
+	 */
+	private $config;
+
+	/**
+	 * WP_Font_Collection constructor.
+	 *
+	 * @since 6.4.0
+	 *
+	 * @param array $config Font collection config options.
+	 *                      See {@see wp_register_font_collection()} for the supported fields.
+	 * @throws Exception If the required parameters are missing.
+	 */
+	public function __construct( $config ) {
+		if ( empty( $config ) || ! is_array( $config ) ) {
+			throw new Exception( 'Font Collection config options is required as a non-empty array.' );
+		}
+
+		if ( empty( $config['id'] ) || ! is_string( $config['id'] ) ) {
+			throw new Exception( 'Font Collection config ID is required as a non-empty string.' );
+		}
+
+		if ( empty( $config['name'] ) || ! is_string( $config['name'] ) ) {
+			throw new Exception( 'Font Collection config name is required as a non-empty string.' );
+		}
+
+		if ( empty( $config['data_json_file'] ) || ! is_string( $config['data_json_file'] ) ) {
+			throw new Exception( 'Font Collection config "data_json_file" option is required as a non-empty string.' );
+		}
+
+		$this->config = $config;
+	}
+
+	/**
+	 * Gets the font collection config.
+	 *
+	 * @since 6.4.0
+	 *
+	 * @return array An array containing the font collection config.
+	 */
+	public function get_config() {
+		return $this->config;
+	}
+
+	/**
+	 * Gets the font collection data.
+	 *
+	 * @since 6.4.0
+	 *
+	 * @return array|WP_Error An array containing the list of font families in theme.json format on success,
+	 *                        else an instance of WP_Error on failure.
+	 */
+	public function get_data() {
+		if ( ! file_exists( $this->config['data_json_file'] ) ) {
+			return new WP_Error( 'font_collection_file_error', __( 'Font Collection data JSON file does not exist.', 'gutenberg' ) );
+		}
+
+		$data = file_get_contents( $this->config['data_json_file'] );
+		if ( empty( $data ) ) {
+			return new WP_Error( 'font_collection_read_error', __( 'Error reading the Font Collection data JSON file contents.', 'gutenberg' ) );
+		}
+
+		$collection_data         = $this->get_config();
+		$collection_data['data'] = $data;
+		unset( $collection_data['data_json_file'] );
+		return $collection_data;
+	}
+}
diff --git a/lib/experimental/fonts/font-library/class-wp-font-library.php b/lib/experimental/fonts/font-library/class-wp-font-library.php
index 6459a91873fea..193ee3b2e5b7d 100644
--- a/lib/experimental/fonts/font-library/class-wp-font-library.php
+++ b/lib/experimental/fonts/font-library/class-wp-font-library.php
@@ -27,6 +27,61 @@ class WP_Font_Library {
 		'woff2' => 'font/woff2',
 	);
 
+	/**
+	 * Font collections.
+	 *
+	 * @since 6.4.0
+	 *
+	 * @var array
+	 */
+	private static $collections = array();
+
+	/**
+	 * Register a new font collection.
+	 *
+	 * @since 6.4.0
+	 *
+	 * @param array $config Font collection config options.
+	 *                      See {@see wp_register_font_collection()} for the supported fields.
+	 * @return WP_Font_Collection|WP_Error A font collection is it was registered successfully and a WP_Error otherwise.
+	 */
+	public static function register_font_collection( $config ) {
+		$new_collection = new WP_Font_Collection( $config );
+
+		if ( isset( self::$collections[ $config['id'] ] ) ) {
+			return new WP_Error( 'font_collection_registration_error', 'Font collection already registered.' );
+		}
+
+		self::$collections[ $config['id'] ] = $new_collection;
+		return $new_collection;
+	}
+
+	/**
+	 * Gets all the font collections available.
+	 *
+	 * @since 6.4.0
+	 *
+	 * @return array List of font collections.
+	 */
+	public static function get_font_collections() {
+		return self::$collections;
+	}
+
+	/**
+	 * Gets a font collection.
+	 *
+	 * @since 6.4.0
+	 *
+	 * @param string $id Font collection id.
+	 * @return array List of font collections.
+	 */
+	public static function get_font_collection( $id ) {
+		if ( array_key_exists( $id, self::$collections ) ) {
+			return self::$collections[ $id ];
+		}
+		return new WP_Error( 'font_collection_not_found', 'Font collection not found.' );
+	}
+
 	/**
 	 * Gets the upload directory for fonts.
 	 *
diff --git a/lib/experimental/fonts/font-library/class-wp-rest-font-library-controller.php b/lib/experimental/fonts/font-library/class-wp-rest-font-library-controller.php
index 59e42d8716124..3096f11759691 100644
--- a/lib/experimental/fonts/font-library/class-wp-rest-font-library-controller.php
+++ b/lib/experimental/fonts/font-library/class-wp-rest-font-library-controller.php
@@ -67,6 +67,66 @@ public function register_routes() {
 				),
 			)
 		);
+
+		register_rest_route(
+			$this->namespace,
+			'/' . $this->rest_base . '/collections',
+			array(
+				array(
+					'methods'             => WP_REST_Server::READABLE,
+					'callback'            => array( $this, 'get_font_collections' ),
+					'permission_callback' => array( $this, 'update_font_library_permissions_check' ),
+				),
+			)
+		);
+
+		register_rest_route(
+			$this->namespace,
+			'/' . $this->rest_base . '/collections' . '/(?P<id>[\/\w-]+)',
+			array(
+				array(
+					'methods'             => WP_REST_Server::READABLE,
+					'callback'            => array( $this, 'get_font_collection' ),
+					'permission_callback' => array( $this, 'update_font_library_permissions_check' ),
+				),
+			)
+		);
+	}
+
+	/**
+	 * Gets a font collection.
+	 *
+	 * @since 6.4.0
+	 *
+	 * @param WP_REST_Request $request Full details about the request.
+	 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
+	 */
+	public function get_font_collection( $request ) {
+		$id         = $request->get_param( 'id' );
+		$collection = WP_Font_Library::get_font_collection( $id );
+
+		if ( is_wp_error( $collection ) ) {
+			$collection->add_data( array( 'status' => 404 ) );
+			return $collection;
+		}
+
+		return new WP_REST_Response( $collection->get_data() );
+	}
+
+	/**
+	 * Gets the font collections available.
+	 *
+	 * @since 6.4.0
+	 *
+	 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
+	 */
+	public function get_font_collections() {
+		$collections = array();
+		foreach ( WP_Font_Library::get_font_collections() as $collection ) {
+			$collections[] = $collection->get_config();
+		}
+
+		return new WP_REST_Response( $collections, 200 );
 	}
 
 	/**
diff --git a/lib/experimental/fonts/font-library/font-library.php b/lib/experimental/fonts/font-library/font-library.php
index 96a33923917ec..0df008a275094 100644
--- a/lib/experimental/fonts/font-library/font-library.php
+++ b/lib/experimental/fonts/font-library/font-library.php
@@ -19,7 +19,7 @@
  *
  * @since 6.4.0
  */
-function gutenberg_init_font_library() {
+function gutenberg_init_font_library_routes() {
 	// @core-merge: This code will go into Core's `create_initial_post_types()`.
 	$args = array(
 		'public'       => true,
@@ -33,5 +33,25 @@ function gutenberg_init_font_library() {
 	$font_library_controller->register_routes();
 }
 
-add_action( 'rest_api_init', 'gutenberg_init_font_library' );
+add_action( 'rest_api_init', 'gutenberg_init_font_library_routes' );
 
+
+if ( ! function_exists( 'wp_register_font_collection' ) ) {
+	/**
+	 * Registers a new Font Collection in the Font Library.
+	 *
+	 * @since 6.4.0
+	 *
+	 * @param string[] $config {
+	 *     Font collection associative array of configuration options.
+	 *
+	 *     @type string $id             The font collection's unique ID.
+	 *     @type string $data_json_file The font collection's data JSON file.
+	 * }
+	 * @return WP_Font_Collection|WP_Error A font collection is it was registered
+	 *                                     successfully, else WP_Error.
+	 */
+	function wp_register_font_collection( $config ) {
+		return WP_Font_Library::register_font_collection( $config );
+	}
+}
diff --git a/lib/load.php b/lib/load.php
index 6560ef566d444..ede4a8674c719 100644
--- a/lib/load.php
+++ b/lib/load.php
@@ -165,6 +165,7 @@ function gutenberg_is_experiment_enabled( $name ) {
 	( defined( 'FONTS_LIBRARY_ENABLE' ) && FONTS_LIBRARY_ENABLE )
 ) {
 	// Loads the Font Library.
+	require __DIR__ . '/experimental/fonts/font-library/class-wp-font-collection.php';
 	require __DIR__ . '/experimental/fonts/font-library/class-wp-font-library.php';
 	require __DIR__ . '/experimental/fonts/font-library/class-wp-font-family-utils.php';
 	require __DIR__ . '/experimental/fonts/font-library/class-wp-font-family.php';
diff --git a/phpunit/tests/fonts/font-library/wpFontCollection/__construct.php b/phpunit/tests/fonts/font-library/wpFontCollection/__construct.php
new file mode 100644
index 0000000000000..0682425dd6282
--- /dev/null
+++ b/phpunit/tests/fonts/font-library/wpFontCollection/__construct.php
@@ -0,0 +1,92 @@
+<?php
+/**
+ * Test WP_Font_Collection::__construct().
+ *
+ * @package WordPress
+ * @subpackage Font Library
+ *
+ * @group fonts
+ * @group font-library
+ *
+ * @covers WP_Font_Collection::__construct
+ */
+class Tests_Fonts_WpFontCollection_Construct extends WP_UnitTestCase {
+
+	public function test_should_initialize_data() {
+		$property = new ReflectionProperty( WP_Font_Collection::class, 'config' );
+		$property->setAccessible( true );
+
+		$config          = array(
+			'id'             => 'my-collection',
+			'name'           => 'My Collection',
+			'description'    => 'My collection description',
+			'data_json_file' => 'my-collection-data.json',
+		);
+		$font_collection = new WP_Font_Collection( $config );
+
+		$actual = $property->getValue( $font_collection );
+		$property->setAccessible( false );
+
+		$this->assertSame( $config, $actual );
+	}
+
+	/**
+	 * @dataProvider data_should_throw_exception
+	 *
+	 * @param mixed  $config Config of the font collection.
+	 * @param string $expected_exception_message Expected exception message.
+	 */
+	public function test_should_throw_exception( $config, $expected_exception_message ) {
+		$this->expectException( 'Exception' );
+		$this->expectExceptionMessage( $expected_exception_message );
+		new WP_Font_Collection( $config );
+	}
+
+	/**
+	 * Data provider.
+	 *
+	 * @return array
+	 */
+	public function data_should_throw_exception() {
+		return array(
+			'no id'                           => array(
+				array(
+					'name'           => 'My Collection',
+					'description'    => 'My collection description',
+					'data_json_file' => 'my-collection-data.json',
+				),
+				'Font Collection config ID is required as a non-empty string.',
+			),
+
+			'no config'                       => array(
+				'',
+				'Font Collection config options is required as a non-empty array.',
+			),
+
+			'empty array'                     => array(
+				array(),
+				'Font Collection config options is required as a non-empty array.',
+			),
+
+			'boolean instead of config array' => array(
+				false,
+				'Font Collection config options is required as a non-empty array.',
+			),
+
+			'null instead of config array'    => array(
+				null,
+				'Font Collection config options is required as a non-empty array.',
+			),
+
+			'missing data_json_file'          => array(
+				array(
+					'id'          => 'my-collection',
+					'name'        => 'My Collection',
+					'description' => 'My collection description',
+				),
+				'Font Collection config "data_json_file" option is required as a non-empty string.',
+			),
+
+		);
+	}
+}
diff --git a/phpunit/tests/fonts/font-library/wpFontCollection/getData.php b/phpunit/tests/fonts/font-library/wpFontCollection/getData.php
new file mode 100644
index 0000000000000..55d12ac1c42fd
--- /dev/null
+++ b/phpunit/tests/fonts/font-library/wpFontCollection/getData.php
@@ -0,0 +1,52 @@
+<?php
+/**
+ * Test WP_Font_Collection::get_data().
+ *
+ * @package WordPress
+ * @subpackage Font Library
+ *
+ * @group fonts
+ * @group font-library
+ *
+ * @covers WP_Font_Collection::get_data
+ */
+class Tests_Fonts_WpFontCollection_GetData extends WP_UnitTestCase {
+
+	/**
+	 * @dataProvider data_should_get_data
+	 *
+	 * @param array $config Font collection config options.
+	 * @param array $expected_data Expected data.
+	 */
+	public function test_should_get_data( $config, $expected_data ) {
+		$collection = new WP_Font_Collection( $config );
+		$this->assertSame( $expected_data, $collection->get_data() );
+	}
+
+	/**
+	 * Data provider.
+	 *
+	 * @return array[]
+	 */
+	public function data_should_get_data() {
+		$mock_file = wp_tempnam( 'my-collection-data-' );
+		file_put_contents( $mock_file, '{"this is mock data":true}' );
+
+		return array(
+			'with a data_json_file' => array(
+				'config'        => array(
+					'id'             => 'my-collection',
+					'name'           => 'My Collection',
+					'description'    => 'My collection description',
+					'data_json_file' => $mock_file,
+				),
+				'expected_data' => array(
+					'id'          => 'my-collection',
+					'name'        => 'My Collection',
+					'description' => 'My collection description',
+					'data'        => '{"this is mock data":true}',
+				),
+			),
+		);
+	}
+}
diff --git a/phpunit/tests/fonts/font-library/wpFontFamily/base.php b/phpunit/tests/fonts/font-library/wpFontFamily/base.php
index 9253885c6b3b2..a630954bb61bf 100644
--- a/phpunit/tests/fonts/font-library/wpFontFamily/base.php
+++ b/phpunit/tests/fonts/font-library/wpFontFamily/base.php
@@ -30,6 +30,7 @@ public static function set_up_before_class() {
 
 		$uploads_dir       = wp_upload_dir();
 		static::$fonts_dir = $uploads_dir['basedir'] . '/fonts/';
+		wp_mkdir_p( static::$fonts_dir );
 	}
 
 	public function set_up() {
diff --git a/phpunit/tests/fonts/font-library/wpFontLibrary/getFontCollection.php b/phpunit/tests/fonts/font-library/wpFontLibrary/getFontCollection.php
new file mode 100644
index 0000000000000..1d4573cbe060c
--- /dev/null
+++ b/phpunit/tests/fonts/font-library/wpFontLibrary/getFontCollection.php
@@ -0,0 +1,36 @@
+<?php
+/**
+ * Test WP_Font_Library::get_font_collections().
+ *
+ * @package WordPress
+ * @subpackage Font Library
+ *
+ * @group fonts
+ * @group font-library
+ *
+ * @covers WP_Font_Library::get_font_collection
+ */
+class Tests_Fonts_WpFontLibrary_GetFontCollection extends WP_UnitTestCase {
+
+	public static function set_up_before_class() {
+		$my_font_collection_config = array(
+			'id'             => 'my-font-collection',
+			'name'           => 'My Font Collection',
+			'description'    => 'Demo about how to a font collection to your WordPress Font Library.',
+			'data_json_file' => path_join( __DIR__, 'my-font-collection-data.json' ),
+		);
+
+		wp_register_font_collection( $my_font_collection_config );
+	}
+
+	public function test_should_get_font_collection() {
+		$font_collection = WP_Font_Library::get_font_collection( 'my-font-collection' );
+		$this->assertInstanceOf( 'WP_Font_Collection', $font_collection );
+	}
+
+	public function test_should_get_no_font_collection_if_the_id_is_not_registered() {
+		$font_collection = WP_Font_Library::get_font_collection( 'not-registered-font-collection' );
+		$this->assertWPError( $font_collection );
+	}
+
+}
diff --git a/phpunit/tests/fonts/font-library/wpFontLibrary/getFontCollections.php b/phpunit/tests/fonts/font-library/wpFontLibrary/getFontCollections.php
new file mode 100644
index 0000000000000..4bda590d6e8fd
--- /dev/null
+++ b/phpunit/tests/fonts/font-library/wpFontLibrary/getFontCollections.php
@@ -0,0 +1,45 @@
+<?php
+/**
+ * Test WP_Font_Library::get_font_collections().
+ *
+ * @package WordPress
+ * @subpackage Font Library
+ *
+ * @group fonts
+ * @group font-library
+ *
+ * @covers WP_Font_Library::get_font_collections
+ */
+class Tests_Fonts_WpFontLibrary_GetFontCollections extends WP_UnitTestCase {
+
+	public static function set_up_before_class() {
+		$my_font_collection_config = array(
+			'id'             => 'my-font-collection',
+			'name'           => 'My Font Collection',
+			'description'    => 'Demo about how to a font collection to your WordPress Font Library.',
+			'data_json_file' => path_join( __DIR__, 'my-font-collection-data.json' ),
+		);
+
+		wp_register_font_collection( $my_font_collection_config );
+
+		$another_font_collection_config = array(
+			'id'             => 'another-font-collection',
+			'name'           => 'Another Font Collection',
+			'description'    => 'Demo about how to a font collection to your WordPress Font Library.',
+			'data_json_file' => path_join( __DIR__, 'another-font-collection-data.json' ),
+		);
+
+		wp_register_font_collection( $another_font_collection_config );
+	}
+
+	public function test_should_get_font_collections() {
+		$font_collections = WP_Font_Library::get_font_collections();
+		$this->assertNotEmpty( $font_collections, 'Sould return an array of font collections.' );
+		$this->assertCount( 2, $font_collections, 'Should return an array with one font collection.' );
+
+		$this->assertArrayHasKey( 'my-font-collection', $font_collections, 'The array should have the key of the registered font collection id.' );
+		$this->assertInstanceOf( 'WP_Font_Collection', $font_collections['my-font-collection'], 'The value of the array $font_collections[id] should be an instance of WP_Font_Collection class.' );
+		$this->assertArrayHasKey( 'another-font-collection', $font_collections, 'The array should have the key of the registered font collection id.' );
+		$this->assertInstanceOf( 'WP_Font_Collection', $font_collections['another-font-collection'], 'The value of the array $font_collections[id] should be an instance of WP_Font_Collection class.' );
+	}
+}
diff --git a/phpunit/tests/fonts/font-library/wpFontLibrary/registerFontCollection.php b/phpunit/tests/fonts/font-library/wpFontLibrary/registerFontCollection.php
new file mode 100644
index 0000000000000..61b5eab873d6c
--- /dev/null
+++ b/phpunit/tests/fonts/font-library/wpFontLibrary/registerFontCollection.php
@@ -0,0 +1,77 @@
+<?php
+/**
+ * Test WP_Font_Library::register_font_collection().
+ *
+ * @package WordPress
+ * @subpackage Font Library
+ *
+ * @group fonts
+ * @group font-library
+ *
+ * @covers WP_Font_Library::register_font_collection
+ */
+class Tests_Fonts_WpFontLibrary_RegisterFontCollection extends WP_UnitTestCase {
+
+	public function test_should_register_font_collection() {
+		$config     = array(
+			'id'             => 'my-collection',
+			'name'           => 'My Collection',
+			'description'    => 'My Collection Description',
+			'data_json_file' => 'my-collection-data.json',
+		);
+		$collection = WP_Font_Library::register_font_collection( $config );
+		$this->assertInstanceOf( 'WP_Font_Collection', $collection );
+	}
+
+	public function test_should_return_error_if_id_is_missing() {
+		$config = array(
+			'name'           => 'My Collection',
+			'description'    => 'My Collection Description',
+			'data_json_file' => 'my-collection-data.json',
+		);
+		$this->expectException( 'Exception' );
+		$this->expectExceptionMessage( 'Font Collection config ID is required as a non-empty string.' );
+		WP_Font_Library::register_font_collection( $config );
+	}
+
+	public function test_should_return_error_if_name_is_missing() {
+		$config = array(
+			'id'             => 'my-collection',
+			'description'    => 'My Collection Description',
+			'data_json_file' => 'my-collection-data.json',
+		);
+		$this->expectException( 'Exception' );
+		$this->expectExceptionMessage( 'Font Collection config name is required as a non-empty string.' );
+		WP_Font_Library::register_font_collection( $config );
+	}
+
+	public function test_should_return_error_if_config_is_empty() {
+		$config = array();
+		$this->expectException( 'Exception' );
+		$this->expectExceptionMessage( 'Font Collection config options is required as a non-empty array.' );
+		WP_Font_Library::register_font_collection( $config );
+	}
+
+	public function test_should_return_error_if_id_is_repeated() {
+		$config1 = array(
+			'id'             => 'my-collection-1',
+			'name'           => 'My Collection 1',
+			'description'    => 'My Collection 1 Description',
+			'data_json_file' => 'my-collection-1-data.json',
+		);
+		$config2 = array(
+			'id'             => 'my-collection-1',
+			'name'           => 'My Collection 2',
+			'description'    => 'My Collection 2 Description',
+			'data_json_file' => 'my-collection-2-data.json',
+		);
+
+		// Register first collection.
+		$collection1 = WP_Font_Library::register_font_collection( $config1 );
+		$this->assertInstanceOf( 'WP_Font_Collection', $collection1, 'A collection should be registered.' );
+
+		// Try to register a second collection with same id.
+		$collection2 = WP_Font_Library::register_font_collection( $config2 );
+		$this->assertWPError( $collection2, 'Second collection with the same id should fail.' );
+	}
+}
diff --git a/phpunit/tests/fonts/font-library/wpRestFontLibraryController/base.php b/phpunit/tests/fonts/font-library/wpRestFontLibraryController/base.php
new file mode 100644
index 0000000000000..f1e712bab85b4
--- /dev/null
+++ b/phpunit/tests/fonts/font-library/wpRestFontLibraryController/base.php
@@ -0,0 +1,34 @@
+<?php
+/**
+ * Test Case for WP_REST_Font_Library_Controller tests.
+ *
+ * @package WordPress
+ * @subpackage Font Library
+ */
+abstract class WP_REST_Font_Library_Controller_UnitTestCase extends WP_UnitTestCase {
+
+	public function set_up() {
+		parent::set_up();
+
+		// Create a user with administrator role.
+		$admin_id = $this->factory->user->create(
+			array(
+				'role' => 'administrator',
+			)
+		);
+		wp_set_current_user( $admin_id );
+	}
+
+	/**
+	 * Tear down each test method.
+	 */
+	public function tear_down() {
+		parent::tear_down();
+
+		// Reset $collections static property of WP_Font_Library class.
+		$reflection = new ReflectionClass( 'WP_Font_Library' );
+		$property   = $reflection->getProperty( 'collections' );
+		$property->setAccessible( true );
+		$property->setValue( array() );
+	}
+}
diff --git a/phpunit/tests/fonts/font-library/wpRestFontLibraryController/getFontCollection.php b/phpunit/tests/fonts/font-library/wpRestFontLibraryController/getFontCollection.php
new file mode 100644
index 0000000000000..2b605302e37ff
--- /dev/null
+++ b/phpunit/tests/fonts/font-library/wpRestFontLibraryController/getFontCollection.php
@@ -0,0 +1,48 @@
+<?php
+/**
+ * Test WP_REST_Font_Library_Controller::get_font_collection().
+ *
+ * @package WordPress
+ * @subpackage Font Library
+ *
+ * @group fonts
+ * @group font-library
+ *
+ * @covers WP_REST_Font_Library_Controller::get_font_collection
+ */
+
+class Tests_Fonts_WPRESTFontLibraryController_GetFontCollection extends WP_REST_Font_Library_Controller_UnitTestCase {
+
+	/**
+	 * Register a mock collection.
+	 */
+	public static function wpSetupBeforeClass() {
+		// Mock font collection data file.
+		$mock_file = wp_tempnam( 'one-collection-' );
+		file_put_contents( $mock_file, '{"this is mock data":true}' );
+
+		// Add a font collection.
+		$config = array(
+			'id'             => 'one-collection',
+			'name'           => 'One Font Collection',
+			'description'    => 'Demo about how to a font collection to your WordPress Font Library.',
+			'data_json_file' => $mock_file,
+		);
+		wp_register_font_collection( $config );
+	}
+
+	public function test_get_font_collection() {
+		$request  = new WP_REST_Request( 'GET', '/wp/v2/fonts/collections/one-collection' );
+		$response = rest_get_server()->dispatch( $request );
+		$data     = $response->get_data();
+		$this->assertSame( 200, $response->get_status(), 'The response status is not 200.' );
+		$this->assertArrayHasKey( 'data', $data, 'The response data does not have the key with the file data.' );
+	}
+
+	public function test_get_non_existing_collection_should_return_404() {
+		$request  = new WP_REST_Request( 'GET', '/wp/v2/fonts/collections/non-existing-collection-id' );
+		$response = rest_get_server()->dispatch( $request );
+		$this->assertSame( 404, $response->get_status() );
+	}
+}
+
diff --git a/phpunit/tests/fonts/font-library/wpRestFontLibraryController/getFontCollections.php b/phpunit/tests/fonts/font-library/wpRestFontLibraryController/getFontCollections.php
new file mode 100644
index 0000000000000..bf527093e4df1
--- /dev/null
+++ b/phpunit/tests/fonts/font-library/wpRestFontLibraryController/getFontCollections.php
@@ -0,0 +1,46 @@
+<?php
+/**
+ * Test WP_REST_Font_Library_Controller::get_font_collections().
+ *
+ * @package WordPress
+ * @subpackage Font Library
+ *
+ * @group fonts
+ * @group font-library
+ *
+ * @covers WP_REST_Font_Library_Controller::get_font_collections
+ */
+
+class Tests_Fonts_WPRESTFontLibraryController_GetFontCollections extends WP_REST_Font_Library_Controller_UnitTestCase {
+
+	public function test_get_font_collections_with_no_collection_registered() {
+		$request  = new WP_REST_Request( 'GET', '/wp/v2/fonts/collections' );
+		$response = rest_get_server()->dispatch( $request );
+		$this->assertSame( 200, $response->get_status() );
+		$this->assertSame( array(), $response->get_data() );
+	}
+
+	public function test_get_font_collections() {
+		// Mock font collection data file.
+		$mock_file = wp_tempnam( 'my-collection-data-' );
+		file_put_contents( $mock_file, '{"this is mock data":true}' );
+
+		// Add a font collection.
+		$config = array(
+			'id'             => 'my-font-collection',
+			'name'           => 'My Font Collection',
+			'description'    => 'Demo about how to a font collection to your WordPress Font Library.',
+			'data_json_file' => $mock_file,
+		);
+		wp_register_font_collection( $config );
+
+		$request  = new WP_REST_Request( 'GET', '/wp/v2/fonts/collections' );
+		$response = rest_get_server()->dispatch( $request );
+		$data     = $response->get_data();
+		$this->assertSame( 200, $response->get_status(), 'The response status is not 200.' );
+		$this->assertCount( 1, $data, 'The response data is not an array with one element.' );
+		$this->assertArrayHasKey( 'id', $data[0], 'The response data does not have the key with the collection ID.' );
+		$this->assertArrayHasKey( 'name', $data[0], 'The response data does not have the key with the collection name.' );
+	}
+}
+
diff --git a/phpunit/tests/fonts/font-library/class-wp-rest-font-library-controller.php b/phpunit/tests/fonts/font-library/wpRestFontLibraryController/installFonts.php
similarity index 82%
rename from phpunit/tests/fonts/font-library/class-wp-rest-font-library-controller.php
rename to phpunit/tests/fonts/font-library/wpRestFontLibraryController/installFonts.php
index f55d221acba96..9ae5f8d49354f 100644
--- a/phpunit/tests/fonts/font-library/class-wp-rest-font-library-controller.php
+++ b/phpunit/tests/fonts/font-library/wpRestFontLibraryController/installFonts.php
@@ -1,81 +1,27 @@
 <?php
 /**
- * Tests for WP_REST_Font_Library_Controller class
+ * Test WP_REST_Font_Library_Controller::install_fonts().
  *
- * @package    Gutenberg
+ * @package WordPress
  * @subpackage Font Library
+ *
+ * @group fonts
+ * @group font-library
+ *
+ * @covers WP_REST_Font_Library_Controller::install_fonts
  */
 
-/**
- * @coversDefaultClass WP_REST_Font_Library_Controller
- */
-class WP_REST_Font_Library_Controller_Test extends WP_UnitTestCase {
-	/**
-	 * @var int
-	 */
-	protected static $admin_id;
-
-	/**
-	 * Creates fake data before our tests run.
-	 *
-	 * @param WP_UnitTest_Factory $factory Helper that lets us create fake data.
-	 */
-	public static function wpSetupBeforeClass( $factory ) {
-		self::$admin_id = $factory->user->create(
-			array(
-				'role' => 'administrator',
-			)
-		);
-	}
-
-	/**
-	 * @covers ::register_routes
-	 */
-	public function test_register_routes() {
-		$routes = rest_get_server()->get_routes();
-		$this->assertArrayHasKey( '/wp/v2/fonts', $routes, 'Rest server has not the fonts path intialized.' );
-		$this->assertCount( 2, $routes['/wp/v2/fonts'], 'Rest server has not the 2 fonts paths initialized.' );
-		$this->assertArrayHasKey( 'POST', $routes['/wp/v2/fonts'][0]['methods'], 'Rest server has not the POST method for fonts intialized.' );
-		$this->assertArrayHasKey( 'DELETE', $routes['/wp/v2/fonts'][1]['methods'], 'Rest server has not the DELETE method for fonts intialized.' );
-	}
-
-	/**
-	 * @covers ::uninstall_fonts
-	 */
-	public function test_uninstall_non_existing_fonts() {
-		wp_set_current_user( self::$admin_id );
-		$uninstall_request = new WP_REST_Request( 'DELETE', '/wp/v2/fonts' );
-
-		$non_existing_font_data = array(
-			array(
-				'slug' => 'non-existing-font',
-				'name' => 'Non existing font',
-			),
-			array(
-				'slug' => 'another-not-installed-font',
-				'name' => 'Another not installed font',
-			),
-		);
-
-		$uninstall_request->set_param( 'fontFamilies', $non_existing_font_data );
-		$response = rest_get_server()->dispatch( $uninstall_request );
-		$response->get_data();
-		$this->assertSame( 500, $response->get_status(), 'The response status is not 500.' );
-	}
-
+class Tests_Fonts_WPRESTFontLibraryController_InstallFonts extends WP_REST_Font_Library_Controller_UnitTestCase {
 
 	/**
-	 * @covers ::install_fonts
-	 * @covers ::uninstall_fonts
 	 *
-	 * @dataProvider data_install_and_uninstall_fonts
+	 * @dataProvider data_install_fonts
 	 *
 	 * @param array $font_families     Font families to install in theme.json format.
 	 * @param array $files             Font files to install.
 	 * @param array $expected_response Expected response data.
 	 */
-	public function test_install_and_uninstall_fonts( $font_families, $files, $expected_response ) {
-		wp_set_current_user( self::$admin_id );
+	public function test_install_fonts( $font_families, $files, $expected_response ) {
 		$install_request    = new WP_REST_Request( 'POST', '/wp/v2/fonts' );
 		$font_families_json = json_encode( $font_families );
 		$install_request->set_param( 'fontFamilies', $font_families_json );
@@ -105,16 +51,12 @@ public function test_install_and_uninstall_fonts( $font_families, $files, $expec
 			$this->assertEquals( $expected_font, $installed_font, 'The endpoint answer is not as expected.' );
 		}
 
-		$uninstall_request = new WP_REST_Request( 'DELETE', '/wp/v2/fonts' );
-		$uninstall_request->set_param( 'fontFamilies', $font_families );
-		$response = rest_get_server()->dispatch( $uninstall_request );
-		$this->assertSame( 200, $response->get_status(), 'The response status is not 200.' );
 	}
 
 	/**
-	 * Data provider for test_install_and_uninstall_fonts
+	 * Data provider for test_install_fonts
 	 */
-	public function data_install_and_uninstall_fonts() {
+	public function data_install_fonts() {
 
 		$temp_file_path1 = wp_tempnam( 'Piazzola1-' );
 		file_put_contents( $temp_file_path1, 'Mocking file content' );
@@ -344,16 +286,12 @@ public function data_install_and_uninstall_fonts() {
 	/**
 	 * Tests failure when fonfaces has improper inputs
 	 *
-	 * @covers ::install_fonts
-	 *
 	 * @dataProvider data_install_with_improper_inputs
 	 *
 	 * @param array $font_families Font families to install in theme.json format.
 	 * @param array $files         Font files to install.
 	 */
 	public function test_install_with_improper_inputs( $font_families, $files = array() ) {
-		wp_set_current_user( self::$admin_id );
-
 		$install_request    = new WP_REST_Request( 'POST', '/wp/v2/fonts' );
 		$font_families_json = json_encode( $font_families );
 		$install_request->set_param( 'fontFamilies', $font_families_json );
@@ -478,3 +416,4 @@ public function data_install_with_improper_inputs() {
 		);
 	}
 }
+
diff --git a/phpunit/tests/fonts/font-library/wpRestFontLibraryController/registerRoutes.php b/phpunit/tests/fonts/font-library/wpRestFontLibraryController/registerRoutes.php
new file mode 100644
index 0000000000000..ee957300090b0
--- /dev/null
+++ b/phpunit/tests/fonts/font-library/wpRestFontLibraryController/registerRoutes.php
@@ -0,0 +1,29 @@
+<?php
+/**
+ * Test WP_REST_Font_Library_Controller::register_routes().
+ *
+ * @package WordPress
+ * @subpackage Font Library
+ *
+ * @group fonts
+ * @group font-library
+ *
+ * @covers WP_REST_Font_Library_Controller::register_routes
+ */
+
+class Tests_Fonts_WPRESTFontLibraryController_RegisterRoutes extends WP_UnitTestCase {
+
+	public function test_register_routes() {
+		$routes = rest_get_server()->get_routes();
+		$this->assertArrayHasKey( '/wp/v2/fonts', $routes, 'Rest server has not the fonts path intialized.' );
+		$this->assertCount( 2, $routes['/wp/v2/fonts'], 'Rest server has not the 2 fonts paths initialized.' );
+		$this->assertCount( 1, $routes['/wp/v2/fonts/collections'], 'Rest server has not the collections path initialized.' );
+		$this->assertCount( 1, $routes['/wp/v2/fonts/collections/(?P<id>[\/\w-]+)'], 'Rest server has not the collection path initialized.' );
+
+		$this->assertArrayHasKey( 'POST', $routes['/wp/v2/fonts'][0]['methods'], 'Rest server has not the POST method for fonts intialized.' );
+		$this->assertArrayHasKey( 'DELETE', $routes['/wp/v2/fonts'][1]['methods'], 'Rest server has not the DELETE method for fonts intialized.' );
+		$this->assertArrayHasKey( 'GET', $routes['/wp/v2/fonts/collections'][0]['methods'], 'Rest server has not the GET method for collections intialized.' );
+		$this->assertArrayHasKey( 'GET', $routes['/wp/v2/fonts/collections/(?P<id>[\/\w-]+)'][0]['methods'], 'Rest server has not the GET method for collection intialized.' );
+	}
+}
+
diff --git a/phpunit/tests/fonts/font-library/wpRestFontLibraryController/uninstallFonts.php b/phpunit/tests/fonts/font-library/wpRestFontLibraryController/uninstallFonts.php
new file mode 100644
index 0000000000000..9edcec970146d
--- /dev/null
+++ b/phpunit/tests/fonts/font-library/wpRestFontLibraryController/uninstallFonts.php
@@ -0,0 +1,100 @@
+<?php
+/**
+ * Test WP_REST_Font_Library_Controller::install_fonts().
+ *
+ * @package WordPress
+ * @subpackage Font Library
+ *
+ * @group fonts
+ * @group font-library
+ *
+ * @covers WP_REST_Font_Library_Controller::install_fonts
+ */
+
+class Tests_Fonts_WPRESTFontLibraryController_UninstallFonts extends WP_REST_Font_Library_Controller_UnitTestCase {
+
+	/**
+	 * Install fonts to test uninstall.
+	 */
+	public function set_up() {
+		parent::set_up();
+
+		// Installs mock fonts to test uninstall.
+		$mock_families = array(
+			array(
+				'fontFamily' => 'Piazzolla',
+				'slug'       => 'piazzolla',
+				'name'       => 'Piazzolla',
+				'fontFace'   => array(
+					array(
+						'fontFamily'      => 'Piazzolla',
+						'fontStyle'       => 'normal',
+						'fontWeight'      => '400',
+						'src'             => 'http://fonts.gstatic.com/s/piazzolla/v33/N0b72SlTPu5rIkWIZjVgI-TckS03oGpPETyEJ88Rbvi0_TzOzKcQhZqx3gX9BRy5m5M.ttf',
+						'downloadFromUrl' => 'http://fonts.gstatic.com/s/piazzolla/v33/N0b72SlTPu5rIkWIZjVgI-TckS03oGpPETyEJ88Rbvi0_TzOzKcQhZqx3gX9BRy5m5M.ttf',
+					),
+				),
+			),
+			array(
+				'fontFamily' => 'Montserrat',
+				'slug'       => 'montserrat',
+				'name'       => 'Montserrat',
+				'fontFace'   => array(
+					array(
+						'fontFamily'      => 'Montserrat',
+						'fontStyle'       => 'normal',
+						'fontWeight'      => '100',
+						'src'             => 'http://fonts.gstatic.com/s/montserrat/v25/JTUHjIg1_i6t8kCHKm4532VJOt5-QNFgpCtr6Uw-Y3tcoqK5.ttf',
+						'downloadFromUrl' => 'http://fonts.gstatic.com/s/montserrat/v25/JTUHjIg1_i6t8kCHKm4532VJOt5-QNFgpCtr6Uw-Y3tcoqK5.ttf',
+					),
+				),
+			),
+		);
+
+		$install_request    = new WP_REST_Request( 'POST', '/wp/v2/fonts' );
+		$font_families_json = json_encode( $mock_families );
+		$install_request->set_param( 'fontFamilies', $font_families_json );
+		rest_get_server()->dispatch( $install_request );
+	}
+
+	public function test_uninstall() {
+		$font_families_to_uninstall = array(
+			array(
+				'slug' => 'piazzolla',
+			),
+			array(
+				'slug' => 'montserrat',
+			),
+		);
+
+		$uninstall_request = new WP_REST_Request( 'DELETE', '/wp/v2/fonts' );
+		$uninstall_request->set_param( 'fontFamilies', $font_families_to_uninstall );
+		$response = rest_get_server()->dispatch( $uninstall_request );
+		echo ( print_r( $response->get_data(), true ) );
+		$this->assertSame( 200, $response->get_status(), 'The response status is not 200.' );
+
+	}
+
+
+	public function test_uninstall_non_existing_fonts() {
+		$uninstall_request = new WP_REST_Request( 'DELETE', '/wp/v2/fonts' );
+
+		$non_existing_font_data = array(
+			array(
+				'slug' => 'non-existing-font',
+				'name' => 'Non existing font',
+			),
+			array(
+				'slug' => 'another-not-installed-font',
+				'name' => 'Another not installed font',
+			),
+		);
+
+		$uninstall_request->set_param( 'fontFamilies', $non_existing_font_data );
+		$response = rest_get_server()->dispatch( $uninstall_request );
+		$response->get_data();
+		$this->assertSame( 500, $response->get_status(), 'The response status is not 500.' );
+	}
+}
+
+

From ed22bf9e81b2ac0f2cfc711268f61b26b4ee987b Mon Sep 17 00:00:00 2001
From: Keanan Koppenhaver <keanan@alphaparticle.com>
Date: Thu, 31 Aug 2023 16:17:20 -0500
Subject: [PATCH 30/34] Updates code examples to have matching open/closing
 tags (#53915)

---
 docs/reference-guides/block-api/block-edit-save.md | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/docs/reference-guides/block-api/block-edit-save.md b/docs/reference-guides/block-api/block-edit-save.md
index 843a8d5be12d3..f38573f35a5f2 100644
--- a/docs/reference-guides/block-api/block-edit-save.md
+++ b/docs/reference-guides/block-api/block-edit-save.md
@@ -385,7 +385,7 @@ edit: ( { attributes, setAttributes } ) => {
 				value={ attributes.content }
 				onChange={ updateFieldValue }
 			/>
-		</p>
+		</div>
 	);
 },
 
@@ -465,7 +465,7 @@ edit: ( { attributes, setAttributes } ) => {
 					setAttributes( { postsToShow: parseInt( val ) } );
 				}}
 			/>
-		</p>
+		</div>
 	);
 },
 

From e2941e9741bb8e21f0d7965d6bf70d43113bade2 Mon Sep 17 00:00:00 2001
From: Pascal Birchler <pascalb@google.com>
Date: Thu, 31 Aug 2023 23:27:59 +0200
Subject: [PATCH 31/34] wp-scripts: add support for Playwright (#53108)

* wp-scripts: add support for Playwright

* Fix lock file

* Lint fixes

* Downgrade Playwright

* Fix config arg & browser install

* Use relative path for testDir

* Fix config inheritance

* Update npm scripts

* Fix e2e config too

* Suppress eslint errors

* Fix lock file

* Move `ASSETS_PATH` definition

* Fix typo

* Update readme & changelogs

* Move changelog entries

* Add missing dependency
---
 .eslintrc.js                                  |  3 +-
 package-lock.json                             | 60 ++++++++--------
 package.json                                  |  7 +-
 packages/eslint-plugin/CHANGELOG.md           |  4 ++
 packages/eslint-plugin/README.md              |  1 +
 .../eslint-plugin/configs/test-playwright.js  |  3 +
 packages/eslint-plugin/package.json           |  1 +
 packages/scripts/CHANGELOG.md                 |  6 +-
 packages/scripts/README.md                    | 41 +++++++++++
 packages/scripts/config/playwright.config.ts  | 63 +++++++++++++++++
 .../scripts/config/playwright/global-setup.ts | 31 ++++++++
 packages/scripts/package.json                 |  3 +
 packages/scripts/scripts/test-playwright.js   | 70 +++++++++++++++++++
 test/e2e/playwright.config.ts                 | 43 ++----------
 test/e2e/specs/editor/blocks/image.spec.js    | 18 ++---
 test/e2e/specs/editor/plugins/nonce.spec.js   |  1 +
 .../various/post-editor-template-mode.spec.js |  1 +
 test/performance/playwright.config.ts         | 47 +++----------
 .../specs/front-end-block-theme.spec.js       |  1 +
 .../specs/front-end-classic-theme.spec.js     |  1 +
 20 files changed, 285 insertions(+), 120 deletions(-)
 create mode 100644 packages/eslint-plugin/configs/test-playwright.js
 create mode 100644 packages/scripts/config/playwright.config.ts
 create mode 100644 packages/scripts/config/playwright/global-setup.ts
 create mode 100644 packages/scripts/scripts/test-playwright.js

diff --git a/.eslintrc.js b/.eslintrc.js
index a81369b61c5f1..fd7e9183781ea 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -294,7 +294,7 @@ module.exports = {
 				'packages/e2e-test-utils-playwright/**/*.[tj]s',
 			],
 			extends: [
-				'plugin:eslint-plugin-playwright/playwright-test',
+				'plugin:@wordpress/eslint-plugin/test-playwright',
 				'plugin:@typescript-eslint/base',
 			],
 			parserOptions: {
@@ -308,7 +308,6 @@ module.exports = {
 			rules: {
 				'@wordpress/no-global-active-element': 'off',
 				'@wordpress/no-global-get-selection': 'off',
-				'playwright/no-page-pause': 'error',
 				'no-restricted-syntax': [
 					'error',
 					{
diff --git a/package-lock.json b/package-lock.json
index cabb3c8c18155..f5d327696992b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -182,7 +182,6 @@
 				"eslint-plugin-import": "2.25.2",
 				"eslint-plugin-jest": "27.2.1",
 				"eslint-plugin-jest-dom": "5.0.2",
-				"eslint-plugin-playwright": "0.8.0",
 				"eslint-plugin-ssr-friendly": "1.0.6",
 				"eslint-plugin-storybook": "0.6.13",
 				"eslint-plugin-testing-library": "5.7.2",
@@ -5670,18 +5669,6 @@
 				"fsevents": "2.3.2"
 			}
 		},
-		"node_modules/@playwright/test/node_modules/playwright-core": {
-			"version": "1.32.0",
-			"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.32.0.tgz",
-			"integrity": "sha512-Z9Ij17X5Z3bjpp6XKujGBp9Gv4eViESac9aDmwgQFUEJBW0K80T21m/Z+XJQlu4cNsvPygw33b6V1Va6Bda5zQ==",
-			"dev": true,
-			"bin": {
-				"playwright": "cli.js"
-			},
-			"engines": {
-				"node": ">=14"
-			}
-		},
 		"node_modules/@pmmmwh/react-refresh-webpack-plugin": {
 			"version": "0.5.2",
 			"resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.2.tgz",
@@ -28815,13 +28802,13 @@
 			"dev": true
 		},
 		"node_modules/eslint-plugin-playwright": {
-			"version": "0.8.0",
-			"resolved": "https://registry.npmjs.org/eslint-plugin-playwright/-/eslint-plugin-playwright-0.8.0.tgz",
-			"integrity": "sha512-9uJH25m6H3jwU5O7bHD5M8cLx46L72EnIUe3dZqTox6M+WzOFzeUWaDJHHCdLGXZ8XlAU4mbCZnP7uhjKepfRA==",
+			"version": "0.15.3",
+			"resolved": "https://registry.npmjs.org/eslint-plugin-playwright/-/eslint-plugin-playwright-0.15.3.tgz",
+			"integrity": "sha512-LQMW5y0DLK5Fnpya7JR1oAYL2/7Y9wDiYw6VZqlKqcRGSgjbVKNqxraphk7ra1U3Bb5EK444xMgUlQPbMg2M1g==",
 			"dev": true,
 			"peerDependencies": {
 				"eslint": ">=7",
-				"eslint-plugin-jest": ">=24"
+				"eslint-plugin-jest": ">=25"
 			},
 			"peerDependenciesMeta": {
 				"eslint-plugin-jest": {
@@ -44185,6 +44172,18 @@
 			"integrity": "sha512-TuvHS8AOIZNAlE77WUDiR4rySV/VMptyMfcfeoMgs4P8apaZM3JrnbzBiixKUv+XR6i+BXrQh8WAnjaSPFO65Q==",
 			"dev": true
 		},
+		"node_modules/playwright-core": {
+			"version": "1.32.0",
+			"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.32.0.tgz",
+			"integrity": "sha512-Z9Ij17X5Z3bjpp6XKujGBp9Gv4eViESac9aDmwgQFUEJBW0K80T21m/Z+XJQlu4cNsvPygw33b6V1Va6Bda5zQ==",
+			"dev": true,
+			"bin": {
+				"playwright": "cli.js"
+			},
+			"engines": {
+				"node": ">=14"
+			}
+		},
 		"node_modules/please-upgrade-node": {
 			"version": "3.2.0",
 			"resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz",
@@ -55338,6 +55337,7 @@
 				"eslint-plugin-jest": "^27.2.1",
 				"eslint-plugin-jsdoc": "^46.4.6",
 				"eslint-plugin-jsx-a11y": "^6.5.1",
+				"eslint-plugin-playwright": "^0.15.3",
 				"eslint-plugin-prettier": "^3.3.0",
 				"eslint-plugin-react": "^7.27.0",
 				"eslint-plugin-react-hooks": "^4.3.0",
@@ -56083,6 +56083,7 @@
 				"@wordpress/babel-preset-default": "file:../babel-preset-default",
 				"@wordpress/browserslist-config": "file:../browserslist-config",
 				"@wordpress/dependency-extraction-webpack-plugin": "file:../dependency-extraction-webpack-plugin",
+				"@wordpress/e2e-test-utils-playwright": "file:../e2e-test-utils-playwright",
 				"@wordpress/eslint-plugin": "file:../eslint-plugin",
 				"@wordpress/jest-preset-default": "file:../jest-preset-default",
 				"@wordpress/npm-package-json-lint-config": "file:../npm-package-json-lint-config",
@@ -56116,6 +56117,7 @@
 				"minimist": "^1.2.0",
 				"npm-package-json-lint": "^6.4.0",
 				"npm-packlist": "^3.0.0",
+				"playwright-core": "1.32.0",
 				"postcss": "^8.4.5",
 				"postcss-loader": "^6.2.1",
 				"prettier": "npm:wp-prettier@2.8.5",
@@ -56142,6 +56144,7 @@
 				"npm": ">=6.14.4"
 			},
 			"peerDependencies": {
+				"@playwright/test": "^1.32.0",
 				"react": "^18.0.0",
 				"react-dom": "^18.0.0"
 			}
@@ -60191,14 +60194,6 @@
 				"@types/node": "*",
 				"fsevents": "2.3.2",
 				"playwright-core": "1.32.0"
-			},
-			"dependencies": {
-				"playwright-core": {
-					"version": "1.32.0",
-					"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.32.0.tgz",
-					"integrity": "sha512-Z9Ij17X5Z3bjpp6XKujGBp9Gv4eViESac9aDmwgQFUEJBW0K80T21m/Z+XJQlu4cNsvPygw33b6V1Va6Bda5zQ==",
-					"dev": true
-				}
 			}
 		},
 		"@pmmmwh/react-refresh-webpack-plugin": {
@@ -67852,6 +67847,7 @@
 				"eslint-plugin-jest": "^27.2.1",
 				"eslint-plugin-jsdoc": "^46.4.6",
 				"eslint-plugin-jsx-a11y": "^6.5.1",
+				"eslint-plugin-playwright": "^0.15.3",
 				"eslint-plugin-prettier": "^3.3.0",
 				"eslint-plugin-react": "^7.27.0",
 				"eslint-plugin-react-hooks": "^4.3.0",
@@ -68284,6 +68280,7 @@
 				"@wordpress/babel-preset-default": "file:../babel-preset-default",
 				"@wordpress/browserslist-config": "file:../browserslist-config",
 				"@wordpress/dependency-extraction-webpack-plugin": "file:../dependency-extraction-webpack-plugin",
+				"@wordpress/e2e-test-utils-playwright": "file:../e2e-test-utils-playwright",
 				"@wordpress/eslint-plugin": "file:../eslint-plugin",
 				"@wordpress/jest-preset-default": "file:../jest-preset-default",
 				"@wordpress/npm-package-json-lint-config": "file:../npm-package-json-lint-config",
@@ -68317,6 +68314,7 @@
 				"minimist": "^1.2.0",
 				"npm-package-json-lint": "^6.4.0",
 				"npm-packlist": "^3.0.0",
+				"playwright-core": "1.32.0",
 				"postcss": "^8.4.5",
 				"postcss-loader": "^6.2.1",
 				"prettier": "npm:wp-prettier@2.8.5",
@@ -80583,9 +80581,9 @@
 			}
 		},
 		"eslint-plugin-playwright": {
-			"version": "0.8.0",
-			"resolved": "https://registry.npmjs.org/eslint-plugin-playwright/-/eslint-plugin-playwright-0.8.0.tgz",
-			"integrity": "sha512-9uJH25m6H3jwU5O7bHD5M8cLx46L72EnIUe3dZqTox6M+WzOFzeUWaDJHHCdLGXZ8XlAU4mbCZnP7uhjKepfRA==",
+			"version": "0.15.3",
+			"resolved": "https://registry.npmjs.org/eslint-plugin-playwright/-/eslint-plugin-playwright-0.15.3.tgz",
+			"integrity": "sha512-LQMW5y0DLK5Fnpya7JR1oAYL2/7Y9wDiYw6VZqlKqcRGSgjbVKNqxraphk7ra1U3Bb5EK444xMgUlQPbMg2M1g==",
 			"dev": true
 		},
 		"eslint-plugin-prettier": {
@@ -92182,6 +92180,12 @@
 			"integrity": "sha512-TuvHS8AOIZNAlE77WUDiR4rySV/VMptyMfcfeoMgs4P8apaZM3JrnbzBiixKUv+XR6i+BXrQh8WAnjaSPFO65Q==",
 			"dev": true
 		},
+		"playwright-core": {
+			"version": "1.32.0",
+			"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.32.0.tgz",
+			"integrity": "sha512-Z9Ij17X5Z3bjpp6XKujGBp9Gv4eViESac9aDmwgQFUEJBW0K80T21m/Z+XJQlu4cNsvPygw33b6V1Va6Bda5zQ==",
+			"dev": true
+		},
 		"please-upgrade-node": {
 			"version": "3.2.0",
 			"resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz",
diff --git a/package.json b/package.json
index d8640a128c10f..9fc475fe70dcc 100644
--- a/package.json
+++ b/package.json
@@ -194,7 +194,6 @@
 		"eslint-plugin-import": "2.25.2",
 		"eslint-plugin-jest": "27.2.1",
 		"eslint-plugin-jest-dom": "5.0.2",
-		"eslint-plugin-playwright": "0.8.0",
 		"eslint-plugin-ssr-friendly": "1.0.6",
 		"eslint-plugin-storybook": "0.6.13",
 		"eslint-plugin-testing-library": "5.7.2",
@@ -314,7 +313,7 @@
 		"test:create-block": "bash ./bin/test-create-block.sh",
 		"test:e2e": "wp-scripts test-e2e --config packages/e2e-tests/jest.config.js",
 		"test:e2e:debug": "wp-scripts --inspect-brk test-e2e  --runInBand --no-cache --verbose --config packages/e2e-tests/jest.config.js --puppeteer-devtools",
-		"test:e2e:playwright": "playwright test --config test/e2e/playwright.config.ts",
+		"test:e2e:playwright": "wp-scripts test-playwright --config test/e2e/playwright.config.ts",
 		"test:e2e:storybook": "playwright test --config test/storybook-playwright/playwright.config.ts",
 		"test:e2e:watch": "npm run test:e2e -- --watch",
 		"test:native": "cross-env NODE_ENV=test jest --config test/native/jest.config.js",
@@ -323,8 +322,8 @@
 		"test:native:perf": "cross-env TEST_RUNNER_ARGS='--runInBand --config test/native/jest.config.js --testMatch \"**/performance/*.native.[jt]s?(x)\"' reassure",
 		"test:native:perf:baseline": "cross-env TEST_RUNNER_ARGS='--runInBand --config test/native/jest.config.js --testMatch \"**/performance/*.native.[jt]s?(x)\"' reassure --baseline",
 		"test:native:update": "npm run test:native -- --updateSnapshot",
-		"test:performance": "playwright test --config test/performance/playwright.config.ts",
-		"test:performance:debug": "playwright test --config test/performance/playwright.config.ts --debug",
+		"test:performance": "wp-scripts test-playwright --config test/performance/playwright.config.ts",
+		"test:performance:debug": "wp-scripts test-playwright --config test/performance/playwright.config.ts --debug",
 		"test:php": "npm-run-all lint:php test:unit:php",
 		"test:php:watch": "wp-env run --env-cwd='wp-content/plugins/gutenberg' tests-cli composer run-script test:watch",
 		"test:unit": "wp-scripts test-unit-js --config test/unit/jest.config.js",
diff --git a/packages/eslint-plugin/CHANGELOG.md b/packages/eslint-plugin/CHANGELOG.md
index d8d39769a279b..05276fb340bb3 100644
--- a/packages/eslint-plugin/CHANGELOG.md
+++ b/packages/eslint-plugin/CHANGELOG.md
@@ -2,6 +2,10 @@
 
 ## Unreleased
 
+### Enhancement
+
+-   Added a new `test-playwright` ruleset using [`eslint-plugin-playwright`](https://www.npmjs.com/package/eslint-plugin-playwright).
+
 ## 15.1.0 (2023-08-31)
 
 ## 15.0.0 (2023-08-16)
diff --git a/packages/eslint-plugin/README.md b/packages/eslint-plugin/README.md
index cbc297d6b99e5..1619778add661 100644
--- a/packages/eslint-plugin/README.md
+++ b/packages/eslint-plugin/README.md
@@ -53,6 +53,7 @@ Alternatively, you can opt-in to only the more granular rulesets offered by the
 -   `react` – rules for React components.
 -   `test-e2e` – rules for end-to-end tests written in Puppeteer.
 -   `test-unit`– rules for unit tests written in Jest.
+-   `test-playwright` – rules for end-to-end tests written in Playwright.
 
 For example, if your project does not use React, you could consider extending including only the ESNext rules in your project using the following `extends` definition:
 
diff --git a/packages/eslint-plugin/configs/test-playwright.js b/packages/eslint-plugin/configs/test-playwright.js
new file mode 100644
index 0000000000000..c3c72be05c1e9
--- /dev/null
+++ b/packages/eslint-plugin/configs/test-playwright.js
@@ -0,0 +1,3 @@
+module.exports = {
+	extends: [ 'plugin:playwright/recommended' ],
+};
diff --git a/packages/eslint-plugin/package.json b/packages/eslint-plugin/package.json
index 9fada24d1782d..d546f19e0b64f 100644
--- a/packages/eslint-plugin/package.json
+++ b/packages/eslint-plugin/package.json
@@ -42,6 +42,7 @@
 		"eslint-plugin-jest": "^27.2.1",
 		"eslint-plugin-jsdoc": "^46.4.6",
 		"eslint-plugin-jsx-a11y": "^6.5.1",
+		"eslint-plugin-playwright": "^0.15.3",
 		"eslint-plugin-prettier": "^3.3.0",
 		"eslint-plugin-react": "^7.27.0",
 		"eslint-plugin-react-hooks": "^4.3.0",
diff --git a/packages/scripts/CHANGELOG.md b/packages/scripts/CHANGELOG.md
index b7dce7e73cf58..1e11550a8f07c 100644
--- a/packages/scripts/CHANGELOG.md
+++ b/packages/scripts/CHANGELOG.md
@@ -2,13 +2,17 @@
 
 ## Unreleased
 
+### Enhancement
+
+-   Added support for `test-playwright` script ([#53108](https://github.com/WordPress/gutenberg/pull/53108)).
+
 ## 26.12.0 (2023-08-31)
 
 ## 26.11.0 (2023-08-16)
 
 ### Enhancement
 
--   Updated `npm-package-json-lint` peer dependency to require v6.0.0 [#53636](https://github.com/WordPress/gutenberg/pull/53636)
+-   Updated `npm-package-json-lint` peer dependency to require v6.0.0 [#53636](https://github.com/WordPress/gutenberg/pull/53636).
 -   The bundled `@svgr/webpack` dependency has been updated from requiring ^6.2.1 to requiring ^8.0.1 ([#53630](https://github.com/WordPress/gutenberg/pull/53630)).
 -   The bundled `cssnano` dependency has been updated from requiring ^5.07 to requiring ^6.0.1 ([#53630](https://github.com/WordPress/gutenberg/pull/53630)).
 
diff --git a/packages/scripts/README.md b/packages/scripts/README.md
index d34dab634a2b4..8e80d806b17e7 100644
--- a/packages/scripts/README.md
+++ b/packages/scripts/README.md
@@ -491,6 +491,47 @@ Should there be any situation where you want to provide your own Jest config, yo
 -   there is a file called `jest-unit.config.js`, `jest-unit.config.json`, `jest.config.js`, or `jest.config.json` in the top-level directory of your package (at the same level than your `package.json`).
 -   a `jest` object can be provided in the `package.json` file with the test configuration.
 
+### `test-plyawright`
+
+Launches the Playwright End-To-End (E2E) test runner. Similar to Puppeteer, it provides a high-level API to control a headless browser.
+
+Refer to the [Getting Started guide](https://playwright.dev/docs/writing-tests) to learn how to write tests.
+
+_Example:_
+
+```json
+{
+	"scripts": {
+		"test:playwright": "wp-scripts test-playwright",
+		"test:playwright:help": "wp-scripts test-playwright --help",
+		"test:playwright:debug": "wp-scripts test-playwright --debug"
+	}
+}
+```
+
+This is how you execute those scripts using the presented setup:
+
+-   `npm run test:playwright` - runs all tests.
+-   `npm run test:playwright:help` - prints all available options to configure the test runner.
+-   `npm run test:playwright:debug` - runs all tests interactively with the Playwright inspector.
+-   `npm run test:playwright FILE_NAME` - runs a specific test file.
+-   `npm run test:playwright -- --watch` - runs all tests interactively with watch mode and enhanced debugging.
+
+By default, Playwright looks for JavaScript or TypeScript files with `.test` or `.spec` suffix in the project root-level `/specs` folder, for example `/specs/login-screen.wrong-credentials.spec.ts`.
+
+This script automatically detects the best config to start Playwright, but sometimes you may need to specify custom options.
+To do so, you can add a file called `playwright.config.ts` or `playwright.config.js` in the top-level directory of your package (at the same level as your `package.json`).
+
+#### Failed Test Artifacts
+
+When tests fail, snapshots will be taken of the page and stored in the `artifacts/` directory at the root of your project. These snapshots may help debug failed tests during development or when running tests in a CI environment.
+
+The `artifacts/` directory can be customized by setting the `WP_ARTIFACTS_PATH` environment variable to the relative path of the desired directory within your project's root. For example: to change the default directory from `artifacts/` to `my/custom/artifacts`, you could use `WP_ARTIFACTS_PATH=my/custom/artifacts npm run test:playwright`.
+
+#### Advanced information
+
+You are able to use all of Playwright's [CLI options](https://playwright.dev/docs/test-cli#reference). You can also run `./node_modules/.bin/wp-scripts test-playwright --help` or `npm run test:playwright:help` (as mentioned above) to view all the available options. Learn more in the [Advanced Usage](#advanced-usage) section.
+
 ## Passing Node.js options
 
 `wp-scripts` supports the full array of [Node.js CLI options](https://nodejs.org/api/cli.html). They can be passed after the `wp-scripts` command and before the script name.
diff --git a/packages/scripts/config/playwright.config.ts b/packages/scripts/config/playwright.config.ts
new file mode 100644
index 0000000000000..16ee210bdfb86
--- /dev/null
+++ b/packages/scripts/config/playwright.config.ts
@@ -0,0 +1,63 @@
+/**
+ * External dependencies
+ */
+const path = require( 'path' );
+const { fileURLToPath } = require( 'url' );
+const { defineConfig, devices } = require( '@playwright/test' );
+
+process.env.WP_ARTIFACTS_PATH ??= path.join( process.cwd(), 'artifacts' );
+process.env.STORAGE_STATE_PATH ??= path.join(
+	process.env.WP_ARTIFACTS_PATH,
+	'storage-states/admin.json'
+);
+
+const config = defineConfig( {
+	reporter: process.env.CI ? [ [ 'github' ] ] : [ [ 'list' ] ],
+	forbidOnly: !! process.env.CI,
+	// fullyParallel: false,
+	workers: 1,
+	retries: process.env.CI ? 2 : 0,
+	timeout: parseInt( process.env.TIMEOUT || '', 10 ) || 100_000, // Defaults to 100 seconds.
+	// Don't report slow test "files", as we will be running our tests in serial.
+	reportSlowTests: null,
+	testDir: './specs',
+	outputDir: path.join( process.env.WP_ARTIFACTS_PATH, 'test-results' ),
+	snapshotPathTemplate:
+		'{testDir}/{testFileDir}/__snapshots__/{arg}-{projectName}{ext}',
+	globalSetup: fileURLToPath(
+		new URL( './playwright/global-setup.ts', 'file:' + __filename ).href
+	),
+	use: {
+		baseURL: process.env.WP_BASE_URL || 'http://localhost:8889',
+		headless: true,
+		viewport: {
+			width: 960,
+			height: 700,
+		},
+		ignoreHTTPSErrors: true,
+		locale: 'en-US',
+		contextOptions: {
+			reducedMotion: 'reduce',
+			strictSelectors: true,
+		},
+		storageState: process.env.STORAGE_STATE_PATH,
+		actionTimeout: 10_000, // 10 seconds.
+		trace: 'retain-on-failure',
+		screenshot: 'only-on-failure',
+		video: 'on-first-retry',
+	},
+	webServer: {
+		command: 'npm run wp-env start',
+		port: 8889,
+		timeout: 120_000, // 120 seconds.
+		reuseExistingServer: true,
+	},
+	projects: [
+		{
+			name: 'chromium',
+			use: { ...devices[ 'Desktop Chrome' ] },
+		},
+	],
+} );
+
+export default config;
diff --git a/packages/scripts/config/playwright/global-setup.ts b/packages/scripts/config/playwright/global-setup.ts
new file mode 100644
index 0000000000000..10f2822fdfe1a
--- /dev/null
+++ b/packages/scripts/config/playwright/global-setup.ts
@@ -0,0 +1,31 @@
+/**
+ * External dependencies
+ */
+import { request } from '@playwright/test';
+import type { FullConfig } from '@playwright/test';
+
+/**
+ * WordPress dependencies
+ */
+import { RequestUtils } from '@wordpress/e2e-test-utils-playwright';
+
+async function globalSetup( config: FullConfig ) {
+	const { storageState, baseURL } = config.projects[ 0 ].use;
+	const storageStatePath =
+		typeof storageState === 'string' ? storageState : undefined;
+
+	const requestContext = await request.newContext( {
+		baseURL,
+	} );
+
+	const requestUtils = new RequestUtils( requestContext, {
+		storageStatePath,
+	} );
+
+	// Authenticate and save the storageState to disk.
+	await requestUtils.setupRest();
+
+	await requestContext.dispose();
+}
+
+export default globalSetup;
diff --git a/packages/scripts/package.json b/packages/scripts/package.json
index faad42de3585a..12ca7854da191 100644
--- a/packages/scripts/package.json
+++ b/packages/scripts/package.json
@@ -38,6 +38,7 @@
 		"@wordpress/babel-preset-default": "file:../babel-preset-default",
 		"@wordpress/browserslist-config": "file:../browserslist-config",
 		"@wordpress/dependency-extraction-webpack-plugin": "file:../dependency-extraction-webpack-plugin",
+		"@wordpress/e2e-test-utils-playwright": "file:../e2e-test-utils-playwright",
 		"@wordpress/eslint-plugin": "file:../eslint-plugin",
 		"@wordpress/jest-preset-default": "file:../jest-preset-default",
 		"@wordpress/npm-package-json-lint-config": "file:../npm-package-json-lint-config",
@@ -71,6 +72,7 @@
 		"minimist": "^1.2.0",
 		"npm-package-json-lint": "^6.4.0",
 		"npm-packlist": "^3.0.0",
+		"playwright-core": "1.32.0",
 		"postcss": "^8.4.5",
 		"postcss-loader": "^6.2.1",
 		"prettier": "npm:wp-prettier@2.8.5",
@@ -90,6 +92,7 @@
 		"webpack-dev-server": "^4.4.0"
 	},
 	"peerDependencies": {
+		"@playwright/test": "^1.32.0",
 		"react": "^18.0.0",
 		"react-dom": "^18.0.0"
 	},
diff --git a/packages/scripts/scripts/test-playwright.js b/packages/scripts/scripts/test-playwright.js
new file mode 100644
index 0000000000000..4c10fb6f62114
--- /dev/null
+++ b/packages/scripts/scripts/test-playwright.js
@@ -0,0 +1,70 @@
+// Do this as the first thing so that any code reading it knows the right env.
+process.env.BABEL_ENV = 'test';
+process.env.NODE_ENV = 'test';
+
+// Makes the script crash on unhandled rejections instead of silently
+// ignoring them. In the future, promise rejections that are not handled will
+// terminate the Node.js process with a non-zero exit code.
+process.on( 'unhandledRejection', ( err ) => {
+	throw err;
+} );
+
+/**
+ * External dependencies
+ */
+const { resolve } = require( 'node:path' );
+const { sync: spawn } = require( 'cross-spawn' );
+
+/**
+ * Internal dependencies
+ */
+const {
+	fromConfigRoot,
+	hasProjectFile,
+	hasArgInCLI,
+	getArgsFromCLI,
+} = require( '../utils' );
+
+const result = spawn(
+	'node',
+	[ require.resolve( 'playwright-core/cli' ), 'install' ],
+	{
+		stdio: 'inherit',
+	}
+);
+
+if ( result.status > 0 ) {
+	process.exit( result.status );
+}
+
+const config =
+	! hasArgInCLI( '--config' ) &&
+	! hasProjectFile( 'playwright.config.ts' ) &&
+	! hasProjectFile( 'playwright.config.js' )
+		? [ '--config', fromConfigRoot( 'playwright.config.ts' ) ]
+		: [];
+
+// Set the default artifacts path.
+if ( ! process.env.WP_ARTIFACTS_PATH ) {
+	process.env.WP_ARTIFACTS_PATH = resolve(
+		process.env.GITHUB_WORKSPACE || process.cwd(),
+		'artifacts'
+	);
+}
+
+const testResult = spawn(
+	'npx',
+	[
+		require.resolve( '@playwright/test/cli' ),
+		'test',
+		...config,
+		...getArgsFromCLI(),
+	],
+	{
+		stdio: 'inherit',
+	}
+);
+
+if ( testResult.status > 0 ) {
+	process.exit( testResult.status );
+}
diff --git a/test/e2e/playwright.config.ts b/test/e2e/playwright.config.ts
index e1724a61d6126..742ca54f4f2ac 100644
--- a/test/e2e/playwright.config.ts
+++ b/test/e2e/playwright.config.ts
@@ -2,56 +2,23 @@
  * External dependencies
  */
 import os from 'os';
-import path from 'path';
 import { fileURLToPath } from 'url';
 import { defineConfig, devices } from '@playwright/test';
 
-const STORAGE_STATE_PATH =
-	process.env.STORAGE_STATE_PATH ||
-	path.join( process.cwd(), 'artifacts/storage-states/admin.json' );
+/**
+ * WordPress dependencies
+ */
+const baseConfig = require( '@wordpress/scripts/config/playwright.config' );
 
 const config = defineConfig( {
+	...baseConfig.default,
 	reporter: process.env.CI
 		? [ [ 'github' ], [ './config/flaky-tests-reporter.ts' ] ]
 		: 'list',
-	forbidOnly: !! process.env.CI,
 	workers: 1,
-	retries: process.env.CI ? 2 : 0,
-	timeout: parseInt( process.env.TIMEOUT || '', 10 ) || 100_000, // Defaults to 100 seconds.
-	// Don't report slow test "files", as we will be running our tests in serial.
-	reportSlowTests: null,
-	testDir: fileURLToPath( new URL( './specs', 'file:' + __filename ).href ),
-	outputDir: path.join( process.cwd(), 'artifacts/test-results' ),
-	snapshotPathTemplate:
-		'{testDir}/{testFileDir}/__snapshots__/{arg}-{projectName}{ext}',
 	globalSetup: fileURLToPath(
 		new URL( './config/global-setup.ts', 'file:' + __filename ).href
 	),
-	use: {
-		baseURL: process.env.WP_BASE_URL || 'http://localhost:8889',
-		headless: true,
-		viewport: {
-			width: 960,
-			height: 700,
-		},
-		ignoreHTTPSErrors: true,
-		locale: 'en-US',
-		contextOptions: {
-			reducedMotion: 'reduce',
-			strictSelectors: true,
-		},
-		storageState: STORAGE_STATE_PATH,
-		actionTimeout: 10_000, // 10 seconds.
-		trace: 'retain-on-failure',
-		screenshot: 'only-on-failure',
-		video: 'on-first-retry',
-	},
-	webServer: {
-		command: 'npm run wp-env start',
-		port: 8889,
-		timeout: 120_000, // 120 seconds.
-		reuseExistingServer: true,
-	},
 	projects: [
 		{
 			name: 'chromium',
diff --git a/test/e2e/specs/editor/blocks/image.spec.js b/test/e2e/specs/editor/blocks/image.spec.js
index c9d7bbe464428..887e96afe707e 100644
--- a/test/e2e/specs/editor/blocks/image.spec.js
+++ b/test/e2e/specs/editor/blocks/image.spec.js
@@ -459,9 +459,10 @@ test.describe( 'Image', () => {
 					attributes: { url },
 				},
 			] = blocks;
-			expect(
-				await imageBlock.getByRole( 'img' ).getAttribute( 'src' )
-			).toBe( url );
+			await expect( imageBlock.getByRole( 'img' ) ).toHaveAttribute(
+				'src',
+				url
+			);
 			expect(
 				new URL( url ).host,
 				'should be updated to the media library'
@@ -492,9 +493,10 @@ test.describe( 'Image', () => {
 				},
 			] = blocks;
 			expect( url ).not.toBe( firstUrl );
-			expect(
-				await imageBlock.getByRole( 'img' ).getAttribute( 'src' )
-			).toBe( url );
+			await expect( imageBlock.getByRole( 'img' ) ).toHaveAttribute(
+				'src',
+				url
+			);
 			expect(
 				new URL( url ).host,
 				'should be updated to the media library'
@@ -1182,7 +1184,7 @@ test.describe( 'Image - interactivity', () => {
 					page.getByRole( 'combobox', {
 						name: 'Animation',
 					} )
-				).not.toBeVisible();
+				).toBeHidden();
 			} );
 			test( 'Animation selector should NOT appear if Behavior is Default', async ( {
 				page,
@@ -1215,7 +1217,7 @@ test.describe( 'Image - interactivity', () => {
 					page.getByRole( 'combobox', {
 						name: 'Animation',
 					} )
-				).not.toBeVisible();
+				).toBeHidden();
 			} );
 		} );
 
diff --git a/test/e2e/specs/editor/plugins/nonce.spec.js b/test/e2e/specs/editor/plugins/nonce.spec.js
index b2c249ac968b8..a05f3618b3641 100644
--- a/test/e2e/specs/editor/plugins/nonce.spec.js
+++ b/test/e2e/specs/editor/plugins/nonce.spec.js
@@ -16,6 +16,7 @@ test.describe( 'Nonce', () => {
 		await admin.createNewPost();
 		await page.keyboard.press( 'Enter' );
 		// Wait until the network is idle.
+		// eslint-disable-next-line playwright/no-networkidle
 		await page.waitForLoadState( 'networkidle' );
 		await page.keyboard.type( 'test' );
 
diff --git a/test/e2e/specs/editor/various/post-editor-template-mode.spec.js b/test/e2e/specs/editor/various/post-editor-template-mode.spec.js
index a8e4d04df378e..5d63ff789e4ec 100644
--- a/test/e2e/specs/editor/various/post-editor-template-mode.spec.js
+++ b/test/e2e/specs/editor/various/post-editor-template-mode.spec.js
@@ -207,6 +207,7 @@ class PostEditorTemplateMode {
 		// Without this, the editor will move focus to body while still typing.
 		// And the save states will not be counted as dirty.
 		// There is likely a bug in the code, waiting for the snackbar above should be enough.
+		// eslint-disable-next-line playwright/no-networkidle
 		await this.page.waitForLoadState( 'networkidle' );
 	}
 
diff --git a/test/performance/playwright.config.ts b/test/performance/playwright.config.ts
index e17d3c7fc31ca..0918b79d3144d 100644
--- a/test/performance/playwright.config.ts
+++ b/test/performance/playwright.config.ts
@@ -3,63 +3,32 @@
  */
 import path from 'path';
 import { fileURLToPath } from 'url';
-import { defineConfig, devices } from '@playwright/test';
+import { defineConfig } from '@playwright/test';
+
+/**
+ * WordPress dependencies
+ */
+const baseConfig = require( '@wordpress/scripts/config/playwright.config' );
 
-process.env.WP_ARTIFACTS_PATH ??= path.join( process.cwd(), 'artifacts' );
-process.env.STORAGE_STATE_PATH ??= path.join(
-	process.env.WP_ARTIFACTS_PATH,
-	'storage-states/admin.json'
-);
 process.env.ASSETS_PATH = path.join( __dirname, 'assets' );
 
 const config = defineConfig( {
+	...baseConfig.default,
 	reporter: process.env.CI
 		? './config/performance-reporter.ts'
 		: [ [ 'list' ], [ './config/performance-reporter.ts' ] ],
 	forbidOnly: !! process.env.CI,
 	fullyParallel: false,
-	workers: 1,
 	retries: 0,
 	timeout: parseInt( process.env.TIMEOUT || '', 10 ) || 600_000, // Defaults to 10 minutes.
 	reportSlowTests: null,
-	testDir: fileURLToPath( new URL( './specs', 'file:' + __filename ).href ),
-	outputDir: path.join( process.env.WP_ARTIFACTS_PATH, 'test-results' ),
-	snapshotPathTemplate:
-		'{testDir}/{testFileDir}/__snapshots__/{arg}-{projectName}{ext}',
 	globalSetup: fileURLToPath(
 		new URL( './config/global-setup.ts', 'file:' + __filename ).href
 	),
 	use: {
-		baseURL: process.env.WP_BASE_URL || 'http://localhost:8889',
-		headless: true,
-		viewport: {
-			width: 960,
-			height: 700,
-		},
-		ignoreHTTPSErrors: true,
-		locale: 'en-US',
-		contextOptions: {
-			reducedMotion: 'reduce',
-			strictSelectors: true,
-		},
-		storageState: process.env.STORAGE_STATE_PATH,
-		actionTimeout: 10_000, // 10 seconds.
-		trace: 'retain-on-failure',
-		screenshot: 'only-on-failure',
+		...baseConfig.default.use,
 		video: 'off',
 	},
-	webServer: {
-		command: 'npm run wp-env start',
-		port: 8889,
-		timeout: 120_000, // 120 seconds.
-		reuseExistingServer: true,
-	},
-	projects: [
-		{
-			name: 'chromium',
-			use: { ...devices[ 'Desktop Chrome' ] },
-		},
-	],
 } );
 
 export default config;
diff --git a/test/performance/specs/front-end-block-theme.spec.js b/test/performance/specs/front-end-block-theme.spec.js
index 6ceedba9bd6d5..55845e49f9515 100644
--- a/test/performance/specs/front-end-block-theme.spec.js
+++ b/test/performance/specs/front-end-block-theme.spec.js
@@ -32,6 +32,7 @@ test.describe( 'Front End Performance', () => {
 			i + 1
 		} of ${ rounds })`, async ( { page } ) => {
 			// Go to the base URL.
+			// eslint-disable-next-line playwright/no-networkidle
 			await page.goto( '/', { waitUntil: 'networkidle' } );
 
 			// Take the measurements.
diff --git a/test/performance/specs/front-end-classic-theme.spec.js b/test/performance/specs/front-end-classic-theme.spec.js
index 880da94a11c60..cbc058f4536d6 100644
--- a/test/performance/specs/front-end-classic-theme.spec.js
+++ b/test/performance/specs/front-end-classic-theme.spec.js
@@ -31,6 +31,7 @@ test.describe( 'Front End Performance', () => {
 			page,
 		} ) => {
 			// Go to the base URL.
+			// eslint-disable-next-line playwright/no-networkidle
 			await page.goto( '/', { waitUntil: 'networkidle' } );
 
 			// Take the measurements.

From c2d130052b73c9e389b12e685afa080b29539185 Mon Sep 17 00:00:00 2001
From: Tonya Mork <tonya.mork@automattic.com>
Date: Thu, 31 Aug 2023 22:46:16 -0500
Subject: [PATCH 32/34] Font Face & Library: load files if ! class_exists
 (#54103)

Prevents Font Face and Font Library PHP files from loading into memory if each main class already exists in memory (i.e. meaning already in WordPress Core).
---
 lib/load.php | 24 +++++++++++++-----------
 1 file changed, 13 insertions(+), 11 deletions(-)

diff --git a/lib/load.php b/lib/load.php
index ede4a8674c719..72bc0ab66a99b 100644
--- a/lib/load.php
+++ b/lib/load.php
@@ -165,19 +165,21 @@ function gutenberg_is_experiment_enabled( $name ) {
 	( defined( 'FONTS_LIBRARY_ENABLE' ) && FONTS_LIBRARY_ENABLE )
 ) {
 	// Loads the Font Library.
-	require __DIR__ . '/experimental/fonts/font-library/class-wp-font-collection.php';
-	require __DIR__ . '/experimental/fonts/font-library/class-wp-font-library.php';
-	require __DIR__ . '/experimental/fonts/font-library/class-wp-font-family-utils.php';
-	require __DIR__ . '/experimental/fonts/font-library/class-wp-font-family.php';
-	require __DIR__ . '/experimental/fonts/font-library/class-wp-rest-font-library-controller.php';
-	require __DIR__ . '/experimental/fonts/font-library/font-library.php';
+	if ( ! class_exists( 'WP_Font_Library' ) ) {
+		require __DIR__ . '/experimental/fonts/font-library/class-wp-font-collection.php';
+		require __DIR__ . '/experimental/fonts/font-library/class-wp-font-library.php';
+		require __DIR__ . '/experimental/fonts/font-library/class-wp-font-family-utils.php';
+		require __DIR__ . '/experimental/fonts/font-library/class-wp-font-family.php';
+		require __DIR__ . '/experimental/fonts/font-library/class-wp-rest-font-library-controller.php';
+		require __DIR__ . '/experimental/fonts/font-library/font-library.php';
+	}
 
 	// Load the Font Face.
-	require __DIR__ . '/compat/wordpress-6.4/fonts/font-face/class-wp-font-face.php';
-	require __DIR__ . '/compat/wordpress-6.4/fonts/font-face/class-wp-font-face-resolver.php';
-
-	// A general purpose file for all fonts PHP functions and hooks.
-	require __DIR__ . '/compat/wordpress-6.4/fonts/fonts.php';
+	if ( ! class_exists( 'WP_Font_Face' ) ) {
+		require __DIR__ . '/compat/wordpress-6.4/fonts/font-face/class-wp-font-face.php';
+		require __DIR__ . '/compat/wordpress-6.4/fonts/font-face/class-wp-font-face-resolver.php';
+		require __DIR__ . '/compat/wordpress-6.4/fonts/fonts.php';
+	}
 
 	// Load the BC Layer to avoid fatal errors of extenders using the Fonts API.
 	// @core-merge: do not merge the BC layer files into WordPress Core.

From 55f85fb31cff86ffe513bda1a6c659888b88dcdd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Greg=20Zi=C3=B3=C5=82kowski?= <grzegorz@gziolo.pl>
Date: Fri, 1 Sep 2023 07:23:26 +0200
Subject: [PATCH 33/34] Workflow: Fix the issue with npm publishing for WP
 major version (#54088)

---
 .github/workflows/publish-npm-packages.yml | 20 +++++++++++++++++++-
 1 file changed, 19 insertions(+), 1 deletion(-)

diff --git a/.github/workflows/publish-npm-packages.yml b/.github/workflows/publish-npm-packages.yml
index dd1f188e0ae2f..52a6d8f9833f2 100644
--- a/.github/workflows/publish-npm-packages.yml
+++ b/.github/workflows/publish-npm-packages.yml
@@ -30,12 +30,14 @@ jobs:
         environment: WordPress packages
         steps:
             - name: Checkout (for CLI)
+              if: ${{ github.event.inputs.release_type != 'wp' }}
               uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
               with:
                   path: cli
                   ref: trunk
 
             - name: Checkout (for publishing)
+              if: ${{ github.event.inputs.release_type != 'wp' }}
               uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
               with:
                   path: publish
@@ -43,6 +45,14 @@ jobs:
                   ref: trunk
                   token: ${{ secrets.GUTENBERG_TOKEN }}
 
+            - name: Checkout (for publishing WP major version)
+              if: ${{ github.event.inputs.release_type == 'wp' && github.event.inputs.wp_version }}
+              uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
+              with:
+                  path: publish
+                  ref: wp/${{ github.event.inputs.wp_version }}
+                  token: ${{ secrets.GUTENBERG_TOKEN }}
+
             - name: Configure git user name and email (for publishing)
               run: |
                   cd publish
@@ -50,11 +60,19 @@ jobs:
                   git config user.email gutenberg@wordpress.org
 
             - name: Setup Node.js
+              if: ${{ github.event.inputs.release_type != 'wp' }}
               uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0
               with:
                   node-version-file: 'cli/.nvmrc'
                   registry-url: 'https://registry.npmjs.org'
 
+            - name: Setup Node.js (for WP major version)
+              if: ${{ github.event.inputs.release_type == 'wp' && github.event.inputs.wp_version }}
+              uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0
+              with:
+                  node-version-file: 'publish/.nvmrc'
+                  registry-url: 'https://registry.npmjs.org'
+
             - name: Publish development packages to npm ("next" dist-tag)
               if: ${{ github.event.inputs.release_type == 'development' }}
               run: |
@@ -73,7 +91,7 @@ jobs:
               env:
                   NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
 
-            - name: Publish packages to npm for WP major ("wp/${{ github.event.inputs.wp_version || 'X.Y' }}" dist-tag)
+            - name: Publish packages to npm for WP major version ("wp/${{ github.event.inputs.wp_version || 'X.Y' }}" dist-tag)
               if: ${{ github.event.inputs.release_type == 'wp' && github.event.inputs.wp_version }}
               run: |
                   cd publish

From 8e0fdcab990978929f2e452926aa5e66babe1c25 Mon Sep 17 00:00:00 2001
From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com>
Date: Fri, 1 Sep 2023 16:31:01 +1000
Subject: [PATCH 34/34] Columns: Adopt button and heading element colors
 (#54104)

---
 docs/reference-guides/core-blocks.md          | 4 ++--
 packages/block-library/src/column/block.json  | 1 +
 packages/block-library/src/columns/block.json | 2 ++
 3 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/docs/reference-guides/core-blocks.md b/docs/reference-guides/core-blocks.md
index 7f66c40bb00ba..1d83564fe3c17 100644
--- a/docs/reference-guides/core-blocks.md
+++ b/docs/reference-guides/core-blocks.md
@@ -98,7 +98,7 @@ A single column within a columns block. ([Source](https://github.com/WordPress/g
 -	**Name:** core/column
 -	**Category:** design
 -	**Parent:** core/columns
--	**Supports:** anchor, color (background, gradients, heading, link, text), layout, spacing (blockGap, padding), typography (fontSize, lineHeight), ~~html~~, ~~reusable~~
+-	**Supports:** anchor, color (background, button, gradients, heading, link, text), layout, spacing (blockGap, padding), typography (fontSize, lineHeight), ~~html~~, ~~reusable~~
 -	**Attributes:** allowedBlocks, templateLock, verticalAlignment, width
 
 ## Columns
@@ -107,7 +107,7 @@ Display content in multiple columns, with blocks added to each column. ([Source]
 
 -	**Name:** core/columns
 -	**Category:** design
--	**Supports:** align (full, wide), anchor, color (background, gradients, link, text), layout (default, ~~allowEditing~~, ~~allowInheriting~~, ~~allowSwitching~~), spacing (blockGap, margin, padding), typography (fontSize, lineHeight), ~~html~~
+-	**Supports:** align (full, wide), anchor, color (background, button, gradients, heading, link, text), layout (default, ~~allowEditing~~, ~~allowInheriting~~, ~~allowSwitching~~), spacing (blockGap, margin, padding), typography (fontSize, lineHeight), ~~html~~
 -	**Attributes:** isStackedOnMobile, templateLock, verticalAlignment
 
 ## Comment Author Avatar (deprecated)
diff --git a/packages/block-library/src/column/block.json b/packages/block-library/src/column/block.json
index dcd29cf100bda..7f61f307fab15 100644
--- a/packages/block-library/src/column/block.json
+++ b/packages/block-library/src/column/block.json
@@ -30,6 +30,7 @@
 		"color": {
 			"gradients": true,
 			"heading": true,
+			"button": true,
 			"link": true,
 			"__experimentalDefaultControls": {
 				"background": true,
diff --git a/packages/block-library/src/columns/block.json b/packages/block-library/src/columns/block.json
index a99fd020ca8d2..dff9c45ae2dde 100644
--- a/packages/block-library/src/columns/block.json
+++ b/packages/block-library/src/columns/block.json
@@ -26,6 +26,8 @@
 		"color": {
 			"gradients": true,
 			"link": true,
+			"heading": true,
+			"button": true,
 			"__experimentalDefaultControls": {
 				"background": true,
 				"text": true