Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Blocks: Add Upload button to audio and video blocks #5431

Merged
merged 1 commit into from
Mar 7, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions blocks/image-placeholder/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,13 @@ import { rawHandler } from '../api';
*/
export default function ImagePlaceholder( { className, icon, label, onSelectImage, multiple = false } ) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think, this could be renamed "MediaPlaceholder" to make the experience even more similar? and provide a media type?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a next iteration, we could bring those two closer by doing proper refactoring. Actually, this was the first step I took, but then I backed off, because I didn't want to change too much in one PR.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also see my comment #5431 (comment), you were too fast :)

const setImage = multiple ? onSelectImage : ( [ image ] ) => onSelectImage( image );
const onFilesDrop = ( files ) => mediaUpload( files, setImage );
const onFilesDrop = ( files ) => mediaUpload( files, setImage, 'image' );
const onHTMLDrop = ( HTML ) => setImage( map(
rawHandler( { HTML, mode: 'BLOCKS' } )
.filter( ( { name } ) => name === 'core/image' ),
'attributes'
) );
const uploadFromFiles = ( event ) => mediaUpload( event.target.files, setImage );
const uploadFromFiles = ( event ) => mediaUpload( event.target.files, setImage, 'image' );
return (
<Placeholder
className={ className }
Expand Down
1 change: 0 additions & 1 deletion blocks/library/audio/editor.scss
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
}

.wp-block-audio .components-placeholder__fieldset {
display: block;
max-width: 400px;

form {
Expand Down
52 changes: 33 additions & 19 deletions blocks/library/audio/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,15 @@
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
import { Button, IconButton, Placeholder, Toolbar } from '@wordpress/components';
import {
Button,
FormFileUpload,
IconButton,
Placeholder,
Toolbar,
} from '@wordpress/components';
import { Component } from '@wordpress/element';
import { mediaUpload } from '@wordpress/utils';

/**
* Internal dependencies
Expand Down Expand Up @@ -85,24 +92,12 @@ export const settings = {
}
return false;
};
const controls = isSelected && (
<BlockControls key="controls">
<Toolbar>
<IconButton
className="components-icon-button components-toolbar__control"
label={ __( 'Edit audio' ) }
onClick={ switchToEditing }
icon="edit"
/>
</Toolbar>
</BlockControls>
);
const setAudio = ( [ audio ] ) => onSelectAudio( audio );
const uploadFromFiles = ( event ) => mediaUpload( event.target.files, setAudio, 'audio' );

if ( editing ) {
return [
controls,
return (
<Placeholder
key="placeholder"
icon="media-audio"
label={ __( 'Audio' ) }
instructions={ __( 'Select an audio file from your library, or upload a new one' ) }
Expand All @@ -120,6 +115,14 @@ export const settings = {
{ __( 'Use URL' ) }
</Button>
</form>
<FormFileUpload
isLarge
className="wp-block-audio__upload-button"
onChange={ uploadFromFiles }
accept="audio/*"
>
{ __( 'Upload' ) }
</FormFileUpload>
<MediaUpload
onSelect={ onSelectAudio }
type="audio"
Expand All @@ -130,13 +133,24 @@ export const settings = {
</Button>
) }
/>
</Placeholder>,
];
</Placeholder>
);
}

/* eslint-disable jsx-a11y/no-static-element-interactions, jsx-a11y/onclick-has-role, jsx-a11y/click-events-have-key-events */
return [
controls,
isSelected && (
<BlockControls key="controls">
<Toolbar>
<IconButton
className="components-icon-button components-toolbar__control"
label={ __( 'Edit audio' ) }
onClick={ switchToEditing }
icon="edit"
/>
</Toolbar>
</BlockControls>
),
<figure key="audio" className={ className }>
<audio controls="controls" src={ src } />
{ ( ( caption && caption.length ) || !! isSelected ) && (
Expand Down
29 changes: 29 additions & 0 deletions blocks/library/audio/test/__snapshots__/index.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,35 @@ exports[`core/audio block edit matches snapshot 1`] = `
Use URL
</button>
</form>
<div
class="components-form-file-upload"
>
<button
class="components-button components-icon-button wp-block-audio__upload-button button button-large"
type="button"
>
<svg
aria-hidden="true"
class="dashicon dashicons-upload"
focusable="false"
height="20"
role="img"
viewBox="0 0 20 20"
width="20"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M8 14V8H5l5-6 5 6h-3v6H8zm-2 2v-6H4v8h12.01v-8H14v6H6z"
/>
</svg>
Upload
</button>
<input
accept="audio/*"
style="display:none"
type="file"
/>
</div>
*** Mock(Media upload button) ***
</div>
</div>
Expand Down
3 changes: 2 additions & 1 deletion blocks/library/gallery/block.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,8 @@ class GalleryBlock extends Component {
setAttributes( {
images: currentImages.concat( images ),
} );
}
},
'image',
);
}

Expand Down
4 changes: 3 additions & 1 deletion blocks/library/image/editor.scss
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,9 @@
}
}

.wp-core-ui .wp-block-image__upload-button.button {
.wp-core-ui .wp-block-audio__upload-button.button,
.wp-core-ui .wp-block-image__upload-button.button,
.wp-core-ui .wp-block-video__upload-button.button {
margin-right: 5px;

.dashicon {
Expand Down
1 change: 0 additions & 1 deletion blocks/library/video/editor.scss
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
}

.wp-block-video .components-placeholder__fieldset {
display: block;
max-width: 400px;

form {
Expand Down
37 changes: 28 additions & 9 deletions blocks/library/video/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,15 @@
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
import { Placeholder, Toolbar, IconButton, Button } from '@wordpress/components';
import {
Button,
FormFileUpload,
IconButton,
Placeholder,
Toolbar,
} from '@wordpress/components';
import { Component } from '@wordpress/element';
import { mediaUpload } from '@wordpress/utils';

/**
* Internal dependencies
Expand Down Expand Up @@ -94,20 +101,24 @@ export const settings = {
}
return false;
};
const setVideo = ( [ audio ] ) => onSelectVideo( audio );
const uploadFromFiles = ( event ) => mediaUpload( event.target.files, setVideo, 'video' );
const controls = isSelected && (
<BlockControls key="controls">
<BlockAlignmentToolbar
value={ align }
onChange={ updateAlignment }
/>
<Toolbar>
<IconButton
className="components-icon-button components-toolbar__control"
label={ __( 'Edit video' ) }
onClick={ switchToEditing }
icon="edit"
/>
</Toolbar>
{ ! editing && (
<Toolbar>
<IconButton
className="components-icon-button components-toolbar__control"
label={ __( 'Edit video' ) }
onClick={ switchToEditing }
icon="edit"
/>
</Toolbar>
) }
</BlockControls>
);

Expand All @@ -133,6 +144,14 @@ export const settings = {
{ __( 'Use URL' ) }
</Button>
</form>
<FormFileUpload
isLarge
className="wp-block-video__upload-button"
onChange={ uploadFromFiles }
accept="video/*"
>
{ __( 'Upload' ) }
</FormFileUpload>
<MediaUpload
onSelect={ onSelectVideo }
type="video"
Expand Down
29 changes: 29 additions & 0 deletions blocks/library/video/test/__snapshots__/index.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,35 @@ exports[`core/video block edit matches snapshot 1`] = `
Use URL
</button>
</form>
<div
class="components-form-file-upload"
>
<button
class="components-button components-icon-button wp-block-video__upload-button button button-large"
type="button"
>
<svg
aria-hidden="true"
class="dashicon dashicons-upload"
focusable="false"
height="20"
role="img"
viewBox="0 0 20 20"
width="20"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M8 14V8H5l5-6 5 6h-3v6H8zm-2 2v-6H4v8h12.01v-8H14v6H6z"
/>
</svg>
Upload
</button>
<input
accept="video/*"
style="display:none"
type="file"
/>
</div>
*** Mock(Media upload button) ***
</div>
</div>
Expand Down
35 changes: 18 additions & 17 deletions utils/mediaupload.js
Original file line number Diff line number Diff line change
@@ -1,45 +1,46 @@
/**
* External Dependencies
*/
import { compact } from 'lodash';
import { compact, startsWith } from 'lodash';

/**
* Media Upload is used by image and gallery blocks to handle uploading an image.
* Media Upload is used by audio, image, gallery and video blocks to handle uploading a media file
* when a file upload button is activated.
*
* TODO: future enhancement to add an upload indicator.
*
* @param {Array} filesList List of files.
* @param {Function} onImagesChange Function to be called each time a file or a temporary representation of the file is available.
* @param {Array} filesList List of files.
* @param {Function} onFileChange Function to be called each time a file or a temporary representation of the file is available.
* @param {string} allowedType The type of media that can be uploaded.
*/
export function mediaUpload( filesList, onImagesChange ) {
export function mediaUpload( filesList, onFileChange, allowedType ) {
// Cast filesList to array
const files = [ ...filesList ];

const imagesSet = [];
const setAndUpdateImages = ( idx, value ) => {
imagesSet[ idx ] = value;
onImagesChange( compact( imagesSet ) );
const filesSet = [];
const setAndUpdateFiles = ( idx, value ) => {
filesSet[ idx ] = value;
onFileChange( compact( filesSet ) );
};
const isAllowedType = ( fileType ) => startsWith( fileType, `${ allowedType }/` );
files.forEach( ( mediaFile, idx ) => {
// Only allow image uploads, may need updating if used for video
if ( ! /^image\//.test( mediaFile.type ) ) {
if ( ! isAllowedType( mediaFile.type ) ) {
return;
}

// Set temporary URL to create placeholder image, this is replaced
// with final image from media gallery when upload is `done` below
imagesSet.push( { url: window.URL.createObjectURL( mediaFile ) } );
onImagesChange( imagesSet );
// Set temporary URL to create placeholder media file, this is replaced
// with final file from media gallery when upload is `done` below
filesSet.push( { url: window.URL.createObjectURL( mediaFile ) } );
onFileChange( filesSet );

return createMediaFromFile( mediaFile ).then(
( savedMedia ) => {
setAndUpdateImages( idx, { id: savedMedia.id, url: savedMedia.source_url, link: savedMedia.link } );
setAndUpdateFiles( idx, { id: savedMedia.id, url: savedMedia.source_url, link: savedMedia.link } );
},
() => {
// Reset to empty on failure.
// TODO: Better failure messaging
setAndUpdateImages( idx, null );
setAndUpdateFiles( idx, null );
}
);
} );
Expand Down