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;
};