From b8e02f45e649fd37e20b761b6d67a14c28df03f9 Mon Sep 17 00:00:00 2001 From: Nicola Heald Date: Tue, 23 Oct 2018 14:54:15 +0100 Subject: [PATCH 1/9] Edit using separate components for each state --- packages/block-library/src/embed/edit.js | 138 ++++++----------------- packages/block-library/src/embed/util.js | 8 ++ 2 files changed, 42 insertions(+), 104 deletions(-) diff --git a/packages/block-library/src/embed/edit.js b/packages/block-library/src/embed/edit.js index 5a68508cbddcd6..c95518bc4ea4a9 100644 --- a/packages/block-library/src/embed/edit.js +++ b/packages/block-library/src/embed/edit.js @@ -2,12 +2,13 @@ * Internal dependencies */ import { findBlock, isFromWordPress } from './util'; -import { HOSTS_NO_PREVIEWS, ASPECT_RATIOS, DEFAULT_EMBED_BLOCK, WORDPRESS_EMBED_BLOCK } from './constants'; +import { ASPECT_RATIOS, DEFAULT_EMBED_BLOCK, WORDPRESS_EMBED_BLOCK } from './constants'; +import { EmbedLoading, EmbedControls, EmbedPreview, EmbedEditUrl } from './components'; + /** * External dependencies */ -import { parse } from 'url'; -import { includes, kebabCase, toLower } from 'lodash'; +import { kebabCase, toLower } from 'lodash'; import classnames from 'classnames/dedupe'; /** @@ -15,18 +16,7 @@ import classnames from 'classnames/dedupe'; */ import { __, 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'; export function getEmbedEditComponent( title, icon ) { return class extends Component { @@ -270,41 +260,10 @@ export function getEmbedEditComponent( title, icon ) { 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 && ( - - - - - - ) } - - ); if ( fetching ) { return ( -
- -

{ __( 'Embedding…' ) }

-
+ ); } @@ -314,68 +273,39 @@ export function getEmbedEditComponent( title, icon ) { // No preview, or we can't embed the current URL, or we've clicked the edit button. if ( ! preview || cannotEmbed || editingURL ) { return ( - } label={ label } className="wp-block-embed"> -
- this.setState( { url: event.target.value } ) } /> - - { cannotEmbed &&

{ __( 'Sorry, we could not embed that content.' ) }

} -
-
+ this.setState( { url: event.target.value } ) } + /> ); } - const html = 'photo' === type ? this.getPhotoHtml( preview ) : preview.html; - const { scripts } = preview; - const parsedUrl = parse( url ); - const cannotPreview = includes( HOSTS_NO_PREVIEWS, parsedUrl.host.replace( /^www\./, '' ) ); - // translators: %s: host providing embed content e.g: www.youtube.com - const iframeTitle = sprintf( __( 'Embedded content from %s' ), parsedUrl.host ); - const sandboxClassnames = classnames( type, className ); - const embedWrapper = 'wp-embed' === type ? ( -
- ) : ( -
- -
- ); - return ( -
- { controls } - { ( cannotPreview ) ? ( - } label={ label }> -

{ url }

-

{ __( 'Previews for this are unavailable in the editor, sorry!' ) }

-
- ) : embedWrapper } - { ( ! RichText.isEmpty( caption ) || isSelected ) && ( - setAttributes( { caption: value } ) } - inlineToolbar - /> - ) } -
+ + + setAttributes( { caption: value } ) } + isSelected={ isSelected } + icon={ icon } + label={ label } + /> + ); } }; diff --git a/packages/block-library/src/embed/util.js b/packages/block-library/src/embed/util.js index afb32f81ca79a5..868d0f8856049f 100644 --- a/packages/block-library/src/embed/util.js +++ b/packages/block-library/src/embed/util.js @@ -8,6 +8,7 @@ import { DEFAULT_EMBED_BLOCK } from './constants'; * External dependencies */ import { includes } from 'lodash'; +import { renderToString } from '@wordpress/element'; /** * Returns true if any of the regular expressions match the URL. @@ -41,3 +42,10 @@ 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 ); +}; From ddf0f98f8ac8ca6681a6c117d5487bb5e1f44636 Mon Sep 17 00:00:00 2001 From: Nicola Heald Date: Tue, 23 Oct 2018 15:51:48 +0100 Subject: [PATCH 2/9] Change maybe to upgrade --- packages/block-library/src/embed/edit.js | 110 +++-------------------- packages/block-library/src/embed/util.js | 52 ++++++++++- 2 files changed, 63 insertions(+), 99 deletions(-) diff --git a/packages/block-library/src/embed/edit.js b/packages/block-library/src/embed/edit.js index c95518bc4ea4a9..b9d442565be094 100644 --- a/packages/block-library/src/embed/edit.js +++ b/packages/block-library/src/embed/edit.js @@ -1,8 +1,8 @@ /** * Internal dependencies */ -import { findBlock, isFromWordPress } from './util'; -import { ASPECT_RATIOS, DEFAULT_EMBED_BLOCK, WORDPRESS_EMBED_BLOCK } from './constants'; +import { isFromWordPress, createUpgradedEmbedBlock } from './util'; +import { ASPECT_RATIOS } from './constants'; import { EmbedLoading, EmbedControls, EmbedPreview, EmbedEditUrl } from './components'; /** @@ -15,8 +15,7 @@ import classnames from 'classnames/dedupe'; * WordPress dependencies */ import { __, sprintf } from '@wordpress/i18n'; -import { Component, renderToString, Fragment } from '@wordpress/element'; -import { createBlock } from '@wordpress/blocks'; +import { Component, Fragment } from '@wordpress/element'; export function getEmbedEditComponent( title, icon ) { return class extends Component { @@ -24,10 +23,8 @@ export function getEmbedEditComponent( title, icon ) { 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 ); @@ -43,8 +40,15 @@ export function getEmbedEditComponent( title, icon ) { } 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 ) { @@ -53,13 +57,7 @@ export function getEmbedEditComponent( title, icon ) { 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 } ); @@ -69,13 +67,6 @@ export function getEmbedEditComponent( title, icon ) { } } - 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(); @@ -86,65 +77,6 @@ export function getEmbedEditComponent( title, icon ) { 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. @@ -175,24 +107,6 @@ export function getEmbedEditComponent( title, icon ) { 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. * diff --git a/packages/block-library/src/embed/util.js b/packages/block-library/src/embed/util.js index 868d0f8856049f..903571fc16e657 100644 --- a/packages/block-library/src/embed/util.js +++ b/packages/block-library/src/embed/util.js @@ -2,13 +2,18 @@ * Internal dependencies */ import { common, others } from './core-embeds'; -import { DEFAULT_EMBED_BLOCK } from './constants'; +import { DEFAULT_EMBED_BLOCK, WORDPRESS_EMBED_BLOCK } from './constants'; /** * External dependencies */ import { includes } from 'lodash'; + +/** + * WordPress dependencies + */ import { renderToString } from '@wordpress/element'; +import { createBlock } from '@wordpress/blocks'; /** * Returns true if any of the regular expressions match the URL. @@ -49,3 +54,48 @@ export const getPhotoHtml = ( photo ) => { const photoPreview =

{

; return renderToString( photoPreview ); }; + +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, + } + ); + } + } + } +}; From 71a2af307c7660a3c3c11b474bef6a2efb8775cd Mon Sep 17 00:00:00 2001 From: Nicola Heald Date: Tue, 23 Oct 2018 16:26:20 +0100 Subject: [PATCH 3/9] WordPress embeds do their own iframe thing, don't make them responsive --- packages/block-library/src/embed/edit.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/block-library/src/embed/edit.js b/packages/block-library/src/embed/edit.js index b9d442565be094..36b33b73ed91d9 100644 --- a/packages/block-library/src/embed/edit.js +++ b/packages/block-library/src/embed/edit.js @@ -90,7 +90,7 @@ export function getEmbedEditComponent( title, icon ) { previewDocument.body.innerHTML = html; const iframe = previewDocument.body.querySelector( 'iframe' ); - if ( iframe && iframe.height && iframe.width ) { + if ( ! isFromWordPress( html ) && 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++ ) { From ececfe9ac3f91d07b11c0fd5e65fe65e46e43953 Mon Sep 17 00:00:00 2001 From: Nicola Heald Date: Tue, 23 Oct 2018 16:26:53 +0100 Subject: [PATCH 4/9] Embed edit components --- .../block-library/src/embed/components.js | 139 ++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 packages/block-library/src/embed/components.js diff --git a/packages/block-library/src/embed/components.js b/packages/block-library/src/embed/components.js new file mode 100644 index 00000000000000..55613744bc2def --- /dev/null +++ b/packages/block-library/src/embed/components.js @@ -0,0 +1,139 @@ +/** + * Internal dependencies + */ +import { HOSTS_NO_PREVIEWS } from './constants'; +import { getPhotoHtml } from './util'; + +/** + * External dependencies + */ +import { parse } from 'url'; +import { includes } from 'lodash'; +import classnames from 'classnames/dedupe'; + +/** + * WordPress dependencies + */ +import { __, sprintf } from '@wordpress/i18n'; +import { Fragment } from '@wordpress/element'; +import { + Button, + Placeholder, + Spinner, + SandBox, + IconButton, + Toolbar, + PanelBody, + ToggleControl, +} from '@wordpress/components'; +import { RichText, BlockControls, BlockIcon, InspectorControls } from '@wordpress/editor'; + +export const EmbedLoading = () => ( +
+ +

{ __( 'Embedding…' ) }

+
+); + +export const EmbedPreview = ( props ) => { + const { preview, url, type, caption, onCaptionChange, isSelected, className, icon, label } = props; + const { scripts } = preview; + + const html = 'photo' === type ? getPhotoHtml( preview ) : preview.html; + const parsedUrl = parse( url ); + const cannotPreview = includes( HOSTS_NO_PREVIEWS, parsedUrl.host.replace( /^www\./, '' ) ); + // translators: %s: host providing embed content e.g: www.youtube.com + const iframeTitle = sprintf( __( 'Embedded content from %s' ), parsedUrl.host ); + const sandboxClassnames = classnames( type, className, 'wp-block-embed__wrapper' ); + + const embedWrapper = 'wp-embed' === type ? ( +
+ ) : ( +
+ +
+ ); + + return ( +
+ { ( cannotPreview ) ? ( + } label={ label }> +

{ url }

+

{ __( 'Previews for this are unavailable in the editor, sorry!' ) }

+
+ ) : embedWrapper } + { ( ! RichText.isEmpty( caption ) || isSelected ) && ( + + ) } +
+ ); +}; + +export const EmbedControls = ( props ) => { + const { showEditButton, supportsResponsive, allowResponsive, getResponsiveHelp, toggleResponsive, switchBackToURLInput } = props; + return ( + + + + { showEditButton && ( + + ) } + + + { supportsResponsive && ( + + + + + + ) } + + ); +}; + +export const EmbedEditUrl = ( props ) => { + const { icon, label, value, onSubmit, onChange, cannotEmbed } = props; + return ( + } label={ label } className="wp-block-embed"> +
+ + + { cannotEmbed &&

{ __( 'Sorry, we could not embed that content.' ) }

} +
+
+ ); +}; From 0733d419b10cb6eede65519248e1e43042abdb0b Mon Sep 17 00:00:00 2001 From: Nicola Heald Date: Tue, 23 Oct 2018 16:44:45 +0100 Subject: [PATCH 5/9] Docs explaining why we need attributes passed in separately --- packages/block-library/src/embed/util.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/packages/block-library/src/embed/util.js b/packages/block-library/src/embed/util.js index 903571fc16e657..890ed9df131d9b 100644 --- a/packages/block-library/src/embed/util.js +++ b/packages/block-library/src/embed/util.js @@ -55,6 +55,20 @@ export const getPhotoHtml = ( photo ) => { 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; From c0eeaf2c8ee27318dbaeeb865facc8db387c1549 Mon Sep 17 00:00:00 2001 From: Nicola Heald Date: Mon, 29 Oct 2018 13:52:54 +0000 Subject: [PATCH 6/9] Moved class name calculations into a util function --- packages/block-library/src/embed/edit.js | 43 ++---------------------- packages/block-library/src/embed/util.js | 37 +++++++++++++++++++- 2 files changed, 39 insertions(+), 41 deletions(-) diff --git a/packages/block-library/src/embed/edit.js b/packages/block-library/src/embed/edit.js index f158a836cfb150..30b864d80cd1c1 100644 --- a/packages/block-library/src/embed/edit.js +++ b/packages/block-library/src/embed/edit.js @@ -1,15 +1,13 @@ /** * Internal dependencies */ -import { isFromWordPress, createUpgradedEmbedBlock } from './util'; -import { ASPECT_RATIOS } from './constants'; +import { isFromWordPress, createUpgradedEmbedBlock, getClassNames } from './util'; import { EmbedLoading, EmbedControls, EmbedPreview, EmbedEditUrl } from './components'; /** * External dependencies */ import { kebabCase, toLower } from 'lodash'; -import classnames from 'classnames/dedupe'; /** * WordPress dependencies @@ -77,37 +75,6 @@ export function getEmbedEditComponent( title, icon, responsive = true ) { setAttributes( { url } ); } - /** - * 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; - } - /*** * Gets block attributes based on the preview and responsive state. * @@ -133,10 +100,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; } @@ -161,12 +125,11 @@ 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 ); this.props.setAttributes( { allowResponsive: ! allowResponsive, - className: classnames( className, responsiveClassNames ), + className: getClassNames( html, className, responsive && ! allowResponsive ), } ); } diff --git a/packages/block-library/src/embed/util.js b/packages/block-library/src/embed/util.js index 890ed9df131d9b..6741274c139468 100644 --- a/packages/block-library/src/embed/util.js +++ b/packages/block-library/src/embed/util.js @@ -2,12 +2,13 @@ * Internal dependencies */ import { common, others } from './core-embeds'; -import { DEFAULT_EMBED_BLOCK, WORDPRESS_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 @@ -113,3 +114,37 @@ export const createUpgradedEmbedBlock = ( props, 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 ) { + 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; +} From c7fdf5c866a91bd35719576fb4a72ed918b4f996 Mon Sep 17 00:00:00 2001 From: Nicola Heald Date: Mon, 29 Oct 2018 14:00:13 +0000 Subject: [PATCH 7/9] Tests for class name calculation --- .../block-library/src/embed/test/index.js | 20 ++++++++++++++++++- packages/block-library/src/embed/util.js | 2 +- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/packages/block-library/src/embed/test/index.js b/packages/block-library/src/embed/test/index.js index ff625b01ae1b47..3931ae7b581e18 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 6741274c139468..deb4b6542c667b 100644 --- a/packages/block-library/src/embed/util.js +++ b/packages/block-library/src/embed/util.js @@ -123,7 +123,7 @@ export const createUpgradedEmbedBlock = ( props, attributesFromPreview ) => { * @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 ) { +export function getClassNames( html, existingClassNames = '', allowResponsive = true ) { const previewDocument = document.implementation.createHTMLDocument( '' ); previewDocument.body.innerHTML = html; const iframe = previewDocument.body.querySelector( 'iframe' ); From 3d87a41774ac1dd331d23fe6bcb7145884458105 Mon Sep 17 00:00:00 2001 From: Nicola Heald Date: Wed, 31 Oct 2018 11:09:40 +0000 Subject: [PATCH 8/9] Components in separate files, better variable naming for support --- .../block-library/src/embed/components.js | 143 +----------------- packages/block-library/src/embed/edit.js | 14 +- .../block-library/src/embed/embed-controls.js | 49 ++++++ .../block-library/src/embed/embed-loading.js | 14 ++ .../src/embed/embed-placeholder.js | 31 ++++ .../block-library/src/embed/embed-preview.js | 69 +++++++++ packages/block-library/src/embed/settings.js | 2 +- packages/block-library/src/embed/util.js | 31 +++- 8 files changed, 199 insertions(+), 154 deletions(-) create mode 100644 packages/block-library/src/embed/embed-controls.js create mode 100644 packages/block-library/src/embed/embed-loading.js create mode 100644 packages/block-library/src/embed/embed-placeholder.js create mode 100644 packages/block-library/src/embed/embed-preview.js diff --git a/packages/block-library/src/embed/components.js b/packages/block-library/src/embed/components.js index e50244b12b7cb8..f5906a16e68150 100644 --- a/packages/block-library/src/embed/components.js +++ b/packages/block-library/src/embed/components.js @@ -1,139 +1,4 @@ -/** - * Internal dependencies - */ -import { HOSTS_NO_PREVIEWS } from './constants'; -import { getPhotoHtml } from './util'; - -/** - * External dependencies - */ -import { parse } from 'url'; -import { includes } from 'lodash'; -import classnames from 'classnames/dedupe'; - -/** - * WordPress dependencies - */ -import { __, _x, sprintf } from '@wordpress/i18n'; -import { Fragment } from '@wordpress/element'; -import { - Button, - Placeholder, - Spinner, - SandBox, - IconButton, - Toolbar, - PanelBody, - ToggleControl, -} from '@wordpress/components'; -import { RichText, BlockControls, BlockIcon, InspectorControls } from '@wordpress/editor'; - -export const EmbedLoading = () => ( -
- -

{ __( 'Embedding…' ) }

-
-); - -export const EmbedPreview = ( props ) => { - const { preview, url, type, caption, onCaptionChange, isSelected, className, icon, label } = props; - const { scripts } = preview; - - const html = 'photo' === type ? getPhotoHtml( preview ) : preview.html; - const parsedUrl = parse( url ); - const cannotPreview = includes( HOSTS_NO_PREVIEWS, parsedUrl.host.replace( /^www\./, '' ) ); - // translators: %s: host providing embed content e.g: www.youtube.com - const iframeTitle = sprintf( __( 'Embedded content from %s' ), parsedUrl.host ); - const sandboxClassnames = classnames( type, className, 'wp-block-embed__wrapper' ); - - const embedWrapper = 'wp-embed' === type ? ( -
- ) : ( -
- -
- ); - - return ( -
- { ( cannotPreview ) ? ( - } label={ label }> -

{ url }

-

{ __( 'Previews for this are unavailable in the editor, sorry!' ) }

-
- ) : embedWrapper } - { ( ! RichText.isEmpty( caption ) || isSelected ) && ( - - ) } -
- ); -}; - -export const EmbedControls = ( props ) => { - const { showEditButton, supportsResponsive, allowResponsive, getResponsiveHelp, toggleResponsive, switchBackToURLInput } = props; - return ( - - - - { showEditButton && ( - - ) } - - - { supportsResponsive && ( - - - - - - ) } - - ); -}; - -export const EmbedEditUrl = ( props ) => { - const { icon, label, value, onSubmit, onChange, cannotEmbed } = props; - return ( - } label={ label } className="wp-block-embed"> -
- - - { cannotEmbed &&

{ __( 'Sorry, we could not embed that content.' ) }

} -
-
- ); -}; +export { default as EmbedControls } from './embed-controls'; +export { default as EmbedLoading } from './embed-loading'; +export { default as EmbedPlaceholder } from './embed-placeholder'; +export { default as EmbedPreview } from './embed-preview'; diff --git a/packages/block-library/src/embed/edit.js b/packages/block-library/src/embed/edit.js index 30b864d80cd1c1..a092b541f55e88 100644 --- a/packages/block-library/src/embed/edit.js +++ b/packages/block-library/src/embed/edit.js @@ -2,7 +2,7 @@ * Internal dependencies */ import { isFromWordPress, createUpgradedEmbedBlock, getClassNames } from './util'; -import { EmbedLoading, EmbedControls, EmbedPreview, EmbedEditUrl } from './components'; +import { EmbedLoading, EmbedControls, EmbedPreview, EmbedPlaceholder } from './components'; /** * External dependencies @@ -125,11 +125,12 @@ export function getEmbedEditComponent( title, icon, responsive = true ) { toggleResponsive() { const { allowResponsive, className } = this.props.attributes; const { html } = this.props.preview; + const newAllowResponsive = ! allowResponsive; this.props.setAttributes( { - allowResponsive: ! allowResponsive, - className: getClassNames( html, className, responsive && ! allowResponsive ), + allowResponsive: newAllowResponsive, + className: getClassNames( html, className, responsive && newAllowResponsive ), } ); } @@ -137,7 +138,7 @@ 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 { fetching, setAttributes, isSelected, className, preview, cannotEmbed, themeSupportsResponsive } = this.props; if ( fetching ) { return ( @@ -151,7 +152,7 @@ export function getEmbedEditComponent( title, icon, responsive = true ) { // No preview, or we can't embed the current URL, or we've clicked the edit button. if ( ! preview || cannotEmbed || editingURL ) { return ( - { + const { + blockSupportsResponsive, + showEditButton, + themeSupportsResponsive, + allowResponsive, + getResponsiveHelp, + toggleResponsive, + switchBackToURLInput, + } = props; + return ( + + + + { showEditButton && ( + + ) } + + + { themeSupportsResponsive && blockSupportsResponsive && ( + + + + + + ) } + + ); +}; + +export default EmbedControls; diff --git a/packages/block-library/src/embed/embed-loading.js b/packages/block-library/src/embed/embed-loading.js new file mode 100644 index 00000000000000..f643cdc9c1dc1a --- /dev/null +++ b/packages/block-library/src/embed/embed-loading.js @@ -0,0 +1,14 @@ +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; +import { Spinner } from '@wordpress/components'; + +const EmbedLoading = () => ( +
+ +

{ __( 'Embedding…' ) }

+
+); + +export default EmbedLoading; diff --git a/packages/block-library/src/embed/embed-placeholder.js b/packages/block-library/src/embed/embed-placeholder.js new file mode 100644 index 00000000000000..1d54279c9027fa --- /dev/null +++ b/packages/block-library/src/embed/embed-placeholder.js @@ -0,0 +1,31 @@ +/** + * WordPress dependencies + */ +import { __, _x } from '@wordpress/i18n'; +import { Button, Placeholder } from '@wordpress/components'; +import { BlockIcon } from '@wordpress/editor'; + +const EmbedPlaceholder = ( props ) => { + const { icon, label, value, onSubmit, onChange, cannotEmbed } = props; + return ( + } label={ label } className="wp-block-embed"> +
+ + + { cannotEmbed &&

{ __( 'Sorry, we could not embed that content.' ) }

} +
+
+ ); +}; + +export default EmbedPlaceholder; diff --git a/packages/block-library/src/embed/embed-preview.js b/packages/block-library/src/embed/embed-preview.js new file mode 100644 index 00000000000000..b3da26ce757529 --- /dev/null +++ b/packages/block-library/src/embed/embed-preview.js @@ -0,0 +1,69 @@ +/** + * Internal dependencies + */ +import { HOSTS_NO_PREVIEWS } from './constants'; +import { getPhotoHtml } from './util'; + +/** + * External dependencies + */ +import { parse } from 'url'; +import { includes } from 'lodash'; +import classnames from 'classnames/dedupe'; + +/** + * WordPress dependencies + */ +import { __, sprintf } from '@wordpress/i18n'; +import { Placeholder, SandBox } from '@wordpress/components'; +import { RichText, BlockIcon } from '@wordpress/editor'; + +const EmbedPreview = ( props ) => { + const { preview, url, type, caption, onCaptionChange, isSelected, className, icon, label } = props; + const { scripts } = preview; + + const html = 'photo' === type ? getPhotoHtml( preview ) : preview.html; + const parsedUrl = parse( url ); + const cannotPreview = includes( HOSTS_NO_PREVIEWS, parsedUrl.host.replace( /^www\./, '' ) ); + // translators: %s: host providing embed content e.g: www.youtube.com + const iframeTitle = sprintf( __( 'Embedded content from %s' ), parsedUrl.host ); + const sandboxClassnames = classnames( type, className, 'wp-block-embed__wrapper' ); + + const embedWrapper = 'wp-embed' === type ? ( +
+ ) : ( +
+ +
+ ); + + return ( +
+ { ( cannotPreview ) ? ( + } label={ label }> +

{ url }

+

{ __( 'Previews for this are unavailable in the editor, sorry!' ) }

+
+ ) : embedWrapper } + { ( ! RichText.isEmpty( caption ) || isSelected ) && ( + + ) } +
+ ); +}; + +export default EmbedPreview; diff --git a/packages/block-library/src/embed/settings.js b/packages/block-library/src/embed/settings.js index 78d8397a718597..35d9b5ce6c4d15 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/util.js b/packages/block-library/src/embed/util.js index deb4b6542c667b..58b07b0a627743 100644 --- a/packages/block-library/src/embed/util.js +++ b/packages/block-library/src/embed/util.js @@ -19,8 +19,8 @@ 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 = [] ) => { @@ -33,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 ) => { @@ -66,8 +66,8 @@ export const getPhotoHtml = ( photo ) => { * 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. + * @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 ) => { @@ -118,12 +118,27 @@ export const createUpgradedEmbedBlock = ( props, 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. + * @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' ); From a6d0d152d207580a933f69569bbbee78aae6ab60 Mon Sep 17 00:00:00 2001 From: Nicola Heald Date: Thu, 1 Nov 2018 10:28:23 +0000 Subject: [PATCH 9/9] Removed components file, removed unneeded editingUrl state change --- packages/block-library/src/embed/components.js | 4 ---- packages/block-library/src/embed/edit.js | 6 ++++-- 2 files changed, 4 insertions(+), 6 deletions(-) delete mode 100644 packages/block-library/src/embed/components.js diff --git a/packages/block-library/src/embed/components.js b/packages/block-library/src/embed/components.js deleted file mode 100644 index f5906a16e68150..00000000000000 --- a/packages/block-library/src/embed/components.js +++ /dev/null @@ -1,4 +0,0 @@ -export { default as EmbedControls } from './embed-controls'; -export { default as EmbedLoading } from './embed-loading'; -export { default as EmbedPlaceholder } from './embed-placeholder'; -export { default as EmbedPreview } from './embed-preview'; diff --git a/packages/block-library/src/embed/edit.js b/packages/block-library/src/embed/edit.js index a092b541f55e88..d5ef390bfa34b6 100644 --- a/packages/block-library/src/embed/edit.js +++ b/packages/block-library/src/embed/edit.js @@ -2,7 +2,10 @@ * Internal dependencies */ import { isFromWordPress, createUpgradedEmbedBlock, getClassNames } from './util'; -import { EmbedLoading, EmbedControls, EmbedPreview, EmbedPlaceholder } from './components'; +import EmbedControls from './embed-controls'; +import EmbedLoading from './embed-loading'; +import EmbedPlaceholder from './embed-placeholder'; +import EmbedPreview from './embed-preview'; /** * External dependencies @@ -58,7 +61,6 @@ export function getEmbedEditComponent( title, icon, responsive = true ) { 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();