diff --git a/src/hoc/withHtmlAttributes.js b/src/hoc/withHtmlAttributes.js
index d35968925..27e6cd812 100644
--- a/src/hoc/withHtmlAttributes.js
+++ b/src/hoc/withHtmlAttributes.js
@@ -7,6 +7,7 @@ import { applyFilters } from '@wordpress/hooks';
import { useUpdateEffect } from 'react-use';
import { convertInlineStyleStringToObject } from '@utils/convertInlineStyleStringToObject';
+import { sanitizeHtmlAttribute } from '@utils/sanitizeHtmlAttribute';
export const booleanAttributes = [
'allowfullscreen',
@@ -60,16 +61,6 @@ function shallowEqual( obj1, obj2 ) {
return true;
}
-const sanitizeAttributeValue = ( value ) => {
- // Replace characters like &, <, >, " with their HTML entity equivalents
- return value
- .replace( /&/g, '&' )
- .replace( //g, '>' )
- .replace( /"/g, '"' )
- .replace( /'/g, ''' );
-};
-
export function withHtmlAttributes( WrappedComponent ) {
return ( ( props ) => {
const {
@@ -88,7 +79,7 @@ export function withHtmlAttributes( WrappedComponent ) {
const isSavingPost = useSelect( ( select ) => select( 'core/editor' ).isSavingPost() );
const { style = '', href, ...otherAttributes } = htmlAttributes;
const escapedAttributes = Object.keys( otherAttributes ).reduce( ( acc, key ) => {
- acc[ key ] = sanitizeAttributeValue( otherAttributes[ key ] );
+ acc[ key ] = sanitizeHtmlAttribute( otherAttributes[ key ] );
return acc;
}, {} );
const [ processedStyle, setProcessedStyle ] = useState( style );
diff --git a/src/tests/unit/sanitizeHtmlAttribute.test.js b/src/tests/unit/sanitizeHtmlAttribute.test.js
new file mode 100644
index 000000000..a81039a01
--- /dev/null
+++ b/src/tests/unit/sanitizeHtmlAttribute.test.js
@@ -0,0 +1,54 @@
+import { sanitizeHtmlAttribute } from '../../utils/sanitizeHtmlAttribute';
+
+describe( 'sanitize HTML attribute', () => {
+ it( 'should return the same value if it is a string', () => {
+ const value = sanitizeHtmlAttribute( 'foo' );
+ expect( value ).toEqual( 'foo' );
+ } );
+
+ it( 'should convert a number to a string', () => {
+ const value = sanitizeHtmlAttribute( 500 );
+ expect( value ).toEqual( '500' );
+ } );
+
+ it( 'should convert an object to a string', () => {
+ const value = sanitizeHtmlAttribute( { foo: 'bar' } );
+ expect( value ).toEqual( '{"foo":"bar"}' );
+ } );
+
+ it( 'should convert an array to a string', () => {
+ const value = sanitizeHtmlAttribute( [ 'foo', 'bar' ] );
+ expect( value ).toEqual( '["foo","bar"]' );
+ } );
+
+ it( 'should handle undefined', () => {
+ const value = sanitizeHtmlAttribute( undefined );
+ expect( value ).toEqual( '' );
+ } );
+
+ it( 'should handle null', () => {
+ const value = sanitizeHtmlAttribute( null );
+ expect( value ).toEqual( '' );
+ } );
+
+ it( 'should escape HTML special characters', () => {
+ const value = sanitizeHtmlAttribute( 'foo & "quote" \'single\'' );
+ expect( value ).toEqual( 'foo & <bar> "quote" 'single'' );
+ } );
+
+ it( 'should handle boolean values', () => {
+ expect( sanitizeHtmlAttribute( true ) ).toEqual( 'true' );
+ expect( sanitizeHtmlAttribute( false ) ).toEqual( 'false' );
+ } );
+
+ it( 'should handle special number values', () => {
+ expect( sanitizeHtmlAttribute( NaN ) ).toEqual( 'NaN' );
+ expect( sanitizeHtmlAttribute( Infinity ) ).toEqual( 'Infinity' );
+ expect( sanitizeHtmlAttribute( -Infinity ) ).toEqual( '-Infinity' );
+ } );
+
+ it( 'should handle empty string', () => {
+ const value = sanitizeHtmlAttribute( '' );
+ expect( value ).toEqual( '' );
+ } );
+} );
diff --git a/src/utils/sanitizeHtmlAttribute.js b/src/utils/sanitizeHtmlAttribute.js
new file mode 100644
index 000000000..328004663
--- /dev/null
+++ b/src/utils/sanitizeHtmlAttribute.js
@@ -0,0 +1,25 @@
+export const sanitizeHtmlAttribute = ( value ) => {
+ if ( null === value || undefined === value ) {
+ return '';
+ }
+
+ let stringValue = '';
+
+ if ( 'object' === typeof value ) {
+ try {
+ stringValue = JSON.stringify( value );
+ } catch ( e ) {
+ return '';
+ }
+ } else {
+ stringValue = String( value );
+ }
+
+ // Replace characters like &, <, >, " with their HTML entity equivalents
+ return stringValue
+ .replace( /&/g, '&' )
+ .replace( //g, '>' )
+ .replace( /"/g, '"' )
+ .replace( /'/g, ''' );
+};