Skip to content

Commit

Permalink
Require an initial click on embed previews for interactivity (#12981)
Browse files Browse the repository at this point in the history
This fixes the block selection UX for video embeds. Previously
the click on the video to select the block would start the
video playing. Now that previous require a click to become
interactive, the initial click selects the block without
side effects.
  • Loading branch information
notnownikki authored and youknowriad committed Mar 6, 2019
1 parent a7f7f35 commit 418accf
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 43 deletions.
9 changes: 9 additions & 0 deletions packages/block-library/src/embed/editor.scss
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,12 @@
word-break: break-word;
}
}

.block-library-embed__interactive-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
opacity: 0;
}
127 changes: 85 additions & 42 deletions packages/block-library/src/embed/embed-preview.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,58 +17,101 @@ import classnames from 'classnames/dedupe';
import { __, sprintf } from '@wordpress/i18n';
import { Placeholder, SandBox } from '@wordpress/components';
import { RichText, BlockIcon } from '@wordpress/editor';
import { Component } from '@wordpress/element';

/**
* Internal dependencies
*/
import WpEmbedPreview from './wp-embed-preview';

const EmbedPreview = ( props ) => {
const { preview, url, type, caption, onCaptionChange, isSelected, className, icon, label } = props;
const { scripts } = preview;
class EmbedPreview extends Component {
constructor() {
super( ...arguments );
this.hideOverlay = this.hideOverlay.bind( this );
this.state = {
interactive: false,
};
}

const html = 'photo' === type ? getPhotoHtml( preview ) : preview.html;
const parsedHost = parse( url ).host.split( '.' );
const parsedHostBaseUrl = parsedHost.splice( parsedHost.length - 2, parsedHost.length - 1 ).join( '.' );
const cannotPreview = includes( HOSTS_NO_PREVIEWS, parsedHostBaseUrl );
// translators: %s: host providing embed content e.g: www.youtube.com
const iframeTitle = sprintf( __( 'Embedded content from %s' ), parsedHostBaseUrl );
const sandboxClassnames = classnames( type, className, 'wp-block-embed__wrapper' );
static getDerivedStateFromProps( nextProps, state ) {
if ( ! nextProps.isSelected && state.interactive ) {
// We only want to change this when the block is not selected, because changing it when
// the block becomes selected makes the overlap disappear too early. Hiding the overlay
// happens on mouseup when the overlay is clicked.
return { interactive: false };
}

const embedWrapper = 'wp-embed' === type ? (
<WpEmbedPreview
html={ html }
/>
) : (
<div className="wp-block-embed__wrapper">
<SandBox
return null;
}

hideOverlay() {
// This is called onMouseUp on the overlay. We can't respond to the `isSelected` prop
// changing, because that happens on mouse down, and the overlay immediately disappears,
// and the mouse event can end up in the preview content. We can't use onClick on
// the overlay to hide it either, because then the editor misses the mouseup event, and
// thinks we're multi-selecting blocks.
this.setState( { interactive: true } );
}

render() {
const { preview, url, type, caption, onCaptionChange, isSelected, className, icon, label } = this.props;
const { scripts } = preview;
const { interactive } = this.state;

const html = 'photo' === type ? getPhotoHtml( preview ) : preview.html;
const parsedHost = parse( url ).host.split( '.' );
const parsedHostBaseUrl = parsedHost.splice( parsedHost.length - 2, parsedHost.length - 1 ).join( '.' );
const cannotPreview = includes( HOSTS_NO_PREVIEWS, parsedHostBaseUrl );
// translators: %s: host providing embed content e.g: www.youtube.com
const iframeTitle = sprintf( __( 'Embedded content from %s' ), parsedHostBaseUrl );
const sandboxClassnames = classnames( type, className, 'wp-block-embed__wrapper' );

// Disabled because the overlay div doesn't actually have a role or functionality
// as far as the user is concerned. We're just catching the first click so that
// the block can be selected without interacting with the embed preview that the overlay covers.
/* eslint-disable jsx-a11y/no-noninteractive-element-interactions */
/* eslint-disable jsx-a11y/no-static-element-interactions */
const embedWrapper = 'wp-embed' === type ? (
<WpEmbedPreview
html={ html }
scripts={ scripts }
title={ iframeTitle }
type={ sandboxClassnames }
/>
</div>
);

return (
<figure className={ classnames( className, 'wp-block-embed', { 'is-type-video': 'video' === type } ) }>
{ ( cannotPreview ) ? (
<Placeholder icon={ <BlockIcon icon={ icon } showColors /> } label={ label }>
<p className="components-placeholder__error"><a href={ url }>{ url }</a></p>
<p className="components-placeholder__error">{ __( 'Sorry, this embedded content cannot be previewed in the editor.' ) }</p>
</Placeholder>
) : embedWrapper }
{ ( ! RichText.isEmpty( caption ) || isSelected ) && (
<RichText
tagName="figcaption"
placeholder={ __( 'Write caption…' ) }
value={ caption }
onChange={ onCaptionChange }
inlineToolbar
) : (
<div className="wp-block-embed__wrapper">
<SandBox
html={ html }
scripts={ scripts }
title={ iframeTitle }
type={ sandboxClassnames }
onFocus={ this.hideOverlay }
/>
) }
</figure>
);
};
{ ! interactive && <div
className="block-library-embed__interactive-overlay"
onMouseUp={ this.hideOverlay } /> }
</div>
);
/* eslint-enable jsx-a11y/no-static-element-interactions */
/* eslint-enable jsx-a11y/no-noninteractive-element-interactions */

return (
<figure className={ classnames( className, 'wp-block-embed', { 'is-type-video': 'video' === type } ) }>
{ ( cannotPreview ) ? (
<Placeholder icon={ <BlockIcon icon={ icon } showColors /> } label={ label }>
<p className="components-placeholder__error"><a href={ url }>{ url }</a></p>
<p className="components-placeholder__error">{ __( 'Sorry, this embedded content cannot be previewed in the editor.' ) }</p>
</Placeholder>
) : embedWrapper }
{ ( ! RichText.isEmpty( caption ) || isSelected ) && (
<RichText
tagName="figcaption"
placeholder={ __( 'Write caption…' ) }
value={ caption }
onChange={ onCaptionChange }
inlineToolbar
/>
) }
</figure>
);
}
}

export default EmbedPreview;
3 changes: 2 additions & 1 deletion packages/components/src/sandbox/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ class Sandbox extends Component {
}

render() {
const { title } = this.props;
const { title, onFocus } = this.props;

return (
<FocusableIframe
Expand All @@ -207,6 +207,7 @@ class Sandbox extends Component {
className="components-sandbox"
sandbox="allow-scripts allow-same-origin allow-presentation"
onLoad={ this.trySandbox }
onFocus={ onFocus }
width={ Math.ceil( this.state.width ) }
height={ Math.ceil( this.state.height ) } />
);
Expand Down

0 comments on commit 418accf

Please sign in to comment.