diff --git a/packages/block-library/src/embed/edit.js b/packages/block-library/src/embed/edit.js
index 84f220d22b8c3..d5ef390bfa34b 100644
--- a/packages/block-library/src/embed/edit.js
+++ b/packages/block-library/src/embed/edit.js
@@ -1,32 +1,22 @@
/**
* Internal dependencies
*/
-import { findBlock, isFromWordPress } from './util';
-import { HOSTS_NO_PREVIEWS, ASPECT_RATIOS, DEFAULT_EMBED_BLOCK, WORDPRESS_EMBED_BLOCK } from './constants';
+import { isFromWordPress, createUpgradedEmbedBlock, getClassNames } from './util';
+import EmbedControls from './embed-controls';
+import EmbedLoading from './embed-loading';
+import EmbedPlaceholder from './embed-placeholder';
+import EmbedPreview from './embed-preview';
+
/**
* External dependencies
*/
-import { parse } from 'url';
-import { includes, kebabCase, toLower } from 'lodash';
-import classnames from 'classnames/dedupe';
+import { kebabCase, toLower } from 'lodash';
/**
* WordPress dependencies
*/
-import { __, _x, sprintf } from '@wordpress/i18n';
-import { Component, renderToString, Fragment } from '@wordpress/element';
-import {
- Button,
- Placeholder,
- Spinner,
- SandBox,
- IconButton,
- Toolbar,
- PanelBody,
- ToggleControl,
-} from '@wordpress/components';
-import { createBlock } from '@wordpress/blocks';
-import { RichText, BlockControls, BlockIcon, InspectorControls } from '@wordpress/editor';
+import { __, sprintf } from '@wordpress/i18n';
+import { Component, Fragment } from '@wordpress/element';
export function getEmbedEditComponent( title, icon, responsive = true ) {
return class extends Component {
@@ -34,10 +24,8 @@ export function getEmbedEditComponent( title, icon, responsive = true ) {
super( ...arguments );
this.switchBackToURLInput = this.switchBackToURLInput.bind( this );
this.setUrl = this.setUrl.bind( this );
- this.maybeSwitchBlock = this.maybeSwitchBlock.bind( this );
this.getAttributesFromPreview = this.getAttributesFromPreview.bind( this );
this.setAttributesFromPreview = this.setAttributesFromPreview.bind( this );
- this.setAspectRatioClassNames = this.setAspectRatioClassNames.bind( this );
this.getResponsiveHelp = this.getResponsiveHelp.bind( this );
this.toggleResponsive = this.toggleResponsive.bind( this );
this.handleIncomingPreview = this.handleIncomingPreview.bind( this );
@@ -53,8 +41,15 @@ export function getEmbedEditComponent( title, icon, responsive = true ) {
}
handleIncomingPreview() {
+ const { allowResponsive } = this.props.attributes;
this.setAttributesFromPreview();
- this.maybeSwitchBlock();
+ const upgradedBlock = createUpgradedEmbedBlock(
+ this.props,
+ this.getAttributesFromPreview( this.props.preview, allowResponsive )
+ );
+ if ( upgradedBlock ) {
+ this.props.onReplace( upgradedBlock );
+ }
}
componentDidUpdate( prevProps ) {
@@ -63,29 +58,15 @@ export function getEmbedEditComponent( title, icon, responsive = true ) {
const switchedPreview = this.props.preview && this.props.attributes.url !== prevProps.attributes.url;
const switchedURL = this.props.attributes.url !== prevProps.attributes.url;
- if ( ( switchedURL || ( hasPreview && ! hadPreview ) ) && this.maybeSwitchBlock() ) {
- // Dont do anything if we are going to switch to a different block,
- // and we've just changed the URL, or we've just received a preview.
- return;
- }
-
- if ( ( hasPreview && ! hadPreview ) || switchedPreview ) {
+ if ( ( hasPreview && ! hadPreview ) || switchedPreview || switchedURL ) {
if ( this.props.cannotEmbed ) {
// Can't embed this URL, and we've just received or switched the preview.
- this.setState( { editingURL: true } );
return;
}
this.handleIncomingPreview();
}
}
- getPhotoHtml( photo ) {
- // 100% width for the preview so it fits nicely into the document, some "thumbnails" are
- // acually the full size photo.
- const photoPreview =
;
- return renderToString( photoPreview );
- }
-
setUrl( event ) {
if ( event ) {
event.preventDefault();
@@ -96,114 +77,6 @@ export function getEmbedEditComponent( title, icon, responsive = true ) {
setAttributes( { url } );
}
- /***
- * Switches to a different embed block type, based on the URL
- * and the HTML in the preview, if the preview or URL match a different block.
- *
- * @return {boolean} Whether the block was switched.
- */
- maybeSwitchBlock() {
- const { preview } = this.props;
- const { url } = this.props.attributes;
-
- if ( ! url ) {
- return false;
- }
-
- const matchingBlock = findBlock( url );
-
- // WordPress blocks can work on multiple sites, and so don't have patterns,
- // so if we're in a WordPress block, assume the user has chosen it for a WordPress URL.
- if ( WORDPRESS_EMBED_BLOCK !== this.props.name && DEFAULT_EMBED_BLOCK !== matchingBlock ) {
- // At this point, we have discovered a more suitable block for this url, so transform it.
- if ( this.props.name !== matchingBlock ) {
- this.props.onReplace( createBlock( matchingBlock, { url } ) );
- return true;
- }
- }
-
- if ( preview ) {
- const { html } = preview;
-
- // We can't match the URL for WordPress embeds, we have to check the HTML instead.
- if ( isFromWordPress( html ) ) {
- // If this is not the WordPress embed block, transform it into one.
- if ( WORDPRESS_EMBED_BLOCK !== this.props.name ) {
- this.props.onReplace(
- createBlock(
- WORDPRESS_EMBED_BLOCK,
- {
- url,
- // By now we have the preview, but when the new block first renders, it
- // won't have had all the attributes set, and so won't get the correct
- // type and it won't render correctly. So, we work out the attributes
- // here so that the initial render works when we switch to the WordPress
- // block. This only affects the WordPress block because it can't be
- // rendered in the usual Sandbox (it has a sandbox of its own) and it
- // relies on the preview to set the correct render type.
- ...this.getAttributesFromPreview(
- this.props.preview, this.props.attributes.allowResponsive
- ),
- }
- )
- );
- return true;
- }
- }
- }
-
- return false;
- }
-
- /**
- * Gets the appropriate CSS class names to enforce an aspect ratio when the embed is resized
- * if the HTML has an iframe with width and height set.
- *
- * @param {string} html The preview HTML that possibly contains an iframe with width and height set.
- * @param {boolean} allowResponsive If the classes should be added, or removed.
- * @return {Object} Object with classnames set for use with `classnames`.
- */
- getAspectRatioClassNames( html, allowResponsive = true ) {
- const previewDocument = document.implementation.createHTMLDocument( '' );
- previewDocument.body.innerHTML = html;
- const iframe = previewDocument.body.querySelector( 'iframe' );
-
- // If we have a fixed aspect iframe, and it's a responsive embed block.
- if ( responsive && iframe && iframe.height && iframe.width ) {
- const aspectRatio = ( iframe.width / iframe.height ).toFixed( 2 );
- // Given the actual aspect ratio, find the widest ratio to support it.
- for ( let ratioIndex = 0; ratioIndex < ASPECT_RATIOS.length; ratioIndex++ ) {
- const potentialRatio = ASPECT_RATIOS[ ratioIndex ];
- if ( aspectRatio >= potentialRatio.ratio ) {
- return {
- [ potentialRatio.className ]: allowResponsive,
- 'wp-has-aspect-ratio': allowResponsive,
- };
- }
- }
- }
-
- return this.props.attributes.className;
- }
-
- /**
- * Sets the aspect ratio related class names returned by `getAspectRatioClassNames`
- * if `allowResponsive` is truthy.
- *
- * @param {string} html The preview HTML.
- */
- setAspectRatioClassNames( html ) {
- const { allowResponsive } = this.props.attributes;
- if ( ! allowResponsive ) {
- return;
- }
- const className = classnames(
- this.props.attributes.className,
- this.getAspectRatioClassNames( html )
- );
- this.props.setAttributes( { className } );
- }
-
/***
* Gets block attributes based on the preview and responsive state.
*
@@ -229,10 +102,7 @@ export function getEmbedEditComponent( title, icon, responsive = true ) {
attributes.providerNameSlug = providerNameSlug;
}
- attributes.className = classnames(
- this.props.attributes.className,
- this.getAspectRatioClassNames( html, allowResponsive )
- );
+ attributes.className = getClassNames( html, this.props.attributes.className, responsive && allowResponsive );
return attributes;
}
@@ -257,12 +127,12 @@ export function getEmbedEditComponent( title, icon, responsive = true ) {
toggleResponsive() {
const { allowResponsive, className } = this.props.attributes;
const { html } = this.props.preview;
- const responsiveClassNames = this.getAspectRatioClassNames( html, ! allowResponsive );
+ const newAllowResponsive = ! allowResponsive;
this.props.setAttributes(
{
- allowResponsive: ! allowResponsive,
- className: classnames( className, responsiveClassNames ),
+ allowResponsive: newAllowResponsive,
+ className: getClassNames( html, className, responsive && newAllowResponsive ),
}
);
}
@@ -270,42 +140,11 @@ export function getEmbedEditComponent( title, icon, responsive = true ) {
render() {
const { url, editingURL } = this.state;
const { caption, type, allowResponsive } = this.props.attributes;
- const { fetching, setAttributes, isSelected, className, preview, cannotEmbed, supportsResponsive } = this.props;
- const controls = (
-
-
-
- { preview && ! cannotEmbed && (
-
- ) }
-
-
- { supportsResponsive && (
-
-
-
-
-
- ) }
-
- );
+ const { fetching, setAttributes, isSelected, className, preview, cannotEmbed, themeSupportsResponsive } = this.props;
if ( fetching ) {
return (
-
+ );
+
+ return (
+
+ );
+};
+
+export default EmbedPreview;
diff --git a/packages/block-library/src/embed/settings.js b/packages/block-library/src/embed/settings.js
index 78d8397a71859..35d9b5ce6c4d1 100644
--- a/packages/block-library/src/embed/settings.js
+++ b/packages/block-library/src/embed/settings.js
@@ -75,7 +75,7 @@ export function getEmbedBlockSettings( { title, description, icon, category = 'e
return {
preview: validPreview ? preview : undefined,
fetching,
- supportsResponsive: themeSupports[ 'responsive-embeds' ],
+ themeSupportsResponsive: themeSupports[ 'responsive-embeds' ],
cannotEmbed,
};
} )
diff --git a/packages/block-library/src/embed/test/index.js b/packages/block-library/src/embed/test/index.js
index ff625b01ae1b4..3931ae7b581e1 100644
--- a/packages/block-library/src/embed/test/index.js
+++ b/packages/block-library/src/embed/test/index.js
@@ -7,7 +7,7 @@ import { render } from 'enzyme';
* Internal dependencies
*/
import { getEmbedEditComponent } from '../edit';
-import { findBlock } from '../util';
+import { findBlock, getClassNames } from '../util';
describe( 'core/embed', () => {
test( 'block edit matches snapshot', () => {
@@ -26,4 +26,22 @@ describe( 'core/embed', () => {
expect( findBlock( youtubeURL ) ).toEqual( 'core-embed/youtube' );
expect( findBlock( unknownURL ) ).toEqual( 'core/embed' );
} );
+
+ test( 'getClassNames returns aspect ratio class names for iframes with width and height', () => {
+ const html = '';
+ const expected = 'wp-embed-aspect-16-9 wp-has-aspect-ratio';
+ expect( getClassNames( html ) ).toEqual( expected );
+ } );
+
+ test( 'getClassNames does not return aspect ratio class names if we do not allow responsive', () => {
+ const html = '';
+ const expected = '';
+ expect( getClassNames( html, '', false ) ).toEqual( expected );
+ } );
+
+ test( 'getClassNames preserves exsiting class names when removing responsive classes', () => {
+ const html = '';
+ const expected = 'lovely';
+ expect( getClassNames( html, 'lovely wp-embed-aspect-16-9 wp-has-aspect-ratio', false ) ).toEqual( expected );
+ } );
} );
diff --git a/packages/block-library/src/embed/util.js b/packages/block-library/src/embed/util.js
index afb32f81ca79a..58b07b0a62774 100644
--- a/packages/block-library/src/embed/util.js
+++ b/packages/block-library/src/embed/util.js
@@ -2,18 +2,25 @@
* Internal dependencies
*/
import { common, others } from './core-embeds';
-import { DEFAULT_EMBED_BLOCK } from './constants';
+import { DEFAULT_EMBED_BLOCK, WORDPRESS_EMBED_BLOCK, ASPECT_RATIOS } from './constants';
/**
* External dependencies
*/
import { includes } from 'lodash';
+import classnames from 'classnames/dedupe';
+
+/**
+ * WordPress dependencies
+ */
+import { renderToString } from '@wordpress/element';
+import { createBlock } from '@wordpress/blocks';
/**
* Returns true if any of the regular expressions match the URL.
*
- * @param {string} url The URL to test.
- * @param {Array} patterns The list of regular expressions to test agains.
+ * @param {string} url The URL to test.
+ * @param {Array} patterns The list of regular expressions to test agains.
* @return {boolean} True if any of the regular expressions match the URL.
*/
export const matchesPatterns = ( url, patterns = [] ) => {
@@ -26,7 +33,7 @@ export const matchesPatterns = ( url, patterns = [] ) => {
* Finds the block name that should be used for the URL, based on the
* structure of the URL.
*
- * @param {string} url The URL to test.
+ * @param {string} url The URL to test.
* @return {string} The name of the block that should be used for this URL, e.g. core-embed/twitter
*/
export const findBlock = ( url ) => {
@@ -41,3 +48,118 @@ export const findBlock = ( url ) => {
export const isFromWordPress = ( html ) => {
return includes( html, 'class="wp-embedded-content" data-secret' );
};
+
+export const getPhotoHtml = ( photo ) => {
+ // 100% width for the preview so it fits nicely into the document, some "thumbnails" are
+ // acually the full size photo.
+ const photoPreview = ;
+ return renderToString( photoPreview );
+};
+
+/***
+ * Creates a more suitable embed block based on the passed in props
+ * and attributes generated from an embed block's preview.
+ *
+ * We require `attributesFromPreview` to be generated from the latest attributes
+ * and preview, and because of the way the react lifecycle operates, we can't
+ * guarantee that the attributes contained in the block's props are the latest
+ * versions, so we require that these are generated separately.
+ * See `getAttributesFromPreview` in the generated embed edit component.
+ *
+ * @param {Object} props The block's props.
+ * @param {Object} attributesFromPreview Attributes generated from the block's most up to date preview.
+ * @return {Object|undefined} A more suitable embed block if one exists.
+ */
+export const createUpgradedEmbedBlock = ( props, attributesFromPreview ) => {
+ const { preview, name } = props;
+ const { url } = props.attributes;
+
+ if ( ! url ) {
+ return;
+ }
+
+ const matchingBlock = findBlock( url );
+
+ // WordPress blocks can work on multiple sites, and so don't have patterns,
+ // so if we're in a WordPress block, assume the user has chosen it for a WordPress URL.
+ if ( WORDPRESS_EMBED_BLOCK !== name && DEFAULT_EMBED_BLOCK !== matchingBlock ) {
+ // At this point, we have discovered a more suitable block for this url, so transform it.
+ if ( name !== matchingBlock ) {
+ return createBlock( matchingBlock, { url } );
+ }
+ }
+
+ if ( preview ) {
+ const { html } = preview;
+
+ // We can't match the URL for WordPress embeds, we have to check the HTML instead.
+ if ( isFromWordPress( html ) ) {
+ // If this is not the WordPress embed block, transform it into one.
+ if ( WORDPRESS_EMBED_BLOCK !== name ) {
+ return createBlock(
+ WORDPRESS_EMBED_BLOCK,
+ {
+ url,
+ // By now we have the preview, but when the new block first renders, it
+ // won't have had all the attributes set, and so won't get the correct
+ // type and it won't render correctly. So, we pass through the current attributes
+ // here so that the initial render works when we switch to the WordPress
+ // block. This only affects the WordPress block because it can't be
+ // rendered in the usual Sandbox (it has a sandbox of its own) and it
+ // relies on the preview to set the correct render type.
+ ...attributesFromPreview,
+ }
+ );
+ }
+ }
+ }
+};
+
+/**
+ * Returns class names with any relevant responsive aspect ratio names.
+ *
+ * @param {string} html The preview HTML that possibly contains an iframe with width and height set.
+ * @param {string} existingClassNames Any existing class names.
+ * @param {boolean} allowResponsive If the responsive class names should be added, or removed.
+ * @return {string} Deduped class names.
+ */
+export function getClassNames( html, existingClassNames = '', allowResponsive = true ) {
+ if ( ! allowResponsive ) {
+ // Remove all of the aspect ratio related class names.
+ const aspectRatioClassNames = {
+ 'wp-has-aspect-ratio': false,
+ };
+ for ( let ratioIndex = 0; ratioIndex < ASPECT_RATIOS.length; ratioIndex++ ) {
+ const aspectRatioToRemove = ASPECT_RATIOS[ ratioIndex ];
+ aspectRatioClassNames[ aspectRatioToRemove.className ] = false;
+ }
+ return classnames(
+ existingClassNames,
+ aspectRatioClassNames
+ );
+ }
+
+ const previewDocument = document.implementation.createHTMLDocument( '' );
+ previewDocument.body.innerHTML = html;
+ const iframe = previewDocument.body.querySelector( 'iframe' );
+
+ // If we have a fixed aspect iframe, and it's a responsive embed block.
+ if ( iframe && iframe.height && iframe.width ) {
+ const aspectRatio = ( iframe.width / iframe.height ).toFixed( 2 );
+ // Given the actual aspect ratio, find the widest ratio to support it.
+ for ( let ratioIndex = 0; ratioIndex < ASPECT_RATIOS.length; ratioIndex++ ) {
+ const potentialRatio = ASPECT_RATIOS[ ratioIndex ];
+ if ( aspectRatio >= potentialRatio.ratio ) {
+ return classnames(
+ existingClassNames,
+ {
+ [ potentialRatio.className ]: allowResponsive,
+ 'wp-has-aspect-ratio': allowResponsive,
+ }
+ );
+ }
+ }
+ }
+
+ return existingClassNames;
+}