diff --git a/assets/stylesheets/_z-index.scss b/assets/stylesheets/_z-index.scss
index b9cd8c4032e0a..4139af987af5e 100644
--- a/assets/stylesheets/_z-index.scss
+++ b/assets/stylesheets/_z-index.scss
@@ -29,6 +29,8 @@ $z-layers: (
".edit-post-header": 30,
".block-library-button__inline-link .editor-url-input__suggestions": 6, // URL suggestions for button block above sibling inserter
".block-library-image__resize-handlers": 1, // Resize handlers above sibling inserter
+ ".wp-block-cover__inner-container": 1, // InnerBlocks area inside cover block.
+ ".wp-block-cover__video-background": 0, // Video background inside cover block.
// Side UI active buttons
".editor-block-mover__control": 1,
diff --git a/packages/block-library/src/cover-image/index.js b/packages/block-library/src/cover-image/index.js
index 2d06ec2112283..a69df89e6a921 100644
--- a/packages/block-library/src/cover-image/index.js
+++ b/packages/block-library/src/cover-image/index.js
@@ -23,6 +23,7 @@ import {
withColors,
getColorClassName,
} from '@wordpress/editor';
+import deprecated from '@wordpress/deprecated';
const validAlignments = [ 'left', 'center', 'right', 'wide', 'full' ];
@@ -75,6 +76,10 @@ export const settings = {
attributes: blockAttributes,
+ supports: {
+ inserter: false,
+ },
+
transforms: {
from: [
{
@@ -132,6 +137,10 @@ export const settings = {
withNotices,
] )(
( { attributes, setAttributes, isSelected, className, noticeOperations, noticeUI, overlayColor, setOverlayColor } ) => {
+ deprecated( 'The Cover Image block', {
+ alternative: 'the Cover block',
+ plugin: 'Gutenberg',
+ } );
const { url, title, align, contentAlign, id, hasParallax, dimRatio } = attributes;
const updateAlignment = ( nextAlign ) => setAttributes( { align: nextAlign } );
const onSelectImage = ( media ) => {
diff --git a/packages/block-library/src/cover-image/test/index.js b/packages/block-library/src/cover-image/test/index.js
index ceb0bc9d5123c..270f59920b22c 100644
--- a/packages/block-library/src/cover-image/test/index.js
+++ b/packages/block-library/src/cover-image/test/index.js
@@ -9,5 +9,8 @@ describe( 'core/cover-image', () => {
const wrapper = blockEditRender( name, settings );
expect( wrapper ).toMatchSnapshot();
+ expect( console ).toHaveWarnedWith(
+ 'The Cover Image block is deprecated and will be removed. Please use the Cover block instead.'
+ );
} );
} );
diff --git a/packages/block-library/src/cover/edit.js b/packages/block-library/src/cover/edit.js
new file mode 100644
index 0000000000000..671efcfff79fe
--- /dev/null
+++ b/packages/block-library/src/cover/edit.js
@@ -0,0 +1,247 @@
+/**
+ * External dependencies
+ */
+import classnames from 'classnames';
+
+/**
+ * WordPress dependencies
+ */
+import {
+ IconButton,
+ PanelBody,
+ RangeControl,
+ ToggleControl,
+ Toolbar,
+ withNotices,
+} from '@wordpress/components';
+import { Fragment, Component } from '@wordpress/element';
+import { __ } from '@wordpress/i18n';
+import { compose } from '@wordpress/compose';
+import {
+ BlockControls,
+ InspectorControls,
+ InnerBlocks,
+ MediaPlaceholder,
+ MediaUpload,
+ PanelColorSettings,
+ withColors,
+} from '@wordpress/editor';
+
+const INNER_BLOCKS_TEMPLATE = [
+ [ 'core/paragraph', {
+ align: 'center',
+ fontSize: 'large',
+ placeholder: __( 'Write title…' ),
+ } ],
+];
+const INNER_BLOCKS_ALLOWED_BLOCKS = [
+ 'core/button',
+ 'core/heading',
+ 'core/paragraph',
+];
+const ALLOWED_MEDIA_TYPES = [ 'image', 'video' ];
+export const IMAGE_BACKGROUND_TYPE = 'image';
+export const VIDEO_BACKGROUND_TYPE = 'video';
+
+class CoverEdit extends Component {
+ constructor() {
+ super( ...arguments );
+ this.onSelectMedia = this.onSelectMedia.bind( this );
+ }
+
+ onSelectMedia( media ) {
+ const { setAttributes } = this.props;
+ if ( ! media || ! media.url ) {
+ setAttributes( { url: undefined, id: undefined } );
+ return;
+ }
+ let mediaType;
+ // for media selections originated from a file upload.
+ if ( media.media_type ) {
+ if ( media.media_type === IMAGE_BACKGROUND_TYPE ) {
+ mediaType = IMAGE_BACKGROUND_TYPE;
+ } else {
+ // only images and videos are accepted so if the media_type is not an image we can assume it is a video.
+ // video contain the media type of 'file' in the object returned from the rest api.
+ mediaType = VIDEO_BACKGROUND_TYPE;
+ }
+ } else { // for media selections originated from existing files in the media library.
+ mediaType = media.type;
+ }
+ if ( mediaType ) {
+ setAttributes( {
+ url: media.url,
+ id: media.id,
+ backgroundType: mediaType,
+ } );
+ return;
+ }
+ setAttributes( { url: media.url, id: media.id } );
+ }
+
+ render() {
+ const {
+ attributes,
+ className,
+ noticeOperations,
+ noticeUI,
+ overlayColor,
+ setAttributes,
+ setOverlayColor,
+ } = this.props;
+
+ const {
+ backgroundType,
+ dimRatio,
+ hasParallax,
+ id,
+ url,
+ } = attributes;
+
+ const toggleParallax = () => setAttributes( {
+ hasParallax: ! hasParallax,
+ } );
+ const setDimRatio = ( ratio ) => setAttributes( { dimRatio: ratio } );
+
+ const style = {
+ ...(
+ backgroundType === IMAGE_BACKGROUND_TYPE ?
+ backgroundImageStyles( url ) :
+ {}
+ ),
+ backgroundColor: overlayColor.color,
+ };
+
+ const classes = classnames(
+ className,
+ dimRatioToClass( dimRatio ),
+ {
+ 'has-background-dim': dimRatio !== 0,
+ 'has-parallax': hasParallax,
+ }
+ );
+
+ const controls = (
+
+ { !! url && (
+
+
+ (
+
+ ) }
+ />
+
+
+ ) }
+ { !! url && (
+
+
+ { IMAGE_BACKGROUND_TYPE === backgroundType && (
+
+ ) }
+
+
+
+
+
+ ) }
+
+ );
+
+ if ( ! url ) {
+ const icon = 'format-image';
+ const label = ( 'Cover' );
+
+ return (
+
+ { controls }
+
+
+ );
+ }
+
+ return (
+
+ { controls }
+
+ { VIDEO_BACKGROUND_TYPE === backgroundType && url && (
+
+ ) }
+
+
+
+
+
+ );
+ }
+}
+
+export default compose( [
+ withColors( { overlayColor: 'background-color' } ),
+ withNotices,
+] )( CoverEdit );
+
+export function dimRatioToClass( ratio ) {
+ return ( ratio === 0 || ratio === 50 ) ?
+ null :
+ 'has-background-dim-' + ( 10 * Math.round( ratio / 10 ) );
+}
+
+export function backgroundImageStyles( url ) {
+ return url ?
+ { backgroundImage: `url(${ url })` } :
+ {};
+}
diff --git a/packages/block-library/src/cover/editor.scss b/packages/block-library/src/cover/editor.scss
new file mode 100644
index 0000000000000..2b7649aea7bfa
--- /dev/null
+++ b/packages/block-library/src/cover/editor.scss
@@ -0,0 +1,3 @@
+.wp-block-cover__inner-container .editor-block-list__block {
+ color: inherit;
+}
diff --git a/packages/block-library/src/cover/index.js b/packages/block-library/src/cover/index.js
new file mode 100644
index 0000000000000..aeaad2b73e189
--- /dev/null
+++ b/packages/block-library/src/cover/index.js
@@ -0,0 +1,118 @@
+/**
+ * External dependencies
+ */
+import classnames from 'classnames';
+
+/**
+ * WordPress dependencies
+ */
+import { __ } from '@wordpress/i18n';
+import {
+ InnerBlocks,
+ getColorClassName,
+} from '@wordpress/editor';
+
+const blockAttributes = {
+ title: {
+ source: 'html',
+ selector: 'p',
+ },
+ url: {
+ type: 'string',
+ },
+ id: {
+ type: 'number',
+ },
+ hasParallax: {
+ type: 'boolean',
+ default: false,
+ },
+ dimRatio: {
+ type: 'number',
+ default: 50,
+ },
+ overlayColor: {
+ type: 'string',
+ },
+ customOverlayColor: {
+ type: 'string',
+ },
+ backgroundType: {
+ type: 'string',
+ default: 'image',
+ },
+};
+
+export const name = 'core/cover';
+
+import {
+ default as CoverEdit,
+ IMAGE_BACKGROUND_TYPE,
+ VIDEO_BACKGROUND_TYPE,
+ dimRatioToClass,
+ backgroundImageStyles,
+} from './edit';
+
+export const settings = {
+ title: __( 'Cover' ),
+
+ description: __( 'Add a full-width image or video, and layer text over it — great for headers.' ),
+
+ icon: ,
+
+ category: 'common',
+
+ attributes: blockAttributes,
+
+ supports: {
+ align: true,
+ },
+
+ edit: CoverEdit,
+
+ save( { attributes, className } ) {
+ const {
+ backgroundType,
+ customOverlayColor,
+ dimRatio,
+ hasParallax,
+ overlayColor,
+ url,
+ } = attributes;
+ const overlayColorClass = getColorClassName(
+ 'background-color',
+ overlayColor
+ );
+ const style = backgroundType === IMAGE_BACKGROUND_TYPE ?
+ backgroundImageStyles( url ) :
+ {};
+ if ( ! overlayColorClass ) {
+ style.backgroundColor = customOverlayColor;
+ }
+
+ const classes = classnames(
+ className,
+ dimRatioToClass( dimRatio ),
+ overlayColorClass,
+ {
+ 'has-background-dim': dimRatio !== 0,
+ 'has-parallax': hasParallax,
+ },
+ );
+
+ return (
+
+ { VIDEO_BACKGROUND_TYPE === backgroundType && url && (
) }
+
+
+
+
+ );
+ },
+};
diff --git a/packages/block-library/src/cover/style.scss b/packages/block-library/src/cover/style.scss
new file mode 100644
index 0000000000000..99cca0bd6d342
--- /dev/null
+++ b/packages/block-library/src/cover/style.scss
@@ -0,0 +1,71 @@
+.wp-block-cover,
+.wp-block-cover.alignleft,
+.wp-block-cover.aligncenter,
+.wp-block-cover.alignright,
+.wp-block-cover.alignwide,
+.wp-block-cover.alignfull {
+ display: flex;
+}
+
+.wp-block-cover {
+ position: relative;
+ background-color: $black;
+ background-size: cover;
+ background-position: center center;
+ min-height: 430px;
+ width: 100%;
+ margin: 0 0 1.5em 0;
+
+ justify-content: center;
+ align-items: center;
+
+ &.has-parallax {
+ background-attachment: fixed;
+ }
+
+ &.has-background-dim::before {
+ content: "";
+ position: absolute;
+ top: 0;
+ left: 0;
+ bottom: 0;
+ right: 0;
+ background-color: inherit;
+ opacity: 0.5;
+ z-index: z-index(".wp-block-cover__inner-container");
+ }
+
+ @for $i from 1 through 10 {
+ &.has-background-dim.has-background-dim-#{ $i * 10 }::before {
+ opacity: $i * 0.1;
+ }
+ }
+
+ .wp-block-cover__inner-container {
+ width: calc(100% - 70px);
+ z-index: z-index(".wp-block-cover__inner-container");
+ color: $light-gray-100;
+ }
+
+ p,
+ h1,
+ h2,
+ h3,
+ h4,
+ h5,
+ h6,
+ .wp-block-subhead {
+ color: inherit;
+ }
+}
+
+.wp-block-cover__video-background {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translateX(-50%) translateY(-50%);
+ width: 100%;
+ height: 100%;
+ z-index: z-index(".wp-block-cover__video-background");
+ object-fit: fill;
+}
diff --git a/packages/block-library/src/cover/test/__snapshots__/index.js.snap b/packages/block-library/src/cover/test/__snapshots__/index.js.snap
new file mode 100644
index 0000000000000..d06c221b6e4ea
--- /dev/null
+++ b/packages/block-library/src/cover/test/__snapshots__/index.js.snap
@@ -0,0 +1,92 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`core/cover block edit matches snapshot 1`] = `
+
+`;
diff --git a/packages/block-library/src/cover/test/index.js b/packages/block-library/src/cover/test/index.js
new file mode 100644
index 0000000000000..a088404e7c41d
--- /dev/null
+++ b/packages/block-library/src/cover/test/index.js
@@ -0,0 +1,13 @@
+/**
+ * Internal dependencies
+ */
+import { name, settings } from '../';
+import { blockEditRender } from '../../test/helpers';
+
+describe( 'core/cover', () => {
+ test( 'block edit matches snapshot', () => {
+ const wrapper = blockEditRender( name, settings );
+
+ expect( wrapper ).toMatchSnapshot();
+ } );
+} );
diff --git a/packages/block-library/src/editor.scss b/packages/block-library/src/editor.scss
index 70aa75c35b7f0..5e215a1f94b72 100644
--- a/packages/block-library/src/editor.scss
+++ b/packages/block-library/src/editor.scss
@@ -4,6 +4,7 @@
@import "./categories/editor.scss";
@import "./code/editor.scss";
@import "./columns/editor.scss";
+@import "./cover/editor.scss";
@import "./cover-image/editor.scss";
@import "./embed/editor.scss";
@import "./file/editor.scss";
diff --git a/packages/block-library/src/index.js b/packages/block-library/src/index.js
index 470ad4aea9feb..e280c024bac22 100644
--- a/packages/block-library/src/index.js
+++ b/packages/block-library/src/index.js
@@ -24,6 +24,7 @@ import * as categories from './categories';
import * as code from './code';
import * as columns from './columns';
import * as column from './columns/column';
+import * as cover from './cover';
import * as coverImage from './cover-image';
import * as embed from './embed';
import * as file from './file';
@@ -69,6 +70,7 @@ export const registerCoreBlocks = () => {
code,
columns,
column,
+ cover,
coverImage,
embed,
...embed.common,
diff --git a/packages/block-library/src/style.scss b/packages/block-library/src/style.scss
index 8ca793de2c055..e94dbd43adbc8 100644
--- a/packages/block-library/src/style.scss
+++ b/packages/block-library/src/style.scss
@@ -4,6 +4,7 @@
@import "./button/style.scss";
@import "./categories/style.scss";
@import "./columns/style.scss";
+@import "./cover/style.scss";
@import "./cover-image/style.scss";
@import "./embed/style.scss";
@import "./file/style.scss";
diff --git a/test/integration/full-content/fixtures/core__cover.html b/test/integration/full-content/fixtures/core__cover.html
new file mode 100644
index 0000000000000..692cc679bf2e3
--- /dev/null
+++ b/test/integration/full-content/fixtures/core__cover.html
@@ -0,0 +1,9 @@
+
+
+
diff --git a/test/integration/full-content/fixtures/core__cover.json b/test/integration/full-content/fixtures/core__cover.json
new file mode 100644
index 0000000000000..eed85fb25a843
--- /dev/null
+++ b/test/integration/full-content/fixtures/core__cover.json
@@ -0,0 +1,31 @@
+[
+ {
+ "clientId": "_clientId_0",
+ "name": "core/cover",
+ "isValid": true,
+ "attributes": {
+ "title": "",
+ "url": "https://cldup.com/uuUqE_dXzy.jpg",
+ "hasParallax": false,
+ "dimRatio": 40,
+ "backgroundType": "image"
+ },
+ "innerBlocks": [
+ {
+ "clientId": "_clientId_0",
+ "name": "core/paragraph",
+ "isValid": true,
+ "attributes": {
+ "content": "Cover!",
+ "align": "center",
+ "dropCap": false,
+ "placeholder": "Write title…",
+ "fontSize": "large"
+ },
+ "innerBlocks": [],
+ "originalContent": "Cover!
"
+ }
+ ],
+ "originalContent": ""
+ }
+]
diff --git a/test/integration/full-content/fixtures/core__cover.parsed.json b/test/integration/full-content/fixtures/core__cover.parsed.json
new file mode 100644
index 0000000000000..6fa81f5ba6b94
--- /dev/null
+++ b/test/integration/full-content/fixtures/core__cover.parsed.json
@@ -0,0 +1,28 @@
+[
+ {
+ "blockName": "core/cover",
+ "attrs": {
+ "url": "https://cldup.com/uuUqE_dXzy.jpg",
+ "dimRatio": 40
+ },
+ "innerBlocks": [
+ {
+ "blockName": "core/paragraph",
+ "attrs": {
+ "align": "center",
+ "placeholder": "Write title…",
+ "fontSize": "large"
+ },
+ "innerBlocks": [],
+ "innerHTML": "\n\t\tCover!
\n\t\t"
+ }
+ ],
+ "innerHTML": "\n\n"
+ },
+ {
+ "blockName": null,
+ "attrs": {},
+ "innerBlocks": [],
+ "innerHTML": "\n"
+ }
+]
diff --git a/test/integration/full-content/fixtures/core__cover.serialized.html b/test/integration/full-content/fixtures/core__cover.serialized.html
new file mode 100644
index 0000000000000..4c125e194c4e7
--- /dev/null
+++ b/test/integration/full-content/fixtures/core__cover.serialized.html
@@ -0,0 +1,5 @@
+
+
+