Commit cd8eae6f4ad for woocommerce

commit cd8eae6f4ad2947b7336330e8f9685ccbfb3a090
Author: Yuliyan Slavchev <yuliyan.slavchev@gmail.com>
Date:   Tue Sep 15 13:40:33 2026 +0300

    Email Editor: Fix template selector layout with Gutenberg 23.9+ (#68551)

    * Email Editor: Fix template selector layout with Gutenberg 23.9+

    * Use Notice component for the Recent templates category

    * Replace HStack with Stack in templates list component

    * Use EmptyState component in TemplateNoResults

    * Fix changelog type

    * Update changelog significance to minor

diff --git a/packages/js/email-editor/README.md b/packages/js/email-editor/README.md
index c848349e283..cb98caa0c30 100644
--- a/packages/js/email-editor/README.md
+++ b/packages/js/email-editor/README.md
@@ -261,18 +261,24 @@ For Jest (or any non-webpack test runner), the runtime fallback applies, so unit
 globalThis.__i18n_text_domain__ = 'your-text-domain';
 ```

-#### Global Styles Engine
+#### Packages that must be bundled

-A of 1.4.3 the email editor package depends on `@wordpress/global-styles-engine`, which is **not enqueued by WordPress core**. Unlike most `@wordpress/*` packages, this package is not available globally in WordPress environments and must be bundled.
+The email editor package depends on a few `@wordpress/*` packages that are **not enqueued by WordPress core**. Unlike most `@wordpress/*` packages, they are not available globally in WordPress environments and must be bundled:

-**Impact**: If your build configuration marks all `@wordpress/*` packages as externals (common in webpack configs), you will encounter runtime errors: `"Cannot find module '@wordpress/global-styles-engine'"`.
+-   `@wordpress/global-styles-engine` (since 1.4.3)
+-   `@wordpress/ui` (since 2.5.0)

-**Solution**: Configure your webpack dependency extraction plugin to bundle this package instead of treating it as an external:
+**Impact**: If your build configuration marks all `@wordpress/*` packages as externals (common in webpack configs), you will encounter runtime errors such as `"Cannot find module '@wordpress/global-styles-engine'"`.
+
+**Solution**: Configure your webpack dependency extraction plugin to bundle these packages instead of treating them as externals:

 ```javascript
 new DependencyExtractionWebpackPlugin( {
     requestToExternal( request ) {
-        if ( request === '@wordpress/global-styles-engine' ) {
+        if (
+            request === '@wordpress/global-styles-engine' ||
+            request === '@wordpress/ui'
+        ) {
             // Return null to bundle this package instead of treating it as external
             return null;
         }
diff --git a/packages/js/email-editor/changelog/wooplug-7713-template-selector-layout-breaks-with-gutenberg-239 b/packages/js/email-editor/changelog/wooplug-7713-template-selector-layout-breaks-with-gutenberg-239
new file mode 100644
index 00000000000..c4cec85dae3
--- /dev/null
+++ b/packages/js/email-editor/changelog/wooplug-7713-template-selector-layout-breaks-with-gutenberg-239
@@ -0,0 +1,4 @@
+Significance: minor
+Type: fix
+
+Fix the template selector layout with Gutenberg 23.9+ by giving it its own styles and using Tabs from @wordpress/ui for the category list
diff --git a/packages/js/email-editor/package.json b/packages/js/email-editor/package.json
index 7f7c3183a62..e83a879fa5b 100644
--- a/packages/js/email-editor/package.json
+++ b/packages/js/email-editor/package.json
@@ -112,6 +112,7 @@
 		"@wordpress/priority-queue": "catalog:wp-min",
 		"@wordpress/private-apis": "catalog:wp-min",
 		"@wordpress/rich-text": "catalog:wp-min",
+		"@wordpress/ui": "catalog:wp-bundled",
 		"@wordpress/url": "catalog:wp-min",
 		"clsx": "2.1.x",
 		"deepmerge": "^4.3.1",
diff --git a/packages/js/email-editor/src/components/template-select/select-modal.tsx b/packages/js/email-editor/src/components/template-select/select-modal.tsx
index df740d90225..515717c1f7f 100644
--- a/packages/js/email-editor/src/components/template-select/select-modal.tsx
+++ b/packages/js/email-editor/src/components/template-select/select-modal.tsx
@@ -8,6 +8,7 @@ import type { UserPatternCategory } from '@wordpress/core-data/build-types/selec
 import { dispatch, useSelect } from '@wordpress/data';
 import { Modal, Button, Flex, FlexItem } from '@wordpress/components';
 import { __ } from '@wordpress/i18n';
+import { Tabs } from '@wordpress/ui';

 /**
  * Internal dependencies
@@ -102,26 +103,43 @@ function SelectTemplateBody( {
 		return () => clearTimeout( timeoutId );
 	}, [ displayCategories, selectedCategory ] );

-	return (
-		<div
-			className={ `block-editor-block-patterns-explorer${
-				displayCategories.length === 0 ? ' no-sidebar' : ''
-			}` }
-		>
-			{ displayCategories.length > 0 && (
-				<TemplateCategoriesListSidebar
-					templateCategories={ displayCategories }
+	if ( displayCategories.length === 0 ) {
+		return (
+			<div className="email-editor-template-select">
+				<TemplateList
+					templates={ templates }
+					onTemplateSelection={ handleTemplateSelection }
 					selectedCategory={ selectedCategory }
-					onClickCategory={ handleCategorySelection }
 				/>
-			) }
+			</div>
+		);
+	}

-			<TemplateList
-				templates={ templates }
-				onTemplateSelection={ handleTemplateSelection }
-				selectedCategory={ selectedCategory }
+	return (
+		<Tabs.Root
+			className="email-editor-template-select"
+			orientation="vertical"
+			value={ selectedCategory }
+			onValueChange={ handleCategorySelection }
+		>
+			<TemplateCategoriesListSidebar
+				templateCategories={ displayCategories }
 			/>
-		</div>
+			{ displayCategories.map( ( { name } ) => (
+				<Tabs.Panel
+					key={ name }
+					value={ name }
+					tabIndex={ -1 }
+					className="email-editor-template-select__panel"
+				>
+					<TemplateList
+						templates={ templates }
+						onTemplateSelection={ handleTemplateSelection }
+						selectedCategory={ name }
+					/>
+				</Tabs.Panel>
+			) ) }
+		</Tabs.Root>
 	);
 }

diff --git a/packages/js/email-editor/src/components/template-select/style.scss b/packages/js/email-editor/src/components/template-select/style.scss
index 922906b2658..50a394fbf82 100644
--- a/packages/js/email-editor/src/components/template-select/style.scss
+++ b/packages/js/email-editor/src/components/template-select/style.scss
@@ -1,54 +1,88 @@
-/* Move the less specific selector before the more specific one */
+@import "~@wordpress/base-styles/variables";
+@import "~@wordpress/base-styles/breakpoints";
+@import "~@wordpress/base-styles/mixins";

-.block-editor-block-patterns-list__item .block-editor-block-preview__container {
-	align-items: flex-start !important;
+$email-editor-template-select-sidebar-width: 280px;
+
+.email-editor-template-select__sidebar {
+	padding-bottom: $grid-unit-30;
+
+	@include break-medium() {
+		bottom: 0;
+		inset-inline-start: 0;
+		overflow-x: visible;
+		overflow-y: auto;
+		padding: $grid-unit-30;
+		position: absolute;
+		top: $header-height + $grid-unit-10;
+		width: $email-editor-template-select-sidebar-width;
+	}
 }

-.block-editor-block-patterns-explorer
-	.block-editor-block-patterns-list
-	.block-editor-block-preview__container {
-	max-height: 300px !important;
+.email-editor-template-select__panel {
+	@include break-medium() {
+		margin-inline-start: $email-editor-template-select-sidebar-width;
+	}
 }

-.block-editor-block-patterns-list__list-item {
-	overflow: hidden;
+.email-editor-template-select__list {
+	padding: $grid-unit-30 0 $grid-unit-40;
 }

-.email-editor-recent-templates-info {
-	background-color: #f0f6fc;
-	margin-bottom: 1.5rem;
-	padding: 0.5rem;
+.email-editor-template-select__templates {
+	display: grid;
+	grid-gap: $grid-unit-40;
+	grid-template-columns: repeat(1, 1fr);
+	margin-bottom: $grid-unit-20;
+
+	@include break-xlarge() {
+		grid-template-columns: repeat(2, 1fr);
+	}

-	path {
-		fill: #007cba;
+	@include break-huge() {
+		grid-template-columns: repeat(3, 1fr);
 	}
 }

-.email-editor-pattern__list-item {
+.email-editor-template-select__template {
 	border: 1px solid #ddd;
 	border-radius: 4px;
+	cursor: pointer;
+	min-height: 240px;
+	overflow: hidden;
+	position: relative;
+}

-	.block-editor-block-preview__container::after {
-		outline: unset !important;
-		outline-offset: unset !important;
-	}
+.email-editor-template-select__template-button {
+	height: 100%;

-	.block-editor-block-patterns-list__item-title {
-		font-size: unset !important;
-		padding-left: 1rem;
+	.block-editor-block-preview__container {
+		align-items: flex-start;
+		border-radius: 4px;
+		display: flex;
+		max-height: 300px;
 	}
 }

-.block-editor-block-patterns-explorer.no-sidebar
-	.block-editor-block-patterns-explorer__list {
-	margin-left: 0;
-	padding-left: 2rem;
+.email-editor-template-select__template-details {
+	margin-top: $grid-unit-10;
+	padding-bottom: $grid-unit-05;
+}
+
+.email-editor-template-select__template-title {
+	flex-grow: 1;
+	padding-inline-start: 1rem;
+	text-align: start;
+}
+
+.email-editor-template-select__no-results {
+	margin-inline: auto;
 }

 .email-editor-modal-footer {
 	background-color: #fff;
 	bottom: 0;
+	inset-inline-end: 0;
 	padding: 1rem 2rem;
 	position: absolute;
-	right: 0;
 }
diff --git a/packages/js/email-editor/src/components/template-select/template-categories-list-sidebar.tsx b/packages/js/email-editor/src/components/template-select/template-categories-list-sidebar.tsx
index 5025f78a7f4..b3b286486cf 100644
--- a/packages/js/email-editor/src/components/template-select/template-categories-list-sidebar.tsx
+++ b/packages/js/email-editor/src/components/template-select/template-categories-list-sidebar.tsx
@@ -1,7 +1,7 @@
 /**
  * External dependencies
  */
-import { Button } from '@wordpress/components';
+import { Tabs } from '@wordpress/ui';

 /**
  * Internal dependencies
@@ -9,36 +9,19 @@ import { Button } from '@wordpress/components';
 import { TemplateCategory } from '../../store';

 type Props = {
-	selectedCategory?: TemplateCategory;
 	templateCategories: Array< { name: TemplateCategory; label: string } >;
-	onClickCategory: ( name: TemplateCategory ) => void;
 };

-export function TemplateCategoriesListSidebar( {
-	selectedCategory,
-	templateCategories,
-	onClickCategory,
-}: Props ) {
-	const baseClassName = 'block-editor-block-patterns-explorer__sidebar';
+export function TemplateCategoriesListSidebar( { templateCategories }: Props ) {
 	return (
-		<div className={ baseClassName }>
-			<div className={ `${ baseClassName }__categories-list` }>
-				{ templateCategories.map( ( { name, label } ) => {
-					return (
-						<Button
-							key={ name }
-							label={ label }
-							className={ `${ baseClassName }__categories-list__item` }
-							isPressed={ selectedCategory === name }
-							onClick={ () => {
-								onClickCategory( name );
-							} }
-						>
-							{ label }
-						</Button>
-					);
-				} ) }
-			</div>
+		<div className="email-editor-template-select__sidebar">
+			<Tabs.List>
+				{ templateCategories.map( ( { name, label } ) => (
+					<Tabs.Tab key={ name } value={ name }>
+						{ label }
+					</Tabs.Tab>
+				) ) }
+			</Tabs.List>
 		</div>
 	);
 }
diff --git a/packages/js/email-editor/src/components/template-select/template-list.tsx b/packages/js/email-editor/src/components/template-select/template-list.tsx
index 51ddf0aa09f..cf40f04d125 100644
--- a/packages/js/email-editor/src/components/template-select/template-list.tsx
+++ b/packages/js/email-editor/src/components/template-select/template-list.tsx
@@ -4,11 +4,9 @@
 import { useMemo, memo } from '@wordpress/element';
 import { store as editorStore } from '@wordpress/editor';
 import { useSelect } from '@wordpress/data';
-import { Icon, info, blockDefault } from '@wordpress/icons';
+import { blockDefault } from '@wordpress/icons';
 import { __ } from '@wordpress/i18n';
-import {
-	__experimentalHStack as HStack, // eslint-disable-line
-} from '@wordpress/components';
+import { EmptyState, Notice, Stack } from '@wordpress/ui';
 // @ts-expect-error No types available for this component
 // eslint-disable-next-line
 import { BlockPreview } from '@wordpress/block-editor';
@@ -28,19 +26,18 @@ type Props = {

 function TemplateNoResults() {
 	return (
-		<div className="block-editor-inserter__no-results">
-			<Icon
-				className="block-editor-inserter__no-results-icon"
-				icon={ blockDefault }
-			/>
-			<p>{ __( 'No recent templates.', __i18n_text_domain__ ) }</p>
-			<p>
+		<EmptyState.Root className="email-editor-template-select__no-results">
+			<EmptyState.Icon icon={ blockDefault } />
+			<EmptyState.Title>
+				{ __( 'No recent templates.', __i18n_text_domain__ ) }
+			</EmptyState.Title>
+			<EmptyState.Description>
 				{ __(
 					'Your recent creations will appear here as soon as you begin.',
 					__i18n_text_domain__
 				) }
-			</p>
-		</div>
+			</EmptyState.Description>
+		</EmptyState.Root>
 	);
 }

@@ -73,14 +70,14 @@ function TemplateListBox( {
 	}

 	return (
-		<div className="block-editor-block-patterns-list" role="listbox">
+		<div className="email-editor-template-select__templates" role="listbox">
 			{ templates.map( ( template ) => (
 				<div
 					key={ `${ template.slug }_${ template.displayName }_${ template.id }` }
-					className="block-editor-block-patterns-list__list-item email-editor-pattern__list-item"
+					className="email-editor-template-select__template"
 				>
 					<div
-						className="block-editor-block-patterns-list__item"
+						className="email-editor-template-select__template-button"
 						role="button"
 						tabIndex={ 0 }
 						onClick={ () => {
@@ -113,11 +110,15 @@ function TemplateListBox( {
 								] }
 							/>

-							<HStack className="block-editor-patterns__pattern-details">
-								<h4 className="block-editor-block-patterns-list__item-title">
+							<Stack
+								direction="row"
+								align="center"
+								className="email-editor-template-select__template-details"
+							>
+								<h4 className="email-editor-template-select__template-title">
 									{ template.displayName }
 								</h4>
-							</HStack>
+							</Stack>
 						</Async>
 					</div>
 				</div>
@@ -148,19 +149,20 @@ export function TemplateList( {
 	);

 	return (
-		<div className="block-editor-block-patterns-explorer__list">
+		<Stack
+			direction="column"
+			gap="xl"
+			className="email-editor-template-select__list"
+		>
 			{ selectedCategory === 'recent' && (
-				<div className="email-editor-recent-templates-info">
-					<HStack spacing={ 1 } expanded={ false } justify="start">
-						<Icon icon={ info } />
-						<p>
-							{ __(
-								'Templates created on the legacy editor will not appear here.',
-								__i18n_text_domain__
-							) }
-						</p>
-					</HStack>
-				</div>
+				<Notice.Root intent="info">
+					<Notice.Description>
+						{ __(
+							'Templates created on the legacy editor will not appear here.',
+							__i18n_text_domain__
+						) }
+					</Notice.Description>
+				</Notice.Root>
 			) }

 			<MemorizedTemplateListBox
@@ -168,6 +170,6 @@ export function TemplateList( {
 				onTemplateSelection={ onTemplateSelection }
 				selectedCategory={ selectedCategory }
 			/>
-		</div>
+		</Stack>
 	);
 }
diff --git a/packages/js/email-editor/src/components/template-select/test/template-categories-list-sidebar.test.tsx b/packages/js/email-editor/src/components/template-select/test/template-categories-list-sidebar.test.tsx
new file mode 100644
index 00000000000..13bc04e76f0
--- /dev/null
+++ b/packages/js/email-editor/src/components/template-select/test/template-categories-list-sidebar.test.tsx
@@ -0,0 +1,57 @@
+/**
+ * External dependencies
+ */
+import { render, screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import { Tabs } from '@wordpress/ui';
+
+/**
+ * Internal dependencies
+ */
+import { TemplateCategoriesListSidebar } from '../template-categories-list-sidebar';
+import type { TemplateCategory } from '../../../store';
+
+const categories: Array< { name: TemplateCategory; label: string } > = [
+	{ name: 'recent', label: 'Recent' },
+	{ name: 'basic', label: 'Basic' },
+];
+
+describe( 'TemplateCategoriesListSidebar', () => {
+	it( 'renders one tab per category', () => {
+		render(
+			<Tabs.Root orientation="vertical" value="basic">
+				<TemplateCategoriesListSidebar
+					templateCategories={ categories }
+				/>
+			</Tabs.Root>
+		);
+
+		const tabs = screen.getAllByRole( 'tab' );
+		expect( tabs ).toHaveLength( 2 );
+		expect( tabs[ 0 ] ).toHaveTextContent( 'Recent' );
+		expect( tabs[ 1 ] ).toHaveTextContent( 'Basic' );
+		expect( tabs[ 1 ] ).toHaveAttribute( 'aria-selected', 'true' );
+	} );
+
+	it( 'reports the clicked category', async () => {
+		const onValueChange = jest.fn();
+		render(
+			<Tabs.Root
+				orientation="vertical"
+				value="basic"
+				onValueChange={ onValueChange }
+			>
+				<TemplateCategoriesListSidebar
+					templateCategories={ categories }
+				/>
+			</Tabs.Root>
+		);
+
+		await userEvent.click( screen.getByRole( 'tab', { name: 'Recent' } ) );
+
+		expect( onValueChange ).toHaveBeenCalledWith(
+			'recent',
+			expect.anything()
+		);
+	} );
+} );
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 4122aee3bbc..5faae16a9b9 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -1312,6 +1312,9 @@ importers:
       '@wordpress/rich-text':
         specifier: catalog:wp-min
         version: 7.33.2(@types/react@18.3.28)(react@18.3.1)
+      '@wordpress/ui':
+        specifier: catalog:wp-bundled
+        version: 0.17.0(@date-fns/tz@1.4.1)(@types/react@18.3.28)(date-fns@4.1.0)(postcss@8.5.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(stylelint@14.16.1)
       '@wordpress/url':
         specifier: catalog:wp-min
         version: 4.33.1