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

Makes cover block dynamic and adds featured image binding #39658

Merged
merged 2 commits into from
Apr 4, 2022
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
2 changes: 1 addition & 1 deletion docs/reference-guides/core-blocks.md
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ Add an image or video with a text overlay — great for headers. ([Source](https
- **Name:** core/cover
- **Category:** media
- **Supports:** align, anchor, color (~~background~~, ~~text~~), spacing (padding), ~~html~~
- **Attributes:** allowedBlocks, alt, backgroundType, contentPosition, customGradient, customOverlayColor, dimRatio, focalPoint, gradient, hasParallax, id, isDark, isRepeated, minHeight, minHeightUnit, overlayColor, templateLock, url
- **Attributes:** allowedBlocks, alt, backgroundType, contentPosition, customGradient, customOverlayColor, dimRatio, focalPoint, gradient, hasParallax, id, isDark, isRepeated, minHeight, minHeightUnit, overlayColor, templateLock, url, useFeaturedImage

## Embed

Expand Down
1 change: 1 addition & 0 deletions lib/blocks.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ function gutenberg_reregister_core_block_types() {
'block.php' => 'core/block',
'calendar.php' => 'core/calendar',
'categories.php' => 'core/categories',
'cover.php' => 'core/cover',
'comment-author-avatar.php' => 'core/comment-author-avatar',
'comment-author-name.php' => 'core/comment-author-name',
'comment-content.php' => 'core/comment-content',
Expand Down
5 changes: 5 additions & 0 deletions packages/block-library/src/cover/block.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@
"url": {
"type": "string"
},
"useFeaturedImage": {
draganescu marked this conversation as resolved.
Show resolved Hide resolved
"type": "boolean",
"default": false
},
"id": {
"type": "number"
},
Expand Down Expand Up @@ -72,6 +76,7 @@
"enum": [ "all", "insert", false ]
}
},
"usesContext": [ "postId", "postType" ],
"supports": {
"anchor": true,
"align": true,
Expand Down
106 changes: 77 additions & 29 deletions packages/block-library/src/cover/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import namesPlugin from 'colord/plugins/names';
/**
* WordPress dependencies
*/
import { useEntityProp, store as coreStore } from '@wordpress/core-data';
import {
Fragment,
useEffect,
Expand All @@ -28,6 +29,7 @@ import {
Spinner,
TextareaControl,
ToggleControl,
ToolbarButton,
__experimentalUseCustomUnits as useCustomUnits,
__experimentalBoxControl as BoxControl,
__experimentalToolsPanelItem as ToolsPanelItem,
Expand All @@ -54,7 +56,7 @@ import {
} from '@wordpress/block-editor';
import { __ } from '@wordpress/i18n';
import { useSelect, useDispatch } from '@wordpress/data';
import { cover as icon } from '@wordpress/icons';
import { postFeaturedImage, cover as icon } from '@wordpress/icons';
import { isBlobURL } from '@wordpress/blob';
import { store as noticesStore } from '@wordpress/notices';

Expand Down Expand Up @@ -298,11 +300,12 @@ function CoverEdit( {
setAttributes,
setOverlayColor,
toggleSelection,
context: { postId, postType },
} ) {
const {
contentPosition,
id,
backgroundType,
useFeaturedImage,
dimRatio,
focalPoint,
hasParallax,
Expand All @@ -311,11 +314,35 @@ function CoverEdit( {
minHeight,
minHeightUnit,
style: styleAttribute,
url,
alt,
allowedBlocks,
templateLock,
} = attributes;

const [ featuredImage ] = useEntityProp(
'postType',
postType,
'featured_media',
postId
);
Comment on lines +322 to +327
Copy link
Contributor

@adamziel adamziel Apr 4, 2022

Choose a reason for hiding this comment

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

useEntityProp only returns a value if the post is already loaded. It's true for the post editor, but can this be used outside of it? Would it break if the post is still being loaded? Or would this block not even be displayed because there is no post content to render? Either way, the future me reading this code would love to find a tiny comment that makes the constraint explicit.

Copy link
Contributor

Choose a reason for hiding this comment

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

Indeed, if there's no post in the context (which can happen in a lot of places, for instance a "category" template), we should probably remove the "use featured image" all together.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Interesting, normally featuredImage can be undefined and if "use featured image" is toggled in a non post context nothing happens. The disabling of the feature cannot be b/c of featuredImage value, we need to know inside the cover block if the surrounding "thing" will eventually be able to provide a featured image. Not sure if this is possible yet. I think checking template inside the block type is quite a coupling effect.

Copy link
Contributor

Choose a reason for hiding this comment

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

We should be checking the "post" in the context IMO. We should have a post type that supports featured images


const media = useSelect(
( select ) =>
featuredImage &&
select( coreStore ).getMedia( featuredImage, { context: 'view' } ),
[ featuredImage ]
);
const mediaUrl = media?.source_url;

// instead of destructuring the attributes
// we define the url and background type
// depending on the value of the useFeaturedImage flag
// to preview in edit the dynamic featured image
const url = useFeaturedImage ? mediaUrl : attributes.url;
const backgroundType = useFeaturedImage
? IMAGE_BACKGROUND_TYPE
: attributes.backgroundType;

const { __unstableMarkNextChangeAsNotPersistent } = useDispatch(
blockEditorStore
);
Expand Down Expand Up @@ -374,6 +401,13 @@ function CoverEdit( {
} );
};

const toggleUseFeaturedImage = () => {
setAttributes( {
useFeaturedImage: ! useFeaturedImage,
dimRatio: dimRatio === 100 ? 50 : dimRatio,
} );
};

const onUploadError = ( message ) => {
createErrorNotice( Array.isArray( message ) ? message[ 2 ] : message, {
type: 'snackbar',
Expand Down Expand Up @@ -458,14 +492,22 @@ function CoverEdit( {
/>
</BlockControls>
<BlockControls group="other">
<MediaReplaceFlow
mediaId={ id }
mediaURL={ url }
allowedTypes={ ALLOWED_MEDIA_TYPES }
accept="image/*,video/*"
onSelect={ onSelectMedia }
name={ ! url ? __( 'Add Media' ) : __( 'Replace' ) }
<ToolbarButton
icon={ postFeaturedImage }
label={ __( 'Use featured image' ) }
isPressed={ useFeaturedImage }
onClick={ toggleUseFeaturedImage }
/>
{ ! useFeaturedImage && (
<MediaReplaceFlow
mediaId={ id }
mediaURL={ url }
allowedTypes={ ALLOWED_MEDIA_TYPES }
accept="image/*,video/*"
onSelect={ onSelectMedia }
name={ ! url ? __( 'Add Media' ) : __( 'Replace' ) }
draganescu marked this conversation as resolved.
Show resolved Hide resolved
/>
) }
</BlockControls>
<InspectorControls>
{ !! url && (
Expand Down Expand Up @@ -499,27 +541,32 @@ function CoverEdit( {
}
/>
) }
{ url && isImageBackground && isImgElement && (
<TextareaControl
label={ __( 'Alt text (alternative text)' ) }
value={ alt }
onChange={ ( newAlt ) =>
setAttributes( { alt: newAlt } )
}
help={
<>
<ExternalLink href="https://www.w3.org/WAI/tutorials/images/decision-tree">
{ ! useFeaturedImage &&
url &&
isImageBackground &&
isImgElement && (
<TextareaControl
label={ __(
'Alt text (alternative text)'
) }
value={ alt }
onChange={ ( newAlt ) =>
setAttributes( { alt: newAlt } )
}
help={
<>
<ExternalLink href="https://www.w3.org/WAI/tutorials/images/decision-tree">
{ __(
'Describe the purpose of the image'
) }
</ExternalLink>
{ __(
'Describe the purpose of the image'
'Leave empty if the image is purely decorative.'
) }
</ExternalLink>
{ __(
'Leave empty if the image is purely decorative.'
) }
</>
}
/>
) }
</>
}
/>
) }
<PanelRow>
<Button
variant="secondary"
Expand All @@ -533,6 +580,7 @@ function CoverEdit( {
focalPoint: undefined,
hasParallax: undefined,
isRepeated: undefined,
useFeaturedImage: false,
} )
}
>
Expand Down
85 changes: 85 additions & 0 deletions packages/block-library/src/cover/index.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<?php
/**
* Server-side rendering of the `core/cover` block.
*
* @package WordPress
*/

/**
* Renders the `core/cover` block on server.
*
* @param array $attributes The block attributes.
* @param array $content The block rendered content.
*
* @return string Returns the cover block markup, if useFeaturedImage is true.
*/
function render_block_core_cover( $attributes, $content ) {
if ( false === $attributes['useFeaturedImage'] ) {
return $content;
}

$current_featured_image = get_the_post_thumbnail_url();

draganescu marked this conversation as resolved.
Show resolved Hide resolved
if ( false === $current_featured_image ) {
return $content;
}

$is_img_element = ! ( $attributes['hasParallax'] || $attributes['isRepeated'] );
$is_image_background = 'image' === $attributes['backgroundType'];

if ( $is_image_background && ! $is_img_element ) {
$content = preg_replace(
'/class=\".*?\"/',
'${0} style="background-image:url(' . esc_url( $current_featured_image ) . ')"',
$content,
1
);
}

if ( $is_image_background && $is_img_element ) {
$object_position = '';
if ( isset( $attributes['focalPoint'] ) ) {
$object_position = round( $attributes['focalPoint']['x'] * 100 ) . '%' . ' ' .
round( $attributes['focalPoint']['x'] * 100 ) . '%';
}

$image_template = '<img
class="wp-block-cover__image-background"
alt="%s"
src="%s"
style="object-position: %s"
data-object-fit="cover"
data-object-position="%s"
/>';

$image = sprintf(
$image_template,
esc_attr( get_the_post_thumbnail_caption() ),
esc_url( $current_featured_image ),
esc_attr( $object_position ),
esc_attr( $object_position )
);

$content = str_replace(
'</span><div',
'</span>' . $image . '<div',
$content
);

}

draganescu marked this conversation as resolved.
Show resolved Hide resolved
return $content;
}

/**
* Registers the `core/cover` block renderer on server.
*/
function register_block_core_cover() {
register_block_type_from_metadata(
__DIR__ . '/cover',
array(
'render_callback' => 'render_block_core_cover',
)
);
}
add_action( 'init', 'register_block_core_cover' );
32 changes: 18 additions & 14 deletions packages/block-library/src/cover/save.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export default function save( { attributes } ) {
customOverlayColor,
dimRatio,
focalPoint,
useFeaturedImage,
hasParallax,
isDark,
isRepeated,
Expand All @@ -60,7 +61,7 @@ export default function save( { attributes } ) {
const isImgElement = ! ( hasParallax || isRepeated );

const style = {
...( isImageBackground && ! isImgElement
...( isImageBackground && ! isImgElement && ! useFeaturedImage
? backgroundImageStyles( url )
: {} ),
minHeight: minHeight || undefined,
Expand Down Expand Up @@ -113,19 +114,22 @@ export default function save( { attributes } ) {
style={ bgStyle }
/>

{ isImageBackground && isImgElement && url && (
<img
className={ classnames(
'wp-block-cover__image-background',
id ? `wp-image-${ id }` : null
) }
alt={ alt }
src={ url }
style={ { objectPosition } }
data-object-fit="cover"
data-object-position={ objectPosition }
/>
) }
{ ! useFeaturedImage &&
isImageBackground &&
isImgElement &&
url && (
<img
className={ classnames(
'wp-block-cover__image-background',
id ? `wp-image-${ id }` : null
) }
alt={ alt }
src={ url }
style={ { objectPosition } }
data-object-fit="cover"
data-object-position={ objectPosition }
/>
) }
{ isVideoBackground && url && (
<video
className={ classnames(
Expand Down
1 change: 1 addition & 0 deletions test/integration/fixtures/blocks/core__cover.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"isValid": true,
"attributes": {
"url": "data:image/jpeg;base64,/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k=",
"useFeaturedImage": false,
"alt": "",
"hasParallax": false,
"isRepeated": false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"name": "core/cover",
"isValid": true,
"attributes": {
"useFeaturedImage": false,
"alt": "",
"hasParallax": false,
"isRepeated": false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"isValid": true,
"attributes": {
"url": "data:image/jpeg;base64,/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k=",
"useFeaturedImage": false,
"alt": "",
"hasParallax": false,
"isRepeated": false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"isValid": true,
"attributes": {
"url": "data:video/mp4;base64,AAAAHGZ0eXBpc29tAAACAGlzb21pc28ybXA0MQAAAAhmcmVlAAAC721kYXQhEAUgpBv/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA3pwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcCEQBSCkG//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADengAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcAAAAsJtb292AAAAbG12aGQAAAAAAAAAAAAAAAAAAAPoAAAALwABAAABAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAB7HRyYWsAAABcdGtoZAAAAAMAAAAAAAAAAAAAAAIAAAAAAAAALwAAAAAAAAAAAAAAAQEAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAACRlZHRzAAAAHGVsc3QAAAAAAAAAAQAAAC8AAAAAAAEAAAAAAWRtZGlhAAAAIG1kaGQAAAAAAAAAAAAAAAAAAKxEAAAIAFXEAAAAAAAtaGRscgAAAAAAAAAAc291bgAAAAAAAAAAAAAAAFNvdW5kSGFuZGxlcgAAAAEPbWluZgAAABBzbWhkAAAAAAAAAAAAAAAkZGluZgAAABxkcmVmAAAAAAAAAAEAAAAMdXJsIAAAAAEAAADTc3RibAAAAGdzdHNkAAAAAAAAAAEAAABXbXA0YQAAAAAAAAABAAAAAAAAAAAAAgAQAAAAAKxEAAAAAAAzZXNkcwAAAAADgICAIgACAASAgIAUQBUAAAAAAfQAAAHz+QWAgIACEhAGgICAAQIAAAAYc3R0cwAAAAAAAAABAAAAAgAABAAAAAAcc3RzYwAAAAAAAAABAAAAAQAAAAIAAAABAAAAHHN0c3oAAAAAAAAAAAAAAAIAAAFzAAABdAAAABRzdGNvAAAAAAAAAAEAAAAsAAAAYnVkdGEAAABabWV0YQAAAAAAAAAhaGRscgAAAAAAAAAAbWRpcmFwcGwAAAAAAAAAAAAAAAAtaWxzdAAAACWpdG9vAAAAHWRhdGEAAAABAAAAAExhdmY1Ni40MC4xMDE=",
"useFeaturedImage": false,
"alt": "",
"hasParallax": false,
"isRepeated": false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"name": "core/cover",
"isValid": true,
"attributes": {
"useFeaturedImage": false,
"alt": "",
"hasParallax": false,
"isRepeated": false,
Expand Down
Loading