Commit 9f7e88f2e20 for woocommerce

commit 9f7e88f2e20d7d7ca4e5f33e5d07eabe2b4b4ae8
Author: Yuliyan Slavchev <yuliyan.slavchev@gmail.com>
Date:   Wed Jun 10 12:17:55 2026 +0300

    Fix email editor crash when opening typography styles panel (#65613)

    * Fix email editor crash when opening typography styles panel

    * add styles.elements path guard

    * Add tests for getElementStyles

diff --git a/packages/js/email-editor/changelog/fix-wooplug-6807-typography-panel-crash b/packages/js/email-editor/changelog/fix-wooplug-6807-typography-panel-crash
new file mode 100644
index 00000000000..50b17212515
--- /dev/null
+++ b/packages/js/email-editor/changelog/fix-wooplug-6807-typography-panel-crash
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Fix crash when opening the typography styles panel in the email editor for elements without typography styles defined.
diff --git a/packages/js/email-editor/src/components/styles-sidebar/test/utils.spec.ts b/packages/js/email-editor/src/components/styles-sidebar/test/utils.spec.ts
new file mode 100644
index 00000000000..140d8a3cfad
--- /dev/null
+++ b/packages/js/email-editor/src/components/styles-sidebar/test/utils.spec.ts
@@ -0,0 +1,80 @@
+/**
+ * Internal dependencies
+ */
+import { EmailStyles } from '../../../store';
+import { getElementStyles, getHeadingElementStyles } from '../utils';
+
+describe( 'getElementStyles', () => {
+	it( 'always returns typography and color objects, even when missing', () => {
+		// `link` carrying only a color (as synced from a site theme) used to
+		// crash consumers that destructure `typography`.
+		const styles = {
+			elements: { link: { color: { text: '#0000ff' } } },
+		} as unknown as EmailStyles;
+
+		const result = getElementStyles( styles, 'link' );
+
+		expect( result.typography ).toEqual( {} );
+		expect( result.color ).toEqual( { text: '#0000ff' } );
+	} );
+
+	it( 'returns empty typography and color when the element is absent', () => {
+		const result = getElementStyles( {} as EmailStyles, 'button' );
+
+		expect( result.typography ).toEqual( {} );
+		expect( result.color ).toEqual( {} );
+	} );
+
+	it( 'falls back to empty typography for the text element', () => {
+		// No top-level typography/color on the styles object.
+		const result = getElementStyles( {} as EmailStyles, 'text' );
+
+		expect( result.typography ).toEqual( {} );
+		expect( result.color ).toEqual( {} );
+	} );
+
+	it( 'preserves existing text typography and color', () => {
+		const styles = {
+			typography: { fontFamily: 'serif' },
+			color: { text: '#111111' },
+		} as unknown as EmailStyles;
+
+		const result = getElementStyles( styles, 'text' );
+
+		expect( result.typography ).toEqual( { fontFamily: 'serif' } );
+		expect( result.color ).toEqual( { text: '#111111' } );
+	} );
+
+	it( 'does not throw for the heading element when elements is undefined', () => {
+		expect( () =>
+			getElementStyles( {} as EmailStyles, 'heading' )
+		).not.toThrow();
+
+		const result = getElementStyles( {} as EmailStyles, 'heading' );
+		expect( result.typography ).toEqual( {} );
+	} );
+} );
+
+describe( 'getHeadingElementStyles', () => {
+	it( 'does not throw when styles.elements is undefined', () => {
+		expect( () =>
+			getHeadingElementStyles( {} as EmailStyles, 'h2' )
+		).not.toThrow();
+	} );
+
+	it( 'merges heading and heading-level typography when merge is true', () => {
+		const styles = {
+			elements: {
+				heading: { typography: { fontWeight: '700' } },
+				h2: { typography: { fontSize: '32px' } },
+			},
+		} as unknown as EmailStyles;
+
+		const result = getHeadingElementStyles( styles, 'h2', true );
+
+		expect( result.typography ).toEqual( {
+			fontWeight: '700',
+			fontSize: '32px',
+		} );
+	} );
+} );
diff --git a/packages/js/email-editor/src/components/styles-sidebar/utils.ts b/packages/js/email-editor/src/components/styles-sidebar/utils.ts
index 747fb47a073..bd11f49520b 100644
--- a/packages/js/email-editor/src/components/styles-sidebar/utils.ts
+++ b/packages/js/email-editor/src/components/styles-sidebar/utils.ts
@@ -32,13 +32,13 @@ export const getHeadingElementStyles = (
 	merge
 		? ( deepmerge.all( [
 				defaultStyleObject,
-				styles.elements.heading || {},
-				styles.elements[ headingLevel ] || {},
+				styles.elements?.heading || {},
+				styles.elements?.[ headingLevel ] || {},
 		  ] ) as EmailStyles )
 		: ( {
 				...defaultStyleObject,
-				...( styles.elements.heading || {} ),
-				...( styles.elements[ headingLevel ] || {} ),
+				...( styles.elements?.heading || {} ),
+				...( styles.elements?.[ headingLevel ] || {} ),
 		  } as EmailStyles );

 export const getElementStyles = (
@@ -47,20 +47,31 @@ export const getElementStyles = (
 	headingLevel = 'heading',
 	merge = false
 ): EmailStyles => {
+	let elementStyles: EmailStyles;
 	switch ( element ) {
 		case 'text':
-			return {
+			elementStyles = {
 				typography: styles.typography,
 				color: styles.color,
 			} as EmailStyles;
+			break;
 		case 'heading':
-			return getHeadingElementStyles(
+			elementStyles = getHeadingElementStyles(
 				styles,
 				headingLevel ?? 'heading',
 				merge
 			);
+			break;
 		default:
-			return ( styles.elements[ element ] ||
+			elementStyles = ( styles.elements?.[ element ] ||
 				defaultStyleObject ) as EmailStyles;
 	}
+
+	// Ensure the `typography` and `color` objects are always available to
+	// consumers so they can safely destructure them.
+	return {
+		...elementStyles,
+		typography: elementStyles.typography ?? {},
+		color: elementStyles.color ?? {},
+	} as EmailStyles;
 };