diff --git a/.wp-env.json b/.wp-env.json
index 0967ef4..dafab87 100644
--- a/.wp-env.json
+++ b/.wp-env.json
@@ -1 +1,5 @@
-{}
+{
+ "plugins": [
+ "."
+ ]
+}
diff --git a/src/blockparty-iframe/block.json b/src/blockparty-iframe/block.json
index b70d68a..f0e57c1 100644
--- a/src/blockparty-iframe/block.json
+++ b/src/blockparty-iframe/block.json
@@ -28,6 +28,21 @@
"url": {
"type": "string",
"default": ""
+ },
+ "iframeAttributes": {
+ "type": "array",
+ "default": [],
+ "items": {
+ "type": "object",
+ "properties": {
+ "key": {
+ "type": "string"
+ },
+ "value": {
+ "type": "string"
+ }
+ }
+ }
}
},
"textdomain": "blockparty-iframe",
diff --git a/src/blockparty-iframe/edit.js b/src/blockparty-iframe/edit.js
index 102c424..c66694e 100644
--- a/src/blockparty-iframe/edit.js
+++ b/src/blockparty-iframe/edit.js
@@ -39,6 +39,7 @@ import { useState } from '@wordpress/element';
import './editor.scss';
import { aspectRatio } from '@wordpress/icons';
+import { convertAttributesToProps, parseIframeCode } from './utils';
/**
* The edit function describes the structure of your block in the context of the
@@ -50,12 +51,18 @@ import { aspectRatio } from '@wordpress/icons';
*/
export default function Edit( { attributes, setAttributes } ) {
const blockProps = useBlockProps();
- const { lazyload, title: initialTitle, url: initialUrl } = attributes;
+ const {
+ lazyload,
+ title: initialTitle,
+ url: initialUrl,
+ iframeAttributes: initialAttributes,
+ } = attributes;
// State local pour les champs TextControl
const [ iframeData, setIframeData ] = useState( {
url: initialUrl || '',
title: initialTitle || '',
+ iframeAttributes: initialAttributes || [],
} );
// hasConfirmed = l’utilisateur a validé l’ajout de l’iframe
@@ -76,6 +83,29 @@ export default function Edit( { attributes, setAttributes } ) {
const showPlaceholder = ! hasConfirmed;
const showIframe = hasConfirmed && isIframeElligible;
+ // Handle URL/iframe code change
+ function handleUrlChange( value ) {
+ // Try to parse as iframe code
+ const parsed = parseIframeCode( value );
+
+ if ( parsed ) {
+ // It's an iframe code, extract URL, title, and attributes
+ setIframeData( {
+ ...iframeData,
+ url: parsed.url,
+ title: parsed.title || iframeData.title, // Use extracted title if available, otherwise keep current
+ iframeAttributes: parsed.attributes,
+ } );
+ } else {
+ // It's a regular URL
+ setIframeData( {
+ ...iframeData,
+ url: value,
+ iframeAttributes: [],
+ } );
+ }
+ }
+
// Handle clic Add iframe
function handleAddIframeButtonClick() {
setAttributes( { ...attributes, ...iframeData } );
@@ -118,28 +148,20 @@ export default function Edit( { attributes, setAttributes } ) {
icon={ aspectRatio }
label={ __( 'Iframe', 'blockparty-iframe' ) }
instructions={ __(
- 'Fill the URL and the title of the iframe.',
+ 'Fill the iframe source and the title of the iframe.',
'blockparty-iframe'
) }
>
- setIframeData( { ...iframeData, url: value } )
- }
- placeholder="https://..."
- type="url"
- help={
- iframeData.url.length &&
- ! isURL( iframeData.url )
- ? __(
- 'The URL is invalid.',
- 'blockparty-iframe'
- )
- : ''
- }
+ onChange={ handleUrlChange }
+ placeholder={ `https://... or
) }
diff --git a/src/blockparty-iframe/save.js b/src/blockparty-iframe/save.js
index eaf123d..e029bf6 100644
--- a/src/blockparty-iframe/save.js
+++ b/src/blockparty-iframe/save.js
@@ -5,6 +5,7 @@
* @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-block-editor/#useblockprops
*/
import { useBlockProps } from '@wordpress/block-editor';
+import { convertAttributesToProps } from './utils';
/**
* The save function defines the way in which the different attributes should
@@ -16,7 +17,7 @@ import { useBlockProps } from '@wordpress/block-editor';
* @return {Element} Element to render.
*/
export default function save( { attributes } ) {
- const { lazyload, title, url } = attributes;
+ const { lazyload, title, url, iframeAttributes } = attributes;
if ( ! url || ! title ) {
return ;
@@ -28,6 +29,7 @@ export default function save( { attributes } ) {
title={ title }
src={ url }
loading={ lazyload ? 'lazy' : 'eager' }
+ { ...convertAttributesToProps( iframeAttributes ) }
/>
);
diff --git a/src/blockparty-iframe/style.scss b/src/blockparty-iframe/style.scss
index 240c2f6..650f27b 100644
--- a/src/blockparty-iframe/style.scss
+++ b/src/blockparty-iframe/style.scss
@@ -11,4 +11,9 @@
width: 100%;
height: 100%;
}
+
+ &:not(.has-aspect-ratio) {
+ aspect-ratio: 1;
+ min-height: unset;
+ }
}
diff --git a/src/blockparty-iframe/utils.js b/src/blockparty-iframe/utils.js
new file mode 100644
index 0000000..88ebb90
--- /dev/null
+++ b/src/blockparty-iframe/utils.js
@@ -0,0 +1,137 @@
+/**
+ * Utility functions for iframe attribute handling.
+ */
+
+/**
+ * Map HTML attribute names to React prop names.
+ *
+ * @param {string} attributeName - The HTML attribute name.
+ * @return {string} The React prop name.
+ */
+export function mapHtmlAttributeToReact( attributeName ) {
+ const attributeMap = {
+ allowfullscreen: 'allowFullScreen',
+ allowpaymentrequest: 'allowPaymentRequest',
+ referrerpolicy: 'referrerPolicy',
+ };
+
+ return attributeMap[ attributeName.toLowerCase() ] || attributeName;
+}
+
+/**
+ * Check if an attribute is a boolean HTML attribute.
+ *
+ * @param {string} attributeName - The name of the attribute to check.
+ * @return {boolean} True if the attribute is boolean, false otherwise.
+ */
+export function isBooleanAttribute( attributeName ) {
+ const booleanAttrs = [ 'allowfullscreen', 'allowpaymentrequest' ];
+
+ return booleanAttrs.includes( attributeName.toLowerCase() );
+}
+
+/**
+ * Convert iframe attributes array to props object for React.
+ * Handles boolean attributes and React prop name mapping correctly.
+ *
+ * @param {Array} attributes - Array of {key, value} objects.
+ * @return {Object} Props object for React component.
+ */
+export function convertAttributesToProps( attributes ) {
+ return Object.fromEntries(
+ ( attributes || [] ).map( ( attr ) => {
+ // Map HTML attribute name to React prop name
+ const propName = mapHtmlAttributeToReact( attr.key );
+
+ // Convert 'true' string to boolean for boolean attributes
+ const value =
+ isBooleanAttribute( attr.key ) && attr.value === 'true'
+ ? true
+ : attr.value;
+
+ return [ propName, value ];
+ } )
+ );
+}
+
+/**
+ * Check if an iframe attribute should be excluded.
+ *
+ * @param {string} attributeName - The name of the attribute to check.
+ * @return {boolean} True if the attribute should be excluded, false otherwise.
+ */
+export function isExcludedIframeAttribute( attributeName ) {
+ const excludedAttrs = [
+ 'src', // Managed separately
+ 'loading', // Managed by lazyload option
+ 'title', // Managed separately
+ 'width', // Managed by block dimension supports
+ 'height', // Managed by block dimension supports
+ 'style', // Requires object format in React, not string
+ 'frameborder', // Deprecated HTML attribute
+ 'marginwidth', // Deprecated HTML attribute
+ 'marginheight', // Deprecated HTML attribute
+ 'scrolling', // Deprecated HTML attribute
+ 'align', // Deprecated HTML attribute
+ 'longdesc', // Deprecated HTML attribute
+ 'name', // Can cause conflicts
+ ];
+
+ return excludedAttrs.includes( attributeName.toLowerCase() );
+}
+
+/**
+ * Parse iframe HTML code and extract src URL, title, and attributes.
+ *
+ * @param {string} value - The value that could be a URL or iframe HTML code.
+ * @return {Object|null} Object with url, title, and attributes array, or null if not an iframe.
+ */
+export function parseIframeCode( value ) {
+ // Check if the value contains iframe tag
+ const iframeRegex = /' );
+ const iframeElement = tempDiv.querySelector( 'iframe' );
+
+ if ( ! iframeElement ) {
+ return null;
+ }
+
+ // Extract src attribute
+ const src = iframeElement.getAttribute( 'src' ) || '';
+
+ // Extract title attribute
+ const title = iframeElement.getAttribute( 'title' ) || '';
+
+ // Extract all other attributes (excluding managed and deprecated ones)
+ const attributes = [];
+
+ for ( const attr of iframeElement.attributes ) {
+ if ( ! isExcludedIframeAttribute( attr.name ) ) {
+ // For boolean attributes, store 'true' as value if present
+ const value = isBooleanAttribute( attr.name ) ? 'true' : attr.value;
+
+ attributes.push( {
+ key: attr.name,
+ value,
+ } );
+ }
+ }
+
+ return {
+ url: src,
+ title,
+ attributes,
+ };
+}