Skip to content

Commit

Permalink
Add 'insert from url' for image block (#9264)
Browse files Browse the repository at this point in the history
* Extract isBlobURL into blob package and update usage

Co-authored-by: imath <[email protected]>

* Add ability to insert an image in image block using a url

Co-authored-by: imath <[email protected]>

* Position url input underneath upload file buttons and apply margin

Co-authored-by: imath <[email protected]>

* Refactor styles to avoid use of element selector

Co-authored-by: imath <[email protected]>

* Ensure form elements have bottom margin to separate them on tiny screens

Co-authored-by: imath <[email protected]>

* Change type of block used in e2e test to a separator

As mentioned in the comment above, this test will not work for blocks that
contain an input. The image block now contains a url input field causing
the test to fail.

Co-authored-by: imath <[email protected]>

* Fix invalid reference to this.isBlobURL

* Add expandable section for url form on media placeholder

* Change icon for media placeholder button to left/right chevron

* Update snapshots

* Try: expanding url input based on whether text is entered

* Try: use link popver for Insert from URL in media placeholder

* Implement URLPopover in MediaPlaceholder

* Use consistent declaration of defaults for props

* Use a generalized selector for buttons in the media placeholder

* Improve copy in URL input placeholder

* Make behaviour when selecting a differnent url the same as other media blocks

* Update snapshot tests

* Revert "Change type of block used in e2e test to a separator"

This reverts commit 6c8546c.

* Use onClose over onClickOutside (onClose implements onClickOutside)

* Rename functions

* Address code review feedback

* Update changelog for blob package

* Updated changelog for editor package
  • Loading branch information
talldan authored and gziolo committed Oct 18, 2018
1 parent f1d163a commit 9066b8c
Show file tree
Hide file tree
Showing 17 changed files with 267 additions and 99 deletions.
6 changes: 6 additions & 0 deletions packages/blob/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
## 2.1.0 (Unreleased)

### New Features

- Added a new `isBlobURL` function.

## 2.0.0 (2018-09-05)

### Breaking Change
Expand Down
14 changes: 14 additions & 0 deletions packages/blob/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,17 @@ export function revokeBlobURL( url ) {

delete cache[ url ];
}

/**
* Check whether a url is a blob url.
*
* @param {string} url The URL.
*
* @return {boolean} Is the url a blob url?
*/
export function isBlobURL( url ) {
if ( ! url || ! url.indexOf ) {
return false;
}
return url.indexOf( 'blob:' ) === 0;
}
17 changes: 17 additions & 0 deletions packages/blob/src/test/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import {
isBlobURL,
} from '../';

describe( 'isBlobURL', () => {
it( 'returns true if the url starts with "blob:"', () => {
expect( isBlobURL( 'blob:thisbitdoesnotmatter' ) ).toBe( true );
} );

it( 'returns false if the url does not start with "blob:"', () => {
expect( isBlobURL( 'https://www.example.com' ) ).toBe( false );
} );

it( 'returns false if the url is not defined', () => {
expect( isBlobURL() ).toBe( false );
} );
} );
4 changes: 2 additions & 2 deletions packages/block-library/src/audio/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
RichText,
mediaUpload,
} from '@wordpress/editor';
import { getBlobByURL } from '@wordpress/blob';
import { getBlobByURL, isBlobURL } from '@wordpress/blob';

const ALLOWED_MEDIA_TYPES = [ 'audio' ];

Expand All @@ -40,7 +40,7 @@ class AudioEdit extends Component {
const { attributes, noticeOperations, setAttributes } = this.props;
const { id, src = '' } = attributes;

if ( ! id && src.indexOf( 'blob:' ) === 0 ) {
if ( ! id && isBlobURL( src ) ) {
const file = getBlobByURL( src );

if ( file ) {
Expand Down
27 changes: 11 additions & 16 deletions packages/block-library/src/audio/test/__snapshots__/index.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -58,26 +58,11 @@ exports[`core/audio block edit matches snapshot 1`] = `
</span>
</div>
</div>
<form>
<input
aria-label="Audio"
class="components-placeholder__input"
placeholder="Enter URL here…"
type="url"
value=""
/>
<button
class="components-button is-button is-default is-large"
type="submit"
>
Use URL
</button>
</form>
<div
class="components-form-file-upload"
>
<button
class="components-button components-icon-button editor-media-placeholder__upload-button is-button is-default is-large"
class="components-button components-icon-button editor-media-placeholder__button is-button is-default is-large"
type="button"
>
<svg
Expand All @@ -102,6 +87,16 @@ exports[`core/audio block edit matches snapshot 1`] = `
type="file"
/>
</div>
<div
class="editor-media-placeholder__url-input-container"
>
<button
class="components-button editor-media-placeholder__button is-button is-default is-large"
type="button"
>
Insert from URL
</button>
</div>
</div>
</div>
`;
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ exports[`core/cover block edit matches snapshot 1`] = `
class="components-form-file-upload"
>
<button
class="components-button components-icon-button editor-media-placeholder__upload-button is-button is-default is-large"
class="components-button components-icon-button editor-media-placeholder__button is-button is-default is-large"
type="button"
>
<svg
Expand Down
10 changes: 3 additions & 7 deletions packages/block-library/src/file/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import classnames from 'classnames';
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
import { getBlobByURL, revokeBlobURL } from '@wordpress/blob';
import { getBlobByURL, revokeBlobURL, isBlobURL } from '@wordpress/blob';
import {
ClipboardButton,
IconButton,
Expand Down Expand Up @@ -52,7 +52,7 @@ class FileEdit extends Component {
const { href } = attributes;

// Upload a file drag-and-dropped into the editor
if ( this.isBlobURL( href ) ) {
if ( isBlobURL( href ) ) {
const file = getBlobByURL( href );

mediaUpload( {
Expand Down Expand Up @@ -87,10 +87,6 @@ class FileEdit extends Component {
}
}

isBlobURL( url = '' ) {
return url.indexOf( 'blob:' ) === 0;
}

confirmCopyURL() {
this.setState( { showCopyConfirmation: true } );
}
Expand Down Expand Up @@ -153,7 +149,7 @@ class FileEdit extends Component {
}

const classes = classnames( className, {
'is-transient': this.isBlobURL( href ),
'is-transient': isBlobURL( href ),
} );

return (
Expand Down
3 changes: 2 additions & 1 deletion packages/block-library/src/gallery/gallery-image.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { __ } from '@wordpress/i18n';
import { BACKSPACE, DELETE } from '@wordpress/keycodes';
import { withSelect } from '@wordpress/data';
import { RichText } from '@wordpress/editor';
import { isBlobURL } from '@wordpress/blob';

class GalleryImage extends Component {
constructor() {
Expand Down Expand Up @@ -105,7 +106,7 @@ class GalleryImage extends Component {

const className = classnames( {
'is-selected': isSelected,
'is-transient': url && 0 === url.indexOf( 'blob:' ),
'is-transient': isBlobURL( url ),
} );

// Disable reason: Each block can be selected by clicking on it and we should keep the same saved markup
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ exports[`core/gallery block edit matches snapshot 1`] = `
class="components-form-file-upload"
>
<button
class="components-button components-icon-button editor-media-placeholder__upload-button is-button is-default is-large"
class="components-button components-icon-button editor-media-placeholder__button is-button is-default is-large"
type="button"
>
<svg
Expand Down
103 changes: 88 additions & 15 deletions packages/block-library/src/image/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
*/
import { __ } from '@wordpress/i18n';
import { Component, Fragment } from '@wordpress/element';
import { getBlobByURL, revokeBlobURL } from '@wordpress/blob';
import { getBlobByURL, revokeBlobURL, isBlobURL } from '@wordpress/blob';
import {
Button,
ButtonGroup,
Expand Down Expand Up @@ -60,31 +60,56 @@ export const pickRelevantMediaFiles = ( image ) => {
return pick( image, [ 'alt', 'id', 'link', 'url', 'caption' ] );
};

/**
* Is the URL a temporary blob URL? A blob URL is one that is used temporarily
* while the image is being uploaded and will not have an id yet allocated.
*
* @param {number=} id The id of the image.
* @param {string=} url The url of the image.
*
* @return {boolean} Is the URL a Blob URL
*/
const isTemporaryImage = ( id, url ) => ! id && isBlobURL( url );

/**
* Is the url for the image hosted externally. An externally hosted image has no id
* and is not a blob url.
*
* @param {number=} id The id of the image.
* @param {string=} url The url of the image.
*
* @return {boolean} Is the url an externally hosted url?
*/
const isExternalImage = ( id, url ) => url && ! id && ! isBlobURL( url );

class ImageEdit extends Component {
constructor() {
constructor( { attributes } ) {
super( ...arguments );
this.updateAlt = this.updateAlt.bind( this );
this.updateAlignment = this.updateAlignment.bind( this );
this.onFocusCaption = this.onFocusCaption.bind( this );
this.onImageClick = this.onImageClick.bind( this );
this.onSelectImage = this.onSelectImage.bind( this );
this.onSelectURL = this.onSelectURL.bind( this );
this.updateImageURL = this.updateImageURL.bind( this );
this.updateWidth = this.updateWidth.bind( this );
this.updateHeight = this.updateHeight.bind( this );
this.updateDimensions = this.updateDimensions.bind( this );
this.onSetCustomHref = this.onSetCustomHref.bind( this );
this.onSetLinkDestination = this.onSetLinkDestination.bind( this );
this.toggleIsEditing = this.toggleIsEditing.bind( this );

this.state = {
captionFocused: false,
isEditing: ! attributes.url,
};
}

componentDidMount() {
const { attributes, setAttributes } = this.props;
const { id, url = '' } = attributes;

if ( ! id && url.indexOf( 'blob:' ) === 0 ) {
if ( isTemporaryImage( id, url ) ) {
const file = getBlobByURL( url );

if ( file ) {
Expand All @@ -100,10 +125,10 @@ class ImageEdit extends Component {
}

componentDidUpdate( prevProps ) {
const { id: prevID, url: prevUrl = '' } = prevProps.attributes;
const { id: prevID, url: prevURL = '' } = prevProps.attributes;
const { id, url = '' } = this.props.attributes;

if ( ! prevID && prevUrl.indexOf( 'blob:' ) === 0 && id && url.indexOf( 'blob:' ) === -1 ) {
if ( isTemporaryImage( prevID, prevURL ) && ! isTemporaryImage( id, url ) ) {
revokeBlobURL( url );
}

Expand All @@ -125,6 +150,10 @@ class ImageEdit extends Component {
return;
}

this.setState( {
isEditing: false,
} );

this.props.setAttributes( {
...pickRelevantMediaFiles( media ),
width: undefined,
Expand All @@ -151,6 +180,21 @@ class ImageEdit extends Component {
} );
}

onSelectURL( newURL ) {
const { url } = this.props.attributes;

if ( newURL !== url ) {
this.props.setAttributes( {
url: newURL,
id: undefined,
} );
}

this.setState( {
isEditing: false,
} );
}

onSetCustomHref( value ) {
this.props.setAttributes( { href: value } );
}
Expand Down Expand Up @@ -213,17 +257,33 @@ class ImageEdit extends Component {
];
}

toggleIsEditing() {
this.setState( {
isEditing: ! this.state.isEditing,
} );
}

render() {
const { isEditing } = this.state;
const { attributes, setAttributes, isLargeViewport, isSelected, className, maxWidth, noticeOperations, noticeUI, toggleSelection, isRTL } = this.props;
const { url, alt, caption, align, id, href, linkDestination, width, height } = attributes;
const isExternal = isExternalImage( id, url );

const controls = (
<BlockControls>
<BlockAlignmentToolbar
value={ align }
onChange={ this.updateAlignment }
/>
{ !! url && (
let toolbarEditButton;
if ( url ) {
if ( isExternal ) {
toolbarEditButton = (
<Toolbar>
<IconButton
className="components-icon-button components-toolbar__control"
label={ __( 'Edit image' ) }
onClick={ this.toggleIsEditing }
icon="edit"
/>
</Toolbar>
);
} else {
toolbarEditButton = (
<Toolbar>
<MediaUpload
onSelect={ this.onSelectImage }
Expand All @@ -239,11 +299,22 @@ class ImageEdit extends Component {
) }
/>
</Toolbar>
) }
);
}
}

const controls = (
<BlockControls>
<BlockAlignmentToolbar
value={ align }
onChange={ this.updateAlignment }
/>
{ toolbarEditButton }
</BlockControls>
);

if ( ! url ) {
if ( isEditing ) {
const src = isExternal ? url : undefined;
return (
<Fragment>
{ controls }
Expand All @@ -255,17 +326,19 @@ class ImageEdit extends Component {
} }
className={ className }
onSelect={ this.onSelectImage }
onSelectURL={ this.onSelectURL }
notices={ noticeUI }
onError={ noticeOperations.createErrorNotice }
accept="image/*"
allowedTypes={ ALLOWED_MEDIA_TYPES }
value={ { id, src } }
/>
</Fragment>
);
}

const classes = classnames( className, {
'is-transient': 0 === url.indexOf( 'blob:' ),
'is-transient': isBlobURL( url ),
'is-resized': !! width || !! height,
'is-focused': isSelected,
} );
Expand Down
Loading

0 comments on commit 9066b8c

Please sign in to comment.