From f9efc9f1c7bccd6388a9398ee6f8ea08b23cb3d2 Mon Sep 17 00:00:00 2001
From: Chris Van Patten
Date: Wed, 17 Oct 2018 22:11:35 -0400
Subject: [PATCH 001/121] Deprecate PanelColor components (#10391)
* Deprecate PanelColor components
* Update deprecation version numbers
* Fix a test broken during the rebase
---
docs/reference/deprecated.md | 2 ++
packages/components/CHANGELOG.md | 6 ++++++
packages/components/src/panel/color.js | 7 +++++++
packages/components/src/panel/test/color.js | 4 ++++
packages/editor/CHANGELOG.md | 6 ++++++
packages/editor/src/components/panel-color/index.js | 7 +++++++
6 files changed, 32 insertions(+)
diff --git a/docs/reference/deprecated.md b/docs/reference/deprecated.md
index d0653f14fed93..d7203f2edba18 100644
--- a/docs/reference/deprecated.md
+++ b/docs/reference/deprecated.md
@@ -4,6 +4,8 @@ Gutenberg's deprecation policy is intended to support backwards-compatibility fo
- `isEditorSidebarPanelOpened` selector (`core/edit-post`) has been removed. Please use `isEditorPanelEnabled` instead.
- `toggleGeneralSidebarEditorPanel` action (`core/edit-post`) has been removed. Please use `toggleEditorPanelOpened` instead.
+- `wp.components.PanelColor` component has been removed. Please use `wp.editor.PanelColorSettings` instead.
+- `wp.editor.PanelColor` component has been removed. Please use `wp.editor.PanelColorSettings` instead.
## 4.2.0
diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md
index d74dfc694f8eb..2665f8e62ee0f 100644
--- a/packages/components/CHANGELOG.md
+++ b/packages/components/CHANGELOG.md
@@ -1,3 +1,9 @@
+## 4.2.0 (Unreleased)
+
+### Deprecation
+
+- `wp.components.PanelColor` has been deprecated in favor of `wp.editor.PanelColorSettings`.
+
## 4.1.0 (2018-10-10)
### New Feature
diff --git a/packages/components/src/panel/color.js b/packages/components/src/panel/color.js
index 249585ae3b026..3c137e79eeeb6 100644
--- a/packages/components/src/panel/color.js
+++ b/packages/components/src/panel/color.js
@@ -1,6 +1,7 @@
/**
* WordPress dependencies
*/
+import deprecated from '@wordpress/deprecated';
import { __, sprintf } from '@wordpress/i18n';
/**
@@ -10,6 +11,12 @@ import PanelBody from './body';
import ColorIndicator from '../color-indicator';
function PanelColor( { colorValue, colorName, title, ...props } ) {
+ deprecated( 'wp.components.PanelColor', {
+ version: '4.3',
+ alternative: 'wp.editor.PanelColorSettings',
+ plugin: 'Gutenberg',
+ } );
+
// translators: %s: The name of the color e.g: "vivid red" or color hex code if name is not available e.g: "#f00".
const currentColorLabel = sprintf( __( '(current color: %s)' ), colorName || colorValue );
return (
diff --git a/packages/components/src/panel/test/color.js b/packages/components/src/panel/test/color.js
index c3ca26962db60..31c00562daaee 100644
--- a/packages/components/src/panel/test/color.js
+++ b/packages/components/src/panel/test/color.js
@@ -13,5 +13,9 @@ describe( 'PanelColor', () => {
const wrapper = shallow( );
expect( wrapper ).toMatchSnapshot();
+
+ expect( console ).toHaveWarnedWith(
+ 'wp.components.PanelColor is deprecated and will be removed from Gutenberg in 4.3. Please use wp.editor.PanelColorSettings instead.'
+ );
} );
} );
diff --git a/packages/editor/CHANGELOG.md b/packages/editor/CHANGELOG.md
index 4b518f3801b40..d82d8b209de0a 100644
--- a/packages/editor/CHANGELOG.md
+++ b/packages/editor/CHANGELOG.md
@@ -1,3 +1,9 @@
+## 4.1.0 (Unreleased)
+
+### Deprecations
+
+- `wp.editor.PanelColor` has been deprecated in favor of `wp.editor.PanelColorSettings`.
+
## 4.0.0 (2018-09-30)
### Breaking Changes
diff --git a/packages/editor/src/components/panel-color/index.js b/packages/editor/src/components/panel-color/index.js
index 5ad46d0d86c1f..327384a127653 100644
--- a/packages/editor/src/components/panel-color/index.js
+++ b/packages/editor/src/components/panel-color/index.js
@@ -8,6 +8,7 @@ import { omit } from 'lodash';
*/
import { PanelColor as PanelColorComponent } from '@wordpress/components';
import { ifCondition, compose } from '@wordpress/compose';
+import deprecated from '@wordpress/deprecated';
/**
* Internal dependencies
@@ -17,6 +18,12 @@ import withColorContext from '../color-palette/with-color-context';
import { getColorObjectByColorValue } from '../colors';
function PanelColor( { colors, title, colorValue, initialOpen, ...props } ) {
+ deprecated( 'wp.editor.PanelColor', {
+ version: '4.3',
+ alternative: 'wp.editor.PanelColorSettings',
+ plugin: 'Gutenberg',
+ } );
+
const colorObject = getColorObjectByColorValue( colors, colorValue );
const colorName = colorObject && colorObject.name;
return (
From ca05c269d7d4e357ca1a9226c6d072993ded7044 Mon Sep 17 00:00:00 2001
From: Danilo Ercoli
Date: Thu, 18 Oct 2018 06:51:25 +0200
Subject: [PATCH 002/121] Refactor `onSplit` and `insertBlockAfter` in RichText
component, Para and Heading blocks for mobile, tryin gto stay closer to the
web version. (#10690)
---
.../block-library/src/heading/edit.native.js | 31 +++++-------
.../src/paragraph/edit.native.js | 42 ++++++++++++++--
.../src/components/rich-text/index.native.js | 48 ++++++++++++++++++-
3 files changed, 96 insertions(+), 25 deletions(-)
diff --git a/packages/block-library/src/heading/edit.native.js b/packages/block-library/src/heading/edit.native.js
index 3d84600da25fd..8eba264968ca0 100644
--- a/packages/block-library/src/heading/edit.native.js
+++ b/packages/block-library/src/heading/edit.native.js
@@ -24,28 +24,11 @@ import './editor.scss';
const minHeight = 50;
class HeadingEdit extends Component {
- constructor() {
- super( ...arguments );
- this.splitBlock = this.splitBlock.bind( this );
- }
-
- // eslint-disable-next-line no-unused-vars
- splitBlock( htmlText, start, end ) {
- const {
- insertBlocksAfter,
- } = this.props;
-
- if ( insertBlocksAfter ) {
- const blocks = [];
- blocks.push( createBlock( 'core/paragraph', { content: 'Test' } ) );
- insertBlocksAfter( blocks );
- }
- }
-
render() {
const {
attributes,
setAttributes,
+ insertBlocksAfter,
} = this.props;
const {
@@ -73,7 +56,17 @@ class HeadingEdit extends Component {
content: newParaBlock.attributes.content,
} );
} }
- onSplit={ this.splitBlock }
+ onSplit={
+ insertBlocksAfter ?
+ ( before, after, ...blocks ) => {
+ setAttributes( { content: before } );
+ insertBlocksAfter( [
+ ...blocks,
+ createBlock( 'core/paragraph', { content: after } ),
+ ] );
+ } :
+ undefined
+ }
onContentSizeChange={ ( event ) => {
setAttributes( { aztecHeight: event.aztecHeight } );
} }
diff --git a/packages/block-library/src/paragraph/edit.native.js b/packages/block-library/src/paragraph/edit.native.js
index 1b35bf84a1088..9fb79f45f8cdf 100644
--- a/packages/block-library/src/paragraph/edit.native.js
+++ b/packages/block-library/src/paragraph/edit.native.js
@@ -13,23 +13,55 @@ import { RichText } from '@wordpress/editor';
const minHeight = 50;
+const name = 'core/paragraph';
+
class ParagraphEdit extends Component {
constructor() {
super( ...arguments );
this.splitBlock = this.splitBlock.bind( this );
}
- // eslint-disable-next-line no-unused-vars
- splitBlock( htmlText, start, end ) {
+ /**
+ * Split handler for RichText value, namely when content is pasted or the
+ * user presses the Enter key.
+ *
+ * @param {?Array} before Optional before value, to be used as content
+ * in place of what exists currently for the
+ * block. If undefined, the block is deleted.
+ * @param {?Array} after Optional after value, to be appended in a new
+ * paragraph block to the set of blocks passed
+ * as spread.
+ * @param {...WPBlock} blocks Optional blocks inserted between the before
+ * and after value blocks.
+ */
+ splitBlock( before, after, ...blocks ) {
const {
+ attributes,
insertBlocksAfter,
+ setAttributes,
} = this.props;
- if ( insertBlocksAfter ) {
- const blocks = [];
- blocks.push( createBlock( 'core/paragraph', { content: 'Test' } ) );
+ if ( after !== null ) {
+ // Append "After" content as a new paragraph block to the end of
+ // any other blocks being inserted after the current paragraph.
+ const newBlock = createBlock( name, { content: after } );
+ blocks.push( newBlock );
+ }
+
+ if ( blocks.length && insertBlocksAfter ) {
insertBlocksAfter( blocks );
}
+
+ const { content } = attributes;
+ if ( before === null ) {
+ // TODO : If before content is omitted, treat as intent to delete block.
+ // onReplace( [] );
+ } else if ( content !== before ) {
+ // Only update content if it has in-fact changed. In case that user
+ // has created a new paragraph at end of an existing one, the value
+ // of before will be strictly equal to the current content.
+ setAttributes( { content: before } );
+ }
}
render() {
diff --git a/packages/editor/src/components/rich-text/index.native.js b/packages/editor/src/components/rich-text/index.native.js
index 71d13f67761c2..8de0a2655d1f0 100644
--- a/packages/editor/src/components/rich-text/index.native.js
+++ b/packages/editor/src/components/rich-text/index.native.js
@@ -14,6 +14,11 @@ import {
import { Component, RawHTML } from '@wordpress/element';
import { withInstanceId, compose } from '@wordpress/compose';
import { Toolbar } from '@wordpress/components';
+import {
+ create,
+ split,
+ toHTMLString,
+} from '@wordpress/rich-text';
/**
* Internal dependencies
@@ -56,12 +61,53 @@ export class RichText extends Component {
this.splitContent( htmlText, start, end );
}
+ /*
+ * Splits the content at the location of the selection.
+ *
+ * Replaces the content of the editor inside this element with the contents
+ * before the selection. Sends the elements after the selection to the `onSplit`
+ * handler.
+ *
+ */
splitContent( htmlText, start, end ) {
const { onSplit } = this.props;
+
if ( ! onSplit ) {
return;
}
- onSplit( htmlText, start, end );
+
+ const record = create( {
+ html: htmlText,
+ range: null,
+ multilineTag: false,
+ removeNode: null,
+ unwrapNode: null,
+ removeAttribute: null,
+ filterString: null,
+ } );
+
+ // TODO : Fix the index position in AztecNative for Android
+ let [ before, after ] = split( { start: start - 6, end: end - 6, ...record } );
+
+ // TODO : Handle here the cases when the split happens at the trailing or leading edge...
+ // See the web version for reference.
+
+ if ( before ) {
+ before = this.valueToFormat( before );
+ }
+
+ if ( after ) {
+ after = this.valueToFormat( after );
+ }
+
+ onSplit( before, after );
+ }
+
+ valueToFormat( { formats, text } ) {
+ const value = toHTMLString( { formats, text }, this.multilineTag );
+ // remove the outer p tags
+ const returningContentWithoutParaTag = value.replace( /|<\/p>/gi, '' );
+ return returningContentWithoutParaTag;
}
onActiveFormatsChange( formats ) {
From 70583acc9d55bf71bb0b73c29d3e3f6381ce1eb2 Mon Sep 17 00:00:00 2001
From: Andrew Duthie
Date: Thu, 18 Oct 2018 03:41:03 -0400
Subject: [PATCH 003/121] Blocks: Add direction attribute to paragraph block
(#10663)
---
packages/block-library/src/paragraph/edit.js | 31 ++++++++++++++++++-
packages/block-library/src/paragraph/index.js | 6 ++++
2 files changed, 36 insertions(+), 1 deletion(-)
diff --git a/packages/block-library/src/paragraph/edit.js b/packages/block-library/src/paragraph/edit.js
index 6e77735fd016f..1290f5566d46b 100644
--- a/packages/block-library/src/paragraph/edit.js
+++ b/packages/block-library/src/paragraph/edit.js
@@ -6,7 +6,7 @@ import classnames from 'classnames';
/**
* WordPress dependencies
*/
-import { __ } from '@wordpress/i18n';
+import { __, _x } from '@wordpress/i18n';
import {
Component,
Fragment,
@@ -14,6 +14,7 @@ import {
import {
PanelBody,
ToggleControl,
+ Toolbar,
withFallbackStyles,
} from '@wordpress/components';
import {
@@ -29,6 +30,7 @@ import {
} from '@wordpress/editor';
import { createBlock } from '@wordpress/blocks';
import { compose } from '@wordpress/compose';
+import { withSelect } from '@wordpress/data';
const { getComputedStyle } = window;
@@ -137,6 +139,7 @@ class ParagraphBlock extends Component {
fallbackFontSize,
fontSize,
setFontSize,
+ isRTL,
} = this.props;
const {
@@ -144,6 +147,7 @@ class ParagraphBlock extends Component {
content,
dropCap,
placeholder,
+ direction,
} = attributes;
return (
@@ -155,6 +159,23 @@ class ParagraphBlock extends Component {
setAttributes( { align: nextAlign } );
} }
/>
+ { isRTL && (
+
+ ) }
@@ -212,6 +233,7 @@ class ParagraphBlock extends Component {
color: textColor.color,
fontSize: fontSize.size ? fontSize.size + 'px' : undefined,
textAlign: align,
+ direction,
} }
value={ content }
onChange={ ( nextContent ) => {
@@ -234,6 +256,13 @@ const ParagraphEdit = compose( [
withColors( 'backgroundColor', { textColor: 'color' } ),
withFontSizes( 'fontSize' ),
applyFallbackStyles,
+ withSelect( ( select ) => {
+ const { getEditorSettings } = select( 'core/editor' );
+
+ return {
+ isRTL: getEditorSettings().isRTL,
+ };
+ } ),
] )( ParagraphBlock );
export default ParagraphEdit;
diff --git a/packages/block-library/src/paragraph/index.js b/packages/block-library/src/paragraph/index.js
index 1e1218c6d2ab5..25e83f1018c03 100644
--- a/packages/block-library/src/paragraph/index.js
+++ b/packages/block-library/src/paragraph/index.js
@@ -65,6 +65,10 @@ const schema = {
customFontSize: {
type: 'number',
},
+ direction: {
+ type: 'string',
+ enum: [ 'ltr', 'rtl' ],
+ },
};
export const name = 'core/paragraph';
@@ -230,6 +234,7 @@ export const settings = {
customTextColor,
fontSize,
customFontSize,
+ direction,
} = attributes;
const textClass = getColorClassName( 'color', textColor );
@@ -258,6 +263,7 @@ export const settings = {
style={ styles }
className={ className ? className : undefined }
value={ content }
+ dir={ direction }
/>
);
},
From b09e27f763d750599e2c037719c704ef62d1f4d3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=B6ren=20Wrede?=
Date: Thu, 18 Oct 2018 09:44:55 +0200
Subject: [PATCH 004/121] bold permalink label (#10566)
---
packages/editor/src/components/post-permalink/style.scss | 1 +
1 file changed, 1 insertion(+)
diff --git a/packages/editor/src/components/post-permalink/style.scss b/packages/editor/src/components/post-permalink/style.scss
index e882c2579958c..c35abcb009dfc 100644
--- a/packages/editor/src/components/post-permalink/style.scss
+++ b/packages/editor/src/components/post-permalink/style.scss
@@ -39,6 +39,7 @@
.editor-post-permalink__label {
margin: 0 10px 0 5px;
+ font-weight: 600;
}
.editor-post-permalink__link {
From 86605e4899c52fe994dc76b1acb54b4f17c299e4 Mon Sep 17 00:00:00 2001
From: Daniel Richards
Date: Thu, 18 Oct 2018 16:13:55 +0800
Subject: [PATCH 005/121] Fix autocomplete keyboard nav in link popover
(#10716)
* Fix autocomplete cursor key navigation
* Avoid running onClickOutside logic for other non-onClickOutside events
* Add e2e tests for keyboard interaction with link container
---
.../format-toolbar/link-container.js | 14 ++-
.../editor/src/components/url-input/index.js | 2 +-
test/e2e/specs/links.test.js | 96 +++++++++++++++++--
3 files changed, 99 insertions(+), 13 deletions(-)
diff --git a/packages/editor/src/components/rich-text/format-toolbar/link-container.js b/packages/editor/src/components/rich-text/format-toolbar/link-container.js
index 84cd17d31c41e..a070ae6e77bb2 100644
--- a/packages/editor/src/components/rich-text/format-toolbar/link-container.js
+++ b/packages/editor/src/components/rich-text/format-toolbar/link-container.js
@@ -99,6 +99,7 @@ class LinkContainer extends Component {
this.onKeyDown = this.onKeyDown.bind( this );
this.onChangeInputValue = this.onChangeInputValue.bind( this );
this.setLinkTarget = this.setLinkTarget.bind( this );
+ this.onClickOutside = this.onClickOutside.bind( this );
this.resetState = this.resetState.bind( this );
this.autocompleteRef = createRef();
@@ -175,15 +176,20 @@ class LinkContainer extends Component {
event.preventDefault();
}
- resetState( event ) {
+ onClickOutside( event ) {
// The autocomplete suggestions list renders in a separate popover (in a portal),
// so onClickOutside fails to detect that a click on a suggestion occured in the
// LinkContainer. Detect clicks on autocomplete suggestions using a ref here, and
- // return early if so.
- if ( this.autocompleteRef.current && this.autocompleteRef.current.contains( event.target ) ) {
+ // return to avoid the popover being closed.
+ const autocompleteElement = this.autocompleteRef.current;
+ if ( autocompleteElement && autocompleteElement.contains( event.target ) ) {
return;
}
+ this.resetState();
+ }
+
+ resetState() {
this.props.stopAddingLink();
this.setState( { editLink: false } );
}
@@ -205,7 +211,7 @@ class LinkContainer extends Component {
key={ `${ record.start }${ record.end }` /* Used to force rerender on selection change */ }
>
(
{
expect( await getEditedPostContent() ).toMatchSnapshot();
} );
- // Test for regressions of https://github.com/WordPress/gutenberg/issues/10496.
- it( 'allows autocomplete suggestions to be selected with the mouse', async () => {
- const titleText = 'Unique post title';
-
- // First create a post that we can search for using the link autocompletion.
+ const createPostWithTitle = async ( titleText ) => {
+ await newPost();
await page.type( '.editor-post-title__input', titleText );
await page.click( '.editor-post-publish-panel__toggle' );
@@ -273,8 +270,16 @@ describe( 'Links', () => {
// Publish the post
await page.click( '.editor-post-publish-button' );
+ // Return the URL of the new post
await page.waitForSelector( '.post-publish-panel__postpublish-post-address input' );
- const postURL = await page.evaluate( () => document.querySelector( '.post-publish-panel__postpublish-post-address input' ).value );
+ return page.evaluate( () => document.querySelector( '.post-publish-panel__postpublish-post-address input' ).value );
+ };
+
+ // Test for regressions of https://github.com/WordPress/gutenberg/issues/10496.
+ it( 'allows autocomplete suggestions to be selected with the mouse', async () => {
+ // First create a post that we can search for using the link autocompletion.
+ const titleText = 'Test post mouse';
+ const postURL = await createPostWithTitle( titleText );
// Now create a new post and try to select the post created previously
// from the autocomplete suggestions.
@@ -297,11 +302,10 @@ describe( 'Links', () => {
const firstSuggestion = autocompleteSuggestions[ 0 ];
// Expect that clicking on the autocomplete suggestion doesn't dismiss the link popover.
- // and that the url input has a value.
await firstSuggestion.click();
expect( await page.$( '.editor-url-popover' ) ).not.toBeNull();
- // Expect the url input value to have been updated with the post url
+ // Expect the url input value to have been updated with the post url.
const inputValue = await page.evaluate( () => document.querySelector( '.editor-url-input input[aria-label="URL"]' ).value );
expect( inputValue ).toEqual( postURL );
@@ -311,4 +315,80 @@ describe( 'Links', () => {
const linkHref = await page.evaluate( () => document.querySelector( '.editor-format-toolbar__link-container-value' ).href );
expect( linkHref ).toEqual( postURL );
} );
+
+ // Test for regressions of https://github.com/WordPress/gutenberg/issues/10496.
+ it( 'allows autocomplete suggestions to be navigated with the keyboard', async () => {
+ const titleText = 'Test post keyboard';
+ const postURL = await createPostWithTitle( titleText );
+
+ await newPost();
+ await clickBlockAppender();
+
+ // Now in a new post and try to create a link from an autocomplete suggestion using the keyboard.
+ await page.keyboard.type( 'This is Gutenberg' );
+ await pressWithModifier( SELECT_WORD_MODIFIER_KEYS, 'ArrowLeft' );
+
+ // Press Cmd+K to insert a link
+ await pressWithModifier( META_KEY, 'K' );
+
+ // Wait for the URL field to auto-focus
+ await waitForAutoFocus();
+
+ await page.keyboard.type( titleText );
+ await page.waitForSelector( '.editor-url-input__suggestion' );
+ const autocompleteSuggestions = await page.$x( `//*[contains(@class, "editor-url-input__suggestion")]//button[contains(text(), '${ titleText }')]` );
+
+ // Expect there to be some autocomplete suggestions.
+ expect( autocompleteSuggestions.length ).toBeGreaterThan( 0 );
+
+ // Expect the the first suggestion to be selected when pressing the down arrow.
+ await page.keyboard.press( 'ArrowDown' );
+ const isSelected = await page.evaluate( () => document.querySelector( '.editor-url-input__suggestion' ).getAttribute( 'aria-selected' ) );
+ expect( isSelected ).toBe( 'true' );
+
+ // Expect the link to apply correctly when pressing Enter.
+ // Note - have avoided using snapshots here since the link url can't be determined ahead of time.
+ await page.keyboard.press( 'Enter' );
+ const linkHref = await page.evaluate( () => document.querySelector( '.editor-format-toolbar__link-container-value' ).href );
+ expect( linkHref ).toEqual( postURL );
+ } );
+
+ it( 'allows use of escape key to dismiss the url popover', async () => {
+ const titleText = 'Test post escape';
+ await createPostWithTitle( titleText );
+
+ await newPost();
+ await clickBlockAppender();
+
+ // Now in a new post and try to create a link from an autocomplete suggestion using the keyboard.
+ await page.keyboard.type( 'This is Gutenberg' );
+ await pressWithModifier( SELECT_WORD_MODIFIER_KEYS, 'ArrowLeft' );
+
+ // Press Cmd+K to insert a link
+ await pressWithModifier( META_KEY, 'K' );
+
+ // Wait for the URL field to auto-focus
+ await waitForAutoFocus();
+ expect( await page.$( '.editor-url-popover' ) ).not.toBeNull();
+
+ // Trigger the autocomplete suggestion list and select the first suggestion.
+ await page.keyboard.type( titleText );
+ await page.waitForSelector( '.editor-url-input__suggestion' );
+ await page.keyboard.press( 'ArrowDown' );
+
+ // Expect the the escape key to dismiss the popover when the autocomplete suggestion list is open.
+ await page.keyboard.press( 'Escape' );
+ expect( await page.$( '.editor-url-popover' ) ).toBeNull();
+
+ // Press Cmd+K to insert a link
+ await pressWithModifier( META_KEY, 'K' );
+
+ // Wait for the URL field to auto-focus
+ await waitForAutoFocus();
+ expect( await page.$( '.editor-url-popover' ) ).not.toBeNull();
+
+ // Expect the the escape key to dismiss the popover normally.
+ await page.keyboard.press( 'Escape' );
+ expect( await page.$( '.editor-url-popover' ) ).toBeNull();
+ } );
} );
From 1dfae320ec3147ee3fc50674b66b37ed3f7a52a1 Mon Sep 17 00:00:00 2001
From: Daniel Bachhuber
Date: Thu, 18 Oct 2018 01:46:20 -0700
Subject: [PATCH 006/121] Update REST Search controller to use 'wp/v2'
namespace (#10670)
* Update REST Search controller to use 'wp/v2' namespace
* Docs: Update REST API url
---
lib/class-wp-rest-search-controller.php | 2 +-
.../editor/src/components/url-input/README.md | 4 ++--
.../editor/src/components/url-input/index.js | 2 +-
phpunit/class-rest-search-controller-test.php | 16 ++++++++--------
4 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/lib/class-wp-rest-search-controller.php b/lib/class-wp-rest-search-controller.php
index 2dca5b82abc2e..69776c5a7be34 100644
--- a/lib/class-wp-rest-search-controller.php
+++ b/lib/class-wp-rest-search-controller.php
@@ -62,7 +62,7 @@ class WP_REST_Search_Controller extends WP_REST_Controller {
* handler instance must extend the `WP_REST_Search_Handler` class.
*/
public function __construct( array $search_handlers ) {
- $this->namespace = 'gutenberg/v1';
+ $this->namespace = 'wp/v2';
$this->rest_base = 'search';
foreach ( $search_handlers as $search_handler ) {
diff --git a/packages/editor/src/components/url-input/README.md b/packages/editor/src/components/url-input/README.md
index 4273b9978fcd9..19b67bf452b5e 100644
--- a/packages/editor/src/components/url-input/README.md
+++ b/packages/editor/src/components/url-input/README.md
@@ -22,7 +22,7 @@ Render a URL input button that pops up an input to search for and select a post
"_links": {
"self": [ { "embeddable": true, "href": "https://example.com/wp-json/wp/v2/pages/1" } ],
"about": [ { "href": "https://example.com/wp-json/wp/v2/types/page" } ],
- "collection": [ { "href": "https://example.com/wp-json/gutenberg/v1/search" } ]
+ "collection": [ { "href": "https://example.com/wp-json/wp/v2/search" } ]
}
}
```
@@ -120,7 +120,7 @@ Renders the URL input field used by the `URLInputButton` component. It can be us
"_links": {
"self": [ { "embeddable": true, "href": "https://example.com/wp-json/wp/v2/pages/1" } ],
"about": [ { "href": "https://example.com/wp-json/wp/v2/types/page" } ],
- "collection": [ { "href": "https://example.com/wp-json/gutenberg/v1/search" } ]
+ "collection": [ { "href": "https://example.com/wp-json/wp/v2/search" } ]
}
}
```
diff --git a/packages/editor/src/components/url-input/index.js b/packages/editor/src/components/url-input/index.js
index b3d14eaeb9f10..23762991cddcf 100644
--- a/packages/editor/src/components/url-input/index.js
+++ b/packages/editor/src/components/url-input/index.js
@@ -86,7 +86,7 @@ class URLInput extends Component {
} );
const request = apiFetch( {
- path: addQueryArgs( '/gutenberg/v1/search', {
+ path: addQueryArgs( '/wp/v2/search', {
search: value,
per_page: 20,
type: 'post',
diff --git a/phpunit/class-rest-search-controller-test.php b/phpunit/class-rest-search-controller-test.php
index 9bd8ab687f58c..26649037e8ff2 100644
--- a/phpunit/class-rest-search-controller-test.php
+++ b/phpunit/class-rest-search-controller-test.php
@@ -82,8 +82,8 @@ public static function wpTearDownAfterClass() {
public function test_register_routes() {
$routes = rest_get_server()->get_routes();
- $this->assertArrayHasKey( '/gutenberg/v1/search', $routes );
- $this->assertCount( 1, $routes['/gutenberg/v1/search'] );
+ $this->assertArrayHasKey( '/wp/v2/search', $routes );
+ $this->assertCount( 1, $routes['/wp/v2/search'] );
}
/**
@@ -291,7 +291,7 @@ public function test_get_items_search_for_foocontent() {
*/
public function test_get_item() {
/** The search controller does not allow getting individual item content */
- $request = new WP_REST_Request( 'GET', '/gutenberg/v1/search' . self::$my_title_post_ids[0] );
+ $request = new WP_REST_Request( 'GET', '/wp/v2/search' . self::$my_title_post_ids[0] );
$response = rest_get_server()->dispatch( $request );
$this->assertEquals( 404, $response->get_status() );
}
@@ -301,7 +301,7 @@ public function test_get_item() {
*/
public function test_create_item() {
/** The search controller does not allow creating content */
- $request = new WP_REST_Request( 'POST', '/gutenberg/v1/search' );
+ $request = new WP_REST_Request( 'POST', '/wp/v2/search' );
$response = rest_get_server()->dispatch( $request );
$this->assertEquals( 404, $response->get_status() );
}
@@ -311,7 +311,7 @@ public function test_create_item() {
*/
public function test_update_item() {
/** The search controller does not allow upading content */
- $request = new WP_REST_Request( 'POST', '/gutenberg/v1/search' . self::$my_title_post_ids[0] );
+ $request = new WP_REST_Request( 'POST', '/wp/v2/search' . self::$my_title_post_ids[0] );
$response = rest_get_server()->dispatch( $request );
$this->assertEquals( 404, $response->get_status() );
}
@@ -321,7 +321,7 @@ public function test_update_item() {
*/
public function test_delete_item() {
/** The search controller does not allow deleting content */
- $request = new WP_REST_Request( 'DELETE', '/gutenberg/v1/search' . self::$my_title_post_ids[0] );
+ $request = new WP_REST_Request( 'DELETE', '/wp/v2/search' . self::$my_title_post_ids[0] );
$response = rest_get_server()->dispatch( $request );
$this->assertEquals( 404, $response->get_status() );
}
@@ -377,7 +377,7 @@ public function test_prepare_item_limit_fields() {
* Tests the item schema is correct.
*/
public function test_get_item_schema() {
- $request = new WP_REST_Request( 'OPTIONS', '/gutenberg/v1/search' );
+ $request = new WP_REST_Request( 'OPTIONS', '/wp/v2/search' );
$response = rest_get_server()->dispatch( $request );
$data = $response->get_data();
$properties = $data['schema']['properties'];
@@ -510,7 +510,7 @@ private function do_request_with_params( $params = array(), $method = 'GET' ) {
* Get a REST request object for given parameters.
*/
private function get_request( $params = array(), $method = 'GET' ) {
- $request = new WP_REST_Request( $method, '/gutenberg/v1/search' );
+ $request = new WP_REST_Request( $method, '/wp/v2/search' );
foreach ( $params as $param => $value ) {
$request->set_param( $param, $value );
From a0e3ee5f31bc565cd36da0be6a911bc167d0d596 Mon Sep 17 00:00:00 2001
From: Joen Asmussen
Date: Thu, 18 Oct 2018 11:02:40 +0200
Subject: [PATCH 007/121] Polish tertiary actions, switches, save state
(#10552)
* Polish Save Draft/Switch to Draft area
* Use a Button for Trash button
* Polish the toggles.
---
packages/components/src/button/index.js | 2 +
packages/components/src/button/style.scss | 24 ++++++++++++
.../components/src/form-toggle/style.scss | 26 ++++++-------
.../components/src/toggle-control/style.scss | 10 ++++-
.../src/components/header/style.scss | 1 -
.../src/components/post-saved-state/index.js | 39 ++++++++++++++++---
.../components/post-saved-state/style.scss | 20 ++++++++--
.../test/__snapshots__/index.js.snap | 11 ++++++
.../components/post-saved-state/test/index.js | 6 ++-
.../post-switch-to-draft-button/index.js | 2 +-
.../editor/src/components/post-trash/index.js | 5 +--
.../src/components/post-trash/style.scss | 14 ++-----
12 files changed, 120 insertions(+), 40 deletions(-)
diff --git a/packages/components/src/button/index.js b/packages/components/src/button/index.js
index c04a9aa5276dd..3d6f9a2cb251c 100644
--- a/packages/components/src/button/index.js
+++ b/packages/components/src/button/index.js
@@ -15,6 +15,7 @@ export function Button( props, ref ) {
isPrimary,
isLarge,
isSmall,
+ isTertiary,
isToggled,
isBusy,
isDefault,
@@ -31,6 +32,7 @@ export function Button( props, ref ) {
'is-primary': isPrimary,
'is-large': isLarge,
'is-small': isSmall,
+ 'is-tertiary': isTertiary,
'is-toggled': isToggled,
'is-busy': isBusy,
'is-link': isLink,
diff --git a/packages/components/src/button/style.scss b/packages/components/src/button/style.scss
index 23be7378bf4a6..6ee83ef41edca 100644
--- a/packages/components/src/button/style.scss
+++ b/packages/components/src/button/style.scss
@@ -191,6 +191,30 @@
padding: 0 8px 1px;
font-size: 11px;
}
+
+ // Buttons that are text-based.
+ &.is-tertiary {
+ color: theme(outlines);
+
+ .dashicon {
+ display: inline-block;
+ flex: 0 0 auto;
+ }
+
+ // Ensure that even SVG icons that don't include the .dashicon class are colored.
+ svg {
+ fill: currentColor;
+ outline: none;
+ }
+
+ &:active:focus:enabled {
+ box-shadow: none;
+ }
+
+ &:not(:disabled):not([aria-disabled="true"]):not(.is-default):hover {
+ color: color(theme(outlines) shade(25%));
+ }
+ }
}
@keyframes components-button__busy-animation {
diff --git a/packages/components/src/form-toggle/style.scss b/packages/components/src/form-toggle/style.scss
index 4a92b0f67fe32..fe66e3455adf0 100644
--- a/packages/components/src/form-toggle/style.scss
+++ b/packages/components/src/form-toggle/style.scss
@@ -5,7 +5,7 @@ $toggle-border-width: 2px;
.components-form-toggle {
position: relative;
- // On/Off icon indicators
+ // On/Off icon indicators.
.components-form-toggle__on,
.components-form-toggle__off {
position: absolute;
@@ -19,10 +19,10 @@ $toggle-border-width: 2px;
}
.components-form-toggle__on {
- left: $toggle-border-width * 3 + 2px; // indent 2px extra because icon is thinner
+ left: $toggle-border-width * 3 + 2px; // Indent 2px extra because icon is thinner.
}
- // unchecked state
+ // Unchecked state.
.components-form-toggle__track {
content: "";
display: inline-block;
@@ -45,7 +45,7 @@ $toggle-border-width: 2px;
border-radius: 50%;
transition: 0.1s transform ease;
background-color: $dark-gray-300;
- border: 5px solid $dark-gray-300; // has explicit border to act as a fill in Windows High Contrast Mode
+ border: 5px solid $dark-gray-300; // Has explicit border to act as a fill in Windows High Contrast Mode.
}
&:hover {
@@ -55,7 +55,7 @@ $toggle-border-width: 2px;
.components-form-toggle__thumb {
background-color: $dark-gray-500;
- border: 5px solid $dark-gray-300; // has explicit border to act as a fill in Windows High Contrast Mode
+ border: 5px solid $dark-gray-300; // Has explicit border to act as a fill in Windows High Contrast Mode.
}
.components-form-toggle__off {
@@ -67,7 +67,7 @@ $toggle-border-width: 2px;
&.is-checked .components-form-toggle__track {
background-color: theme(toggle);
border: $toggle-border-width solid theme(toggle);
- border: #{ $toggle-height / 2 } solid transparent; // expand the border to fake a solid in Windows High Contrast Mode
+ border: #{ $toggle-height / 2 } solid transparent; // Expand the border to fake a solid in Windows High Contrast Mode.
}
&__input:focus + .components-form-toggle__track {
@@ -77,7 +77,7 @@ $toggle-border-width: 2px;
&.is-checked {
.components-form-toggle__thumb {
background-color: $white;
- border-width: 0; // zero out the border color to make the thumb invisible in Windows High Contrast Mode
+ border-width: 0; // Zero out the border color to make the thumb invisible in Windows High Contrast Mode.
transform: translateX($toggle-width - ($toggle-border-width * 4) - ($toggle-height - ($toggle-border-width * 4)));
}
@@ -100,20 +100,20 @@ $toggle-border-width: 2px;
z-index: z-index(".components-form-toggle__input");
}
-// Ensure on indicator works in normal and Windows high contrast mode both
+// Ensure on indicator works in normal and Windows high contrast mode both.
.components-form-toggle .components-form-toggle__on {
- // outlines show up in windows high contrast mode
+ // Outlines show up in windows high contrast mode.
outline: $border-width solid transparent;
outline-offset: -1px;
- // this colors the indicator black, then inverts it for normal mode
+ // This colors the indicator black, then inverts it for normal mode.
border: $border-width solid $black;
- filter: invert(100%) contrast(500%); // this makes the icon white for normal mode, and it makes it dark blue in Windows High Contrast Mode
+ filter: invert(100%) contrast(500%); // This makes the icon white for normal mode, and it makes it dark blue in Windows High Contrast Mode.
}
@supports (-ms-high-contrast-adjust: auto) {
- // Edge stacks outlines on top of the SVG itself, and when showing them in high contrast mode it means they get inverted again
- // Therefore, show a different style for the on indicator only in Edge and IE11
+ // Edge stacks outlines on top of the SVG itself, and when showing them in high contrast mode it means they get inverted again.
+ // Therefore, show a different style for the on indicator only in Edge and IE11.
.components-form-toggle .components-form-toggle__on {
filter: none;
border: $border-width solid $white;
diff --git a/packages/components/src/toggle-control/style.scss b/packages/components/src/toggle-control/style.scss
index b425b7b571392..c9489553d6720 100644
--- a/packages/components/src/toggle-control/style.scss
+++ b/packages/components/src/toggle-control/style.scss
@@ -1,4 +1,12 @@
.components-toggle-control .components-base-control__field {
display: flex;
- justify-content: space-between;
+ margin-bottom: $grid-size-small * 3;
+
+ .components-base-control__label {
+ order: 1;
+ }
+
+ .components-form-toggle {
+ margin-right: $grid-size-large;
+ }
}
diff --git a/packages/edit-post/src/components/header/style.scss b/packages/edit-post/src/components/header/style.scss
index bf3b689b56c43..a1f659231b077 100644
--- a/packages/edit-post/src/components/header/style.scss
+++ b/packages/edit-post/src/components/header/style.scss
@@ -94,7 +94,6 @@
background: $dark-gray-500;
}
- &.editor-post-switch-to-draft,
&.editor-post-preview,
&.editor-post-publish-button,
&.editor-post-publish-panel__toggle {
diff --git a/packages/editor/src/components/post-saved-state/index.js b/packages/editor/src/components/post-saved-state/index.js
index dd822087b0581..21b047c5f1079 100644
--- a/packages/editor/src/components/post-saved-state/index.js
+++ b/packages/editor/src/components/post-saved-state/index.js
@@ -8,11 +8,12 @@ import { get } from 'lodash';
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
-import { Dashicon, IconButton } from '@wordpress/components';
+import { Dashicon, Button, IconButton } from '@wordpress/components';
import { Component } from '@wordpress/element';
import { withSelect, withDispatch } from '@wordpress/data';
import { displayShortcut } from '@wordpress/keycodes';
import { withSafeTimeout, compose } from '@wordpress/compose';
+import { withViewportMatch } from '@wordpress/viewport';
/**
* Internal dependencies
@@ -42,7 +43,19 @@ export class PostSavedState extends Component {
}
render() {
- const { post, isNew, isScheduled, isPublished, isDirty, isSaving, isSaveable, onSave, isAutosaving, isPending } = this.props;
+ const {
+ post,
+ isNew,
+ isScheduled,
+ isPublished,
+ isDirty,
+ isSaving,
+ isSaveable,
+ onSave,
+ isAutosaving,
+ isPending,
+ isLargeViewport,
+ } = this.props;
const { forceSavedMessage } = this.state;
const hasPublishAction = get( post, [ '_links', 'wp:action-publish' ], false );
if ( isSaving ) {
@@ -84,15 +97,28 @@ export class PostSavedState extends Component {
return null;
}
+ const label = isPending ? __( 'Save as Pending' ) : __( 'Save Draft' );
+ if ( ! isLargeViewport ) {
+ return (
+
+ );
+ }
+
return (
-
- { isPending ? __( 'Save as Pending' ) : __( 'Save Draft' ) }
-
+ { label }
+
);
}
}
@@ -126,4 +152,5 @@ export default compose( [
onSave: dispatch( 'core/editor' ).savePost,
} ) ),
withSafeTimeout,
+ withViewportMatch( { isLargeViewport: 'medium' } ),
] )( PostSavedState );
diff --git a/packages/editor/src/components/post-saved-state/style.scss b/packages/editor/src/components/post-saved-state/style.scss
index 6823668be51a1..6ac4bc3fca945 100644
--- a/packages/editor/src/components/post-saved-state/style.scss
+++ b/packages/editor/src/components/post-saved-state/style.scss
@@ -1,7 +1,7 @@
.editor-post-saved-state {
display: flex;
align-items: center;
- color: $dark-gray-500;
+ color: $light-gray-900; // Doesn't need to meet AA because button is disabled and it's supporting text.
overflow: hidden;
&.is-saving {
@@ -14,11 +14,18 @@
}
}
+.editor-post-saved-state {
+ width: $icon-button-size - 8px;
+
+ @include break-small() {
+ width: auto;
+ }
+}
+
.editor-post-saved-state,
.editor-post-save-draft {
white-space: nowrap;
padding: 8px 4px;
- width: $icon-button-size - 8px;
.dashicon {
margin-right: 8px;
@@ -26,7 +33,6 @@
@include break-small() {
padding: 8px;
- width: auto;
text-indent: inherit;
.dashicon {
@@ -34,3 +40,11 @@
}
}
}
+
+.editor-post-save-draft {
+ @include break-small() {
+ .dashicon {
+ display: none;
+ }
+ }
+}
diff --git a/packages/editor/src/components/post-saved-state/test/__snapshots__/index.js.snap b/packages/editor/src/components/post-saved-state/test/__snapshots__/index.js.snap
index afcc69e633bdc..a36dbb35124ef 100644
--- a/packages/editor/src/components/post-saved-state/test/__snapshots__/index.js.snap
+++ b/packages/editor/src/components/post-saved-state/test/__snapshots__/index.js.snap
@@ -1,3 +1,14 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`PostSavedState returns a switch to draft link if the post is published 1`] = ` `;
+
+exports[`PostSavedState should return Save button if edits to be saved 1`] = `
+<[object Object]
+ className="editor-post-save-draft"
+ isTertiary={true}
+ onClick={[MockFunction]}
+ shortcut="Ctrl+S"
+>
+ Save Draft
+[object Object]>
+`;
diff --git a/packages/editor/src/components/post-saved-state/test/index.js b/packages/editor/src/components/post-saved-state/test/index.js
index 2e49f2db8c061..25e60154997a2 100644
--- a/packages/editor/src/components/post-saved-state/test/index.js
+++ b/packages/editor/src/components/post-saved-state/test/index.js
@@ -60,10 +60,12 @@ describe( 'PostSavedState', () => {
isDirty={ true }
isSaving={ false }
isSaveable={ true }
- onSave={ saveSpy } />
+ isLargeViewport
+ onSave={ saveSpy }
+ />
);
- expect( wrapper.name() ).toBe( 'IconButton' );
+ expect( wrapper ).toMatchSnapshot();
wrapper.simulate( 'click' );
expect( saveSpy ).toHaveBeenCalled();
} );
diff --git a/packages/editor/src/components/post-switch-to-draft-button/index.js b/packages/editor/src/components/post-switch-to-draft-button/index.js
index 93895cb831f54..155f5711cf5e8 100644
--- a/packages/editor/src/components/post-switch-to-draft-button/index.js
+++ b/packages/editor/src/components/post-switch-to-draft-button/index.js
@@ -27,9 +27,9 @@ function PostSwitchToDraftButton( { isSaving, isPublished, isScheduled, onClick
return (
{ __( 'Switch to Draft' ) }
diff --git a/packages/editor/src/components/post-trash/index.js b/packages/editor/src/components/post-trash/index.js
index 70567566bd3f6..119afd59d3a0d 100644
--- a/packages/editor/src/components/post-trash/index.js
+++ b/packages/editor/src/components/post-trash/index.js
@@ -2,7 +2,7 @@
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
-import { Button, Dashicon } from '@wordpress/components';
+import { Button } from '@wordpress/components';
import { withSelect, withDispatch } from '@wordpress/data';
import { compose } from '@wordpress/compose';
@@ -14,9 +14,8 @@ function PostTrash( { isNew, postId, postType, ...props } ) {
const onClick = () => props.trashPost( postId, postType );
return (
-
+
{ __( 'Move to trash' ) }
-
);
}
diff --git a/packages/editor/src/components/post-trash/style.scss b/packages/editor/src/components/post-trash/style.scss
index da058d224bcdb..2e0dfed15d7d3 100644
--- a/packages/editor/src/components/post-trash/style.scss
+++ b/packages/editor/src/components/post-trash/style.scss
@@ -1,16 +1,10 @@
.editor-post-trash.components-button {
- margin-left: auto !important;
- text-decoration: underline;
- color: #a00;
-
- .dashicon {
- display: inline-block;
- vertical-align: middle;
- margin: -3px -4px 0 10px;
- }
+ width: 100%;
+ color: darken($alert-red, 10%);
+ justify-content: center;
&:hover,
&:focus {
- color: #dc3232;
+ color: darken($alert-red, 15%);
}
}
From 73ce8fd222e9655ce52406827e98697208c3cbbd Mon Sep 17 00:00:00 2001
From: Jorge
Date: Thu, 18 Oct 2018 10:12:15 +0100
Subject: [PATCH 008/121] Fix: Font size default classes. (#10719)
---
packages/block-library/src/style.scss | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/packages/block-library/src/style.scss b/packages/block-library/src/style.scss
index 8ca793de2c055..fc84d096ccca3 100644
--- a/packages/block-library/src/style.scss
+++ b/packages/block-library/src/style.scss
@@ -113,7 +113,12 @@
font-size: 13px;
}
-.has-regular-font-size {
+.has-regular-font-size, // not used now, kept because of backward compatibility.
+.has-normal-font-size {
+ font-size: 16px;
+}
+
+.has-medium-font-size {
font-size: 20px;
}
@@ -121,6 +126,7 @@
font-size: 36px;
}
-.has-larger-font-size {
+.has-larger-font-size, // not used now, kept because of backward compatibility.
+.has-huge-font-size, {
font-size: 42px;
}
From 4ba27ff4d020d3fef4a2776e302f40a48bc826b4 Mon Sep 17 00:00:00 2001
From: Joen Asmussen
Date: Thu, 18 Oct 2018 14:21:14 +0200
Subject: [PATCH 009/121] Columns Block: Make responsive, make editor and theme
match (#10541)
* Add basic responsiveness.
* Refactor columns metrics. Level the playing field in editor and frontend.
* Add space between colums.
Fixes #7818.
Fixes #6048.
* Remove "beta" designation from columns block.
* Columns block: Fix column width when editing
* Column block: Improve padding for the first and last item in a row
* Tests: Rename Columns block name also in e2e tests
---
.../block-library/src/columns/editor.scss | 91 +++++++++++++++++--
packages/block-library/src/columns/index.js | 9 +-
packages/block-library/src/columns/style.scss | 42 +++++++++
.../specs/block-hierarchy-navigation.test.js | 6 +-
4 files changed, 128 insertions(+), 20 deletions(-)
diff --git a/packages/block-library/src/columns/editor.scss b/packages/block-library/src/columns/editor.scss
index 7eda4c25be534..ebe614483c4a5 100644
--- a/packages/block-library/src/columns/editor.scss
+++ b/packages/block-library/src/columns/editor.scss
@@ -6,13 +6,6 @@
margin-left: 0;
margin-right: 0;
- &:first-child {
- margin-left: -$block-padding;
- }
- &:last-child {
- margin-right: -$block-padding;
- }
-
// This max-width is used to constrain the main editor column, it should not cascade into columns
.editor-block-list__block {
max-width: none;
@@ -47,14 +40,92 @@
> .editor-inner-blocks > .editor-block-list__layout {
display: flex;
+ // Responsiveness: Allow wrapping on mobile.
+ flex-wrap: wrap;
+
+ @include break-medium() {
+ flex-wrap: nowrap;
+ }
+
> [data-type="core/column"] {
display: flex;
flex-direction: column;
flex: 1;
- width: 0;
- .editor-block-list__block-edit {
- flex-basis: 100%;
+ // The Column block is a child of the Columns block and is mostly a passthrough container.
+ // Therefore it shouldn't add additional paddings and margins, so we reset these, and compensate for margins.
+ // @todo In the future, if a passthrough feature lands, it would be good to apply these rules to such an element in a more generic way.
+ margin-top: -$block-padding;
+ margin-bottom: -$block-padding;
+
+ // On mobile, only a single column is shown, so match adjacent block paddings.
+ padding-left: 0;
+ padding-right: 0;
+ margin-left: -$block-padding;
+ margin-right: -$block-padding;
+ @include break-small () {
+ padding-left: $block-padding;
+ padding-right: $block-padding;
+ margin-right: inherit;
+ // Every .editor-block-list__block has auto-left/right margins, which centers columns.
+ // Since they aren't centered on the front-end, we explicitly set a zero left margin here.
+ margin-left: 0;
+ }
+
+ @include break-small() {
+ > .editor-block-contextual-toolbar {
+ top: $block-toolbar-height - $border-width;
+ transform: translateY(-$block-toolbar-height - $border-width);
+ margin-left: -$block-padding - $block-padding - $border-width;
+ }
+
+ > .editor-block-list__block-edit::before {
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ }
+
+ > .editor-block-list__breadcrumb {
+ margin-right: -$block-padding - $border-width;
+ }
+ }
+
+ // Responsiveness: Show at most one columns on mobile.
+ flex-basis: 100%;
+
+ // Beyond mobile, allow 2 columns.
+ @include break-small() {
+ flex-basis: 50%;
+ flex-grow: 0;
+ }
+
+ // Add space between columns. Themes can customize this if they wish to work differently.
+ // This has to match the same padding applied in style.scss.
+ // Only apply this beyond the mobile breakpoint, as there's only a single column on mobile.
+ @include break-small() {
+ > .editor-block-list__block-edit {
+ padding-left: $grid-size-large;
+ padding-right: $grid-size-large;
+ }
+
+ &:nth-child(odd) > .editor-block-list__block-edit {
+ padding-left: 0;
+ }
+
+ &:nth-child(even) > .editor-block-list__block-edit {
+ padding-right: 0;
+ }
+ }
+
+ @include break-medium() {
+ &:not(:first-child) > .editor-block-list__block-edit {
+ padding-left: $grid-size-large;
+ }
+
+ &:not(:last-child) > .editor-block-list__block-edit {
+ padding-right: $grid-size-large;
+ }
}
}
}
diff --git a/packages/block-library/src/columns/index.js b/packages/block-library/src/columns/index.js
index 5474cfae1e07a..bf299fc3b5d90 100644
--- a/packages/block-library/src/columns/index.js
+++ b/packages/block-library/src/columns/index.js
@@ -8,7 +8,7 @@ import memoize from 'memize';
/**
* WordPress dependencies
*/
-import { __, sprintf } from '@wordpress/i18n';
+import { __ } from '@wordpress/i18n';
import { PanelBody, RangeControl } from '@wordpress/components';
import { Fragment } from '@wordpress/element';
import { createBlock } from '@wordpress/blocks';
@@ -42,12 +42,7 @@ const getColumnsTemplate = memoize( ( columns ) => {
export const name = 'core/columns';
export const settings = {
- title: sprintf(
- /* translators: Block title modifier */
- __( '%1$s (%2$s)' ),
- __( 'Columns' ),
- __( 'beta' )
- ),
+ title: __( 'Columns' ),
icon: ,
diff --git a/packages/block-library/src/columns/style.scss b/packages/block-library/src/columns/style.scss
index 9e234b17202da..6a7aed4971d8b 100644
--- a/packages/block-library/src/columns/style.scss
+++ b/packages/block-library/src/columns/style.scss
@@ -1,7 +1,49 @@
.wp-block-columns {
display: flex;
+
+ // Responsiveness: Allow wrapping on mobile.
+ flex-wrap: wrap;
+
+ @include break-medium() {
+ flex-wrap: nowrap;
+ }
}
.wp-block-column {
flex: 1;
+ margin-bottom: 1em;
+
+ // Responsiveness: Show at most one columns on mobile.
+ flex-basis: 100%;
+
+ // Beyond mobile, allow 2 columns.
+ @include break-small() {
+ flex-basis: 50%;
+ flex-grow: 0;
+ }
+
+ // Add space between columns. Themes can customize this if they wish to work differently.
+ // Only apply this beyond the mobile breakpoint, as there's only a single column on mobile.
+ @include break-small() {
+ padding-left: $grid-size-large;
+ padding-right: $grid-size-large;
+
+ &:nth-child(odd) {
+ padding-left: 0;
+ }
+
+ &:nth-child(even) {
+ padding-right: 0;
+ }
+ }
+
+ @include break-medium() {
+ &:not(:first-child) {
+ padding-left: $grid-size-large;
+ }
+
+ &:not(:last-child) {
+ padding-right: $grid-size-large;
+ }
+ }
}
diff --git a/test/e2e/specs/block-hierarchy-navigation.test.js b/test/e2e/specs/block-hierarchy-navigation.test.js
index adabb4349353c..4fa9d59f7c856 100644
--- a/test/e2e/specs/block-hierarchy-navigation.test.js
+++ b/test/e2e/specs/block-hierarchy-navigation.test.js
@@ -21,14 +21,14 @@ describe( 'Navigating the block hierarchy', () => {
} );
it( 'should navigate using the block hierarchy dropdown menu', async () => {
- await insertBlock( 'Columns (beta)' );
+ await insertBlock( 'Columns' );
// Add a paragraph in the first column.
await page.keyboard.type( 'First column' );
// Navigate to the columns blocks.
await page.click( '[aria-label="Block Navigation"]' );
- const columnsBlockMenuItem = ( await page.$x( "//button[contains(@class,'editor-block-navigation__item') and contains(text(), 'Columns (beta)')]" ) )[ 0 ];
+ const columnsBlockMenuItem = ( await page.$x( "//button[contains(@class,'editor-block-navigation__item') and contains(text(), 'Columns')]" ) )[ 0 ];
await columnsBlockMenuItem.click();
// Tweak the columns count.
@@ -53,7 +53,7 @@ describe( 'Navigating the block hierarchy', () => {
} );
it( 'should navigate block hierarchy using only the keyboard', async () => {
- await insertBlock( 'Columns (beta)' );
+ await insertBlock( 'Columns' );
// Add a paragraph in the first column.
await page.keyboard.type( 'First column' );
From 7316f165f0582d62c2d10a6241187c915c2c9dfd Mon Sep 17 00:00:00 2001
From: Riad Benguella
Date: Thu, 18 Oct 2018 13:55:34 +0100
Subject: [PATCH 010/121] Remove 4.1 deprecated features (#10732)
---
docs/data/data-core-editor.md | 4 ----
packages/editor/CHANGELOG.md | 6 +++++-
packages/editor/src/store/actions.js | 20 --------------------
packages/editor/src/store/effects.js | 13 -------------
4 files changed, 5 insertions(+), 38 deletions(-)
diff --git a/docs/data/data-core-editor.md b/docs/data/data-core-editor.md
index fc156bf39b237..fd3e2fb5e376d 100644
--- a/docs/data/data-core-editor.md
+++ b/docs/data/data-core-editor.md
@@ -1545,10 +1545,6 @@ Returns an action object resetting the template validity.
* isValid: template validity flag.
-### checkTemplateValidity
-
-Returns an action object to check the template validity.
-
### synchronizeTemplate
Returns an action object synchronize the template with the list of blocks
diff --git a/packages/editor/CHANGELOG.md b/packages/editor/CHANGELOG.md
index d82d8b209de0a..239f2262b15ff 100644
--- a/packages/editor/CHANGELOG.md
+++ b/packages/editor/CHANGELOG.md
@@ -1,4 +1,8 @@
-## 4.1.0 (Unreleased)
+## 5.0.0 (Unreleased)
+
+### Breaking Changes
+
+- The `checkTemplateValidity` action has been removed. Validity is verified automatically upon block reset.
### Deprecations
diff --git a/packages/editor/src/store/actions.js b/packages/editor/src/store/actions.js
index f8eb4cdb36ec8..2c36c35871b56 100644
--- a/packages/editor/src/store/actions.js
+++ b/packages/editor/src/store/actions.js
@@ -11,7 +11,6 @@ import {
getDefaultBlockName,
createBlock,
} from '@wordpress/blocks';
-import deprecated from '@wordpress/deprecated';
/**
* Returns an action object used in signalling that editor has initialized with
@@ -368,25 +367,6 @@ export function setTemplateValidity( isValid ) {
};
}
-/**
- * Returns an action object to check the template validity.
- *
- * @return {Object} Action object.
- */
-export function checkTemplateValidity() {
- // TODO: Hello future deprecation remover. Please ensure also to remove all
- // references to CHECK_TEMPLATE_VALIDITY, notably its effect handler.
- deprecated( 'checkTemplateValidity action (`core/editor`)', {
- version: '4.1',
- plugin: 'Gutenberg',
- hint: 'Validity is verified automatically upon block reset.',
- } );
-
- return {
- type: 'CHECK_TEMPLATE_VALIDITY',
- };
-}
-
/**
* Returns an action object synchronize the template with the list of blocks
*
diff --git a/packages/editor/src/store/effects.js b/packages/editor/src/store/effects.js
index cfda318c49599..78bb1c2538db2 100644
--- a/packages/editor/src/store/effects.js
+++ b/packages/editor/src/store/effects.js
@@ -261,19 +261,6 @@ export default {
return resetBlocks( updatedBlockList );
},
- CHECK_TEMPLATE_VALIDITY( action, { getState } ) {
- const state = getState();
- const blocks = getBlocks( state );
- const template = getTemplate( state );
- const templateLock = getTemplateLock( state );
- const isValid = (
- ! template ||
- templateLock !== 'all' ||
- doBlocksMatchTemplate( blocks, template )
- );
-
- return setTemplateValidity( isValid );
- },
FETCH_REUSABLE_BLOCKS: ( action, store ) => {
fetchReusableBlocks( action, store );
},
From c3501c7bb70093495d9977cf37c3ef71112e9a38 Mon Sep 17 00:00:00 2001
From: Jorge
Date: Thu, 18 Oct 2018 15:07:50 +0100
Subject: [PATCH 011/121] Rename cover image to cover; Add video in cover
block; (#10659)
## Description
This commit renames cover image block into cover and adds video support o cover.
The approach used to rename the block is the same used for the move of core/text to core/paragraph.
A change in api/parser.js. This approach may be seen as a temporary solution until a final approach to this use case exists, and we feel comfortable in exposing an API for this.
Transformations were updated, and a video transform was added.
## How has this been tested?
Verify the cover block still works as expected, containing the same functionality the existed in the cover image.
Verify it is possible to set video as background in the cover block.
---
assets/stylesheets/_z-index.scss | 2 +
.../src/{cover-image => cover}/editor.scss | 3 +-
.../src/{cover-image => cover}/index.js | 193 +++++++++++++++---
.../src/{cover-image => cover}/style.scss | 24 ++-
.../test/__snapshots__/index.js.snap | 10 +-
.../src/{cover-image => cover}/test/index.js | 2 +-
packages/block-library/src/editor.scss | 2 +-
packages/block-library/src/index.js | 4 +-
packages/block-library/src/style.scss | 2 +-
packages/blocks/src/api/parser.js | 5 +
post-content.php | 6 +-
.../fixtures/core__cover-image.html | 5 -
.../fixtures/core__cover-image.json | 16 --
.../fixtures/core__cover-image.parsed.json | 17 --
.../core__cover-image.serialized.html | 3 -
.../full-content/fixtures/core__cover.html | 5 +
.../full-content/fixtures/core__cover.json | 17 ++
.../fixtures/core__cover.parsed.json | 17 ++
.../fixtures/core__cover.serialized.html | 3 +
.../fixtures/core__cover__video-overlay.html | 6 +
.../fixtures/core__cover__video-overlay.json | 18 ++
.../core__cover__video-overlay.parsed.json | 19 ++
...core__cover__video-overlay.serialized.html | 3 +
.../fixtures/core__cover__video.html | 6 +
.../fixtures/core__cover__video.json | 17 ++
.../fixtures/core__cover__video.parsed.json | 18 ++
.../core__cover__video.serialized.html | 3 +
27 files changed, 341 insertions(+), 85 deletions(-)
rename packages/block-library/src/{cover-image => cover}/editor.scss (93%)
rename packages/block-library/src/{cover-image => cover}/index.js (61%)
rename packages/block-library/src/{cover-image => cover}/style.scss (71%)
rename packages/block-library/src/{cover-image => cover}/test/__snapshots__/index.js.snap (92%)
rename packages/block-library/src/{cover-image => cover}/test/index.js (87%)
delete mode 100644 test/integration/full-content/fixtures/core__cover-image.html
delete mode 100644 test/integration/full-content/fixtures/core__cover-image.json
delete mode 100644 test/integration/full-content/fixtures/core__cover-image.parsed.json
delete mode 100644 test/integration/full-content/fixtures/core__cover-image.serialized.html
create mode 100644 test/integration/full-content/fixtures/core__cover.html
create mode 100644 test/integration/full-content/fixtures/core__cover.json
create mode 100644 test/integration/full-content/fixtures/core__cover.parsed.json
create mode 100644 test/integration/full-content/fixtures/core__cover.serialized.html
create mode 100644 test/integration/full-content/fixtures/core__cover__video-overlay.html
create mode 100644 test/integration/full-content/fixtures/core__cover__video-overlay.json
create mode 100644 test/integration/full-content/fixtures/core__cover__video-overlay.parsed.json
create mode 100644 test/integration/full-content/fixtures/core__cover__video-overlay.serialized.html
create mode 100644 test/integration/full-content/fixtures/core__cover__video.html
create mode 100644 test/integration/full-content/fixtures/core__cover__video.json
create mode 100644 test/integration/full-content/fixtures/core__cover__video.parsed.json
create mode 100644 test/integration/full-content/fixtures/core__cover__video.serialized.html
diff --git a/assets/stylesheets/_z-index.scss b/assets/stylesheets/_z-index.scss
index b9cd8c4032e0a..bcb054a4ea41b 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.has-background-dim::before": 1, // Overlay area inside block cover need to be higher than the video background.
+ ".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/editor.scss b/packages/block-library/src/cover/editor.scss
similarity index 93%
rename from packages/block-library/src/cover-image/editor.scss
rename to packages/block-library/src/cover/editor.scss
index bc55badc130be..e69a8080342b1 100644
--- a/packages/block-library/src/cover-image/editor.scss
+++ b/packages/block-library/src/cover/editor.scss
@@ -1,4 +1,5 @@
-.wp-block-cover-image {
+.wp-block-cover-image,
+.wp-block-cover {
.editor-rich-text__tinymce[data-is-empty="true"]::before {
position: inherit;
}
diff --git a/packages/block-library/src/cover-image/index.js b/packages/block-library/src/cover/index.js
similarity index 61%
rename from packages/block-library/src/cover-image/index.js
rename to packages/block-library/src/cover/index.js
index 2d06ec2112283..9d9ef6f0f1c5e 100644
--- a/packages/block-library/src/cover-image/index.js
+++ b/packages/block-library/src/cover/index.js
@@ -58,16 +58,22 @@ const blockAttributes = {
customOverlayColor: {
type: 'string',
},
+ backgroundType: {
+ type: 'string',
+ default: 'image',
+ },
};
-export const name = 'core/cover-image';
+export const name = 'core/cover';
-const ALLOWED_MEDIA_TYPES = [ 'image' ];
+const ALLOWED_MEDIA_TYPES = [ 'image', 'video' ];
+const IMAGE_BACKGROUND_TYPE = 'image';
+const VIDEO_BACKGROUND_TYPE = 'video';
export const settings = {
- title: __( 'Cover Image' ),
+ title: __( 'Cover' ),
- description: __( 'Add a full-width image, and layer text over it — great for headers.' ),
+ description: __( 'Add a full-width image or video, and layer text over it — great for headers.' ),
icon: ,
@@ -81,14 +87,14 @@ export const settings = {
type: 'block',
blocks: [ 'core/heading' ],
transform: ( { content } ) => (
- createBlock( 'core/cover-image', { title: content } )
+ createBlock( 'core/cover', { title: content } )
),
},
{
type: 'block',
blocks: [ 'core/image' ],
transform: ( { caption, url, align, id } ) => (
- createBlock( 'core/cover-image', {
+ createBlock( 'core/cover', {
title: caption,
url,
align,
@@ -96,6 +102,19 @@ export const settings = {
} )
),
},
+ {
+ type: 'block',
+ blocks: [ 'core/video' ],
+ transform: ( { caption, src, align, id } ) => (
+ createBlock( 'core/cover', {
+ title: caption,
+ url: src,
+ align,
+ id,
+ backgroundType: VIDEO_BACKGROUND_TYPE,
+ } )
+ ),
+ },
],
to: [
{
@@ -108,6 +127,9 @@ export const settings = {
{
type: 'block',
blocks: [ 'core/image' ],
+ isMatch: ( { backgroundType, url } ) => {
+ return ! url || backgroundType === IMAGE_BACKGROUND_TYPE;
+ },
transform: ( { title, url, align, id } ) => (
createBlock( 'core/image', {
caption: title,
@@ -117,6 +139,21 @@ export const settings = {
} )
),
},
+ {
+ type: 'block',
+ blocks: [ 'core/video' ],
+ isMatch: ( { backgroundType, url } ) => {
+ return ! url || backgroundType === VIDEO_BACKGROUND_TYPE;
+ },
+ transform: ( { title, url, align, id } ) => (
+ createBlock( 'core/video', {
+ caption: title,
+ src: url,
+ id,
+ align,
+ } )
+ ),
+ },
],
},
@@ -132,21 +169,57 @@ export const settings = {
withNotices,
] )(
( { attributes, setAttributes, isSelected, className, noticeOperations, noticeUI, overlayColor, setOverlayColor } ) => {
- const { url, title, align, contentAlign, id, hasParallax, dimRatio } = attributes;
+ const {
+ align,
+ backgroundType,
+ contentAlign,
+ dimRatio,
+ hasParallax,
+ id,
+ title,
+ url,
+ } = attributes;
const updateAlignment = ( nextAlign ) => setAttributes( { align: nextAlign } );
- const onSelectImage = ( media ) => {
+ const onSelectMedia = ( media ) => {
if ( ! media || ! media.url ) {
setAttributes( { url: undefined, id: undefined } );
return;
}
- setAttributes( { url: media.url, id: media.id } );
+ 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.
+ // Videos 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.
+ if (
+ media.type !== IMAGE_BACKGROUND_TYPE &&
+ media.type !== VIDEO_BACKGROUND_TYPE
+ ) {
+ return;
+ }
+ mediaType = media.type;
+ }
+ setAttributes( {
+ url: media.url,
+ id: media.id,
+ backgroundType: mediaType,
+ } );
};
const toggleParallax = () => setAttributes( { hasParallax: ! hasParallax } );
const setDimRatio = ( ratio ) => setAttributes( { dimRatio: ratio } );
const setTitle = ( newTitle ) => setAttributes( { title: newTitle } );
const style = {
- ...backgroundImageStyles( url ),
+ ...(
+ backgroundType === IMAGE_BACKGROUND_TYPE ?
+ backgroundImageStyles( url ) :
+ {}
+ ),
backgroundColor: overlayColor.color,
};
@@ -177,13 +250,13 @@ export const settings = {
/>
(
@@ -195,12 +268,14 @@ export const settings = {
{ !! url && (
-
-
+
+ { IMAGE_BACKGROUND_TYPE === backgroundType && (
+
+ ) }
- ) : __( 'Cover Image' );
+ ) : __( 'Cover' );
return (
@@ -245,10 +320,11 @@ export const settings = {
className={ className }
labels={ {
title: label,
- name: __( 'an image' ),
+ /* translators: Fragment of the sentence: "Drag %s, upload a new one or select a file from your library." */
+ name: __( 'an image or a video' ),
} }
- onSelect={ onSelectImage }
- accept="image/*"
+ onSelect={ onSelectMedia }
+ accept="image/*,video/*"
allowedTypes={ ALLOWED_MEDIA_TYPES }
notices={ noticeUI }
onError={ noticeOperations.createErrorNotice }
@@ -265,10 +341,19 @@ export const settings = {
style={ style }
className={ classes }
>
+ { VIDEO_BACKGROUND_TYPE === backgroundType && (
+
+ ) }
{ ( ! RichText.isEmpty( title ) || isSelected ) && (
+ { VIDEO_BACKGROUND_TYPE === backgroundType && url && ( ) }
{ ! RichText.isEmpty( title ) && (
-
+
) }
);
},
deprecated: [ {
+ attributes: {
+ ...blockAttributes,
+ },
+
+ supports: {
+ className: false,
+ },
+
+ save( { attributes } ) {
+ const { url, title, hasParallax, dimRatio, align, contentAlign, overlayColor, customOverlayColor } = attributes;
+ const overlayColorClass = getColorClassName( 'background-color', overlayColor );
+ const style = backgroundImageStyles( url );
+ if ( ! overlayColorClass ) {
+ style.backgroundColor = customOverlayColor;
+ }
+
+ const classes = classnames(
+ 'wp-block-cover-image',
+ dimRatioToClass( dimRatio ),
+ overlayColorClass,
+ {
+ 'has-background-dim': dimRatio !== 0,
+ 'has-parallax': hasParallax,
+ [ `has-${ contentAlign }-content` ]: contentAlign !== 'center',
+ },
+ align ? `align${ align }` : null,
+ );
+
+ return (
+
+ { ! RichText.isEmpty( title ) && (
+
+ ) }
+
+ );
+ },
+ }, {
attributes: {
...blockAttributes,
title: {
diff --git a/packages/block-library/src/cover-image/style.scss b/packages/block-library/src/cover/style.scss
similarity index 71%
rename from packages/block-library/src/cover-image/style.scss
rename to packages/block-library/src/cover/style.scss
index 723b35fdc52c7..00a3ab812ed25 100644
--- a/packages/block-library/src/cover-image/style.scss
+++ b/packages/block-library/src/cover/style.scss
@@ -1,4 +1,5 @@
-.wp-block-cover-image {
+.wp-block-cover-image,
+.wp-block-cover {
position: relative;
background-color: $black;
background-size: cover;
@@ -14,7 +15,8 @@
justify-content: flex-start;
h2,
- .wp-block-cover-image-text {
+ .wp-block-cover-image-text,
+ .wp-block-cover-text {
margin-left: 0;
text-align: left;
}
@@ -24,14 +26,16 @@
justify-content: flex-end;
h2,
- .wp-block-cover-image-text {
+ .wp-block-cover-image-text,
+ .wp-block-cover-text {
margin-right: 0;
text-align: right;
}
}
h2,
- .wp-block-cover-image-text {
+ .wp-block-cover-image-text,
+ .wp-block-cover-text {
color: $white;
font-size: 2em;
line-height: 1.25;
@@ -62,6 +66,7 @@
right: 0;
background-color: inherit;
opacity: 0.5;
+ z-index: z-index(".wp-block-cover.has-background-dim::before");
}
@for $i from 1 through 10 {
@@ -83,3 +88,14 @@
width: 100%;
}
}
+
+.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-image/test/__snapshots__/index.js.snap b/packages/block-library/src/cover/test/__snapshots__/index.js.snap
similarity index 92%
rename from packages/block-library/src/cover-image/test/__snapshots__/index.js.snap
rename to packages/block-library/src/cover/test/__snapshots__/index.js.snap
index f6690fdae6fe9..d06c221b6e4ea 100644
--- a/packages/block-library/src/cover-image/test/__snapshots__/index.js.snap
+++ b/packages/block-library/src/cover/test/__snapshots__/index.js.snap
@@ -1,8 +1,8 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`core/cover-image block edit matches snapshot 1`] = `
+exports[`core/cover block edit matches snapshot 1`] = `
- Drag an image, upload a new one or select a file from your library.
+ Drag an image or a video, upload a new one or select a file from your library.
diff --git a/packages/block-library/src/cover-image/test/index.js b/packages/block-library/src/cover/test/index.js
similarity index 87%
rename from packages/block-library/src/cover-image/test/index.js
rename to packages/block-library/src/cover/test/index.js
index ceb0bc9d5123c..a088404e7c41d 100644
--- a/packages/block-library/src/cover-image/test/index.js
+++ b/packages/block-library/src/cover/test/index.js
@@ -4,7 +4,7 @@
import { name, settings } from '../';
import { blockEditRender } from '../../test/helpers';
-describe( 'core/cover-image', () => {
+describe( 'core/cover', () => {
test( 'block edit matches snapshot', () => {
const wrapper = blockEditRender( name, settings );
diff --git a/packages/block-library/src/editor.scss b/packages/block-library/src/editor.scss
index 70aa75c35b7f0..58c8803144a31 100644
--- a/packages/block-library/src/editor.scss
+++ b/packages/block-library/src/editor.scss
@@ -4,7 +4,7 @@
@import "./categories/editor.scss";
@import "./code/editor.scss";
@import "./columns/editor.scss";
-@import "./cover-image/editor.scss";
+@import "./cover/editor.scss";
@import "./embed/editor.scss";
@import "./file/editor.scss";
@import "./classic/editor.scss";
diff --git a/packages/block-library/src/index.js b/packages/block-library/src/index.js
index 470ad4aea9feb..01644d7bd20d6 100644
--- a/packages/block-library/src/index.js
+++ b/packages/block-library/src/index.js
@@ -24,7 +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 coverImage from './cover-image';
+import * as cover from './cover';
import * as embed from './embed';
import * as file from './file';
import * as html from './html';
@@ -69,7 +69,7 @@ export const registerCoreBlocks = () => {
code,
columns,
column,
- coverImage,
+ cover,
embed,
...embed.common,
...embed.others,
diff --git a/packages/block-library/src/style.scss b/packages/block-library/src/style.scss
index fc84d096ccca3..0d18fe6895497 100644
--- a/packages/block-library/src/style.scss
+++ b/packages/block-library/src/style.scss
@@ -4,7 +4,7 @@
@import "./button/style.scss";
@import "./categories/style.scss";
@import "./columns/style.scss";
-@import "./cover-image/style.scss";
+@import "./cover/style.scss";
@import "./embed/style.scss";
@import "./file/style.scss";
@import "./gallery/style.scss";
diff --git a/packages/blocks/src/api/parser.js b/packages/blocks/src/api/parser.js
index 496eb6ce81cb3..68cc6af5ca180 100644
--- a/packages/blocks/src/api/parser.js
+++ b/packages/blocks/src/api/parser.js
@@ -416,6 +416,11 @@ export function createBlockWithFallback( blockNode ) {
// freeform content fallback.
let name = originalName || freeformContentFallbackBlock;
+ // Convert 'core/cover-image' block in existing content to 'core/cover'.
+ if ( 'core/cover-image' === name ) {
+ name = 'core/cover';
+ }
+
// Convert 'core/text' blocks in existing content to 'core/paragraph'.
if ( 'core/text' === name || 'core/cover-text' === name ) {
name = 'core/paragraph';
diff --git a/post-content.php b/post-content.php
index 45ef6109a44d0..7dac5620a8efd 100644
--- a/post-content.php
+++ b/post-content.php
@@ -1,6 +1,6 @@
-
-
-
+
+
+
pieces of content—somewhat similar to LEGO bricks—that you can move around and interact with. Move your cursor around and you’ll notice the different blocks light up with outlines and arrows. Press the arrows to reposition blocks quickly, without fearing about losing things in the process of copying and pasting.', 'gutenberg' ); ?>
diff --git a/test/integration/full-content/fixtures/core__cover-image.html b/test/integration/full-content/fixtures/core__cover-image.html
deleted file mode 100644
index 86b5915701b0b..0000000000000
--- a/test/integration/full-content/fixtures/core__cover-image.html
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
diff --git a/test/integration/full-content/fixtures/core__cover-image.json b/test/integration/full-content/fixtures/core__cover-image.json
deleted file mode 100644
index 342a160d3e4de..0000000000000
--- a/test/integration/full-content/fixtures/core__cover-image.json
+++ /dev/null
@@ -1,16 +0,0 @@
-[
- {
- "clientId": "_clientId_0",
- "name": "core/cover-image",
- "isValid": true,
- "attributes": {
- "title": "Guten Berg!",
- "url": "https://cldup.com/uuUqE_dXzy.jpg",
- "contentAlign": "center",
- "hasParallax": false,
- "dimRatio": 40
- },
- "innerBlocks": [],
- "originalContent": "
"
- }
-]
diff --git a/test/integration/full-content/fixtures/core__cover-image.parsed.json b/test/integration/full-content/fixtures/core__cover-image.parsed.json
deleted file mode 100644
index 93bc4fbc8ddfd..0000000000000
--- a/test/integration/full-content/fixtures/core__cover-image.parsed.json
+++ /dev/null
@@ -1,17 +0,0 @@
-[
- {
- "blockName": "core/cover-image",
- "attrs": {
- "url": "https://cldup.com/uuUqE_dXzy.jpg",
- "dimRatio": 40
- },
- "innerBlocks": [],
- "innerHTML": "\n
\n"
- },
- {
- "blockName": null,
- "attrs": {},
- "innerBlocks": [],
- "innerHTML": "\n"
- }
-]
diff --git a/test/integration/full-content/fixtures/core__cover-image.serialized.html b/test/integration/full-content/fixtures/core__cover-image.serialized.html
deleted file mode 100644
index d64692d60c8fe..0000000000000
--- a/test/integration/full-content/fixtures/core__cover-image.serialized.html
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
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..ae26d922c2644
--- /dev/null
+++ b/test/integration/full-content/fixtures/core__cover.html
@@ -0,0 +1,5 @@
+
+
+
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..dea09d92c11c8
--- /dev/null
+++ b/test/integration/full-content/fixtures/core__cover.json
@@ -0,0 +1,17 @@
+[
+ {
+ "clientId": "_clientId_0",
+ "name": "core/cover",
+ "isValid": true,
+ "attributes": {
+ "title": "Guten Berg!",
+ "url": "https://cldup.com/uuUqE_dXzy.jpg",
+ "contentAlign": "center",
+ "hasParallax": false,
+ "dimRatio": 40,
+ "backgroundType": "image"
+ },
+ "innerBlocks": [],
+ "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..ba371b9c5960f
--- /dev/null
+++ b/test/integration/full-content/fixtures/core__cover.parsed.json
@@ -0,0 +1,17 @@
+[
+ {
+ "blockName": "core/cover",
+ "attrs": {
+ "url": "https://cldup.com/uuUqE_dXzy.jpg",
+ "dimRatio": 40
+ },
+ "innerBlocks": [],
+ "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..a6d795bb56175
--- /dev/null
+++ b/test/integration/full-content/fixtures/core__cover.serialized.html
@@ -0,0 +1,3 @@
+
+
+
diff --git a/test/integration/full-content/fixtures/core__cover__video-overlay.html b/test/integration/full-content/fixtures/core__cover__video-overlay.html
new file mode 100644
index 0000000000000..2a3530954f29b
--- /dev/null
+++ b/test/integration/full-content/fixtures/core__cover__video-overlay.html
@@ -0,0 +1,6 @@
+
+
+
diff --git a/test/integration/full-content/fixtures/core__cover__video-overlay.json b/test/integration/full-content/fixtures/core__cover__video-overlay.json
new file mode 100644
index 0000000000000..08f0de4eb4c15
--- /dev/null
+++ b/test/integration/full-content/fixtures/core__cover__video-overlay.json
@@ -0,0 +1,18 @@
+[
+ {
+ "clientId": "_clientId_0",
+ "name": "core/cover",
+ "isValid": true,
+ "attributes": {
+ "title": "Guten Berg!",
+ "url": "data:video/mp4;base64,AAAAHGZ0eXBpc29tAAACAGlzb21pc28ybXA0MQAAAAhmcmVlAAAC721kYXQhEAUgpBv/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA3pwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcCEQBSCkG//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADengAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcAAAAsJtb292AAAAbG12aGQAAAAAAAAAAAAAAAAAAAPoAAAALwABAAABAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAB7HRyYWsAAABcdGtoZAAAAAMAAAAAAAAAAAAAAAIAAAAAAAAALwAAAAAAAAAAAAAAAQEAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAACRlZHRzAAAAHGVsc3QAAAAAAAAAAQAAAC8AAAAAAAEAAAAAAWRtZGlhAAAAIG1kaGQAAAAAAAAAAAAAAAAAAKxEAAAIAFXEAAAAAAAtaGRscgAAAAAAAAAAc291bgAAAAAAAAAAAAAAAFNvdW5kSGFuZGxlcgAAAAEPbWluZgAAABBzbWhkAAAAAAAAAAAAAAAkZGluZgAAABxkcmVmAAAAAAAAAAEAAAAMdXJsIAAAAAEAAADTc3RibAAAAGdzdHNkAAAAAAAAAAEAAABXbXA0YQAAAAAAAAABAAAAAAAAAAAAAgAQAAAAAKxEAAAAAAAzZXNkcwAAAAADgICAIgACAASAgIAUQBUAAAAAAfQAAAHz+QWAgIACEhAGgICAAQIAAAAYc3R0cwAAAAAAAAABAAAAAgAABAAAAAAcc3RzYwAAAAAAAAABAAAAAQAAAAIAAAABAAAAHHN0c3oAAAAAAAAAAAAAAAIAAAFzAAABdAAAABRzdGNvAAAAAAAAAAEAAAAsAAAAYnVkdGEAAABabWV0YQAAAAAAAAAhaGRscgAAAAAAAAAAbWRpcmFwcGwAAAAAAAAAAAAAAAAtaWxzdAAAACWpdG9vAAAAHWRhdGEAAAABAAAAAExhdmY1Ni40MC4xMDE=",
+ "contentAlign": "center",
+ "hasParallax": false,
+ "dimRatio": 10,
+ "customOverlayColor": "#3615d9",
+ "backgroundType": "video"
+ },
+ "innerBlocks": [],
+ "originalContent": "
"
+ }
+]
diff --git a/test/integration/full-content/fixtures/core__cover__video-overlay.parsed.json b/test/integration/full-content/fixtures/core__cover__video-overlay.parsed.json
new file mode 100644
index 0000000000000..71dd6c0a0294a
--- /dev/null
+++ b/test/integration/full-content/fixtures/core__cover__video-overlay.parsed.json
@@ -0,0 +1,19 @@
+[
+ {
+ "blockName": "core/cover",
+ "attrs": {
+ "url": "data:video/mp4;base64,AAAAHGZ0eXBpc29tAAACAGlzb21pc28ybXA0MQAAAAhmcmVlAAAC721kYXQhEAUgpBv/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA3pwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcCEQBSCkG//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADengAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcAAAAsJtb292AAAAbG12aGQAAAAAAAAAAAAAAAAAAAPoAAAALwABAAABAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAB7HRyYWsAAABcdGtoZAAAAAMAAAAAAAAAAAAAAAIAAAAAAAAALwAAAAAAAAAAAAAAAQEAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAACRlZHRzAAAAHGVsc3QAAAAAAAAAAQAAAC8AAAAAAAEAAAAAAWRtZGlhAAAAIG1kaGQAAAAAAAAAAAAAAAAAAKxEAAAIAFXEAAAAAAAtaGRscgAAAAAAAAAAc291bgAAAAAAAAAAAAAAAFNvdW5kSGFuZGxlcgAAAAEPbWluZgAAABBzbWhkAAAAAAAAAAAAAAAkZGluZgAAABxkcmVmAAAAAAAAAAEAAAAMdXJsIAAAAAEAAADTc3RibAAAAGdzdHNkAAAAAAAAAAEAAABXbXA0YQAAAAAAAAABAAAAAAAAAAAAAgAQAAAAAKxEAAAAAAAzZXNkcwAAAAADgICAIgACAASAgIAUQBUAAAAAAfQAAAHz+QWAgIACEhAGgICAAQIAAAAYc3R0cwAAAAAAAAABAAAAAgAABAAAAAAcc3RzYwAAAAAAAAABAAAAAQAAAAIAAAABAAAAHHN0c3oAAAAAAAAAAAAAAAIAAAFzAAABdAAAABRzdGNvAAAAAAAAAAEAAAAsAAAAYnVkdGEAAABabWV0YQAAAAAAAAAhaGRscgAAAAAAAAAAbWRpcmFwcGwAAAAAAAAAAAAAAAAtaWxzdAAAACWpdG9vAAAAHWRhdGEAAAABAAAAAExhdmY1Ni40MC4xMDE=",
+ "dimRatio": 10,
+ "customOverlayColor": "#3615d9",
+ "backgroundType": "video"
+ },
+ "innerBlocks": [],
+ "innerHTML": "\n
\n"
+ },
+ {
+ "blockName": null,
+ "attrs": {},
+ "innerBlocks": [],
+ "innerHTML": "\n"
+ }
+]
diff --git a/test/integration/full-content/fixtures/core__cover__video-overlay.serialized.html b/test/integration/full-content/fixtures/core__cover__video-overlay.serialized.html
new file mode 100644
index 0000000000000..14786a0f0d189
--- /dev/null
+++ b/test/integration/full-content/fixtures/core__cover__video-overlay.serialized.html
@@ -0,0 +1,3 @@
+
+
+
diff --git a/test/integration/full-content/fixtures/core__cover__video.html b/test/integration/full-content/fixtures/core__cover__video.html
new file mode 100644
index 0000000000000..0ab2fd73ccdf8
--- /dev/null
+++ b/test/integration/full-content/fixtures/core__cover__video.html
@@ -0,0 +1,6 @@
+
+
+
diff --git a/test/integration/full-content/fixtures/core__cover__video.json b/test/integration/full-content/fixtures/core__cover__video.json
new file mode 100644
index 0000000000000..1fa4d25d66cda
--- /dev/null
+++ b/test/integration/full-content/fixtures/core__cover__video.json
@@ -0,0 +1,17 @@
+[
+ {
+ "clientId": "_clientId_0",
+ "name": "core/cover",
+ "isValid": true,
+ "attributes": {
+ "title": "Guten Berg!",
+ "url": "data:video/mp4;base64,AAAAHGZ0eXBpc29tAAACAGlzb21pc28ybXA0MQAAAAhmcmVlAAAC721kYXQhEAUgpBv/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA3pwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcCEQBSCkG//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADengAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcAAAAsJtb292AAAAbG12aGQAAAAAAAAAAAAAAAAAAAPoAAAALwABAAABAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAB7HRyYWsAAABcdGtoZAAAAAMAAAAAAAAAAAAAAAIAAAAAAAAALwAAAAAAAAAAAAAAAQEAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAACRlZHRzAAAAHGVsc3QAAAAAAAAAAQAAAC8AAAAAAAEAAAAAAWRtZGlhAAAAIG1kaGQAAAAAAAAAAAAAAAAAAKxEAAAIAFXEAAAAAAAtaGRscgAAAAAAAAAAc291bgAAAAAAAAAAAAAAAFNvdW5kSGFuZGxlcgAAAAEPbWluZgAAABBzbWhkAAAAAAAAAAAAAAAkZGluZgAAABxkcmVmAAAAAAAAAAEAAAAMdXJsIAAAAAEAAADTc3RibAAAAGdzdHNkAAAAAAAAAAEAAABXbXA0YQAAAAAAAAABAAAAAAAAAAAAAgAQAAAAAKxEAAAAAAAzZXNkcwAAAAADgICAIgACAASAgIAUQBUAAAAAAfQAAAHz+QWAgIACEhAGgICAAQIAAAAYc3R0cwAAAAAAAAABAAAAAgAABAAAAAAcc3RzYwAAAAAAAAABAAAAAQAAAAIAAAABAAAAHHN0c3oAAAAAAAAAAAAAAAIAAAFzAAABdAAAABRzdGNvAAAAAAAAAAEAAAAsAAAAYnVkdGEAAABabWV0YQAAAAAAAAAhaGRscgAAAAAAAAAAbWRpcmFwcGwAAAAAAAAAAAAAAAAtaWxzdAAAACWpdG9vAAAAHWRhdGEAAAABAAAAAExhdmY1Ni40MC4xMDE=",
+ "contentAlign": "center",
+ "hasParallax": false,
+ "dimRatio": 40,
+ "backgroundType": "video"
+ },
+ "innerBlocks": [],
+ "originalContent": "
"
+ }
+]
diff --git a/test/integration/full-content/fixtures/core__cover__video.parsed.json b/test/integration/full-content/fixtures/core__cover__video.parsed.json
new file mode 100644
index 0000000000000..218846a689b1f
--- /dev/null
+++ b/test/integration/full-content/fixtures/core__cover__video.parsed.json
@@ -0,0 +1,18 @@
+[
+ {
+ "blockName": "core/cover",
+ "attrs": {
+ "url": "data:video/mp4;base64,AAAAHGZ0eXBpc29tAAACAGlzb21pc28ybXA0MQAAAAhmcmVlAAAC721kYXQhEAUgpBv/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA3pwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcCEQBSCkG//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADengAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcAAAAsJtb292AAAAbG12aGQAAAAAAAAAAAAAAAAAAAPoAAAALwABAAABAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAB7HRyYWsAAABcdGtoZAAAAAMAAAAAAAAAAAAAAAIAAAAAAAAALwAAAAAAAAAAAAAAAQEAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAACRlZHRzAAAAHGVsc3QAAAAAAAAAAQAAAC8AAAAAAAEAAAAAAWRtZGlhAAAAIG1kaGQAAAAAAAAAAAAAAAAAAKxEAAAIAFXEAAAAAAAtaGRscgAAAAAAAAAAc291bgAAAAAAAAAAAAAAAFNvdW5kSGFuZGxlcgAAAAEPbWluZgAAABBzbWhkAAAAAAAAAAAAAAAkZGluZgAAABxkcmVmAAAAAAAAAAEAAAAMdXJsIAAAAAEAAADTc3RibAAAAGdzdHNkAAAAAAAAAAEAAABXbXA0YQAAAAAAAAABAAAAAAAAAAAAAgAQAAAAAKxEAAAAAAAzZXNkcwAAAAADgICAIgACAASAgIAUQBUAAAAAAfQAAAHz+QWAgIACEhAGgICAAQIAAAAYc3R0cwAAAAAAAAABAAAAAgAABAAAAAAcc3RzYwAAAAAAAAABAAAAAQAAAAIAAAABAAAAHHN0c3oAAAAAAAAAAAAAAAIAAAFzAAABdAAAABRzdGNvAAAAAAAAAAEAAAAsAAAAYnVkdGEAAABabWV0YQAAAAAAAAAhaGRscgAAAAAAAAAAbWRpcmFwcGwAAAAAAAAAAAAAAAAtaWxzdAAAACWpdG9vAAAAHWRhdGEAAAABAAAAAExhdmY1Ni40MC4xMDE=",
+ "dimRatio": 40,
+ "backgroundType": "video"
+ },
+ "innerBlocks": [],
+ "innerHTML": "\n
\n"
+ },
+ {
+ "blockName": null,
+ "attrs": {},
+ "innerBlocks": [],
+ "innerHTML": "\n"
+ }
+]
diff --git a/test/integration/full-content/fixtures/core__cover__video.serialized.html b/test/integration/full-content/fixtures/core__cover__video.serialized.html
new file mode 100644
index 0000000000000..03398311e20f6
--- /dev/null
+++ b/test/integration/full-content/fixtures/core__cover__video.serialized.html
@@ -0,0 +1,3 @@
+
+
+
From 1e823485d9dff26aac093058577daf2ed4702201 Mon Sep 17 00:00:00 2001
From: Danilo Ercoli
Date: Thu, 18 Oct 2018 17:10:48 +0200
Subject: [PATCH 012/121] Handle the case when the split occurs at the trailing
or leading edge of the field. (#10737)
---
.../src/components/rich-text/index.native.js | 17 +++++++++++++----
1 file changed, 13 insertions(+), 4 deletions(-)
diff --git a/packages/editor/src/components/rich-text/index.native.js b/packages/editor/src/components/rich-text/index.native.js
index 8de0a2655d1f0..48a0ec86b1634 100644
--- a/packages/editor/src/components/rich-text/index.native.js
+++ b/packages/editor/src/components/rich-text/index.native.js
@@ -15,6 +15,7 @@ import { Component, RawHTML } from '@wordpress/element';
import { withInstanceId, compose } from '@wordpress/compose';
import { Toolbar } from '@wordpress/components';
import {
+ isEmpty,
create,
split,
toHTMLString,
@@ -87,10 +88,18 @@ export class RichText extends Component {
} );
// TODO : Fix the index position in AztecNative for Android
- let [ before, after ] = split( { start: start - 6, end: end - 6, ...record } );
-
- // TODO : Handle here the cases when the split happens at the trailing or leading edge...
- // See the web version for reference.
+ let [ before, after ] = split( { start, end, ...record } );
+
+ // In case split occurs at the trailing or leading edge of the field,
+ // assume that the before/after values respectively reflect the current
+ // value. This also provides an opportunity for the parent component to
+ // determine whether the before/after value has changed using a trivial
+ // strict equality operation.
+ if ( isEmpty( after ) ) {
+ before = record;
+ } else if ( isEmpty( before ) ) {
+ after = record;
+ }
if ( before ) {
before = this.valueToFormat( before );
From f1d163a9bb457bc22c456dfafd4906eaada9a87c Mon Sep 17 00:00:00 2001
From: Andrew Duthie
Date: Thu, 18 Oct 2018 11:13:41 -0400
Subject: [PATCH 013/121] Writing Flow: Avoid horizontal writing flow
navigation in native inputs (#9978)
---
.../src/components/writing-flow/index.js | 30 +++++++++++
.../src/components/writing-flow/test/index.js | 51 +++++++++++++++++++
test/e2e/specs/writing-flow.test.js | 28 ++++++++++
3 files changed, 109 insertions(+)
create mode 100644 packages/editor/src/components/writing-flow/test/index.js
diff --git a/packages/editor/src/components/writing-flow/index.js b/packages/editor/src/components/writing-flow/index.js
index e13b81eaf6dfd..6f85e5ed1e8af 100644
--- a/packages/editor/src/components/writing-flow/index.js
+++ b/packages/editor/src/components/writing-flow/index.js
@@ -49,6 +49,29 @@ const isTabbableTextField = overEvery( [
focus.tabbable.isTabbableIndex,
] );
+/**
+ * Returns true if the element should consider edge navigation upon a keyboard
+ * event of the given directional key code, or false otherwise.
+ *
+ * @param {Element} element HTML element to test.
+ * @param {number} keyCode KeyboardEvent keyCode to test.
+ * @param {boolean} hasModifier Whether a modifier is pressed.
+ *
+ * @return {boolean} Whether element should consider edge navigation.
+ */
+export function isNavigationCandidate( element, keyCode, hasModifier ) {
+ const isVertical = ( keyCode === UP || keyCode === DOWN );
+
+ // Currently, all elements support unmodified vertical navigation.
+ if ( isVertical && ! hasModifier ) {
+ return true;
+ }
+
+ // Native inputs should not navigate horizontally.
+ const { tagName } = element;
+ return tagName !== 'INPUT' && tagName !== 'TEXTAREA';
+}
+
class WritingFlow extends Component {
constructor() {
super( ...arguments );
@@ -209,6 +232,7 @@ class WritingFlow extends Component {
const isVertical = isUp || isDown;
const isNav = isHorizontal || isVertical;
const isShift = event.shiftKey;
+ const hasModifier = isShift || event.ctrlKey || event.altKey || event.metaKey;
const isNavEdge = isVertical ? isVerticalEdge : isHorizontalEdge;
// This logic inside this condition needs to be checked before
@@ -243,6 +267,12 @@ class WritingFlow extends Component {
return;
}
+ // Abort if our current target is not a candidate for navigation (e.g.
+ // preserve native input behaviors).
+ if ( ! isNavigationCandidate( target, keyCode, hasModifier ) ) {
+ return;
+ }
+
if ( ! isVertical ) {
this.verticalRect = null;
} else if ( ! this.verticalRect ) {
diff --git a/packages/editor/src/components/writing-flow/test/index.js b/packages/editor/src/components/writing-flow/test/index.js
new file mode 100644
index 0000000000000..88ff30f107a72
--- /dev/null
+++ b/packages/editor/src/components/writing-flow/test/index.js
@@ -0,0 +1,51 @@
+/**
+ * WordPress dependencies
+ */
+import { UP, DOWN, LEFT, RIGHT } from '@wordpress/keycodes';
+
+/**
+ * Internal dependencies
+ */
+import { isNavigationCandidate } from '../';
+
+describe( 'isNavigationCandidate', () => {
+ let elements;
+ beforeAll( () => {
+ elements = {};
+ elements.input = document.createElement( 'input' );
+ elements.contentEditable = document.createElement( 'p' );
+ elements.contentEditable.contentEditable = true;
+ } );
+
+ it( 'should return true if vertically navigating input without modifier', () => {
+ [ UP, DOWN ].forEach( ( keyCode ) => {
+ const result = isNavigationCandidate( elements.input, keyCode, false );
+
+ expect( result ).toBe( true );
+ } );
+ } );
+
+ it( 'should return false if vertically navigating input with modifier', () => {
+ [ UP, DOWN ].forEach( ( keyCode ) => {
+ const result = isNavigationCandidate( elements.input, keyCode, true );
+
+ expect( result ).toBe( false );
+ } );
+ } );
+
+ it( 'should return false if horizontally navigating input', () => {
+ [ LEFT, RIGHT ].forEach( ( keyCode ) => {
+ const result = isNavigationCandidate( elements.input, keyCode, false );
+
+ expect( result ).toBe( false );
+ } );
+ } );
+
+ it( 'should return true if horizontally navigating non-input', () => {
+ [ LEFT, RIGHT ].forEach( ( keyCode ) => {
+ const result = isNavigationCandidate( elements.contentEditable, keyCode, false );
+
+ expect( result ).toBe( true );
+ } );
+ } );
+} );
diff --git a/test/e2e/specs/writing-flow.test.js b/test/e2e/specs/writing-flow.test.js
index 2d484b958b416..67c91280ba410 100644
--- a/test/e2e/specs/writing-flow.test.js
+++ b/test/e2e/specs/writing-flow.test.js
@@ -195,4 +195,32 @@ describe( 'adding blocks', () => {
await pressWithModifier( 'Shift', 'Enter' );
expect( await getEditedPostContent() ).toMatchSnapshot();
} );
+
+ it( 'should navigate native inputs vertically, not horizontally', async () => {
+ // See: https://github.com/WordPress/gutenberg/issues/9626
+
+ // Title is within the editor's writing flow, and is a
-
+
+
+ Insert from URL
+
+
`;
diff --git a/packages/block-library/src/cover/test/__snapshots__/index.js.snap b/packages/block-library/src/cover/test/__snapshots__/index.js.snap
index d06c221b6e4ea..0592141c6088c 100644
--- a/packages/block-library/src/cover/test/__snapshots__/index.js.snap
+++ b/packages/block-library/src/cover/test/__snapshots__/index.js.snap
@@ -62,7 +62,7 @@ exports[`core/cover block edit matches snapshot 1`] = `
class="components-form-file-upload"
>
{
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,
};
}
@@ -84,7 +109,7 @@ class ImageEdit extends Component {
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 ) {
@@ -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 );
}
@@ -125,6 +150,10 @@ class ImageEdit extends Component {
return;
}
+ this.setState( {
+ isEditing: false,
+ } );
+
this.props.setAttributes( {
...pickRelevantMediaFiles( media ),
width: undefined,
@@ -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 } );
}
@@ -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 = (
-
-
- { !! url && (
+ let toolbarEditButton;
+ if ( url ) {
+ if ( isExternal ) {
+ toolbarEditButton = (
+
+
+
+ );
+ } else {
+ toolbarEditButton = (
- ) }
+ );
+ }
+ }
+
+ const controls = (
+
+
+ { toolbarEditButton }
);
- if ( ! url ) {
+ if ( isEditing ) {
+ const src = isExternal ? url : undefined;
return (
{ controls }
@@ -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 } }
/>
);
}
const classes = classnames( className, {
- 'is-transient': 0 === url.indexOf( 'blob:' ),
+ 'is-transient': isBlobURL( url ),
'is-resized': !! width || !! height,
'is-focused': isSelected,
} );
diff --git a/packages/block-library/src/video/edit.js b/packages/block-library/src/video/edit.js
index 7158496b1a4e2..1d9b7b195a2eb 100644
--- a/packages/block-library/src/video/edit.js
+++ b/packages/block-library/src/video/edit.js
@@ -22,7 +22,7 @@ import {
RichText,
mediaUpload,
} from '@wordpress/editor';
-import { getBlobByURL } from '@wordpress/blob';
+import { getBlobByURL, isBlobURL } from '@wordpress/blob';
const ALLOWED_MEDIA_TYPES = [ 'video' ];
@@ -45,7 +45,7 @@ class VideoEdit extends Component {
componentDidMount() {
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 ) {
mediaUpload( {
diff --git a/packages/block-library/src/video/test/__snapshots__/index.js.snap b/packages/block-library/src/video/test/__snapshots__/index.js.snap
index 33d0d470ec7da..0f7157024c04c 100644
--- a/packages/block-library/src/video/test/__snapshots__/index.js.snap
+++ b/packages/block-library/src/video/test/__snapshots__/index.js.snap
@@ -58,26 +58,11 @@ exports[`core/video block edit matches snapshot 1`] = `
-
+
+
+ Insert from URL
+
+
`;
diff --git a/packages/editor/CHANGELOG.md b/packages/editor/CHANGELOG.md
index 239f2262b15ff..251dd2ac549eb 100644
--- a/packages/editor/CHANGELOG.md
+++ b/packages/editor/CHANGELOG.md
@@ -8,6 +8,10 @@
- `wp.editor.PanelColor` has been deprecated in favor of `wp.editor.PanelColorSettings`.
+### New Features
+
+- Added `onClose` prop to `URLPopover` component.
+
## 4.0.0 (2018-09-30)
### Breaking Changes
diff --git a/packages/editor/src/components/media-placeholder/index.js b/packages/editor/src/components/media-placeholder/index.js
index bb2dc71b3d797..ba53f6ce54c0b 100644
--- a/packages/editor/src/components/media-placeholder/index.js
+++ b/packages/editor/src/components/media-placeholder/index.js
@@ -12,6 +12,7 @@ import {
FormFileUpload,
Placeholder,
DropZone,
+ IconButton,
} from '@wordpress/components';
import { __, sprintf } from '@wordpress/i18n';
import { Component } from '@wordpress/element';
@@ -21,18 +22,46 @@ import deprecated from '@wordpress/deprecated';
* Internal dependencies
*/
import MediaUpload from '../media-upload';
+import URLPopover from '../url-popover';
import { mediaUpload } from '../../utils/';
+const InsertFromURLPopover = ( { src, onChange, onSubmit, onClose } ) => (
+
+
+
+);
+
class MediaPlaceholder extends Component {
constructor() {
super( ...arguments );
this.state = {
src: '',
+ isURLInputVisible: false,
};
this.onChangeSrc = this.onChangeSrc.bind( this );
this.onSubmitSrc = this.onSubmitSrc.bind( this );
this.onUpload = this.onUpload.bind( this );
this.onFilesUpload = this.onFilesUpload.bind( this );
+ this.openURLInput = this.openURLInput.bind( this );
+ this.closeURLInput = this.closeURLInput.bind( this );
}
getAllowedTypes() {
@@ -73,15 +102,14 @@ class MediaPlaceholder extends Component {
}
onChangeSrc( event ) {
- this.setState( {
- src: event.target.value,
- } );
+ this.setState( { src: event.target.value } );
}
onSubmitSrc( event ) {
event.preventDefault();
if ( this.state.src && this.props.onSelectURL ) {
this.props.onSelectURL( this.state.src );
+ this.closeURLInput();
}
}
@@ -101,6 +129,14 @@ class MediaPlaceholder extends Component {
} );
}
+ openURLInput() {
+ this.setState( { isURLInputVisible: true } );
+ }
+
+ closeURLInput() {
+ this.setState( { isURLInputVisible: false } );
+ }
+
render() {
const {
accept,
@@ -114,7 +150,14 @@ class MediaPlaceholder extends Component {
multiple = false,
notices,
} = this.props;
+
+ const {
+ isURLInputVisible,
+ src,
+ } = this.state;
+
const allowedTypes = this.getAllowedTypes();
+
return (
- { onSelectURL && (
-
- ) }
(
-
+
{ __( 'Media Library' ) }
) }
/>
+ { onSelectURL && (
+
+
+ { __( 'Insert from URL' ) }
+
+ { isURLInputVisible && (
+
+ ) }
+
+ ) }
);
}
diff --git a/packages/editor/src/components/media-placeholder/style.scss b/packages/editor/src/components/media-placeholder/style.scss
index 43a862d045c34..5e0f813d01ce5 100644
--- a/packages/editor/src/components/media-placeholder/style.scss
+++ b/packages/editor/src/components/media-placeholder/style.scss
@@ -1,23 +1,37 @@
-.editor-media-placeholder {
- .components-placeholder__input {
- margin-top: 0.5em;
- }
+.editor-media-placeholder__url-input-container {
+ width: 100%;
- .components-button.is-large {
- margin-top: 0.5em;
+ // Reset the margin to ensure the url popover is adjacent to the button.
+ .editor-media-placeholder__button {
+ margin-bottom: 0;
}
+}
- .components-placeholder__fieldset {
- max-width: 400px;
+.editor-media-placeholder__url-input-form {
+ display: flex;
- form {
- max-width: none;
+ // Selector requires a lot of specificity to override base styles.
+ input[type="url"].editor-media-placeholder__url-input-field {
+ width: 100%;
+ @include break-small() {
+ width: 300px;
}
+
+ flex-grow: 1;
+ border: none;
+ border-radius: 0;
+ margin: 2px;
+
}
}
-.editor-media-placeholder__upload-button.components-button {
+.editor-media-placeholder__url-input-submit-button {
+ flex-shrink: 1;
+}
+
+.editor-media-placeholder__button {
margin-right: 5px;
+ margin-bottom: 0.5rem;
.dashicon {
vertical-align: middle;
diff --git a/packages/editor/src/components/url-popover/README.md b/packages/editor/src/components/url-popover/README.md
index b8f62067e7298..da0fc4dbefb21 100644
--- a/packages/editor/src/components/url-popover/README.md
+++ b/packages/editor/src/components/url-popover/README.md
@@ -62,7 +62,7 @@ class MyURLPopover extends Component {
Edit URL
{ isVisible && (
(
From cbf425bc76d5797082cd63c8ecb4b6122f15910f Mon Sep 17 00:00:00 2001
From: Robert Anderson
Date: Fri, 19 Oct 2018 02:45:35 +1100
Subject: [PATCH 015/121] Add Custom Fields and Advanced Panels to Options
modal (#10676)
* First pass at adding 'Advanced Panels' to Options modal
Allows the user to enable or disable 'Advanced Panels' (AKA Meta Boxes)
via the Options modal.
---
docs/data/data-core-edit-post.md | 62 +++++++++++------
docs/reference/deprecated.md | 3 +-
lib/meta-box-partial-page.php | 32 ++++++---
.../src/components/meta-boxes/index.js | 29 +++++---
.../meta-boxes/meta-box-visibility.js | 33 +++++++++
.../src/components/options-modal/index.js | 12 +++-
packages/edit-post/src/store/actions.js | 56 +++++----------
packages/edit-post/src/store/effects.js | 16 +++--
packages/edit-post/src/store/reducer.js | 23 ++++---
packages/edit-post/src/store/selectors.js | 69 ++++++++++++++++---
packages/edit-post/src/store/test/reducer.js | 20 +++---
.../edit-post/src/store/test/selectors.js | 55 ++++++++++++---
12 files changed, 288 insertions(+), 122 deletions(-)
create mode 100644 packages/edit-post/src/components/meta-boxes/meta-box-visibility.js
diff --git a/docs/data/data-core-edit-post.md b/docs/data/data-core-edit-post.md
index 0c711665b345c..87ff8d8bb05a9 100644
--- a/docs/data/data-core-edit-post.md
+++ b/docs/data/data-core-edit-post.md
@@ -195,6 +195,19 @@ Returns an array of active meta box locations.
Active meta box locations.
+### isMetaBoxLocationVisible
+
+Returns true if a metabox location is active and visible
+
+*Parameters*
+
+ * state: Post editor state.
+ * location: Meta box location to test.
+
+*Returns*
+
+Whether the meta box location is active and visible.
+
### isMetaBoxLocationActive
Returns true if there is an active meta box in the given location, or false
@@ -209,6 +222,31 @@ otherwise.
Whether the meta box location is active.
+### getMetaBoxesPerLocation
+
+Returns the list of all the available meta boxes for a given location.
+
+*Parameters*
+
+ * state: Global application state.
+ * location: Meta box location to test.
+
+*Returns*
+
+List of meta boxes.
+
+### getAllMetaBoxes
+
+Returns the list of all the available meta boxes.
+
+*Parameters*
+
+ * state: Global application state.
+
+*Returns*
+
+List of meta boxes.
+
### getMetaBox
Returns the state of legacy meta boxes.
@@ -326,30 +364,14 @@ Returns an action object used to toggle a plugin name flag.
* pluginName: Plugin name.
-### initializeMetaBoxState
-
-Returns an action object used to check the state of meta boxes at a location.
-
-This should only be fired once to initialize meta box state. If a meta box
-area is empty, this will set the store state to indicate that React should
-not render the meta box area.
-
-Example: metaBoxes = { side: true, normal: false }.
-
-This indicates that the sidebar has a meta box but the normal area does not.
-
-*Parameters*
-
- * metaBoxes: Whether meta box locations are active.
-
-### setActiveMetaBoxLocations
+### setAvailableMetaBoxesPerLocation
-Returns an action object used in signaling that the active meta box
-locations have changed.
+Returns an action object used in signaling
+what Meta boxes are available in which location.
*Parameters*
- * locations: New active meta box locations.
+ * metaBoxesPerLocation: Meta boxes per location.
### requestMetaBoxUpdates
diff --git a/docs/reference/deprecated.md b/docs/reference/deprecated.md
index d7203f2edba18..8b064f1a5a2ce 100644
--- a/docs/reference/deprecated.md
+++ b/docs/reference/deprecated.md
@@ -12,7 +12,8 @@ Gutenberg's deprecation policy is intended to support backwards-compatibility fo
- Writing resolvers as async generators has been removed. Use the controls plugin instead.
- `wp.components.AccessibleSVG` component has been removed. Please use `wp.components.SVG` instead.
- The `wp.editor.UnsavedChangesWarning` component no longer accepts a `forceIsDirty` prop.
-- `initializeMetaBoxState` action (`core/edit-post`) has been removed. Use `setActiveMetaBoxLocations` action (`core/edit-post`) instead.
+- `setActiveMetaBoxLocations` action (`core/edit-post`) has been removed.
+- `initializeMetaBoxState` action (`core/edit-post`) has been removed.
- `wp.editPost.initializeEditor` no longer returns an object. Use the `setActiveMetaBoxLocations` action (`core/edit-post`) in place of the existing object's `initializeMetaBoxes` function.
- `setMetaBoxSavedData` action (`core/edit-post`) has been removed.
- `getMetaBoxes` selector (`core/edit-post`) has been removed. Use `getActiveMetaBoxLocations` selector (`core/edit-post`) instead.
diff --git a/lib/meta-box-partial-page.php b/lib/meta-box-partial-page.php
index b6a7187813c5e..ad1a6805d327e 100644
--- a/lib/meta-box-partial-page.php
+++ b/lib/meta-box-partial-page.php
@@ -112,8 +112,8 @@ function gutenberg_filter_meta_boxes( $meta_boxes ) {
$core_normal_meta_boxes = array(
'revisionsdiv',
'postexcerpt',
- 'trackbacksdiv',
'postcustom',
+ 'trackbacksdiv',
'commentstatusdiv',
'commentsdiv',
'slugdiv',
@@ -289,9 +289,9 @@ function the_gutenberg_metaboxes() {
*
* @param array $wp_meta_boxes Global meta box state.
*/
- $wp_meta_boxes = apply_filters( 'filter_gutenberg_meta_boxes', $wp_meta_boxes );
- $locations = array( 'side', 'normal', 'advanced' );
- $active_meta_box_locations = array();
+ $wp_meta_boxes = apply_filters( 'filter_gutenberg_meta_boxes', $wp_meta_boxes );
+ $locations = array( 'side', 'normal', 'advanced' );
+ $priorities = array( 'high', 'sorted', 'core', 'default', 'low' );
// Render meta boxes.
?>
instead of in the
.
+ * Default 'false'.
*/
-function register_tinymce_scripts() {
- global $tinymce_version, $concatenate_scripts, $compress_scripts;
- if ( ! isset( $concatenate_scripts ) ) {
- script_concat_settings();
- }
- $suffix = SCRIPT_DEBUG ? '' : '.min';
- $compressed = $compress_scripts && $concatenate_scripts && isset( $_SERVER['HTTP_ACCEPT_ENCODING'] )
- && false !== stripos( $_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip' );
- // Load tinymce.js when running from /src, otherwise load wp-tinymce.js.gz (in production) or
- // tinymce.min.js (when SCRIPT_DEBUG is true).
- $mce_suffix = false !== strpos( get_bloginfo( 'version' ), '-src' ) ? '' : '.min';
- if ( $compressed ) {
- wp_register_script( 'wp-tinymce', includes_url( 'js/tinymce/' ) . 'wp-tinymce.php', array(), $tinymce_version );
- } else {
- wp_register_script( 'wp-tinymce-root', includes_url( 'js/tinymce/' ) . "tinymce{$mce_suffix}.js", array(), $tinymce_version );
- wp_register_script( 'wp-tinymce', includes_url( 'js/tinymce/' ) . "plugins/compat3x/plugin{$suffix}.js", array( 'wp-tinymce-root' ), $tinymce_version );
- }
+function gutenberg_override_script( $handle, $src, $deps = array(), $ver = false, $in_footer = false ) {
+ wp_deregister_script( $handle );
+ wp_register_script( $handle, $src, $deps, $ver, $in_footer );
+}
+
+/**
+ * Registers a style according to `wp_register_style`. Honors this request by
+ * deregistering any style by the same handler before registration.
+ *
+ * @since 4.1.0
+ *
+ * @param string $handle Name of the stylesheet. Should be unique.
+ * @param string $src Full URL of the stylesheet, or path of the stylesheet relative to the WordPress root directory.
+ * @param array $deps Optional. An array of registered stylesheet handles this stylesheet depends on. Default empty array.
+ * @param string|bool|null $ver Optional. String specifying stylesheet version number, if it has one, which is added to the URL
+ * as a query string for cache busting purposes. If version is set to false, a version
+ * number is automatically added equal to current installed WordPress version.
+ * If set to null, no version is added.
+ * @param string $media Optional. The media for which this stylesheet has been defined.
+ * Default 'all'. Accepts media types like 'all', 'print' and 'screen', or media queries like
+ * '(orientation: portrait)' and '(max-width: 640px)'.
+ */
+function gutenberg_override_style( $handle, $src, $deps = array(), $ver = false, $media = 'all' ) {
+ wp_deregister_style( $handle );
+ wp_register_style( $handle, $src, $deps, $ver, $media );
}
/**
@@ -99,7 +144,7 @@ function gutenberg_register_scripts_and_styles() {
register_tinymce_scripts();
- wp_register_script(
+ gutenberg_override_script(
'wp-polyfill',
null,
array(
@@ -118,63 +163,63 @@ function gutenberg_register_scripts_and_styles() {
)
)
);
- wp_register_script(
+ gutenberg_override_script(
'wp-url',
gutenberg_url( 'build/url/index.js' ),
array( 'wp-polyfill' ),
filemtime( gutenberg_dir_path() . 'build/url/index.js' ),
true
);
- wp_register_script(
+ gutenberg_override_script(
'wp-autop',
gutenberg_url( 'build/autop/index.js' ),
array( 'wp-polyfill' ),
filemtime( gutenberg_dir_path() . 'build/autop/index.js' ),
true
);
- wp_register_script(
+ gutenberg_override_script(
'wp-wordcount',
gutenberg_url( 'build/wordcount/index.js' ),
array( 'wp-polyfill' ),
filemtime( gutenberg_dir_path() . 'build/wordcount/index.js' ),
true
);
- wp_register_script(
+ gutenberg_override_script(
'wp-dom-ready',
gutenberg_url( 'build/dom-ready/index.js' ),
array( 'wp-polyfill' ),
filemtime( gutenberg_dir_path() . 'build/dom-ready/index.js' ),
true
);
- wp_register_script(
+ gutenberg_override_script(
'wp-a11y',
gutenberg_url( 'build/a11y/index.js' ),
array( 'wp-dom-ready', 'wp-polyfill' ),
filemtime( gutenberg_dir_path() . 'build/a11y/index.js' ),
true
);
- wp_register_script(
+ gutenberg_override_script(
'wp-hooks',
gutenberg_url( 'build/hooks/index.js' ),
array( 'wp-polyfill' ),
filemtime( gutenberg_dir_path() . 'build/hooks/index.js' ),
true
);
- wp_register_script(
+ gutenberg_override_script(
'wp-i18n',
gutenberg_url( 'build/i18n/index.js' ),
array( 'wp-polyfill' ),
filemtime( gutenberg_dir_path() . 'build/i18n/index.js' ),
true
);
- wp_register_script(
+ gutenberg_override_script(
'wp-is-shallow-equal',
gutenberg_url( 'build/is-shallow-equal/index.js' ),
array( 'wp-polyfill' ),
filemtime( gutenberg_dir_path() . 'build/is-shallow-equal/index.js' ),
true
);
- wp_register_script(
+ gutenberg_override_script(
'wp-token-list',
gutenberg_url( 'build/token-list/index.js' ),
array( 'lodash', 'wp-polyfill' ),
@@ -183,7 +228,7 @@ function gutenberg_register_scripts_and_styles() {
);
// Editor Scripts.
- wp_register_script(
+ gutenberg_override_script(
'wp-api-fetch',
gutenberg_url( 'build/api-fetch/index.js' ),
array( 'wp-polyfill', 'wp-hooks', 'wp-i18n' ),
@@ -207,42 +252,42 @@ function gutenberg_register_scripts_and_styles() {
'after'
);
- wp_register_script(
+ gutenberg_override_script(
'wp-deprecated',
gutenberg_url( 'build/deprecated/index.js' ),
array( 'wp-polyfill', 'wp-hooks' ),
filemtime( gutenberg_dir_path() . 'build/deprecated/index.js' ),
true
);
- wp_register_script(
+ gutenberg_override_script(
'wp-blob',
gutenberg_url( 'build/blob/index.js' ),
array( 'wp-polyfill' ),
filemtime( gutenberg_dir_path() . 'build/blob/index.js' ),
true
);
- wp_register_script(
+ gutenberg_override_script(
'wp-compose',
gutenberg_url( 'build/compose/index.js' ),
array( 'lodash', 'wp-element', 'wp-is-shallow-equal', 'wp-polyfill' ),
filemtime( gutenberg_dir_path() . 'build/compose/index.js' ),
true
);
- wp_register_script(
+ gutenberg_override_script(
'wp-keycodes',
gutenberg_url( 'build/keycodes/index.js' ),
array( 'lodash', 'wp-polyfill' ),
filemtime( gutenberg_dir_path() . 'build/keycodes/index.js' ),
true
);
- wp_register_script(
+ gutenberg_override_script(
'wp-html-entities',
gutenberg_url( 'build/html-entities/index.js' ),
array( 'wp-polyfill' ),
filemtime( gutenberg_dir_path() . 'build/html-entities/index.js' ),
true
);
- wp_register_script(
+ gutenberg_override_script(
'wp-data',
gutenberg_url( 'build/data/index.js' ),
array(
@@ -273,49 +318,49 @@ function gutenberg_register_scripts_and_styles() {
)
)
);
- wp_register_script(
+ gutenberg_override_script(
'wp-core-data',
gutenberg_url( 'build/core-data/index.js' ),
array( 'wp-data', 'wp-api-fetch', 'wp-polyfill', 'wp-url', 'lodash' ),
filemtime( gutenberg_dir_path() . 'build/core-data/index.js' ),
true
);
- wp_register_script(
+ gutenberg_override_script(
'wp-dom',
gutenberg_url( 'build/dom/index.js' ),
array( 'lodash', 'wp-polyfill', 'wp-tinymce' ),
filemtime( gutenberg_dir_path() . 'build/dom/index.js' ),
true
);
- wp_register_script(
+ gutenberg_override_script(
'wp-block-serialization-default-parser',
gutenberg_url( 'build/block-serialization-default-parser/index.js' ),
array(),
filemtime( gutenberg_dir_path() . 'build/block-serialization-default-parser/index.js' ),
true
);
- wp_register_script(
+ gutenberg_override_script(
'wp-block-serialization-spec-parser',
gutenberg_url( 'build/block-serialization-spec-parser/index.js' ),
array( 'wp-polyfill' ),
filemtime( gutenberg_dir_path() . 'build/block-serialization-spec-parser/index.js' ),
true
);
- wp_register_script(
+ gutenberg_override_script(
'wp-shortcode',
gutenberg_url( 'build/shortcode/index.js' ),
array( 'wp-polyfill', 'lodash' ),
filemtime( gutenberg_dir_path() . 'build/shortcode/index.js' ),
true
);
- wp_register_script(
+ gutenberg_override_script(
'wp-redux-routine',
gutenberg_url( 'build/redux-routine/index.js' ),
array( 'wp-polyfill' ),
filemtime( gutenberg_dir_path() . 'build/redux-routine/index.js' ),
true
);
- wp_register_script(
+ gutenberg_override_script(
'wp-date',
gutenberg_url( 'build/date/index.js' ),
array( 'moment', 'wp-polyfill' ),
@@ -358,28 +403,28 @@ function gutenberg_register_scripts_and_styles() {
),
'after'
);
- wp_register_script(
+ gutenberg_override_script(
'wp-element',
gutenberg_url( 'build/element/index.js' ),
array( 'wp-polyfill', 'react', 'react-dom', 'lodash', 'wp-escape-html' ),
filemtime( gutenberg_dir_path() . 'build/element/index.js' ),
true
);
- wp_register_script(
+ gutenberg_override_script(
'wp-escape-html',
gutenberg_url( 'build/escape-html/index.js' ),
array( 'wp-polyfill' ),
filemtime( gutenberg_dir_path() . 'build/element/index.js' ),
true
);
- wp_register_script(
+ gutenberg_override_script(
'wp-rich-text',
gutenberg_url( 'build/rich-text/index.js' ),
array( 'wp-polyfill', 'wp-escape-html', 'lodash' ),
filemtime( gutenberg_dir_path() . 'build/rich-text/index.js' ),
true
);
- wp_register_script(
+ gutenberg_override_script(
'wp-components',
gutenberg_url( 'build/components/index.js' ),
array(
@@ -407,7 +452,7 @@ function gutenberg_register_scripts_and_styles() {
'wp-components',
sprintf( 'wp.components.unstable__setSiteURL(%s);', json_encode( site_url() ) )
);
- wp_register_script(
+ gutenberg_override_script(
'wp-blocks',
gutenberg_url( 'build/blocks/index.js' ),
array(
@@ -428,14 +473,14 @@ function gutenberg_register_scripts_and_styles() {
filemtime( gutenberg_dir_path() . 'build/blocks/index.js' ),
true
);
- wp_register_script(
+ gutenberg_override_script(
'wp-viewport',
gutenberg_url( 'build/viewport/index.js' ),
array( 'wp-polyfill', 'wp-element', 'wp-data', 'wp-compose', 'lodash' ),
filemtime( gutenberg_dir_path() . 'build/viewport/index.js' ),
true
);
- wp_register_script(
+ gutenberg_override_script(
'wp-block-library',
gutenberg_url( 'build/block-library/index.js' ),
array(
@@ -464,7 +509,7 @@ function gutenberg_register_scripts_and_styles() {
filemtime( gutenberg_dir_path() . 'build/block-library/index.js' ),
true
);
- wp_register_script(
+ gutenberg_override_script(
'wp-nux',
gutenberg_url( 'build/nux/index.js' ),
array(
@@ -479,7 +524,7 @@ function gutenberg_register_scripts_and_styles() {
filemtime( gutenberg_dir_path() . 'build/nux/index.js' ),
true
);
- wp_register_script(
+ gutenberg_override_script(
'wp-plugins',
gutenberg_url( 'build/plugins/index.js' ),
array( 'lodash', 'wp-compose', 'wp-element', 'wp-hooks', 'wp-polyfill' ),
@@ -590,7 +635,7 @@ function gutenberg_register_scripts_and_styles() {
)
);
- wp_register_script(
+ gutenberg_override_script(
'wp-editor',
gutenberg_url( 'build/editor/index.js' ),
array(
@@ -626,7 +671,7 @@ function gutenberg_register_scripts_and_styles() {
filemtime( gutenberg_dir_path() . 'build/editor/index.js' )
);
- wp_register_script(
+ gutenberg_override_script(
'wp-edit-post',
gutenberg_url( 'build/edit-post/index.js' ),
array(
@@ -660,7 +705,7 @@ function gutenberg_register_scripts_and_styles() {
true
);
- wp_register_script(
+ gutenberg_override_script(
'wp-list-reusable-blocks',
gutenberg_url( 'build/list-reusable-blocks/index.js' ),
array(
@@ -678,7 +723,7 @@ function gutenberg_register_scripts_and_styles() {
// Editor Styles.
// This empty stylesheet is defined to ensure backwards compatibility.
- wp_register_style( 'wp-blocks', false );
+ gutenberg_override_style( 'wp-blocks', false );
$fonts_url = '';
@@ -694,14 +739,14 @@ function gutenberg_register_scripts_and_styles() {
$fonts_url = esc_url_raw( add_query_arg( $query_args, 'https://fonts.googleapis.com/css' ) );
}
- wp_register_style(
+ gutenberg_override_style(
'wp-editor-font',
$fonts_url,
array(),
null
);
- wp_register_style(
+ gutenberg_override_style(
'wp-editor',
gutenberg_url( 'build/editor/style.css' ),
array( 'wp-components', 'wp-editor-font', 'wp-nux' ),
@@ -709,7 +754,7 @@ function gutenberg_register_scripts_and_styles() {
);
wp_style_add_data( 'wp-editor', 'rtl', 'replace' );
- wp_register_style(
+ gutenberg_override_style(
'wp-edit-post',
gutenberg_url( 'build/edit-post/style.css' ),
array( 'wp-components', 'wp-editor', 'wp-edit-blocks', 'wp-block-library', 'wp-nux' ),
@@ -717,7 +762,7 @@ function gutenberg_register_scripts_and_styles() {
);
wp_style_add_data( 'wp-edit-post', 'rtl', 'replace' );
- wp_register_style(
+ gutenberg_override_style(
'wp-components',
gutenberg_url( 'build/components/style.css' ),
array(),
@@ -725,7 +770,7 @@ function gutenberg_register_scripts_and_styles() {
);
wp_style_add_data( 'wp-components', 'rtl', 'replace' );
- wp_register_style(
+ gutenberg_override_style(
'wp-block-library',
gutenberg_url( 'build/block-library/style.css' ),
current_theme_supports( 'wp-block-styles' ) ? array( 'wp-block-library-theme' ) : array(),
@@ -733,7 +778,7 @@ function gutenberg_register_scripts_and_styles() {
);
wp_style_add_data( 'wp-block-library', 'rtl', 'replace' );
- wp_register_style(
+ gutenberg_override_style(
'wp-edit-blocks',
gutenberg_url( 'build/block-library/editor.css' ),
array(
@@ -746,7 +791,7 @@ function gutenberg_register_scripts_and_styles() {
);
wp_style_add_data( 'wp-edit-blocks', 'rtl', 'replace' );
- wp_register_style(
+ gutenberg_override_style(
'wp-nux',
gutenberg_url( 'build/nux/style.css' ),
array( 'wp-components' ),
@@ -754,7 +799,7 @@ function gutenberg_register_scripts_and_styles() {
);
wp_style_add_data( 'wp-nux', 'rtl', 'replace' );
- wp_register_style(
+ gutenberg_override_style(
'wp-block-library-theme',
gutenberg_url( 'build/block-library/theme.css' ),
array(),
@@ -762,7 +807,7 @@ function gutenberg_register_scripts_and_styles() {
);
wp_style_add_data( 'wp-block-library-theme', 'rtl', 'replace' );
- wp_register_style(
+ gutenberg_override_style(
'wp-list-reusable-blocks',
gutenberg_url( 'build/list-reusable-blocks/style.css' ),
array( 'wp-components' ),
@@ -983,7 +1028,7 @@ function gutenberg_register_vendor_script( $handle, $src, $deps = array() ) {
if ( ! $f ) {
// Failed to open the file for writing, probably due to server
// permissions. Enqueue the script directly from the URL instead.
- wp_register_script( $handle, $src, $deps, null );
+ gutenberg_override_script( $handle, $src, $deps, null );
return;
}
fclose( $f );
@@ -996,12 +1041,12 @@ function gutenberg_register_vendor_script( $handle, $src, $deps = array() ) {
// The request failed. If the file is already cached, continue to
// use this file. If not, then unlink the 0 byte file, and enqueue
// the script directly from the URL.
- wp_register_script( $handle, $src, $deps, null );
+ gutenberg_override_script( $handle, $src, $deps, null );
unlink( $full_path );
return;
}
}
- wp_register_script(
+ gutenberg_override_script(
$handle,
gutenberg_url( 'vendor/' . $filename ),
$deps,
diff --git a/lib/load.php b/lib/load.php
index 3798ad4c37cae..ee1c973f99568 100644
--- a/lib/load.php
+++ b/lib/load.php
@@ -53,9 +53,24 @@
// Register server-side code for individual blocks.
-require dirname( __FILE__ ) . '/../packages/block-library/src/archives/index.php';
-require dirname( __FILE__ ) . '/../packages/block-library/src/block/index.php';
-require dirname( __FILE__ ) . '/../packages/block-library/src/categories/index.php';
-require dirname( __FILE__ ) . '/../packages/block-library/src/latest-comments/index.php';
-require dirname( __FILE__ ) . '/../packages/block-library/src/latest-posts/index.php';
-require dirname( __FILE__ ) . '/../packages/block-library/src/shortcode/index.php';
+if ( ! function_exists( 'render_block_core_archives' ) ) {
+ require dirname( __FILE__ ) . '/../packages/block-library/src/archives/index.php';
+}
+if ( ! function_exists( 'render_block_core_block' ) ) {
+ require dirname( __FILE__ ) . '/../packages/block-library/src/block/index.php';
+}
+if ( ! function_exists( 'render_block_core_categories' ) ) {
+ require dirname( __FILE__ ) . '/../packages/block-library/src/categories/index.php';
+}
+// Currently merged in core as `gutenberg_render_block_core_latest_comments`,
+// expected to change soon.
+if ( ! function_exists( 'render_block_core_latest_comments' )
+ && ! function_exists( 'gutenberg_render_block_core_latest_comments' ) ) {
+ require dirname( __FILE__ ) . '/../packages/block-library/src/latest-comments/index.php';
+}
+if ( ! function_exists( 'render_block_core_latest_posts' ) ) {
+ require dirname( __FILE__ ) . '/../packages/block-library/src/latest-posts/index.php';
+}
+if ( ! function_exists( 'render_block_core_shortcode' ) ) {
+ require dirname( __FILE__ ) . '/../packages/block-library/src/shortcode/index.php';
+}
diff --git a/lib/register.php b/lib/register.php
index 1fc63f8c22d6d..89ee8411fb262 100644
--- a/lib/register.php
+++ b/lib/register.php
@@ -310,28 +310,30 @@ function gutenberg_can_edit_post_type( $post_type ) {
return apply_filters( 'gutenberg_can_edit_post_type', $can_edit, $post_type );
}
-/**
- * Determine whether a post or content string has blocks.
- *
- * This test optimizes for performance rather than strict accuracy, detecting
- * the pattern of a block but not validating its structure. For strict accuracy
- * you should use the block parser on post content.
- *
- * @since 3.6.0
- * @see gutenberg_parse_blocks()
- *
- * @param int|string|WP_Post|null $post Optional. Post content, post ID, or post object. Defaults to global $post.
- * @return bool Whether the post has blocks.
- */
-function has_blocks( $post = null ) {
- if ( ! is_string( $post ) ) {
- $wp_post = get_post( $post );
- if ( $wp_post instanceof WP_Post ) {
- $post = $wp_post->post_content;
+if ( ! function_exists( 'has_blocks' ) ) {
+ /**
+ * Determine whether a post or content string has blocks.
+ *
+ * This test optimizes for performance rather than strict accuracy, detecting
+ * the pattern of a block but not validating its structure. For strict accuracy
+ * you should use the block parser on post content.
+ *
+ * @since 3.6.0
+ * @see gutenberg_parse_blocks()
+ *
+ * @param int|string|WP_Post|null $post Optional. Post content, post ID, or post object. Defaults to global $post.
+ * @return bool Whether the post has blocks.
+ */
+ function has_blocks( $post = null ) {
+ if ( ! is_string( $post ) ) {
+ $wp_post = get_post( $post );
+ if ( $wp_post instanceof WP_Post ) {
+ $post = $wp_post->post_content;
+ }
}
- }
- return false !== strpos( (string) $post, '
+
+
+
+
+
+
+
diff --git a/test/integration/full-content/fixtures/core__media-text.json b/test/integration/full-content/fixtures/core__media-text.json
new file mode 100644
index 0000000000000..a4f26e4c9a5c5
--- /dev/null
+++ b/test/integration/full-content/fixtures/core__media-text.json
@@ -0,0 +1,32 @@
+[
+ {
+ "clientId": "_clientId_0",
+ "name": "core/media-text",
+ "isValid": true,
+ "attributes": {
+ "align": "wide",
+ "mediaAlt": "",
+ "mediaPosition": "left",
+ "mediaId": 17985,
+ "mediaUrl": "http://localhost/wp-content/uploads/2018/09/1600px-Mount_Everest_as_seen_from_Drukair2_PLW_edit.jpg",
+ "mediaType": "image",
+ "mediaWidth": 50
+ },
+ "innerBlocks": [
+ {
+ "clientId": "_clientId_0",
+ "name": "core/paragraph",
+ "isValid": true,
+ "attributes": {
+ "content": "My Content",
+ "dropCap": false,
+ "placeholder": "Content…",
+ "fontSize": "large"
+ },
+ "innerBlocks": [],
+ "originalContent": "
My Content
"
+ }
+ ],
+ "originalContent": "
\n\t
\n\t\t \n\t \n\t
\n\t\t\n\t
\n
"
+ }
+]
diff --git a/test/integration/full-content/fixtures/core__media-text.parsed.json b/test/integration/full-content/fixtures/core__media-text.parsed.json
new file mode 100644
index 0000000000000..82df709985669
--- /dev/null
+++ b/test/integration/full-content/fixtures/core__media-text.parsed.json
@@ -0,0 +1,27 @@
+[
+ {
+ "blockName": "core/media-text",
+ "attrs": {
+ "mediaId": 17985,
+ "mediaType": "image"
+ },
+ "innerBlocks": [
+ {
+ "blockName": "core/paragraph",
+ "attrs": {
+ "placeholder": "Content…",
+ "fontSize": "large"
+ },
+ "innerBlocks": [],
+ "innerHTML": "\n\t\t
My Content
\n\t\t"
+ }
+ ],
+ "innerHTML": "\n
\n\t
\n\t\t \n\t \n\t
\n\t\t\n\t
\n
\n"
+ },
+ {
+ "blockName": null,
+ "attrs": {},
+ "innerBlocks": [],
+ "innerHTML": "\n"
+ }
+]
diff --git a/test/integration/full-content/fixtures/core__media-text.serialized.html b/test/integration/full-content/fixtures/core__media-text.serialized.html
new file mode 100644
index 0000000000000..5e70ba8e96eaa
--- /dev/null
+++ b/test/integration/full-content/fixtures/core__media-text.serialized.html
@@ -0,0 +1,5 @@
+
+
+
diff --git a/test/integration/full-content/fixtures/core__media-text__image-alt-no-align.html b/test/integration/full-content/fixtures/core__media-text__image-alt-no-align.html
new file mode 100644
index 0000000000000..66e424e3b148a
--- /dev/null
+++ b/test/integration/full-content/fixtures/core__media-text__image-alt-no-align.html
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
diff --git a/test/integration/full-content/fixtures/core__media-text__image-alt-no-align.json b/test/integration/full-content/fixtures/core__media-text__image-alt-no-align.json
new file mode 100644
index 0000000000000..d8f8ffa431fa6
--- /dev/null
+++ b/test/integration/full-content/fixtures/core__media-text__image-alt-no-align.json
@@ -0,0 +1,32 @@
+[
+ {
+ "clientId": "_clientId_0",
+ "name": "core/media-text",
+ "isValid": true,
+ "attributes": {
+ "align": "none",
+ "mediaAlt": "my alt",
+ "mediaPosition": "left",
+ "mediaId": 17985,
+ "mediaUrl": "http://localhost/wp-content/uploads/2018/09/1600px-Mount_Everest_as_seen_from_Drukair2_PLW_edit.jpg",
+ "mediaType": "image",
+ "mediaWidth": 50
+ },
+ "innerBlocks": [
+ {
+ "clientId": "_clientId_0",
+ "name": "core/paragraph",
+ "isValid": true,
+ "attributes": {
+ "content": "Content",
+ "dropCap": false,
+ "placeholder": "Content…",
+ "fontSize": "large"
+ },
+ "innerBlocks": [],
+ "originalContent": "
Content
"
+ }
+ ],
+ "originalContent": "
\n\t
\n\t\t \n\t \n\t
\n\t\t\n\t
\n
"
+ }
+]
diff --git a/test/integration/full-content/fixtures/core__media-text__image-alt-no-align.parsed.json b/test/integration/full-content/fixtures/core__media-text__image-alt-no-align.parsed.json
new file mode 100644
index 0000000000000..5e9fc5b3f5e1e
--- /dev/null
+++ b/test/integration/full-content/fixtures/core__media-text__image-alt-no-align.parsed.json
@@ -0,0 +1,28 @@
+[
+ {
+ "blockName": "core/media-text",
+ "attrs": {
+ "align": "none",
+ "mediaId": 17985,
+ "mediaType": "image"
+ },
+ "innerBlocks": [
+ {
+ "blockName": "core/paragraph",
+ "attrs": {
+ "placeholder": "Content…",
+ "fontSize": "large"
+ },
+ "innerBlocks": [],
+ "innerHTML": "\n\t\t
Content
\n\t\t"
+ }
+ ],
+ "innerHTML": "\n
\n\t
\n\t\t \n\t \n\t
\n\t\t\n\t
\n
\n"
+ },
+ {
+ "blockName": null,
+ "attrs": {},
+ "innerBlocks": [],
+ "innerHTML": "\n"
+ }
+]
diff --git a/test/integration/full-content/fixtures/core__media-text__image-alt-no-align.serialized.html b/test/integration/full-content/fixtures/core__media-text__image-alt-no-align.serialized.html
new file mode 100644
index 0000000000000..3a28587c29baf
--- /dev/null
+++ b/test/integration/full-content/fixtures/core__media-text__image-alt-no-align.serialized.html
@@ -0,0 +1,5 @@
+
+
+
diff --git a/test/integration/full-content/fixtures/core__media-text__media-right-custom-width.html b/test/integration/full-content/fixtures/core__media-text__media-right-custom-width.html
new file mode 100644
index 0000000000000..b0772fb7b44d9
--- /dev/null
+++ b/test/integration/full-content/fixtures/core__media-text__media-right-custom-width.html
@@ -0,0 +1,12 @@
+
+
+
diff --git a/test/integration/full-content/fixtures/core__media-text__media-right-custom-width.json b/test/integration/full-content/fixtures/core__media-text__media-right-custom-width.json
new file mode 100644
index 0000000000000..7fadb222f20e5
--- /dev/null
+++ b/test/integration/full-content/fixtures/core__media-text__media-right-custom-width.json
@@ -0,0 +1,33 @@
+[
+ {
+ "clientId": "_clientId_0",
+ "name": "core/media-text",
+ "isValid": true,
+ "attributes": {
+ "align": "full",
+ "mediaAlt": "",
+ "mediaPosition": "right",
+ "mediaId": 17897,
+ "mediaUrl": "http://localhost/wp-content/uploads/2018/09/Jul-26-2018-11-34-54.mp4",
+ "mediaType": "video",
+ "mediaWidth": 41
+ },
+ "innerBlocks": [
+ {
+ "clientId": "_clientId_0",
+ "name": "core/paragraph",
+ "isValid": true,
+ "attributes": {
+ "content": "My video",
+ "align": "right",
+ "dropCap": false,
+ "placeholder": "Content…",
+ "fontSize": "large"
+ },
+ "innerBlocks": [],
+ "originalContent": "
My video
"
+ }
+ ],
+ "originalContent": "
\n\t
\n\t\t \n\t \n\t
\n\t\t\n\t
\n
"
+ }
+]
diff --git a/test/integration/full-content/fixtures/core__media-text__media-right-custom-width.parsed.json b/test/integration/full-content/fixtures/core__media-text__media-right-custom-width.parsed.json
new file mode 100644
index 0000000000000..d344565dadc83
--- /dev/null
+++ b/test/integration/full-content/fixtures/core__media-text__media-right-custom-width.parsed.json
@@ -0,0 +1,31 @@
+[
+ {
+ "blockName": "core/media-text",
+ "attrs": {
+ "align": "full",
+ "mediaPosition": "right",
+ "mediaId": 17897,
+ "mediaType": "video",
+ "mediaWidth": 41
+ },
+ "innerBlocks": [
+ {
+ "blockName": "core/paragraph",
+ "attrs": {
+ "align": "right",
+ "placeholder": "Content…",
+ "fontSize": "large"
+ },
+ "innerBlocks": [],
+ "innerHTML": "\n\t\t
My video
\n\t\t"
+ }
+ ],
+ "innerHTML": "\n
\n\t
\n\t\t \n\t \n\t
\n\t\t\n\t
\n
\n"
+ },
+ {
+ "blockName": null,
+ "attrs": {},
+ "innerBlocks": [],
+ "innerHTML": "\n"
+ }
+]
diff --git a/test/integration/full-content/fixtures/core__media-text__media-right-custom-width.serialized.html b/test/integration/full-content/fixtures/core__media-text__media-right-custom-width.serialized.html
new file mode 100644
index 0000000000000..a11efac920f28
--- /dev/null
+++ b/test/integration/full-content/fixtures/core__media-text__media-right-custom-width.serialized.html
@@ -0,0 +1,5 @@
+
+
+
diff --git a/test/integration/full-content/fixtures/core__media-text__video.html b/test/integration/full-content/fixtures/core__media-text__video.html
new file mode 100644
index 0000000000000..48c13cac64d08
--- /dev/null
+++ b/test/integration/full-content/fixtures/core__media-text__video.html
@@ -0,0 +1,12 @@
+
+
+
diff --git a/test/integration/full-content/fixtures/core__media-text__video.json b/test/integration/full-content/fixtures/core__media-text__video.json
new file mode 100644
index 0000000000000..4296ee2ae4bb6
--- /dev/null
+++ b/test/integration/full-content/fixtures/core__media-text__video.json
@@ -0,0 +1,32 @@
+[
+ {
+ "clientId": "_clientId_0",
+ "name": "core/media-text",
+ "isValid": true,
+ "attributes": {
+ "align": "wide",
+ "mediaAlt": "",
+ "mediaPosition": "left",
+ "mediaId": 17897,
+ "mediaUrl": "http://localhost/wp-content/uploads/2018/09/Jul-26-2018-11-34-54.mp4",
+ "mediaType": "video",
+ "mediaWidth": 50
+ },
+ "innerBlocks": [
+ {
+ "clientId": "_clientId_0",
+ "name": "core/paragraph",
+ "isValid": true,
+ "attributes": {
+ "content": "My Content",
+ "dropCap": false,
+ "placeholder": "Content…",
+ "fontSize": "large"
+ },
+ "innerBlocks": [],
+ "originalContent": "
My Content
"
+ }
+ ],
+ "originalContent": "
\n\t
\n\t\t \n\t \n\t
\n\t\t\n\t
\n
"
+ }
+]
diff --git a/test/integration/full-content/fixtures/core__media-text__video.parsed.json b/test/integration/full-content/fixtures/core__media-text__video.parsed.json
new file mode 100644
index 0000000000000..ff58f6ab6c5fd
--- /dev/null
+++ b/test/integration/full-content/fixtures/core__media-text__video.parsed.json
@@ -0,0 +1,27 @@
+[
+ {
+ "blockName": "core/media-text",
+ "attrs": {
+ "mediaId": 17897,
+ "mediaType": "video"
+ },
+ "innerBlocks": [
+ {
+ "blockName": "core/paragraph",
+ "attrs": {
+ "placeholder": "Content…",
+ "fontSize": "large"
+ },
+ "innerBlocks": [],
+ "innerHTML": "\n\t\t
My Content
\n\t\t"
+ }
+ ],
+ "innerHTML": "\n
\n\t
\n\t\t \n\t \n\t
\n\t\t\n\t
\n
\n"
+ },
+ {
+ "blockName": null,
+ "attrs": {},
+ "innerBlocks": [],
+ "innerHTML": "\n"
+ }
+]
diff --git a/test/integration/full-content/fixtures/core__media-text__video.serialized.html b/test/integration/full-content/fixtures/core__media-text__video.serialized.html
new file mode 100644
index 0000000000000..f7302c9a33c5d
--- /dev/null
+++ b/test/integration/full-content/fixtures/core__media-text__video.serialized.html
@@ -0,0 +1,5 @@
+
+
+
From 48c120966e155f4c4134133ae92bfe38f2a1f846 Mon Sep 17 00:00:00 2001
From: Nicola Heald
Date: Fri, 19 Oct 2018 14:01:29 +0100
Subject: [PATCH 026/121] Use mock response for demo test if Vimeo is down
(#10720)
---
test/e2e/specs/demo.test.js | 28 +++++++++++++++++++++++-----
1 file changed, 23 insertions(+), 5 deletions(-)
diff --git a/test/e2e/specs/demo.test.js b/test/e2e/specs/demo.test.js
index 544e8d9b59cec..10bbf32da5ae9 100644
--- a/test/e2e/specs/demo.test.js
+++ b/test/e2e/specs/demo.test.js
@@ -8,6 +8,15 @@ import fetch from 'node-fetch';
*/
import { visitAdmin } from '../support/utils';
+const MOCK_VIMEO_RESPONSE = {
+ url: 'https://vimeo.com/22439234',
+ html: '',
+ type: 'video',
+ provider_name: 'Vimeo',
+ provider_url: 'https://vimeo.com',
+ version: '1.0',
+};
+
describe( 'new editor state', () => {
beforeAll( async () => {
// Intercept embed requests so that scripts loaded from third parties
@@ -27,13 +36,22 @@ describe( 'new editor state', () => {
}
);
const preview = await response.json();
- // Remove the first src attribute. This stops the Vimeo iframe loading the actual
- // embedded content, but the height and width are preserved so layout related
- // attributes, like aspect ratio CSS classes, remain the same.
- preview.html = preview.html.replace( /src=[^\s]+/, '' );
+ let responseBody;
+ if ( 'Embed Handler' === preview.provider_name ) {
+ // If Vimeo is down, that would make this test fail. So if we get a
+ // response from 'Embed Handler' instead of 'Vimeo', respond with a mock
+ // response, so we don't hold up development while Vimeo is down.
+ responseBody = MOCK_VIMEO_RESPONSE;
+ } else {
+ // Remove the first src attribute. This stops the Vimeo iframe loading the actual
+ // embedded content, but the height and width are preserved so layout related
+ // attributes, like aspect ratio CSS classes, remain the same.
+ preview.html = preview.html.replace( /src=[^\s]+/, '' );
+ responseBody = preview;
+ }
request.respond( {
content: 'application/json',
- body: JSON.stringify( preview ),
+ body: JSON.stringify( responseBody ),
} );
} else {
request.continue();
From 3fa55cbf7a5e4033b957d3695364c1feda78057b Mon Sep 17 00:00:00 2001
From: Kelly Dwan
Date: Fri, 19 Oct 2018 07:43:17 -0700
Subject: [PATCH 027/121] ColorPalette: Add an accessible color picker based on
ChromePicker (#10564)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* Add color picker component
* Add color picker styles to build
* Update to buttons for focus handles
* Update styles
* Update class name
* Add color-picker component to exports
* Update name to ColorPicker
* Add labels to color input groups
* Fix describedby prop name
* Update view to use `getDerivedStateFromProps`
* Add “Input” component to handle intermediate state when entering values
Specifically needed for hex colors, which can be invalid while they’re being entered
* Add space around text inputs
* Translate toggle label
* Add focus styles to colourpicker pointers.
* Remove react-color dependency
* Add class name to better find the component in the devtools
* Add tinycolor2 dependency
* Add reference to react-color license & copyright
* Better label
* Update snapshot
* Decouple valueKey and label
so we can use semantic labels.
* Use KeyboardShortcuts component to avoid separate instances of Mousetrap
* Use createRef
* Normalize HSV / HSL values
H: 0 - 260
S, L, V: 0 - 100
* Omit `valueKey` from being passed to input
* Use createRef in all components
* Update saturation handle label
* Prevent key events on slider handles
Fixes the issue where VoiceOver grabs arrow-key input as navigation
* Use a focusable div for slider handle
* Apply focus style to alpha handle
* Add description to hue controls
* Add speak commands for view change
* Update snapshot
* Remove test as the component API surface changed
We have done some changes to the ChromePicker so this test
can no longer be replicated.
* Add README, tests and update Changelog
* Remove react-color dependency
* Add docs manifest file
---
docs/manifest.json | 6 +
package-lock.json | 33 +-
packages/components/CHANGELOG.md | 4 +
packages/components/package.json | 4 +-
.../components/src/color-palette/index.js | 4 +-
.../test/__snapshots__/index.js.snap | 115 +++++--
.../src/color-palette/test/index.js | 7 -
.../components/src/color-picker/README.md | 23 ++
packages/components/src/color-picker/alpha.js | 177 +++++++++++
packages/components/src/color-picker/hue.js | 174 +++++++++++
packages/components/src/color-picker/index.js | 120 ++++++++
.../components/src/color-picker/inputs.js | 289 ++++++++++++++++++
.../components/src/color-picker/saturation.js | 186 +++++++++++
.../components/src/color-picker/style.scss | 209 +++++++++++++
.../test/__snapshots__/index.js.snap | 87 ++++++
.../components/src/color-picker/test/index.js | 26 ++
packages/components/src/color-picker/utils.js | 212 +++++++++++++
packages/components/src/index.js | 1 +
packages/components/src/style.scss | 1 +
19 files changed, 1606 insertions(+), 72 deletions(-)
create mode 100644 packages/components/src/color-picker/README.md
create mode 100644 packages/components/src/color-picker/alpha.js
create mode 100644 packages/components/src/color-picker/hue.js
create mode 100644 packages/components/src/color-picker/index.js
create mode 100644 packages/components/src/color-picker/inputs.js
create mode 100644 packages/components/src/color-picker/saturation.js
create mode 100644 packages/components/src/color-picker/style.scss
create mode 100644 packages/components/src/color-picker/test/__snapshots__/index.js.snap
create mode 100644 packages/components/src/color-picker/test/index.js
create mode 100644 packages/components/src/color-picker/utils.js
diff --git a/docs/manifest.json b/docs/manifest.json
index 95de517d25076..7c2ca6b45ee09 100644
--- a/docs/manifest.json
+++ b/docs/manifest.json
@@ -581,6 +581,12 @@
"markdown_source": "https://raw.githubusercontent.com/WordPress/gutenberg/master/packages/components/src/color-palette/README.md",
"parent": "components"
},
+ {
+ "title": "ColorPicker",
+ "slug": "color-picker",
+ "markdown_source": "https://raw.githubusercontent.com/WordPress/gutenberg/master/packages/components/src/color-picker/README.md",
+ "parent": "components"
+ },
{
"title": "Dashicon",
"slug": "dashicon",
diff --git a/package-lock.json b/package-lock.json
index 274fe95ad01bf..ab5506def2374 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2142,9 +2142,9 @@
"mousetrap": "^1.6.2",
"re-resizable": "^4.7.1",
"react-click-outside": "^2.3.1",
- "react-color": "^2.13.4",
"react-dates": "^17.1.1",
"rememo": "^3.0.0",
+ "tinycolor2": "^1.4.1",
"uuid": "^3.1.0"
}
},
@@ -13726,11 +13726,6 @@
"integrity": "sha512-NcWuJFHDA8V3wkDgR/j4+gZx+YQwstPgfQDV8ndUeWWzta3dnDTBxpVzqS9lkmJAuV5YX35lmyojl6HO5JXAgw==",
"dev": true
},
- "material-colors": {
- "version": "1.2.6",
- "resolved": "https://registry.npmjs.org/material-colors/-/material-colors-1.2.6.tgz",
- "integrity": "sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg=="
- },
"math-expression-evaluator": {
"version": "1.2.17",
"resolved": "https://registry.npmjs.org/math-expression-evaluator/-/math-expression-evaluator-1.2.17.tgz",
@@ -17113,22 +17108,10 @@
"hoist-non-react-statics": "^1.2.0"
}
},
- "react-color": {
- "version": "2.13.4",
- "resolved": "https://registry.npmjs.org/react-color/-/react-color-2.13.4.tgz",
- "integrity": "sha512-rNJTTxMPTImI1NpFaKLggDIvHgKOYRXj0krVh8c+Mo1YNsrLko8O94yiFqqdnSQgtIPteiAcGEJgBo9V5+uqaw==",
- "requires": {
- "lodash": "^4.0.1",
- "material-colors": "^1.2.1",
- "prop-types": "^15.5.4",
- "reactcss": "^1.2.0",
- "tinycolor2": "^1.1.2"
- }
- },
"react-dates": {
- "version": "17.1.1",
- "resolved": "https://registry.npmjs.org/react-dates/-/react-dates-17.1.1.tgz",
- "integrity": "sha512-kUQEf6AnXa0h067lW9teAYhzcSNjh8L1ZpUOdB1obzPTBTrzlSu94CvPOfTI43Rf+yiBWOXIpL36Ub+rKf4oKA==",
+ "version": "17.2.0",
+ "resolved": "https://registry.npmjs.org/react-dates/-/react-dates-17.2.0.tgz",
+ "integrity": "sha512-RDlerU8DdRRrlYS0MQ7Z9igPWABGLDwz6+ykBNff67RM3Sset2TDqeuOr+R5o00Ggn5U47GeLsGcSDxlZd9cHw==",
"requires": {
"airbnb-prop-types": "^2.10.0",
"consolidated-events": "^1.1.1 || ^2.0.0",
@@ -17337,14 +17320,6 @@
"global-cache": "^1.2.1"
}
},
- "reactcss": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/reactcss/-/reactcss-1.2.3.tgz",
- "integrity": "sha512-KiwVUcFu1RErkI97ywr8nvx8dNOpT03rbnma0SSalTYjkrPYaEajR4a/MRt6DZ46K6arDRbWMNHF+xH7G7n/8A==",
- "requires": {
- "lodash": "^4.0.1"
- }
- },
"read": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz",
diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md
index 2665f8e62ee0f..106fd58c41746 100644
--- a/packages/components/CHANGELOG.md
+++ b/packages/components/CHANGELOG.md
@@ -1,5 +1,9 @@
## 4.2.0 (Unreleased)
+### New Feature
+
+- Added a new `ColorPicker` component ([#10564](https://github.com/WordPress/gutenberg/pull/10564)).
+
### Deprecation
- `wp.components.PanelColor` has been deprecated in favor of `wp.editor.PanelColorSettings`.
diff --git a/packages/components/package.json b/packages/components/package.json
index a43560f88b920..018c38ea6e525 100644
--- a/packages/components/package.json
+++ b/packages/components/package.json
@@ -42,9 +42,9 @@
"mousetrap": "^1.6.2",
"re-resizable": "^4.7.1",
"react-click-outside": "^2.3.1",
- "react-color": "^2.13.4",
"react-dates": "^17.1.1",
"rememo": "^3.0.0",
+ "tinycolor2": "^1.4.1",
"uuid": "^3.1.0"
},
"devDependencies": {
@@ -54,4 +54,4 @@
"publishConfig": {
"access": "public"
}
-}
+}
\ No newline at end of file
diff --git a/packages/components/src/color-palette/index.js b/packages/components/src/color-palette/index.js
index 664d5f4ed92a5..5ab52f277df71 100644
--- a/packages/components/src/color-palette/index.js
+++ b/packages/components/src/color-palette/index.js
@@ -2,7 +2,6 @@
* External dependencies
*/
import classnames from 'classnames';
-import { ChromePicker } from 'react-color';
import { map } from 'lodash';
/**
@@ -16,6 +15,7 @@ import { __, sprintf } from '@wordpress/i18n';
import Button from '../button';
import Dropdown from '../dropdown';
import Tooltip from '../tooltip';
+import ColorPicker from '../color-picker';
export default function ColorPalette( { colors, disableCustomColors = false, value, onChange, className } ) {
function applyOrUnset( color ) {
@@ -71,7 +71,7 @@ export default function ColorPalette( { colors, disableCustomColors = false, val
) }
renderContent={ () => (
- onChange( color.hex ) }
disableAlpha
diff --git a/packages/components/src/color-palette/test/__snapshots__/index.js.snap b/packages/components/src/color-palette/test/__snapshots__/index.js.snap
index 7562ecac5015c..cb4980913feb5 100644
--- a/packages/components/src/color-palette/test/__snapshots__/index.js.snap
+++ b/packages/components/src/color-palette/test/__snapshots__/index.js.snap
@@ -1,38 +1,89 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`ColorPalette Dropdown .renderContent should render dropdown content 1`] = `
-
+
`;
exports[`ColorPalette Dropdown .renderToggle should render dropdown content 1`] = `
diff --git a/packages/components/src/color-palette/test/index.js b/packages/components/src/color-palette/test/index.js
index 1478430d37ccc..ef1263535d0f4 100644
--- a/packages/components/src/color-palette/test/index.js
+++ b/packages/components/src/color-palette/test/index.js
@@ -90,13 +90,6 @@ describe( 'ColorPalette', () => {
test( 'should render dropdown content', () => {
expect( renderedContent ).toMatchSnapshot();
} );
-
- test( 'should call onToggle on click.', () => {
- renderedContent.simulate( 'changeComplete', { hex: currentColor } );
-
- expect( onChange ).toHaveBeenCalledTimes( 1 );
- expect( onChange ).toHaveBeenCalledWith( currentColor );
- } );
} );
} );
} );
diff --git a/packages/components/src/color-picker/README.md b/packages/components/src/color-picker/README.md
new file mode 100644
index 0000000000000..d2b1307a00d7b
--- /dev/null
+++ b/packages/components/src/color-picker/README.md
@@ -0,0 +1,23 @@
+# ColorPicker
+
+Accessible color picker.
+
+_Parts of the source code were derived and modified from [react-color](https://github.com/casesandberg/react-color/), released under the MIT license._
+
+## Usage
+```jsx
+import { ColorPicker } from '@wordpress/components';
+import { withState } from '@wordpress/compose';
+
+const MyColorPicker = withState( {
+ color: '#f00',
+} )( ( { color, setState } ) => {
+ return (
+ setState( value.hex ) }
+ disableAlpha
+ />
+ );
+} );
+```
diff --git a/packages/components/src/color-picker/alpha.js b/packages/components/src/color-picker/alpha.js
new file mode 100644
index 0000000000000..7e08d687f71bd
--- /dev/null
+++ b/packages/components/src/color-picker/alpha.js
@@ -0,0 +1,177 @@
+/**
+ * Parts of this source were derived and modified from react-color,
+ * released under the MIT license.
+ *
+ * https://github.com/casesandberg/react-color/
+ *
+ * Copyright (c) 2015 Case Sandberg
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/**
+ * External dependencies
+ */
+import { noop } from 'lodash';
+
+/**
+ * WordPress dependencies
+ */
+import { __ } from '@wordpress/i18n';
+import { Component, createRef } from '@wordpress/element';
+import { TAB } from '@wordpress/keycodes';
+
+/**
+ * Internal dependencies
+ */
+import { calculateAlphaChange } from './utils';
+import KeyboardShortcuts from '../keyboard-shortcuts';
+
+export class Alpha extends Component {
+ constructor() {
+ super( ...arguments );
+
+ this.container = createRef();
+ this.increase = this.increase.bind( this );
+ this.decrease = this.decrease.bind( this );
+ this.handleChange = this.handleChange.bind( this );
+ this.handleMouseDown = this.handleMouseDown.bind( this );
+ this.handleMouseUp = this.handleMouseUp.bind( this );
+ }
+
+ componentWillUnmount() {
+ this.unbindEventListeners();
+ }
+
+ increase( amount = 0.01 ) {
+ const { hsl, onChange = noop } = this.props;
+ amount = parseInt( amount * 100, 10 );
+ const change = {
+ h: hsl.h,
+ s: hsl.s,
+ l: hsl.l,
+ a: ( parseInt( hsl.a * 100, 10 ) + amount ) / 100,
+ source: 'rgb',
+ };
+ onChange( change );
+ }
+
+ decrease( amount = 0.01 ) {
+ const { hsl, onChange = noop } = this.props;
+ const intValue = parseInt( hsl.a * 100, 10 ) - parseInt( amount * 100, 10 );
+ const change = {
+ h: hsl.h,
+ s: hsl.s,
+ l: hsl.l,
+ a: hsl.a <= amount ? 0 : intValue / 100,
+ source: 'rgb',
+ };
+ onChange( change );
+ }
+
+ handleChange( e ) {
+ const { onChange = noop } = this.props;
+ const change = calculateAlphaChange( e, this.props, this.container.current );
+ if ( change ) {
+ onChange( change, e );
+ }
+ }
+
+ handleMouseDown( e ) {
+ this.handleChange( e );
+ window.addEventListener( 'mousemove', this.handleChange );
+ window.addEventListener( 'mouseup', this.handleMouseUp );
+ }
+
+ handleMouseUp() {
+ this.unbindEventListeners();
+ }
+
+ preventKeyEvents( event ) {
+ if ( event.keyCode === TAB ) {
+ return;
+ }
+ event.preventDefault();
+ }
+
+ unbindEventListeners() {
+ window.removeEventListener( 'mousemove', this.handleChange );
+ window.removeEventListener( 'mouseup', this.handleMouseUp );
+ }
+
+ render() {
+ const { rgb } = this.props;
+ const rgbString = `${ rgb.r },${ rgb.g },${ rgb.b }`;
+ const gradient = {
+ background: `linear-gradient(to right, rgba(${ rgbString }, 0) 0%, rgba(${ rgbString }, 1) 100%)`,
+ };
+ const pointerLocation = { left: `${ rgb.a * 100 }%` };
+
+ const shortcuts = {
+ up: () => this.increase(),
+ right: () => this.increase(),
+ 'shift+up': () => this.increase( 0.1 ),
+ 'shift+right': () => this.increase( 0.1 ),
+ pageup: () => this.increase( 0.1 ),
+ end: () => this.increase( 1 ),
+ down: () => this.decrease(),
+ left: () => this.decrease(),
+ 'shift+down': () => this.decrease( 0.1 ),
+ 'shift+left': () => this.decrease( 0.1 ),
+ pagedown: () => this.decrease( 0.1 ),
+ home: () => this.decrease( 1 ),
+ };
+
+ return (
+
+
+
+ { /* eslint-disable jsx-a11y/no-static-element-interactions */ }
+
+ { /* eslint-enable jsx-a11y/no-static-element-interactions */ }
+
+
+ );
+ }
+}
+
+export default Alpha;
diff --git a/packages/components/src/color-picker/hue.js b/packages/components/src/color-picker/hue.js
new file mode 100644
index 0000000000000..8f78c43936579
--- /dev/null
+++ b/packages/components/src/color-picker/hue.js
@@ -0,0 +1,174 @@
+/**
+ * Parts of this source were derived and modified from react-color,
+ * released under the MIT license.
+ *
+ * https://github.com/casesandberg/react-color/
+ *
+ * Copyright (c) 2015 Case Sandberg
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/**
+ * External dependencies
+ */
+import { noop } from 'lodash';
+
+/**
+ * WordPress dependencies
+ */
+import { withInstanceId } from '@wordpress/compose';
+import { __ } from '@wordpress/i18n';
+import { Component, createRef } from '@wordpress/element';
+import { TAB } from '@wordpress/keycodes';
+
+/**
+ * Internal dependencies
+ */
+import { calculateHueChange } from './utils';
+import KeyboardShortcuts from '../keyboard-shortcuts';
+
+export class Hue extends Component {
+ constructor() {
+ super( ...arguments );
+
+ this.container = createRef();
+ this.increase = this.increase.bind( this );
+ this.decrease = this.decrease.bind( this );
+ this.handleChange = this.handleChange.bind( this );
+ this.handleMouseDown = this.handleMouseDown.bind( this );
+ this.handleMouseUp = this.handleMouseUp.bind( this );
+ }
+
+ componentWillUnmount() {
+ this.unbindEventListeners();
+ }
+
+ increase( amount = 1 ) {
+ const { hsl, onChange = noop } = this.props;
+ const change = {
+ h: hsl.h + amount >= 359 ? 359 : hsl.h + amount,
+ s: hsl.s,
+ l: hsl.l,
+ a: hsl.a,
+ source: 'rgb',
+ };
+ onChange( change );
+ }
+
+ decrease( amount = 1 ) {
+ const { hsl, onChange = noop } = this.props;
+ const change = {
+ h: hsl.h <= amount ? 0 : hsl.h - amount,
+ s: hsl.s,
+ l: hsl.l,
+ a: hsl.a,
+ source: 'rgb',
+ };
+ onChange( change );
+ }
+
+ handleChange( e ) {
+ const { onChange = noop } = this.props;
+ const change = calculateHueChange( e, this.props, this.container.current );
+ if ( change ) {
+ onChange( change, e );
+ }
+ }
+
+ handleMouseDown( e ) {
+ this.handleChange( e );
+ window.addEventListener( 'mousemove', this.handleChange );
+ window.addEventListener( 'mouseup', this.handleMouseUp );
+ }
+
+ handleMouseUp() {
+ this.unbindEventListeners();
+ }
+
+ preventKeyEvents( event ) {
+ if ( event.keyCode === TAB ) {
+ return;
+ }
+ event.preventDefault();
+ }
+
+ unbindEventListeners() {
+ window.removeEventListener( 'mousemove', this.handleChange );
+ window.removeEventListener( 'mouseup', this.handleMouseUp );
+ }
+
+ render() {
+ const { hsl = {}, instanceId } = this.props;
+
+ const pointerLocation = { left: `${ ( hsl.h * 100 ) / 360 }%` };
+ const shortcuts = {
+ up: () => this.increase(),
+ right: () => this.increase(),
+ 'shift+up': () => this.increase( 10 ),
+ 'shift+right': () => this.increase( 10 ),
+ pageup: () => this.increase( 10 ),
+ end: () => this.increase( 359 ),
+ down: () => this.decrease(),
+ left: () => this.decrease(),
+ 'shift+down': () => this.decrease( 10 ),
+ 'shift+left': () => this.decrease( 10 ),
+ pagedown: () => this.decrease( 10 ),
+ home: () => this.decrease( 359 ),
+ };
+
+ return (
+
+
+
+ { /* eslint-disable jsx-a11y/no-static-element-interactions */ }
+
+
+
+ { __( 'Move the arrow left or right to change hue.' ) }
+
+
+ { /* eslint-enable jsx-a11y/no-static-element-interactions */ }
+
+
+ );
+ }
+}
+
+export default withInstanceId( Hue );
diff --git a/packages/components/src/color-picker/index.js b/packages/components/src/color-picker/index.js
new file mode 100644
index 0000000000000..ba786b33fcb32
--- /dev/null
+++ b/packages/components/src/color-picker/index.js
@@ -0,0 +1,120 @@
+/**
+ * Parts of this source were derived and modified from react-color,
+ * released under the MIT license.
+ *
+ * https://github.com/casesandberg/react-color/
+ *
+ * Copyright (c) 2015 Case Sandberg
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/**
+ * External dependencies
+ */
+import classnames from 'classnames';
+import { debounce, noop, partial } from 'lodash';
+
+/**
+ * WordPress dependencies
+ */
+import { Component } from '@wordpress/element';
+
+/**
+ * Internal dependencies
+ */
+import Alpha from './alpha';
+import Hue from './hue';
+import Inputs from './inputs';
+import Saturation from './saturation';
+import { colorToState, simpleCheckForValidColor } from './utils';
+
+export default class ColorPicker extends Component {
+ constructor( { color = '0071a1' } ) {
+ super( ...arguments );
+ this.state = colorToState( color );
+ this.handleChange = this.handleChange.bind( this );
+ }
+
+ handleChange( data ) {
+ const { oldHue, onChangeComplete = noop } = this.props;
+ const isValidColor = simpleCheckForValidColor( data );
+ if ( isValidColor ) {
+ const colors = colorToState( data, data.h || oldHue );
+ this.setState(
+ colors,
+ debounce( partial( onChangeComplete, colors ), 100 )
+ );
+ }
+ }
+
+ render() {
+ const { className, disableAlpha } = this.props;
+ const { color, hex, hsl, hsv, rgb } = this.state;
+ const classes = classnames( className, {
+ 'components-color-picker': true,
+ 'is-alpha-disabled': disableAlpha,
+ 'is-alpha-enabled': ! disableAlpha,
+ } );
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ { disableAlpha ? null : (
+
+ ) }
+
+
+
+
+
+
+ );
+ }
+}
diff --git a/packages/components/src/color-picker/inputs.js b/packages/components/src/color-picker/inputs.js
new file mode 100644
index 0000000000000..c42aa0022f6c6
--- /dev/null
+++ b/packages/components/src/color-picker/inputs.js
@@ -0,0 +1,289 @@
+/**
+ * External dependencies
+ */
+import { omit } from 'lodash';
+
+/**
+ * WordPress dependencies
+ */
+import { speak } from '@wordpress/a11y';
+import { __ } from '@wordpress/i18n';
+import { Component } from '@wordpress/element';
+import { DOWN, ENTER, UP } from '@wordpress/keycodes';
+
+/**
+ * Internal dependencies
+ */
+import IconButton from '../icon-button';
+import { isValidHex } from './utils';
+import TextControl from '../text-control';
+
+/* Wrapper for TextControl, only used to handle intermediate state while typing. */
+class Input extends Component {
+ constructor( { value } ) {
+ super( ...arguments );
+ this.state = { value: String( value ).toLowerCase() };
+ this.handleBlur = this.handleBlur.bind( this );
+ this.handleChange = this.handleChange.bind( this );
+ this.handleKeyDown = this.handleKeyDown.bind( this );
+ }
+
+ componentWillReceiveProps( nextProps ) {
+ if ( nextProps.value !== this.props.value ) {
+ this.setState( {
+ value: String( nextProps.value ).toLowerCase(),
+ } );
+ }
+ }
+
+ handleBlur() {
+ const { valueKey, onChange } = this.props;
+ const { value } = this.state;
+ onChange( { [ valueKey ]: value } );
+ }
+
+ handleChange( value ) {
+ const { valueKey, onChange } = this.props;
+ // Protect against expanding a value while we're typing.
+ if ( value.length > 4 ) {
+ onChange( { [ valueKey ]: value } );
+ }
+ this.setState( { value } );
+ }
+
+ handleKeyDown( { keyCode } ) {
+ if ( keyCode !== ENTER && keyCode !== UP && keyCode !== DOWN ) {
+ return;
+ }
+ const { value } = this.state;
+ const { valueKey, onChange } = this.props;
+ onChange( { [ valueKey ]: value } );
+ }
+
+ render() {
+ const { label, ...props } = this.props;
+ const { value } = this.state;
+ return (
+ this.handleChange( newValue ) }
+ onBlur={ this.handleBlur }
+ onKeyDown={ this.handleKeyDown }
+ { ...omit( props, [ 'onChange', 'value', 'valueKey' ] ) }
+ />
+ );
+ }
+}
+
+export class Inputs extends Component {
+ constructor( { hsl } ) {
+ super( ...arguments );
+
+ const view = hsl.a === 1 ? 'hex' : 'rgb';
+ this.state = { view };
+
+ this.toggleViews = this.toggleViews.bind( this );
+ this.handleChange = this.handleChange.bind( this );
+ }
+
+ static getDerivedStateFromProps( props, state ) {
+ if ( props.hsl.a !== 1 && state.view === 'hex' ) {
+ return { view: 'rgb' };
+ }
+ return null;
+ }
+
+ toggleViews() {
+ if ( this.state.view === 'hex' ) {
+ this.setState( { view: 'rgb' } );
+
+ speak( __( 'RGB mode active' ) );
+ } else if ( this.state.view === 'rgb' ) {
+ this.setState( { view: 'hsl' } );
+
+ speak( __( 'Hue/saturation/lightness mode active' ) );
+ } else if ( this.state.view === 'hsl' ) {
+ if ( this.props.hsl.a === 1 ) {
+ this.setState( { view: 'hex' } );
+
+ speak( __( 'Hex color mode active' ) );
+ } else {
+ this.setState( { view: 'rgb' } );
+
+ speak( __( 'RGB mode active' ) );
+ }
+ }
+ }
+
+ handleChange( data ) {
+ if ( data.hex ) {
+ if ( isValidHex( data.hex ) ) {
+ this.props.onChange( {
+ hex: data.hex,
+ source: 'hex',
+ } );
+ }
+ } else if ( data.r || data.g || data.b ) {
+ this.props.onChange( {
+ r: data.r || this.props.rgb.r,
+ g: data.g || this.props.rgb.g,
+ b: data.b || this.props.rgb.b,
+ source: 'rgb',
+ } );
+ } else if ( data.a ) {
+ if ( data.a < 0 ) {
+ data.a = 0;
+ } else if ( data.a > 1 ) {
+ data.a = 1;
+ }
+
+ this.props.onChange( {
+ h: this.props.hsl.h,
+ s: this.props.hsl.s,
+ l: this.props.hsl.l,
+ a: Math.round( data.a * 100 ) / 100,
+ source: 'rgb',
+ } );
+ } else if ( data.h || data.s || data.l ) {
+ this.props.onChange( {
+ h: data.h || this.props.hsl.h,
+ s: data.s || this.props.hsl.s,
+ l: data.l || this.props.hsl.l,
+ source: 'hsl',
+ } );
+ }
+ }
+
+ renderFields() {
+ const { disableAlpha = false } = this.props;
+ if ( this.state.view === 'hex' ) {
+ return (
+
+
+
+ );
+ } else if ( this.state.view === 'rgb' ) {
+ return (
+
+
+ { __( 'Color value in RGB' ) }
+
+
+
+
+
+ { disableAlpha ? null : (
+
+ ) }
+
+
+ );
+ } else if ( this.state.view === 'hsl' ) {
+ return (
+
+
+ { __( 'Color value in HSL' ) }
+
+
+
+
+
+ { disableAlpha ? null : (
+
+ ) }
+
+
+ );
+ }
+ }
+
+ render() {
+ return (
+
+ { this.renderFields() }
+
+
+
+
+ );
+ }
+}
+
+export default Inputs;
diff --git a/packages/components/src/color-picker/saturation.js b/packages/components/src/color-picker/saturation.js
new file mode 100644
index 0000000000000..6ba852756609a
--- /dev/null
+++ b/packages/components/src/color-picker/saturation.js
@@ -0,0 +1,186 @@
+/**
+ * Parts of this source were derived and modified from react-color,
+ * released under the MIT license.
+ *
+ * https://github.com/casesandberg/react-color/
+ *
+ * Copyright (c) 2015 Case Sandberg
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/**
+ * External dependencies
+ */
+import { clamp, noop, throttle } from 'lodash';
+
+/**
+ * WordPress dependencies
+ */
+import { __ } from '@wordpress/i18n';
+import { Component, createRef } from '@wordpress/element';
+import { TAB } from '@wordpress/keycodes';
+import { withInstanceId } from '@wordpress/compose';
+
+/**
+ * Internal dependencies
+ */
+import { calculateSaturationChange } from './utils';
+import KeyboardShortcuts from '../keyboard-shortcuts';
+
+export class Saturation extends Component {
+ constructor( props ) {
+ super( props );
+
+ this.throttle = throttle( ( fn, data, e ) => {
+ fn( data, e );
+ }, 50 );
+
+ this.container = createRef();
+ this.saturate = this.saturate.bind( this );
+ this.brighten = this.brighten.bind( this );
+ this.handleChange = this.handleChange.bind( this );
+ this.handleMouseDown = this.handleMouseDown.bind( this );
+ this.handleMouseUp = this.handleMouseUp.bind( this );
+ }
+
+ componentWillUnmount() {
+ this.throttle.cancel();
+ this.unbindEventListeners();
+ }
+
+ saturate( amount = 0.01 ) {
+ const { hsv, onChange = noop } = this.props;
+ const intSaturation = clamp(
+ hsv.s + Math.round( amount * 100 ),
+ 0,
+ 100
+ );
+ const change = {
+ h: hsv.h,
+ s: intSaturation,
+ v: hsv.v,
+ a: hsv.a,
+ source: 'rgb',
+ };
+
+ onChange( change );
+ }
+
+ brighten( amount = 0.01 ) {
+ const { hsv, onChange = noop } = this.props;
+ const intValue = clamp(
+ hsv.v + Math.round( amount * 100 ),
+ 0,
+ 100
+ );
+ const change = {
+ h: hsv.h,
+ s: hsv.s,
+ v: intValue,
+ a: hsv.a,
+ source: 'rgb',
+ };
+
+ onChange( change );
+ }
+
+ handleChange( e ) {
+ const { onChange = noop } = this.props;
+ const change = calculateSaturationChange( e, this.props, this.container.current );
+ this.throttle( onChange, change, e );
+ }
+
+ handleMouseDown( e ) {
+ this.handleChange( e );
+ window.addEventListener( 'mousemove', this.handleChange );
+ window.addEventListener( 'mouseup', this.handleMouseUp );
+ }
+
+ handleMouseUp() {
+ this.unbindEventListeners();
+ }
+
+ preventKeyEvents( event ) {
+ if ( event.keyCode === TAB ) {
+ return;
+ }
+ event.preventDefault();
+ }
+
+ unbindEventListeners() {
+ window.removeEventListener( 'mousemove', this.handleChange );
+ window.removeEventListener( 'mouseup', this.handleMouseUp );
+ }
+
+ render() {
+ const { hsv, hsl, instanceId } = this.props;
+ const pointerLocation = {
+ top: `${ -( hsv.v ) + 100 }%`,
+ left: `${ hsv.s }%`,
+ };
+ const shortcuts = {
+ up: () => this.brighten(),
+ 'shift+up': () => this.brighten( 0.1 ),
+ pageup: () => this.brighten( 1 ),
+ down: () => this.brighten( -0.01 ),
+ 'shift+down': () => this.brighten( -0.1 ),
+ pagedown: () => this.brighten( -1 ),
+ right: () => this.saturate(),
+ 'shift+right': () => this.saturate( 0.1 ),
+ end: () => this.saturate( 1 ),
+ left: () => this.saturate( -0.01 ),
+ 'shift+left': () => this.saturate( -0.1 ),
+ home: () => this.saturate( -1 ),
+ };
+
+ /* eslint-disable jsx-a11y/no-static-element-interactions */
+ return (
+
+
+
+
+
+
+ { __(
+ 'Use your arrow keys to change the base color. Move up to lighten the color, down to darken, left to increase saturation, and right to decrease saturation.'
+ ) }
+
+
+
+ );
+ /* eslint-enable jsx-a11y/no-static-element-interactions */
+ }
+}
+
+export default withInstanceId( Saturation );
diff --git a/packages/components/src/color-picker/style.scss b/packages/components/src/color-picker/style.scss
new file mode 100644
index 0000000000000..336c684dfeca9
--- /dev/null
+++ b/packages/components/src/color-picker/style.scss
@@ -0,0 +1,209 @@
+/**
+ * Parts of this source were derived and modified from react-color,
+ * released under the MIT license.
+ *
+ * https://github.com/casesandberg/react-color/
+ *
+ * Copyright (c) 2015 Case Sandberg
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+.components-color-picker {
+ width: 100%;
+ overflow: hidden;
+}
+.components-color-picker__saturation {
+ width: 100%;
+ padding-bottom: 55%;
+ position: relative;
+}
+.components-color-picker__body {
+ padding: 16px 16px 12px;
+}
+.components-color-picker__controls {
+ display: flex;
+}
+.components-color-picker__saturation-pointer,
+.components-color-picker__hue-pointer,
+.components-color-picker__alpha-pointer {
+ padding: 0;
+ position: absolute;
+ cursor: pointer;
+ box-shadow: none;
+ border: none;
+}
+
+/* CURRENT COLOR COMPONENT */
+.components-color-picker__swatch {
+ margin-right: 8px;
+ width: 32px;
+ height: 32px;
+ border-radius: 50%;
+ position: relative;
+ overflow: hidden;
+ background-image:
+ linear-gradient(45deg, #ddd 25%, transparent 25%),
+ linear-gradient(-45deg, #ddd 25%, transparent 25%),
+ linear-gradient(45deg, transparent 75%, #ddd 75%),
+ linear-gradient(-45deg, transparent 75%, #ddd 75%);
+ background-size: 10px 10px;
+ background-position: 0 0, 0 5px, 5px -5px, -5px 0;
+
+ .is-alpha-disabled & {
+ width: 12px;
+ height: 12px;
+ margin-top: 0;
+ }
+}
+.components-color-picker__active {
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ border-radius: 50%;
+ box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1);
+ z-index: 2;
+}
+
+/* SATURATION COMPONENT */
+.components-color-picker__saturation-color,
+.components-color-picker__saturation-white,
+.components-color-picker__saturation-black {
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+}
+.components-color-picker__saturation-color {
+ overflow: hidden;
+}
+.components-color-picker__saturation-white {
+ background: linear-gradient(to right, #fff, rgba(255, 255, 255, 0));
+}
+.components-color-picker__saturation-black {
+ background: linear-gradient(to top, #000, rgba(0, 0, 0, 0));
+}
+.components-color-picker__saturation-pointer {
+ width: 8px;
+ height: 8px;
+ box-shadow:
+ 0 0 0 1.5px #fff,
+ inset 0 0 1px 1px rgba(0, 0, 0, 0.3),
+ 0 0 1px 2px rgba(0, 0, 0, 0.4);
+ border-radius: 50%;
+ background-color: transparent;
+ transform: translate(-4px, -4px);
+}
+
+/* HUE & ALPHA BARS */
+.components-color-picker__toggles {
+ flex: 1;
+}
+.components-color-picker__alpha {
+ background-image:
+ linear-gradient(45deg, #ddd 25%, transparent 25%),
+ linear-gradient(-45deg, #ddd 25%, transparent 25%),
+ linear-gradient(45deg, transparent 75%, #ddd 75%),
+ linear-gradient(-45deg, transparent 75%, #ddd 75%);
+ background-size: 10px 10px;
+ background-position: 0 0, 0 5px, 5px -5px, -5px 0;
+}
+.components-color-picker__hue-gradient,
+.components-color-picker__alpha-gradient {
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+}
+.components-color-picker__hue,
+.components-color-picker__alpha {
+ height: 12px;
+ position: relative;
+}
+.is-alpha-enabled .components-color-picker__hue {
+ margin-bottom: 8px;
+}
+.components-color-picker__hue-bar,
+.components-color-picker__alpha-bar {
+ position: relative;
+ margin: 0 3px;
+ height: 100%;
+ padding: 0 2px;
+}
+.components-color-picker__hue-gradient {
+ background: linear-gradient(to right, #f00 0%, #ff0 17%, #0f0 33%, #0ff 50%, #00f 67%, #f0f 83%, #f00 100%);
+}
+.components-color-picker__hue-pointer,
+.components-color-picker__alpha-pointer {
+ left: 0;
+ width: 14px;
+ height: 14px;
+ border-radius: 50%;
+ box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.37);
+ background: #fff;
+ transform: translate(-7px, -1px);
+}
+
+.components-color-picker__hue-pointer,
+.components-color-picker__saturation-pointer {
+ transition: box-shadow 0.1s linear;
+}
+
+.components-color-picker__saturation-pointer:focus {
+ box-shadow:
+ 0 0 0 2px #fff,
+ 0 0 0 4px $blue-medium-500,
+ 0 0 5px 0 $blue-medium-500,
+ inset 0 0 1px 1px rgba(0, 0, 0, 0.3),
+ 0 0 1px 2px rgba(0, 0, 0, 0.4);
+}
+
+.components-color-picker__hue-pointer:focus,
+.components-color-picker__alpha-pointer:focus {
+ border-color: $blue-medium-500;
+ box-shadow:
+ 0 0 0 2px $blue-medium-500,
+ 0 0 3px 0 $blue-medium-500;
+ outline: 2px solid transparent;
+ outline-offset: -2px;
+}
+
+
+/* INPUTS COMPONENT */
+.components-color-picker__inputs-wrapper {
+ margin: 0 -4px;
+ padding-top: 16px;
+ display: flex;
+ align-items: flex-end;
+
+ fieldset {
+ flex: 1;
+ }
+}
+.components-color-picker__inputs-fields {
+ display: flex;
+
+ .components-base-control__field {
+ margin: 0 4px;
+ }
+}
diff --git a/packages/components/src/color-picker/test/__snapshots__/index.js.snap b/packages/components/src/color-picker/test/__snapshots__/index.js.snap
new file mode 100644
index 0000000000000..c143801f9e29f
--- /dev/null
+++ b/packages/components/src/color-picker/test/__snapshots__/index.js.snap
@@ -0,0 +1,87 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`ColorPicker should render color picker 1`] = `
+
+`;
diff --git a/packages/components/src/color-picker/test/index.js b/packages/components/src/color-picker/test/index.js
new file mode 100644
index 0000000000000..5510da6e138e2
--- /dev/null
+++ b/packages/components/src/color-picker/test/index.js
@@ -0,0 +1,26 @@
+/**
+ * External dependencies
+ */
+import ShallowRenderer from 'react-test-renderer/shallow';
+
+/**
+ * Internal dependencies
+ */
+import ColorPicker from '../';
+
+describe( 'ColorPicker', () => {
+ test( 'should render color picker', () => {
+ const color = '#fff';
+
+ const renderer = new ShallowRenderer();
+ renderer.render(
+ {} }
+ disableAlpha
+ />
+ );
+
+ expect( renderer.getRenderOutput() ).toMatchSnapshot();
+ } );
+} );
diff --git a/packages/components/src/color-picker/utils.js b/packages/components/src/color-picker/utils.js
new file mode 100644
index 0000000000000..dada93c31c16e
--- /dev/null
+++ b/packages/components/src/color-picker/utils.js
@@ -0,0 +1,212 @@
+/**
+ * Parts of this source were derived and modified from react-color,
+ * released under the MIT license.
+ *
+ * https://github.com/casesandberg/react-color/
+ *
+ * Copyright (c) 2015 Case Sandberg
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/**
+ * External dependencies
+ */
+import { each } from 'lodash';
+import tinycolor from 'tinycolor2';
+
+/**
+ * Given a hex color, get all other color properties (rgb, alpha, etc).
+ *
+ * @param {Object|string} data A hex color string or an object with a hex property
+ * @param {string} oldHue A reference to the hue of the previous color, otherwise dragging the saturation to zero will reset the current hue to zero as well. See https://github.com/casesandberg/react-color/issues/29#issuecomment-132686909.
+ * @return {Object} An object of different color representations.
+ */
+export function colorToState( data = {}, oldHue = false ) {
+ const color = data.hex ? tinycolor( data.hex ) : tinycolor( data );
+ const hsl = color.toHsl();
+ hsl.h = Math.round( hsl.h );
+ hsl.s = Math.round( hsl.s * 100 );
+ hsl.l = Math.round( hsl.l * 100 );
+ const hsv = color.toHsv();
+ hsv.h = Math.round( hsv.h );
+ hsv.s = Math.round( hsv.s * 100 );
+ hsv.v = Math.round( hsv.v * 100 );
+ const rgb = color.toRgb();
+ const hex = color.toHex();
+ if ( hsl.s === 0 ) {
+ hsl.h = oldHue || 0;
+ hsv.h = oldHue || 0;
+ }
+ const transparent = hex === '000000' && rgb.a === 0;
+
+ return {
+ color,
+ hex: transparent ? 'transparent' : `#${ hex }`,
+ hsl,
+ hsv,
+ oldHue: data.h || oldHue || hsl.h,
+ rgb,
+ source: data.source,
+ };
+}
+
+/**
+ * Get the top/left offsets of a point in a container, also returns the container width/height.
+ *
+ * @param {Event} e Mouse or touch event with a location coordinate.
+ * @param {HTMLElement} container The container div, returned point is relative to this container.
+ * @return {Object} An object of the offset positions & container size.
+ */
+function getPointOffset( e, container ) {
+ e.preventDefault();
+ const {
+ left: containerLeft,
+ top: containerTop,
+ width,
+ height,
+ } = container.getBoundingClientRect();
+ const x = typeof e.pageX === 'number' ? e.pageX : e.touches[ 0 ].pageX;
+ const y = typeof e.pageY === 'number' ? e.pageY : e.touches[ 0 ].pageY;
+ let left = x - ( containerLeft + window.pageXOffset );
+ let top = y - ( containerTop + window.pageYOffset );
+
+ if ( left < 0 ) {
+ left = 0;
+ } else if ( left > width ) {
+ left = width;
+ } else if ( top < 0 ) {
+ top = 0;
+ } else if ( top > height ) {
+ top = height;
+ }
+
+ return { top, left, width, height };
+}
+
+/**
+ * Check if a string is a valid hex color code.
+ *
+ * @param {string} hex A possible hex color.
+ * @return {boolean} True if the color is a valid hex color.
+ */
+export function isValidHex( hex ) {
+ // disable hex4 and hex8
+ const lh = String( hex ).charAt( 0 ) === '#' ? 1 : 0;
+ return (
+ hex.length !== 4 + lh && hex.length < 7 + lh && tinycolor( hex ).isValid()
+ );
+}
+
+/**
+ * Check an object for any valid color properties.
+ *
+ * @param {Object} data A possible object representing a color.
+ * @return {Object|boolean} If a valid representation of color, returns the data object. Otherwise returns false.
+ */
+export function simpleCheckForValidColor( data ) {
+ const keysToCheck = [ 'r', 'g', 'b', 'a', 'h', 's', 'l', 'v' ];
+ let checked = 0;
+ let passed = 0;
+ each( keysToCheck, ( letter ) => {
+ if ( data[ letter ] ) {
+ checked += 1;
+ if ( ! isNaN( data[ letter ] ) ) {
+ passed += 1;
+ }
+ }
+ } );
+ return checked === passed ? data : false;
+}
+
+/**
+ * Calculate the current alpha based on a mouse or touch event
+ *
+ * @param {Event} e A mouse or touch event on the alpha bar.
+ * @param {Object} props The current component props
+ * @param {HTMLElement} container The container div for the alpha bar graph.
+ * @return {Object|null} If the alpha value has changed, returns a new color object.
+ */
+export function calculateAlphaChange( e, props, container ) {
+ const { left, width } = getPointOffset( e, container );
+ const a = left < 0 ? 0 : Math.round( ( left * 100 ) / width ) / 100;
+
+ if ( props.hsl.a !== a ) {
+ return {
+ h: props.hsl.h,
+ s: props.hsl.s,
+ l: props.hsl.l,
+ a,
+ source: 'rgb',
+ };
+ }
+ return null;
+}
+
+/**
+ * Calculate the current hue based on a mouse or touch event
+ *
+ * @param {Event} e A mouse or touch event on the hue bar.
+ * @param {Object} props The current component props
+ * @param {HTMLElement} container The container div for the hue bar graph.
+ * @return {Object|null} If the hue value has changed, returns a new color object.
+ */
+export function calculateHueChange( e, props, container ) {
+ const { left, width } = getPointOffset( e, container );
+ const percent = ( left * 100 ) / width;
+ const h = left >= width ? 359 : ( 360 * percent ) / 100;
+
+ if ( props.hsl.h !== h ) {
+ return {
+ h,
+ s: props.hsl.s,
+ l: props.hsl.l,
+ a: props.hsl.a,
+ source: 'rgb',
+ };
+ }
+ return null;
+}
+
+/**
+ * Calculate the current saturation & brightness based on a mouse or touch event
+ *
+ * @param {Event} e A mouse or touch event on the saturation graph.
+ * @param {Object} props The current component props
+ * @param {HTMLElement} container The container div for the 2D saturation graph.
+ * @return {Object} Returns a new color object.
+ */
+export function calculateSaturationChange( e, props, container ) {
+ const { top, left, width, height } = getPointOffset( e, container );
+ const saturation = left < 0 ? 0 : ( left * 100 ) / width;
+ let bright = top >= height ? 0 : -( ( top * 100 ) / height ) + 100;
+ // `v` values less than 1 are considered in the [0,1] range, causing unexpected behavior at the bottom
+ // of the chart. To fix this, we assume any value less than 1 should be 0 brightness.
+ if ( bright < 1 ) {
+ bright = 0;
+ }
+
+ return {
+ h: props.hsl.h,
+ s: saturation,
+ v: bright,
+ a: props.hsl.a,
+ source: 'rgb',
+ };
+}
diff --git a/packages/components/src/index.js b/packages/components/src/index.js
index 2668702a35556..fcaf2b981c921 100644
--- a/packages/components/src/index.js
+++ b/packages/components/src/index.js
@@ -9,6 +9,7 @@ export { default as CheckboxControl } from './checkbox-control';
export { default as ClipboardButton } from './clipboard-button';
export { default as ColorIndicator } from './color-indicator';
export { default as ColorPalette } from './color-palette';
+export { default as ColorPicker } from './color-picker';
export { default as Dashicon } from './dashicon';
export { DateTimePicker, DatePicker, TimePicker } from './date-time';
export { default as Disabled } from './disabled';
diff --git a/packages/components/src/style.scss b/packages/components/src/style.scss
index 3a2bb018ac41c..751eae5aaa3c1 100644
--- a/packages/components/src/style.scss
+++ b/packages/components/src/style.scss
@@ -5,6 +5,7 @@
@import "./checkbox-control/style.scss";
@import "./color-indicator/style.scss";
@import "./color-palette/style.scss";
+@import "./color-picker/style.scss";
@import "./dashicon/style.scss";
@import "./date-time/style.scss";
@import "./disabled/style.scss";
From 274348e509f5e75d792ed1792ca96be4eca02040 Mon Sep 17 00:00:00 2001
From: Brandon Payton
Date: Fri, 19 Oct 2018 08:16:14 -0700
Subject: [PATCH 028/121] Address review of unregistered type handler (#10776)
---
packages/block-library/src/missing/index.js | 4 +---
packages/blocks/src/api/parser.js | 5 ++++-
2 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/packages/block-library/src/missing/index.js b/packages/block-library/src/missing/index.js
index 058a667c33ad0..1ef9391b9264c 100644
--- a/packages/block-library/src/missing/index.js
+++ b/packages/block-library/src/missing/index.js
@@ -37,9 +37,7 @@ function MissingBlockWarning( { attributes, convertToHTML } ) {
{ messageHTML }
-
- { originalUndelimitedContent }
-
+ { originalUndelimitedContent }
);
}
diff --git a/packages/blocks/src/api/parser.js b/packages/blocks/src/api/parser.js
index 68cc6af5ca180..0ff1bff1ca9db 100644
--- a/packages/blocks/src/api/parser.js
+++ b/packages/blocks/src/api/parser.js
@@ -410,7 +410,7 @@ export function createBlockWithFallback( blockNode ) {
attributes = attributes || {};
// Trim content to avoid creation of intermediary freeform segments.
- const originalUndelimitedContent = innerHTML = innerHTML.trim();
+ innerHTML = innerHTML.trim();
// Use type from block content if available. Otherwise, default to the
// freeform content fallback.
@@ -437,6 +437,9 @@ export function createBlockWithFallback( blockNode ) {
let blockType = getBlockType( name );
if ( ! blockType ) {
+ // Preserve undelimited content for use by the unregistered type handler.
+ const originalUndelimitedContent = innerHTML;
+
// If detected as a block which is not registered, preserve comment
// delimiters in content of unregistered type handler.
if ( name ) {
From 1292d8bbfd887ca10d7852099c92170e12d93571 Mon Sep 17 00:00:00 2001
From: Kelly Dwan
Date: Fri, 19 Oct 2018 12:45:31 -0400
Subject: [PATCH 029/121] Color Picker: Fall back to min-width of popover
(#10805)
---
packages/components/src/color-palette/style.scss | 5 -----
1 file changed, 5 deletions(-)
diff --git a/packages/components/src/color-palette/style.scss b/packages/components/src/color-palette/style.scss
index 4d3428e1c2565..6627ad0c12420 100644
--- a/packages/components/src/color-palette/style.scss
+++ b/packages/components/src/color-palette/style.scss
@@ -145,11 +145,6 @@ $color-palette-circle-spacing: 14px;
background-clip: content-box, content-box, content-box, content-box, content-box, content-box, padding-box, padding-box, padding-box, padding-box, padding-box, padding-box;
}
-.components-color-palette__picker:not(.is-mobile).components-popover .components-popover__content {
- // ChromePicker has a hardcoded width, so we need to override the popover min-width.
- min-width: unset;
-}
-
.block-editor__container .components-popover.components-color-palette__picker.is-bottom {
z-index: z-index(".block-editor__container .components-popover.components-color-palette__picker.is-bottom");
}
From 9d727858c028fc270c97c62d0e2e827259ac3497 Mon Sep 17 00:00:00 2001
From: Andrea Fercia
Date: Fri, 19 Oct 2018 20:06:34 +0200
Subject: [PATCH 030/121] Make the color picker saturation control work with
windows screen readers. (#10807)
---
packages/components/src/color-picker/saturation.js | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/packages/components/src/color-picker/saturation.js b/packages/components/src/color-picker/saturation.js
index 6ba852756609a..8e86655025e92 100644
--- a/packages/components/src/color-picker/saturation.js
+++ b/packages/components/src/color-picker/saturation.js
@@ -150,7 +150,7 @@ export class Saturation extends Component {
home: () => this.saturate( -1 ),
};
- /* eslint-disable jsx-a11y/no-static-element-interactions */
+ /* eslint-disable jsx-a11y/no-static-element-interactions, jsx-a11y/no-noninteractive-element-interactions */
return (
+ onTouchStart={ this.handleChange }
+ role="application"
+ >
);
- /* eslint-enable jsx-a11y/no-static-element-interactions */
+ /* eslint-enable jsx-a11y/no-static-element-interactions, jsx-a11y/no-noninteractive-element-interactions */
}
}
From e8aaadad2f45885ed1be21a2e1995139bc2aab55 Mon Sep 17 00:00:00 2001
From: Andrew Duthie
Date: Fri, 19 Oct 2018 15:08:15 -0400
Subject: [PATCH 031/121] Components: Add `info` prop support to MenuItem,
FeatureToggle (#10802)
* Components: Allow aria-label override on IconButton
* Components: Add `info` prop support to MenuItem
* Edit Post: Add `info` prop support to FeatureToggle
* Edit Post: Add feature toggle info texts
* Components: Menu Item: Break text beyond mobile toolbar
* Components: Icon Button: Indent text by SVG margin
Avoids offset indent on wrapped text
* Tweak menu item styling
* Avoid italic text
---
packages/components/CHANGELOG.md | 13 +++-
packages/components/src/icon-button/index.js | 2 +-
.../components/src/icon-button/style.scss | 5 +-
.../components/src/icon-button/test/index.js | 5 ++
packages/components/src/menu-item/README.md | 47 ++++++++++++
packages/components/src/menu-item/index.js | 76 +++++++++++++------
packages/components/src/menu-item/style.scss | 18 +++--
.../test/__snapshots__/index.js.snap | 27 +++++++
.../components/src/menu-item/test/index.js | 27 ++++++-
.../components/header/feature-toggle/index.js | 4 +-
.../components/header/writing-menu/index.js | 18 ++++-
.../reusable-block-delete-button.js.snap | 4 +-
.../test/block-mode-toggle.js | 20 +++--
.../test/reusable-block-convert-button.js | 30 +++++---
.../test/reusable-block-delete-button.js | 14 +++-
.../__snapshots__/plugins-api.test.js.snap | 2 +-
test/e2e/specs/links.test.js | 19 ++---
17 files changed, 249 insertions(+), 82 deletions(-)
diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md
index 106fd58c41746..59ae8b21f3865 100644
--- a/packages/components/CHANGELOG.md
+++ b/packages/components/CHANGELOG.md
@@ -3,10 +3,15 @@
### New Feature
- Added a new `ColorPicker` component ([#10564](https://github.com/WordPress/gutenberg/pull/10564)).
+- `MenuItem` now accepts an `info` prop for including an extended description.
+
+### Bug Fix
+
+- `IconButton` correctly respects a passed `aria-label` prop.
### Deprecation
-- `wp.components.PanelColor` has been deprecated in favor of `wp.editor.PanelColorSettings`.
+- `PanelColor` has been deprecated in favor of `wp.editor.PanelColorSettings`.
## 4.1.0 (2018-10-10)
@@ -18,7 +23,7 @@
### Breaking Change
-- `wp.components.Draggable` as a DOM node drag handler has been removed. Please, use `wp.components.Draggable` as a wrap component for your DOM node drag handler.
+- `Draggable` as a DOM node drag handler has been removed. Please, use `Draggable` as a wrap component for your DOM node drag handler.
### Deprecation
@@ -29,9 +34,9 @@
### Breaking Change
- `withAPIData` has been removed. Please use the Core Data module or `@wordpress/api-fetch` directly instead.
-- `wp.components.Draggable` as a DOM node drag handler has been deprecated. Please, use `wp.components.Draggable` as a wrap component for your DOM node drag handler.
+- `Draggable` as a DOM node drag handler has been deprecated. Please, use `Draggable` as a wrap component for your DOM node drag handler.
- Change how required built-ins are polyfilled with Babel 7 ([#9171](https://github.com/WordPress/gutenberg/pull/9171)). If you're using an environment that has limited or no support for ES2015+ such as lower versions of IE then using [core-js](https://github.com/zloirock/core-js) or [@babel/polyfill](https://babeljs.io/docs/en/next/babel-polyfill) will add support for these methods.
-- `wp.components.withContext` has been removed. Please use `wp.element.createContext` instead. See: https://reactjs.org/docs/context.html.
+- `withContext` has been removed. Please use `wp.element.createContext` instead. See: https://reactjs.org/docs/context.html.
### New Feature
diff --git a/packages/components/src/icon-button/index.js b/packages/components/src/icon-button/index.js
index b6521f790fbfa..745152409ccd4 100644
--- a/packages/components/src/icon-button/index.js
+++ b/packages/components/src/icon-button/index.js
@@ -41,7 +41,7 @@ class IconButton extends Component {
);
let element = (
-
+
{ isString( icon ) ? : icon }
{ children }
diff --git a/packages/components/src/icon-button/style.scss b/packages/components/src/icon-button/style.scss
index c25c9c820345c..cc3ee2f89564b 100644
--- a/packages/components/src/icon-button/style.scss
+++ b/packages/components/src/icon-button/style.scss
@@ -8,7 +8,6 @@
color: $dark-gray-500;
position: relative;
overflow: hidden;
- text-indent: 4px;
border-radius: $radius-round-rectangle;
.dashicon {
@@ -20,6 +19,10 @@
svg {
fill: currentColor;
outline: none;
+
+ &:not:only-child {
+ margin-right: 4px;
+ }
}
&:not(:disabled):not([aria-disabled="true"]):not(.is-default):hover {
diff --git a/packages/components/src/icon-button/test/index.js b/packages/components/src/icon-button/test/index.js
index b8f8276afcf62..d5b67eec7930f 100644
--- a/packages/components/src/icon-button/test/index.js
+++ b/packages/components/src/icon-button/test/index.js
@@ -41,6 +41,11 @@ describe( 'IconButton', () => {
expect( iconButton.find( 'Button' ).prop( 'aria-label' ) ).toBe( 'WordPress' );
} );
+ it( 'should support explicit aria-label override', () => {
+ const iconButton = shallow( );
+ expect( iconButton.prop( 'aria-label' ) ).toBe( 'Custom' );
+ } );
+
it( 'should add an additional className', () => {
const iconButton = shallow( );
expect( iconButton.hasClass( 'test' ) ).toBe( true );
diff --git a/packages/components/src/menu-item/README.md b/packages/components/src/menu-item/README.md
index 974cd07b7dd25..c7f4777bd43b0 100644
--- a/packages/components/src/menu-item/README.md
+++ b/packages/components/src/menu-item/README.md
@@ -1,5 +1,7 @@
# MenuItem
+MenuItem is a component which renders a button intended to be used in combination with the [DropdownMenu component](../dropdown-menu).
+
## Usage
```jsx
@@ -18,3 +20,48 @@ const MyMenuItem = withState( {
) );
```
+
+## Props
+
+MenuItem supports the following props. Any additional props are passed through to the underlying [Button](../button) or [IconButton](../icon-button) component.
+
+### `children`
+
+- Type: `WPElement`
+- Required: No
+
+Element to render as child of button.
+
+Element
+
+### `label`
+
+- Type: `string`
+- Required: No
+
+String to use as primary button label text, applied as `aria-label`. Useful in cases where an `info` prop is passed, where `label` should be the minimal text of the button, described in further detail by `info`.
+
+Defaults to the value of `children`, if `children` is passed as a string.
+
+### `info`
+
+- Type: `string`
+- Required: No
+
+Text to use as description for button text.
+
+Refer to documentation for [`label`](#label).
+
+### `icon`
+
+- Type: `string`
+- Required: No
+
+Refer to documentation for [IconButton's `icon` prop](../icon-button/README.md#icon).
+
+### `shortcut`
+
+- Type: `string`
+- Required: No
+
+Refer to documentation for [Shortcut's `shortcut` prop](../shortcut/README.md#shortcut).
diff --git a/packages/components/src/menu-item/index.js b/packages/components/src/menu-item/index.js
index 0904eb2463e85..e281c6bf0ea65 100644
--- a/packages/components/src/menu-item/index.js
+++ b/packages/components/src/menu-item/index.js
@@ -7,7 +7,8 @@ import { isString } from 'lodash';
/**
* WordPress dependencies
*/
-import { cloneElement } from '@wordpress/element';
+import { createElement, cloneElement } from '@wordpress/element';
+import { withInstanceId } from '@wordpress/compose';
/**
* Internal dependencies
@@ -21,11 +22,45 @@ import IconButton from '../icon-button';
*
* @return {WPElement} More menu item.
*/
-function MenuItem( { children, className, icon, shortcut, isSelected, role = 'menuitem', ...props } ) {
+export function MenuItem( {
+ children,
+ label = children,
+ info,
+ className,
+ icon,
+ shortcut,
+ isSelected,
+ role = 'menuitem',
+ instanceId,
+ ...props
+} ) {
className = classnames( 'components-menu-item__button', className, {
'has-icon': icon,
} );
+ // Avoid using label if it is passed as non-string children.
+ label = isString( label ) ? label : undefined;
+
+ if ( info ) {
+ const infoId = 'edit-post-feature-toggle__info-' + instanceId;
+
+ // Deconstructed props is scoped to the function; mutation is fine.
+ props[ 'aria-describedby' ] = infoId;
+
+ children = (
+
+ { children }
+
+ { info }
+
+
+ );
+ }
+
+ let tagName = Button;
+
if ( icon ) {
if ( ! isString( icon ) ) {
icon = cloneElement( icon, {
@@ -35,31 +70,22 @@ function MenuItem( { children, className, icon, shortcut, isSelected, role = 'me
} );
}
- return (
-
- { children }
-
-
- );
+ tagName = IconButton;
+ props.icon = icon;
}
- return (
-
- { children }
-
-
+ return createElement(
+ tagName,
+ {
+ 'aria-label': label,
+ 'aria-checked': isSelected,
+ role,
+ className,
+ ...props,
+ },
+ children,
+
);
}
-export default MenuItem;
+export default withInstanceId( MenuItem );
diff --git a/packages/components/src/menu-item/style.scss b/packages/components/src/menu-item/style.scss
index d36b174ec893a..55c7137a3c672 100644
--- a/packages/components/src/menu-item/style.scss
+++ b/packages/components/src/menu-item/style.scss
@@ -3,7 +3,7 @@
width: 100%;
padding: 8px;
text-align: left;
- color: $dark-gray-500;
+ color: $dark-gray-600;
// Target plugin icons that can have arbitrary classes by using an aggressive selector.
.dashicon,
@@ -24,13 +24,17 @@
&:focus:not(:disabled):not([aria-disabled="true"]) {
@include menu-style__focus;
}
+}
- // Don't wrap text until viewport is beyond the mobile breakpoint.
- @include break-mobile() {
- .components-popover:not(.is-mobile) & {
- white-space: nowrap;
- }
- }
+.components-menu-item__info-wrapper {
+ display: flex;
+ flex-direction: column;
+}
+
+.components-menu-item__info {
+ margin-top: $grid-size-small;
+ font-size: $default-font-size - 1px;
+ opacity: 0.82;
}
.components-menu-item__shortcut {
diff --git a/packages/components/src/menu-item/test/__snapshots__/index.js.snap b/packages/components/src/menu-item/test/__snapshots__/index.js.snap
index 1427a4d1d2083..74eea47847f4a 100644
--- a/packages/components/src/menu-item/test/__snapshots__/index.js.snap
+++ b/packages/components/src/menu-item/test/__snapshots__/index.js.snap
@@ -3,6 +3,7 @@
exports[`MenuItem should match snapshot when all props provided 1`] = `
`;
+exports[`MenuItem should match snapshot when info is provided 1`] = `
+
+
+ My item
+
+ Extended description of My Item
+
+
+
+
+`;
+
exports[`MenuItem should match snapshot when isSelected and role are optionally provided 1`] = `
diff --git a/packages/components/src/menu-item/test/index.js b/packages/components/src/menu-item/test/index.js
index 1e3f89fbc05c7..9fbd8b869ff47 100644
--- a/packages/components/src/menu-item/test/index.js
+++ b/packages/components/src/menu-item/test/index.js
@@ -7,12 +7,12 @@ import { noop } from 'lodash';
/**
* Internal dependencies
*/
-import MenuItem from '../';
+import { MenuItem } from '../';
jest.mock( '../../button' );
describe( 'MenuItem', () => {
- test( 'should match snapshot when only label provided', () => {
+ it( 'should match snapshot when only label provided', () => {
const wrapper = shallow(
My item
@@ -22,7 +22,7 @@ describe( 'MenuItem', () => {
expect( wrapper ).toMatchSnapshot();
} );
- test( 'should match snapshot when all props provided', () => {
+ it( 'should match snapshot when all props provided', () => {
const wrapper = shallow(
{
expect( wrapper ).toMatchSnapshot();
} );
- test( 'should match snapshot when isSelected and role are optionally provided', () => {
+ it( 'should match snapshot when isSelected and role are optionally provided', () => {
const wrapper = shallow(
{
expect( wrapper ).toMatchSnapshot();
} );
+
+ it( 'should match snapshot when info is provided', () => {
+ const wrapper = shallow(
+
+ My item
+
+ );
+
+ expect( wrapper.prop( 'aria-label' ) ).not.toBeUndefined();
+ expect( wrapper ).toMatchSnapshot();
+ } );
+
+ it( 'should avoid using aria-label if only has non-string children', () => {
+ const wrapper = shallow(
+
+ );
+
+ expect( wrapper.prop( 'aria-label' ) ).toBeUndefined();
+ } );
} );
diff --git a/packages/edit-post/src/components/header/feature-toggle/index.js b/packages/edit-post/src/components/header/feature-toggle/index.js
index afa2763916b0d..3b811e653fac4 100644
--- a/packages/edit-post/src/components/header/feature-toggle/index.js
+++ b/packages/edit-post/src/components/header/feature-toggle/index.js
@@ -5,13 +5,15 @@ import { withSelect, withDispatch } from '@wordpress/data';
import { compose } from '@wordpress/compose';
import { MenuItem } from '@wordpress/components';
-function FeatureToggle( { onToggle, isActive, label } ) {
+function FeatureToggle( { onToggle, isActive, label, info } ) {
return (
{ label }
diff --git a/packages/edit-post/src/components/header/writing-menu/index.js b/packages/edit-post/src/components/header/writing-menu/index.js
index 64fb516ef3518..f99d0be3c67d3 100644
--- a/packages/edit-post/src/components/header/writing-menu/index.js
+++ b/packages/edit-post/src/components/header/writing-menu/index.js
@@ -15,9 +15,21 @@ function WritingMenu( { onClose } ) {
-
-
-
+
+
+
);
}
diff --git a/packages/editor/src/components/block-settings-menu/test/__snapshots__/reusable-block-delete-button.js.snap b/packages/editor/src/components/block-settings-menu/test/__snapshots__/reusable-block-delete-button.js.snap
index fe0ee5827c499..c31a0f8e9f9b4 100644
--- a/packages/editor/src/components/block-settings-menu/test/__snapshots__/reusable-block-delete-button.js.snap
+++ b/packages/editor/src/components/block-settings-menu/test/__snapshots__/reusable-block-delete-button.js.snap
@@ -1,11 +1,11 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`ReusableBlockDeleteButton matches the snapshot 1`] = `
-
Remove from Reusable Blocks
-
+
`;
diff --git a/packages/editor/src/components/block-settings-menu/test/block-mode-toggle.js b/packages/editor/src/components/block-settings-menu/test/block-mode-toggle.js
index 61f819da34dba..a8d8e321eb154 100644
--- a/packages/editor/src/components/block-settings-menu/test/block-mode-toggle.js
+++ b/packages/editor/src/components/block-settings-menu/test/block-mode-toggle.js
@@ -1,7 +1,7 @@
/**
* External dependencies
*/
-import { shallow } from 'enzyme';
+import ShallowRenderer from 'react-test-renderer/shallow';
/**
* Internal dependencies
@@ -9,34 +9,40 @@ import { shallow } from 'enzyme';
import { BlockModeToggle } from '../block-mode-toggle';
describe( 'BlockModeToggle', () => {
+ function getShallowRenderOutput( element ) {
+ const renderer = new ShallowRenderer();
+ renderer.render( element );
+ return renderer.getRenderOutput();
+ }
+
it( "should not render the HTML mode button if the block doesn't support it", () => {
- const wrapper = shallow(
+ const wrapper = getShallowRenderOutput(
);
- expect( wrapper.equals( null ) ).toBe( true );
+ expect( wrapper ).toBe( null );
} );
it( 'should render the HTML mode button', () => {
- const wrapper = shallow(
+ const wrapper = getShallowRenderOutput(
);
- const text = wrapper.find( 'MenuItem' ).first().prop( 'children' );
+ const text = wrapper.props.children;
expect( text ).toEqual( 'Edit as HTML' );
} );
it( 'should render the Visual mode button', () => {
- const wrapper = shallow(
+ const wrapper = getShallowRenderOutput(
);
- const text = wrapper.find( 'MenuItem' ).first().prop( 'children' );
+ const text = wrapper.props.children;
expect( text ).toEqual( 'Edit visually' );
} );
diff --git a/packages/editor/src/components/block-settings-menu/test/reusable-block-convert-button.js b/packages/editor/src/components/block-settings-menu/test/reusable-block-convert-button.js
index 84b8b7085f8ff..1aabafc55ef92 100644
--- a/packages/editor/src/components/block-settings-menu/test/reusable-block-convert-button.js
+++ b/packages/editor/src/components/block-settings-menu/test/reusable-block-convert-button.js
@@ -1,7 +1,7 @@
/**
* External dependencies
*/
-import { shallow } from 'enzyme';
+import ShallowRenderer from 'react-test-renderer/shallow';
/**
* Internal dependencies
@@ -9,40 +9,48 @@ import { shallow } from 'enzyme';
import { ReusableBlockConvertButton } from '../reusable-block-convert-button';
describe( 'ReusableBlockConvertButton', () => {
+ function getShallowRenderOutput( element ) {
+ const renderer = new ShallowRenderer();
+ renderer.render( element );
+ return renderer.getRenderOutput();
+ }
+
it( 'should not render when isVisible false', () => {
- const wrapper = shallow(
+ const wrapper = getShallowRenderOutput(
);
- expect( wrapper.children() ).not.toExist();
+ expect( wrapper ).toBe( null );
} );
it( 'should allow converting a static block to a reusable block', () => {
const onConvert = jest.fn();
- const wrapper = shallow(
+ const wrapper = getShallowRenderOutput(
);
- const button = wrapper.find( 'MenuItem' ).first();
- expect( button.children().text() ).toBe( 'Add to Reusable Blocks' );
- button.simulate( 'click' );
+ expect( wrapper.props.children[ 1 ] ).toBeFalsy();
+ const button = wrapper.props.children[ 0 ];
+ expect( button.props.children ).toBe( 'Add to Reusable Blocks' );
+ button.props.onClick();
expect( onConvert ).toHaveBeenCalled();
} );
it( 'should allow converting a reusable block to static', () => {
const onConvert = jest.fn();
- const wrapper = shallow(
+ const wrapper = getShallowRenderOutput(
);
- const button = wrapper.find( 'MenuItem' ).first();
- expect( button.children().text() ).toBe( 'Convert to Regular Block' );
- button.simulate( 'click' );
+ expect( wrapper.props.children[ 0 ] ).toBeFalsy();
+ const button = wrapper.props.children[ 1 ];
+ expect( button.props.children ).toBe( 'Convert to Regular Block' );
+ button.props.onClick();
expect( onConvert ).toHaveBeenCalled();
} );
} );
diff --git a/packages/editor/src/components/block-settings-menu/test/reusable-block-delete-button.js b/packages/editor/src/components/block-settings-menu/test/reusable-block-delete-button.js
index 63caddb28a591..9da36d7f24e60 100644
--- a/packages/editor/src/components/block-settings-menu/test/reusable-block-delete-button.js
+++ b/packages/editor/src/components/block-settings-menu/test/reusable-block-delete-button.js
@@ -1,7 +1,7 @@
/**
* External dependencies
*/
-import { shallow } from 'enzyme';
+import ShallowRenderer from 'react-test-renderer/shallow';
import { noop } from 'lodash';
/**
@@ -10,8 +10,14 @@ import { noop } from 'lodash';
import { ReusableBlockDeleteButton } from '../reusable-block-delete-button';
describe( 'ReusableBlockDeleteButton', () => {
+ function getShallowRenderOutput( element ) {
+ const renderer = new ShallowRenderer();
+ renderer.render( element );
+ return renderer.getRenderOutput();
+ }
+
it( 'matches the snapshot', () => {
- const wrapper = shallow(
+ const wrapper = getShallowRenderOutput(
{
it( 'should allow deleting a reusable block', () => {
const onDelete = jest.fn();
- const wrapper = shallow(
+ const wrapper = getShallowRenderOutput(
);
- wrapper.find( 'MenuItem' ).simulate( 'click' );
+ wrapper.props.onClick();
expect( onDelete ).toHaveBeenCalledWith( 123 );
} );
} );
diff --git a/test/e2e/specs/__snapshots__/plugins-api.test.js.snap b/test/e2e/specs/__snapshots__/plugins-api.test.js.snap
index c4f89eddc19fd..0b62fc522bf7e 100644
--- a/test/e2e/specs/__snapshots__/plugins-api.test.js.snap
+++ b/test/e2e/specs/__snapshots__/plugins-api.test.js.snap
@@ -1,3 +1,3 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`Using Plugins API Sidebar Should open plugins sidebar using More Menu item and render content 1`] = `""`;
+exports[`Using Plugins API Sidebar Should open plugins sidebar using More Menu item and render content 1`] = `""`;
diff --git a/test/e2e/specs/links.test.js b/test/e2e/specs/links.test.js
index 0bd1859b00249..23546f8c72045 100644
--- a/test/e2e/specs/links.test.js
+++ b/test/e2e/specs/links.test.js
@@ -192,17 +192,14 @@ describe( 'Links', () => {
expect( await getEditedPostContent() ).toMatchSnapshot();
} );
- const toggleFixedToolbar = async ( b ) => {
- await page.click( '.edit-post-more-menu button' );
- const button = ( await page.$x( "//button[contains(text(), 'Unified Toolbar')]" ) )[ 0 ];
- const buttonClassNameProperty = await button.getProperty( 'className' );
- const buttonClassName = await buttonClassNameProperty.jsonValue();
- const isSelected = buttonClassName.indexOf( 'is-selected' ) !== -1;
- if ( isSelected !== b ) {
- await button.click();
- } else {
- await page.click( '.edit-post-more-menu button' );
- }
+ const toggleFixedToolbar = async ( isFixed ) => {
+ await page.evaluate( ( _isFixed ) => {
+ const { select, dispatch } = wp.data;
+ const isCurrentlyFixed = select( 'core/edit-post' ).isFeatureActive( 'fixedToolbar' );
+ if ( isCurrentlyFixed !== _isFixed ) {
+ dispatch( 'core/edit-post' ).toggleFeature( 'fixedToolbar' );
+ }
+ }, isFixed );
};
it( 'allows Left to be pressed during creation when the toolbar is fixed to top', async () => {
From 611fe3152108e5dd46f525ad033665e928c3ce62 Mon Sep 17 00:00:00 2001
From: Andrew Duthie
Date: Fri, 19 Oct 2018 15:23:45 -0400
Subject: [PATCH 032/121] REST API: Use posts endpoint for reusable blocks
(#10751)
* REST API: Use posts endpoint for reusable blocks
* REST: Restore WP_REST_Blocks_Controller for permissions check
* Reusable Blocks: Enable post listing edit
* Reusable Blocks: Trash on delete action
* List Reusable Blocks: Provide context to post request
* Reusable Blocks: Verify edit capability on export action
* List Reusable Blocks: Import as published
---
gutenberg.php | 9 +-
lib/class-wp-rest-blocks-controller.php | 87 ----------
lib/register.php | 5 +-
.../src/store/effects/reusable-blocks.js | 31 ++--
.../src/store/effects/test/reusable-blocks.js | 18 +-
.../list-reusable-blocks/src/utils/export.js | 11 +-
.../list-reusable-blocks/src/utils/import.js | 1 +
phpunit/class-rest-blocks-controller-test.php | 164 +-----------------
8 files changed, 50 insertions(+), 276 deletions(-)
diff --git a/gutenberg.php b/gutenberg.php
index 024bb9f2cd8be..d68b92c18237b 100644
--- a/gutenberg.php
+++ b/gutenberg.php
@@ -266,8 +266,15 @@ function gutenberg_add_edit_link( $actions, $post ) {
$title = _draft_or_post_title( $post->ID );
if ( 'wp_block' === $post->post_type ) {
- unset( $actions['edit'] );
unset( $actions['inline hide-if-no-js'] );
+
+ // Export uses block raw content, which is only returned from the post
+ // REST endpoint via `context=edit`, requiring edit capability.
+ $post_type = get_post_type_object( $post->post_type );
+ if ( ! current_user_can( $post_type->cap->edit_post, $post->ID ) ) {
+ return $actions;
+ }
+
$actions['export'] = sprintf(
'%s ',
$post->ID,
diff --git a/lib/class-wp-rest-blocks-controller.php b/lib/class-wp-rest-blocks-controller.php
index 75f69afe91759..9689820c7494b 100644
--- a/lib/class-wp-rest-blocks-controller.php
+++ b/lib/class-wp-rest-blocks-controller.php
@@ -33,91 +33,4 @@ public function check_read_permission( $post ) {
return parent::check_read_permission( $post );
}
-
- /**
- * Handle a DELETE request.
- *
- * @since 1.10.0
- *
- * @param WP_REST_Request $request Full details about the request.
- * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
- */
- public function delete_item( $request ) {
- // Always hard-delete a block.
- $request->set_param( 'force', true );
-
- return parent::delete_item( $request );
- }
-
- /**
- * Given an update or create request, build the post object that is saved to
- * the database.
- *
- * @since 1.10.0
- *
- * @param WP_REST_Request $request Request object.
- * @return stdClass|WP_Error Post object or WP_Error.
- */
- public function prepare_item_for_database( $request ) {
- $prepared_post = parent::prepare_item_for_database( $request );
-
- // Force blocks to always be published.
- $prepared_post->post_status = 'publish';
-
- return $prepared_post;
- }
-
- /**
- * Given a block from the database, build the array that is returned from an
- * API response.
- *
- * @since 1.10.0
- *
- * @param WP_Post $post Post object that backs the block.
- * @param WP_REST_Request $request Request object.
- * @return WP_REST_Response Response object.
- */
- public function prepare_item_for_response( $post, $request ) {
- $data = array(
- 'id' => $post->ID,
- 'title' => $post->post_title,
- 'content' => $post->post_content,
- );
-
- $response = rest_ensure_response( $data );
-
- return apply_filters( "rest_prepare_{$this->post_type}", $response, $post, $request );
- }
-
- /**
- * Builds the block's schema, conforming to JSON Schema.
- *
- * @since 1.10.0
- *
- * @return array Item schema data.
- */
- public function get_item_schema() {
- return array(
- '$schema' => 'http://json-schema.org/schema#',
- 'title' => $this->post_type,
- 'type' => 'object',
- 'properties' => array(
- 'id' => array(
- 'description' => __( 'Unique identifier for the block.', 'gutenberg' ),
- 'type' => 'integer',
- 'readonly' => true,
- ),
- 'title' => array(
- 'description' => __( 'The block’s title.', 'gutenberg' ),
- 'type' => 'string',
- 'required' => true,
- ),
- 'content' => array(
- 'description' => __( 'The block’s HTML content.', 'gutenberg' ),
- 'type' => 'string',
- 'required' => true,
- ),
- ),
- );
- }
}
diff --git a/lib/register.php b/lib/register.php
index 89ee8411fb262..0401af2f8a7c7 100644
--- a/lib/register.php
+++ b/lib/register.php
@@ -461,7 +461,10 @@ function gutenberg_register_post_types() {
'create_posts' => 'create_blocks',
),
'map_meta_cap' => true,
- 'supports' => false,
+ 'supports' => array(
+ 'title',
+ 'editor',
+ ),
)
);
diff --git a/packages/editor/src/store/effects/reusable-blocks.js b/packages/editor/src/store/effects/reusable-blocks.js
index d1cb383469328..a895d9eae615f 100644
--- a/packages/editor/src/store/effects/reusable-blocks.js
+++ b/packages/editor/src/store/effects/reusable-blocks.js
@@ -36,6 +36,7 @@ import {
getBlocks,
getBlocksByClientId,
} from '../selectors';
+import { getPostRawValue } from '../reducer';
/**
* Module Constants
@@ -61,26 +62,25 @@ export const fetchReusableBlocks = async ( action, store ) => {
let result;
if ( id ) {
- result = apiFetch( { path: `/wp/v2/${ postType.rest_base }/${ id }` } );
+ result = apiFetch( { path: `/wp/v2/${ postType.rest_base }/${ id }?context=edit` } );
} else {
- result = apiFetch( { path: `/wp/v2/${ postType.rest_base }?per_page=-1` } );
+ result = apiFetch( { path: `/wp/v2/${ postType.rest_base }?per_page=-1&context=edit` } );
}
try {
const reusableBlockOrBlocks = await result;
dispatch( receiveReusableBlocksAction( map(
castArray( reusableBlockOrBlocks ),
- ( reusableBlock ) => {
- const parsedBlocks = parse( reusableBlock.content );
- if ( parsedBlocks.length === 1 ) {
- return {
- reusableBlock,
- parsedBlock: parsedBlocks[ 0 ],
- };
- }
+ ( post ) => {
+ const parsedBlocks = parse( post.content.raw );
return {
- reusableBlock,
- parsedBlock: createBlock( 'core/template', {}, parsedBlocks ),
+ reusableBlock: {
+ id: post.id,
+ title: getPostRawValue( post.title ),
+ },
+ parsedBlock: parsedBlocks.length === 1 ?
+ parsedBlocks[ 0 ] :
+ createBlock( 'core/template', {}, parsedBlocks ),
};
}
) ) );
@@ -119,7 +119,7 @@ export const saveReusableBlocks = async ( action, store ) => {
const reusableBlock = getBlock( state, clientId );
const content = serialize( reusableBlock.name === 'core/template' ? reusableBlock.innerBlocks : reusableBlock );
- const data = isTemporary ? { title, content } : { id, title, content };
+ const data = isTemporary ? { title, content, status: 'publish' } : { id, title, content, status: 'publish' };
const path = isTemporary ? `/wp/v2/${ postType.rest_base }` : `/wp/v2/${ postType.rest_base }/${ id }`;
const method = isTemporary ? 'POST' : 'PUT';
@@ -184,7 +184,10 @@ export const deleteReusableBlocks = async ( action, store ) => {
] ) );
try {
- await apiFetch( { path: `/wp/v2/${ postType.rest_base }/${ id }`, method: 'DELETE' } );
+ await apiFetch( {
+ path: `/wp/v2/${ postType.rest_base }/${ id }`,
+ method: 'DELETE',
+ } );
dispatch( {
type: 'DELETE_REUSABLE_BLOCK_SUCCESS',
id,
diff --git a/packages/editor/src/store/effects/test/reusable-blocks.js b/packages/editor/src/store/effects/test/reusable-blocks.js
index 2ff4081a650a7..dcfe687f74412 100644
--- a/packages/editor/src/store/effects/test/reusable-blocks.js
+++ b/packages/editor/src/store/effects/test/reusable-blocks.js
@@ -70,8 +70,12 @@ describe( 'reusable blocks effects', () => {
const blockPromise = Promise.resolve( [
{
id: 123,
- title: 'My cool block',
- content: '',
+ title: {
+ raw: 'My cool block',
+ },
+ content: {
+ raw: '',
+ },
},
] );
const postTypePromise = Promise.resolve( {
@@ -97,7 +101,6 @@ describe( 'reusable blocks effects', () => {
reusableBlock: {
id: 123,
title: 'My cool block',
- content: '',
},
parsedBlock: expect.objectContaining( {
name: 'core/test-block',
@@ -115,8 +118,12 @@ describe( 'reusable blocks effects', () => {
it( 'should fetch a single reusable block', async () => {
const blockPromise = Promise.resolve( {
id: 123,
- title: 'My cool block',
- content: '',
+ title: {
+ raw: 'My cool block',
+ },
+ content: {
+ raw: '',
+ },
} );
const postTypePromise = Promise.resolve( {
slug: 'wp_block', rest_base: 'blocks',
@@ -141,7 +148,6 @@ describe( 'reusable blocks effects', () => {
reusableBlock: {
id: 123,
title: 'My cool block',
- content: '',
},
parsedBlock: expect.objectContaining( {
name: 'core/test-block',
diff --git a/packages/list-reusable-blocks/src/utils/export.js b/packages/list-reusable-blocks/src/utils/export.js
index e3ca3b8b07788..a0d75bd427c1e 100644
--- a/packages/list-reusable-blocks/src/utils/export.js
+++ b/packages/list-reusable-blocks/src/utils/export.js
@@ -1,7 +1,7 @@
/**
* External dependencies
*/
-import { pick, kebabCase } from 'lodash';
+import { kebabCase } from 'lodash';
/**
* WordPress dependencies
@@ -20,12 +20,15 @@ import { download } from './file';
*/
async function exportReusableBlock( id ) {
const postType = await apiFetch( { path: `/wp/v2/types/wp_block` } );
- const reusableBlock = await apiFetch( { path: `/wp/v2/${ postType.rest_base }/${ id }` } );
+ const post = await apiFetch( { path: `/wp/v2/${ postType.rest_base }/${ id }?context=edit` } );
+ const title = post.title.raw;
+ const content = post.content.raw;
const fileContent = JSON.stringify( {
__file: 'wp_block',
- ...pick( reusableBlock, [ 'title', 'content' ] ),
+ title,
+ content,
}, null, 2 );
- const fileName = kebabCase( reusableBlock.title ) + '.json';
+ const fileName = kebabCase( title ) + '.json';
download( fileName, fileContent, 'application/json' );
}
diff --git a/packages/list-reusable-blocks/src/utils/import.js b/packages/list-reusable-blocks/src/utils/import.js
index 6bf0204895284..e885cbb6b2b24 100644
--- a/packages/list-reusable-blocks/src/utils/import.js
+++ b/packages/list-reusable-blocks/src/utils/import.js
@@ -42,6 +42,7 @@ async function importReusableBlock( file ) {
data: {
title: parsedContent.title,
content: parsedContent.content,
+ status: 'publish',
},
method: 'POST',
} );
diff --git a/phpunit/class-rest-blocks-controller-test.php b/phpunit/class-rest-blocks-controller-test.php
index 7b92f200a2afc..a5c58c6f49805 100644
--- a/phpunit/class-rest-blocks-controller-test.php
+++ b/phpunit/class-rest-blocks-controller-test.php
@@ -8,7 +8,7 @@
/**
* Tests for WP_REST_Blocks_Controller.
*/
-class REST_Blocks_Controller_Test extends WP_Test_REST_Controller_Testcase {
+class REST_Blocks_Controller_Test extends WP_UnitTestCase {
/**
* Our fake block's post ID.
@@ -55,161 +55,6 @@ public static function wpTearDownAfterClass() {
self::delete_user( self::$user_id );
}
- /**
- * Check that our routes get set up properly.
- */
- public function test_register_routes() {
- $routes = rest_get_server()->get_routes();
-
- $this->assertArrayHasKey( '/wp/v2/blocks', $routes );
- $this->assertCount( 2, $routes['/wp/v2/blocks'] );
- $this->assertArrayHasKey( '/wp/v2/blocks/(?P[\d]+)', $routes );
- $this->assertCount( 3, $routes['/wp/v2/blocks/(?P[\d]+)'] );
- }
-
- /**
- * Check that we can GET a collection of blocks.
- */
- public function test_get_items() {
- wp_set_current_user( self::$user_id );
-
- $request = new WP_REST_Request( 'GET', '/wp/v2/blocks' );
- $response = rest_get_server()->dispatch( $request );
-
- $this->assertEquals( 200, $response->get_status() );
- $this->assertEquals(
- array(
- array(
- 'id' => self::$post_id,
- 'title' => 'My cool block',
- 'content' => 'Hello!
',
- ),
- ),
- $response->get_data()
- );
- }
-
- /**
- * Check that we can GET a single block.
- */
- public function test_get_item() {
- wp_set_current_user( self::$user_id );
-
- $request = new WP_REST_Request( 'GET', '/wp/v2/blocks/' . self::$post_id );
- $response = rest_get_server()->dispatch( $request );
-
- $this->assertEquals( 200, $response->get_status() );
- $this->assertEquals(
- array(
- 'id' => self::$post_id,
- 'title' => 'My cool block',
- 'content' => 'Hello!
',
- ),
- $response->get_data()
- );
- }
-
- /**
- * Check that we can POST to create a new block.
- */
- public function test_create_item() {
- wp_set_current_user( self::$user_id );
-
- $request = new WP_REST_Request( 'POST', '/wp/v2/blocks/' . self::$post_id );
- $request->set_body_params(
- array(
- 'title' => 'New cool block',
- 'content' => 'Wow!
',
- )
- );
-
- $response = rest_get_server()->dispatch( $request );
-
- $this->assertEquals( 200, $response->get_status() );
-
- $data = $response->get_data();
-
- $this->assertArrayHasKey( 'id', $data );
- $this->assertArrayHasKey( 'title', $data );
- $this->assertArrayHasKey( 'content', $data );
-
- $this->assertEquals( self::$post_id, $data['id'] );
- $this->assertEquals( 'New cool block', $data['title'] );
- $this->assertEquals( 'Wow!
', $data['content'] );
- }
-
- /**
- * Check that we can PUT to update a block.
- */
- public function test_update_item() {
- wp_set_current_user( self::$user_id );
-
- $request = new WP_REST_Request( 'PUT', '/wp/v2/blocks/' . self::$post_id );
- $request->set_body_params(
- array(
- 'title' => 'Updated cool block',
- 'content' => 'Nice!
',
- )
- );
-
- $response = rest_get_server()->dispatch( $request );
-
- $this->assertEquals( 200, $response->get_status() );
-
- $data = $response->get_data();
-
- $this->assertArrayHasKey( 'id', $data );
- $this->assertArrayHasKey( 'title', $data );
- $this->assertArrayHasKey( 'content', $data );
-
- $this->assertEquals( self::$post_id, $data['id'] );
- $this->assertEquals( 'Updated cool block', $data['title'] );
- $this->assertEquals( 'Nice!
', $data['content'] );
- }
-
- /**
- * Check that we can DELETE a block.
- */
- public function test_delete_item() {
- wp_set_current_user( self::$user_id );
-
- $request = new WP_REST_Request( 'DELETE', '/wp/v2/blocks/' . self::$post_id );
-
- $response = rest_get_server()->dispatch( $request );
-
- $this->assertEquals( 200, $response->get_status() );
-
- $data = $response->get_data();
-
- $this->assertArrayHasKey( 'deleted', $data );
- $this->assertArrayHasKey( 'previous', $data );
-
- $this->assertTrue( $data['deleted'] );
-
- $this->assertArrayHasKey( 'id', $data['previous'] );
- $this->assertArrayHasKey( 'title', $data['previous'] );
- $this->assertArrayHasKey( 'content', $data['previous'] );
-
- $this->assertEquals( self::$post_id, $data['previous']['id'] );
- $this->assertEquals( 'My cool block', $data['previous']['title'] );
- $this->assertEquals( 'Hello!
', $data['previous']['content'] );
- }
-
- /**
- * Check that we have defined a JSON schema.
- */
- public function test_get_item_schema() {
- $request = new WP_REST_Request( 'OPTIONS', '/wp/v2/blocks' );
- $response = rest_get_server()->dispatch( $request );
- $data = $response->get_data();
- $properties = $data['schema']['properties'];
-
- $this->assertEquals( 3, count( $properties ) );
- $this->assertArrayHasKey( 'id', $properties );
- $this->assertArrayHasKey( 'title', $properties );
- $this->assertArrayHasKey( 'content', $properties );
- }
-
/**
* Test cases for test_capabilities().
*/
@@ -331,11 +176,4 @@ public function test_capabilities( $action, $role, $expected_status ) {
self::delete_user( $user_id );
}
}
-
- public function test_context_param() {
- $this->markTestSkipped( 'Controller doesn\'t implement get_context_param().' );
- }
- public function test_prepare_item() {
- $this->markTestSkipped( 'Controller doesn\'t implement prepare_item().' );
- }
}
From 5a546b0bdb1109ca2dbf84d7236652bce30a32dd Mon Sep 17 00:00:00 2001
From: Arnaud Banvillet
Date: Fri, 19 Oct 2018 22:41:54 +0200
Subject: [PATCH 033/121] Docs: Fix deprecated-blocks example (#10717)
## Description
I changed a small error in the deprecated-blocks documentation
## How has this been tested?
I looked in my Github repo if the markdown file preview was ok
## Types of changes
Typo in documentation
---
docs/block-api/deprecated-blocks.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/docs/block-api/deprecated-blocks.md b/docs/block-api/deprecated-blocks.md
index 7c977089b926a..99d84d0cf0ed5 100644
--- a/docs/block-api/deprecated-blocks.md
+++ b/docs/block-api/deprecated-blocks.md
@@ -112,7 +112,7 @@ registerBlockType( 'gutenberg/block-with-deprecated-version', {
},
save: function( props ) {
- return el( 'div', {}, props.attributes.text );
+ return el( 'div', {}, props.attributes.content );
},
deprecated: [
@@ -153,7 +153,7 @@ registerBlockType( 'gutenberg/block-with-deprecated-version', {
},
save( props ) {
- return { props.attributes.text }
;
+ return { props.attributes.content }
;
},
deprecated: [
From a30a4f0188a632d27fa89755f9d185d8130eefcf Mon Sep 17 00:00:00 2001
From: Matias Ventura
Date: Fri, 19 Oct 2018 23:43:23 +0200
Subject: [PATCH 034/121] Update plugin version to 4.1 RC1. (#10749)
---
gutenberg.php | 2 +-
package-lock.json | 2 +-
package.json | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/gutenberg.php b/gutenberg.php
index d68b92c18237b..fdc1cf2ab1767 100644
--- a/gutenberg.php
+++ b/gutenberg.php
@@ -3,7 +3,7 @@
* Plugin Name: Gutenberg
* Plugin URI: https://github.com/WordPress/gutenberg
* Description: Printing since 1440. This is the development plugin for the new block editor in core.
- * Version: 4.0.0
+ * Version: 4.1.0-rc.1
* Author: Gutenberg Team
*
* @package gutenberg
diff --git a/package-lock.json b/package-lock.json
index ab5506def2374..1867236d23dc4 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "gutenberg",
- "version": "4.0.0",
+ "version": "4.1.0-rc.1",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
diff --git a/package.json b/package.json
index 10fd5c336e2ea..7368a6e70f24c 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "gutenberg",
- "version": "4.0.0",
+ "version": "4.1.0-rc.1",
"private": true,
"description": "A new WordPress editor experience",
"repository": "git+https://github.com/WordPress/gutenberg.git",
From 4549bbc565b4eb9fa88ea31356132a74f06443e9 Mon Sep 17 00:00:00 2001
From: Matthew Riley MacPherson
Date: Fri, 19 Oct 2018 22:47:18 +0100
Subject: [PATCH 035/121] Oops, forgot to update these to master
---
docs/reference/release.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/reference/release.md b/docs/reference/release.md
index 4e8de1edd8692..e909ab5281aab 100644
--- a/docs/reference/release.md
+++ b/docs/reference/release.md
@@ -47,7 +47,7 @@ Creating a release candidate involves:
1. [Create a new release on GitHub](https://github.com/WordPress/gutenberg/releases/new).
2. If you were releasing the `5.0.0` release candidate, label it `v5.0.0-rc.1`.
3. The GitHub release screen should look like this:
-[![GitHub Release Screenshot](https://raw.githubusercontent.com/WordPress/gutenberg/docs/more-release-docs-tweaks/docs/reference/release-screenshot.png)](https://raw.githubusercontent.com/WordPress/gutenberg/docs/more-release-docs-tweaks/docs/reference/release-screenshot.png)
+[![GitHub Release Screenshot](https://raw.githubusercontent.com/WordPress/gutenberg/master/docs/reference/release-screenshot.png)](https://raw.githubusercontent.com/WordPress/gutenberg/master/docs/reference/release-screenshot.png)
4. Creative emojis related to a key feature in this release are encouraged. Emojis are fun!
5. Publish the release.
From 3e9ae4b54e10449867c04b1ef91f2ffdbd806f57 Mon Sep 17 00:00:00 2001
From: Riad Benguella
Date: Fri, 19 Oct 2018 23:03:45 +0100
Subject: [PATCH 036/121] Update the published packages changelog + revert
lerna changelog config (#10819)
---
lerna.json | 1 -
packages/blob/CHANGELOG.md | 2 ++
packages/block-library/CHANGELOG.md | 2 ++
packages/blocks/CHANGELOG.md | 2 ++
packages/components/CHANGELOG.md | 2 ++
packages/compose/CHANGELOG.md | 2 ++
packages/core-data/CHANGELOG.md | 2 ++
packages/data/CHANGELOG.md | 2 ++
packages/dom/CHANGELOG.md | 2 ++
packages/edit-post/CHANGELOG.md | 2 ++
packages/editor/CHANGELOG.md | 2 ++
packages/element/CHANGELOG.md | 2 ++
packages/escape-html/CHANGELOG.md | 2 +-
packages/list-reusable-blocks/CHANGELOG.md | 2 ++
packages/nux/CHANGELOG.md | 2 ++
packages/plugins/CHANGELOG.md | 2 ++
packages/redux-routine/CHANGELOG.md | 2 +-
packages/rich-text/CHANGELOG.md | 2 +-
packages/viewport/CHANGELOG.md | 2 ++
19 files changed, 33 insertions(+), 4 deletions(-)
diff --git a/lerna.json b/lerna.json
index fd005a960f1e6..431f4dec535da 100644
--- a/lerna.json
+++ b/lerna.json
@@ -6,7 +6,6 @@
},
"ignoreChanges": [
"**/benchmark/*.js",
- "**/CHANGELOG.md",
"**/test/**"
],
"packages": [
diff --git a/packages/blob/CHANGELOG.md b/packages/blob/CHANGELOG.md
index 5a0b5f114d7c3..b9d1a7dcbb94a 100644
--- a/packages/blob/CHANGELOG.md
+++ b/packages/blob/CHANGELOG.md
@@ -4,6 +4,8 @@
- Added a new `isBlobURL` function.
+## 2.0.3 (2018-10-18)
+
## 2.0.0 (2018-09-05)
### Breaking Change
diff --git a/packages/block-library/CHANGELOG.md b/packages/block-library/CHANGELOG.md
index ea310dd99d548..4a92aa3faf1fa 100644
--- a/packages/block-library/CHANGELOG.md
+++ b/packages/block-library/CHANGELOG.md
@@ -1,3 +1,5 @@
+## 2.1.2 (2018-10-18)
+
## 2.1.0 (2018-10-10)
### New Features
diff --git a/packages/blocks/CHANGELOG.md b/packages/blocks/CHANGELOG.md
index 7227e8d2887e3..03bf9e20e3842 100644
--- a/packages/blocks/CHANGELOG.md
+++ b/packages/blocks/CHANGELOG.md
@@ -1,3 +1,5 @@
+## 4.0.3 (2018-10-18)
+
## 4.0.0 (2018-09-30)
### Breaking Changes
diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md
index 59ae8b21f3865..69844765bb221 100644
--- a/packages/components/CHANGELOG.md
+++ b/packages/components/CHANGELOG.md
@@ -13,6 +13,8 @@
- `PanelColor` has been deprecated in favor of `wp.editor.PanelColorSettings`.
+## 4.1.2 (2018-10-18)
+
## 4.1.0 (2018-10-10)
### New Feature
diff --git a/packages/compose/CHANGELOG.md b/packages/compose/CHANGELOG.md
index a49cefcd0d75c..5f9b733622058 100644
--- a/packages/compose/CHANGELOG.md
+++ b/packages/compose/CHANGELOG.md
@@ -1,3 +1,5 @@
+## 2.0.4 (2018-10-18)
+
## 2.0.0 (2018-09-05)
### Breaking Change
diff --git a/packages/core-data/CHANGELOG.md b/packages/core-data/CHANGELOG.md
index f9a01b5abef62..ca3a1f13ab58e 100644
--- a/packages/core-data/CHANGELOG.md
+++ b/packages/core-data/CHANGELOG.md
@@ -1,3 +1,5 @@
+## 2.0.4 (2018-10-18)
+
## 2.0.0 (2018-09-05)
### Breaking Change
diff --git a/packages/data/CHANGELOG.md b/packages/data/CHANGELOG.md
index a531cb243ae7f..f2c5f3a71b507 100644
--- a/packages/data/CHANGELOG.md
+++ b/packages/data/CHANGELOG.md
@@ -1,3 +1,5 @@
+## 2.1.3 (2018-10-18)
+
## 2.1.0 (2018-09-30)
## New Features
diff --git a/packages/dom/CHANGELOG.md b/packages/dom/CHANGELOG.md
index a49cefcd0d75c..79111040fa8e2 100644
--- a/packages/dom/CHANGELOG.md
+++ b/packages/dom/CHANGELOG.md
@@ -1,3 +1,5 @@
+## 2.0.3 (2018-10-18)
+
## 2.0.0 (2018-09-05)
### Breaking Change
diff --git a/packages/edit-post/CHANGELOG.md b/packages/edit-post/CHANGELOG.md
index a0d14dd84bb07..c4da9c026a1b0 100644
--- a/packages/edit-post/CHANGELOG.md
+++ b/packages/edit-post/CHANGELOG.md
@@ -1,3 +1,5 @@
+## 1.0.2 (2018-10-18)
+
## 1.0.0 (2018-10-10)
### New Features
diff --git a/packages/editor/CHANGELOG.md b/packages/editor/CHANGELOG.md
index 251dd2ac549eb..226d41d72bc20 100644
--- a/packages/editor/CHANGELOG.md
+++ b/packages/editor/CHANGELOG.md
@@ -12,6 +12,8 @@
- Added `onClose` prop to `URLPopover` component.
+## 4.0.3 (2018-10-18)
+
## 4.0.0 (2018-09-30)
### Breaking Changes
diff --git a/packages/element/CHANGELOG.md b/packages/element/CHANGELOG.md
index d59d78cbf2c93..6c355b4be4fe2 100644
--- a/packages/element/CHANGELOG.md
+++ b/packages/element/CHANGELOG.md
@@ -1,3 +1,5 @@
+## 2.1.3 (2018-10-18)
+
## 2.1.0 (2018-09-30)
- New API method `isEmptyElement` was introduced ([9861](https://github.com/WordPress/gutenberg/pull/9681/)).
diff --git a/packages/escape-html/CHANGELOG.md b/packages/escape-html/CHANGELOG.md
index 16c97e5a1bf32..4df1fd5bec719 100644
--- a/packages/escape-html/CHANGELOG.md
+++ b/packages/escape-html/CHANGELOG.md
@@ -1,3 +1,3 @@
-## 1.0.0 (Unreleased)
+## 1.0.0 (2018-10-18)
- Initial release.
diff --git a/packages/list-reusable-blocks/CHANGELOG.md b/packages/list-reusable-blocks/CHANGELOG.md
index d33808d32e30d..8a9a831a1f6ca 100644
--- a/packages/list-reusable-blocks/CHANGELOG.md
+++ b/packages/list-reusable-blocks/CHANGELOG.md
@@ -1,3 +1,5 @@
+## 1.1.2 (2018-10-18)
+
## 1.1.0 (2018-10-10)
### New Features
diff --git a/packages/nux/CHANGELOG.md b/packages/nux/CHANGELOG.md
index a49cefcd0d75c..5f9b733622058 100644
--- a/packages/nux/CHANGELOG.md
+++ b/packages/nux/CHANGELOG.md
@@ -1,3 +1,5 @@
+## 2.0.4 (2018-10-18)
+
## 2.0.0 (2018-09-05)
### Breaking Change
diff --git a/packages/plugins/CHANGELOG.md b/packages/plugins/CHANGELOG.md
index a49cefcd0d75c..5f9b733622058 100644
--- a/packages/plugins/CHANGELOG.md
+++ b/packages/plugins/CHANGELOG.md
@@ -1,3 +1,5 @@
+## 2.0.4 (2018-10-18)
+
## 2.0.0 (2018-09-05)
### Breaking Change
diff --git a/packages/redux-routine/CHANGELOG.md b/packages/redux-routine/CHANGELOG.md
index b1935b639e224..6ca66d38b1887 100644
--- a/packages/redux-routine/CHANGELOG.md
+++ b/packages/redux-routine/CHANGELOG.md
@@ -1,4 +1,4 @@
-## 3.0.1 (Unreleased)
+## 3.0.2 (2018-10-18)
### Bug Fix
diff --git a/packages/rich-text/CHANGELOG.md b/packages/rich-text/CHANGELOG.md
index 16c97e5a1bf32..4df1fd5bec719 100644
--- a/packages/rich-text/CHANGELOG.md
+++ b/packages/rich-text/CHANGELOG.md
@@ -1,3 +1,3 @@
-## 1.0.0 (Unreleased)
+## 1.0.0 (2018-10-18)
- Initial release.
diff --git a/packages/viewport/CHANGELOG.md b/packages/viewport/CHANGELOG.md
index a49cefcd0d75c..5f9b733622058 100644
--- a/packages/viewport/CHANGELOG.md
+++ b/packages/viewport/CHANGELOG.md
@@ -1,3 +1,5 @@
+## 2.0.4 (2018-10-18)
+
## 2.0.0 (2018-09-05)
### Breaking Change
From c74dd9f9f939caac7fdbd1c0053a3213b9b106b3 Mon Sep 17 00:00:00 2001
From: Riad Benguella
Date: Fri, 19 Oct 2018 23:16:58 +0100
Subject: [PATCH 037/121] chore(release): publish
- @wordpress/blob@2.1.0
- @wordpress/block-library@2.1.3
- @wordpress/blocks@4.0.4
- @wordpress/components@4.2.0
- @wordpress/compose@2.0.5
- @wordpress/core-data@2.0.5
- @wordpress/data@2.1.4
- @wordpress/dom@2.0.4
- @wordpress/edit-post@1.0.3
- @wordpress/editor@5.0.0
- @wordpress/element@2.1.4
- @wordpress/escape-html@1.0.1
- @wordpress/list-reusable-blocks@1.1.3
- @wordpress/nux@2.0.5
- @wordpress/plugins@2.0.5
- @wordpress/redux-routine@3.0.3
- @wordpress/rich-text@1.0.1
- @wordpress/viewport@2.0.5
---
packages/blob/package.json | 2 +-
packages/block-library/package.json | 2 +-
packages/blocks/package.json | 2 +-
packages/components/package.json | 4 ++--
packages/compose/package.json | 2 +-
packages/core-data/package.json | 2 +-
packages/data/package.json | 2 +-
packages/dom/package.json | 2 +-
packages/edit-post/package.json | 2 +-
packages/editor/package.json | 2 +-
packages/element/package.json | 2 +-
packages/escape-html/package.json | 2 +-
packages/list-reusable-blocks/package.json | 2 +-
packages/nux/package.json | 2 +-
packages/plugins/package.json | 2 +-
packages/redux-routine/package.json | 2 +-
packages/rich-text/package.json | 2 +-
packages/viewport/package.json | 2 +-
18 files changed, 19 insertions(+), 19 deletions(-)
diff --git a/packages/blob/package.json b/packages/blob/package.json
index 96d2a91adf7c2..01119602f55ed 100644
--- a/packages/blob/package.json
+++ b/packages/blob/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/blob",
- "version": "2.0.3",
+ "version": "2.1.0",
"description": "Blob utilities for WordPress.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/block-library/package.json b/packages/block-library/package.json
index 9c341a7bdba46..01308b546811a 100644
--- a/packages/block-library/package.json
+++ b/packages/block-library/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/block-library",
- "version": "2.1.2",
+ "version": "2.1.3",
"description": "Block library for the WordPress editor.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/blocks/package.json b/packages/blocks/package.json
index c5fa238e71e95..8ca13799999ae 100644
--- a/packages/blocks/package.json
+++ b/packages/blocks/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/blocks",
- "version": "4.0.3",
+ "version": "4.0.4",
"description": "Block API for WordPress.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/components/package.json b/packages/components/package.json
index 018c38ea6e525..6494611c19583 100644
--- a/packages/components/package.json
+++ b/packages/components/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/components",
- "version": "4.1.2",
+ "version": "4.2.0",
"description": "UI components for WordPress.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
@@ -54,4 +54,4 @@
"publishConfig": {
"access": "public"
}
-}
\ No newline at end of file
+}
diff --git a/packages/compose/package.json b/packages/compose/package.json
index 373e1c89670df..4c18d608cbbc0 100644
--- a/packages/compose/package.json
+++ b/packages/compose/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/compose",
- "version": "2.0.4",
+ "version": "2.0.5",
"description": "WordPress higher-order components (HOCs).",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/core-data/package.json b/packages/core-data/package.json
index ea3a6f3a3a68b..a829391c5bce3 100644
--- a/packages/core-data/package.json
+++ b/packages/core-data/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/core-data",
- "version": "2.0.4",
+ "version": "2.0.5",
"description": "Access to and manipulation of core WordPress entities.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/data/package.json b/packages/data/package.json
index 8722e382ff181..df149b29db0fa 100644
--- a/packages/data/package.json
+++ b/packages/data/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/data",
- "version": "2.1.3",
+ "version": "2.1.4",
"description": "Data module for WordPress.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/dom/package.json b/packages/dom/package.json
index c7ce330d00476..38177cbfd2f96 100644
--- a/packages/dom/package.json
+++ b/packages/dom/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/dom",
- "version": "2.0.3",
+ "version": "2.0.4",
"description": "DOM utilities module for WordPress.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/edit-post/package.json b/packages/edit-post/package.json
index 5b28c7ed722c2..24efc07ffab98 100644
--- a/packages/edit-post/package.json
+++ b/packages/edit-post/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/edit-post",
- "version": "1.0.2",
+ "version": "1.0.3",
"description": "Edit Post module for WordPress.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/editor/package.json b/packages/editor/package.json
index c12006a29c91c..db733a7e241f7 100644
--- a/packages/editor/package.json
+++ b/packages/editor/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/editor",
- "version": "4.0.3",
+ "version": "5.0.0",
"description": "Building blocks for WordPress editors.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/element/package.json b/packages/element/package.json
index 794f849472349..e134526ddcd98 100644
--- a/packages/element/package.json
+++ b/packages/element/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/element",
- "version": "2.1.3",
+ "version": "2.1.4",
"description": "Element React module for WordPress.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/escape-html/package.json b/packages/escape-html/package.json
index 60fed031e42e6..c70aee37a4b3b 100644
--- a/packages/escape-html/package.json
+++ b/packages/escape-html/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/escape-html",
- "version": "1.0.0",
+ "version": "1.0.1",
"description": "Escape HTML utils.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/list-reusable-blocks/package.json b/packages/list-reusable-blocks/package.json
index 24a7a700cb9b1..983d41d36e116 100644
--- a/packages/list-reusable-blocks/package.json
+++ b/packages/list-reusable-blocks/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/list-reusable-blocks",
- "version": "1.1.2",
+ "version": "1.1.3",
"description": "Adding Export/Import support to the reusable blocks listing.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/nux/package.json b/packages/nux/package.json
index 6dbef6da3f9bc..44d6b23e156cb 100644
--- a/packages/nux/package.json
+++ b/packages/nux/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/nux",
- "version": "2.0.4",
+ "version": "2.0.5",
"description": "NUX (New User eXperience) module for WordPress.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/plugins/package.json b/packages/plugins/package.json
index 536a14bc429ff..660e34226a926 100644
--- a/packages/plugins/package.json
+++ b/packages/plugins/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/plugins",
- "version": "2.0.4",
+ "version": "2.0.5",
"description": "Plugins module for WordPress.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/redux-routine/package.json b/packages/redux-routine/package.json
index 094392c4b9c82..5eef2653d0782 100644
--- a/packages/redux-routine/package.json
+++ b/packages/redux-routine/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/redux-routine",
- "version": "3.0.2",
+ "version": "3.0.3",
"description": "Redux middleware for generator coroutines.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/rich-text/package.json b/packages/rich-text/package.json
index fe9ce5ca2f756..8280b49dd6076 100644
--- a/packages/rich-text/package.json
+++ b/packages/rich-text/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/rich-text",
- "version": "1.0.0",
+ "version": "1.0.1",
"description": "Rich text value and manipulation API.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/viewport/package.json b/packages/viewport/package.json
index 2096919ddc81f..7b12efc24aea5 100644
--- a/packages/viewport/package.json
+++ b/packages/viewport/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/viewport",
- "version": "2.0.4",
+ "version": "2.0.5",
"description": "Viewport module for WordPress.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
From a9a17c9060e18b4589dff8341d5ff7052cb11806 Mon Sep 17 00:00:00 2001
From: Stephen Edgar
Date: Sat, 20 Oct 2018 19:32:46 +1100
Subject: [PATCH 038/121] Revert 123811df9613cdf9dae3e9ec8372166b1b3a42e4
(#10823)
---
lerna.json | 1 +
1 file changed, 1 insertion(+)
diff --git a/lerna.json b/lerna.json
index 431f4dec535da..fd005a960f1e6 100644
--- a/lerna.json
+++ b/lerna.json
@@ -6,6 +6,7 @@
},
"ignoreChanges": [
"**/benchmark/*.js",
+ "**/CHANGELOG.md",
"**/test/**"
],
"packages": [
From ba7c5b59f1c6593d09e01d25e1287aa1dcf6700e Mon Sep 17 00:00:00 2001
From: Riad Benguella
Date: Sat, 20 Oct 2018 17:59:55 +0100
Subject: [PATCH 039/121] Fix importing react-dates styles into the components
package stylesheet (#10831)
---
packages/components/src/date-time/style.scss | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/components/src/date-time/style.scss b/packages/components/src/date-time/style.scss
index 6718ea8a63b43..f2930c10241b3 100644
--- a/packages/components/src/date-time/style.scss
+++ b/packages/components/src/date-time/style.scss
@@ -1,5 +1,5 @@
// We can't reference this package with ~ because of how Lerna handles packages. 😩
-@import "../../node_modules/react-dates/lib/css/_datepicker.css";
+@import "node_modules/react-dates/lib/css/_datepicker";
.components-datetime {
.components-datetime__calendar-help {
From cc498251b94b5010345afa663ffabaa431c4740c Mon Sep 17 00:00:00 2001
From: Riad Benguella
Date: Sat, 20 Oct 2018 18:15:00 +0100
Subject: [PATCH 040/121] Fix fullscreen mode (#10830)
* Fix fullscreen mode
* Add e2e test for fullscreen mode
---
.../components/header/writing-menu/index.js | 2 +-
test/e2e/specs/fullscreen-mode.test.js | 27 +++++++++++++++++++
test/e2e/support/utils.js | 3 +--
3 files changed, 29 insertions(+), 3 deletions(-)
create mode 100644 test/e2e/specs/fullscreen-mode.test.js
diff --git a/packages/edit-post/src/components/header/writing-menu/index.js b/packages/edit-post/src/components/header/writing-menu/index.js
index f99d0be3c67d3..3b2ff3e47f5f3 100644
--- a/packages/edit-post/src/components/header/writing-menu/index.js
+++ b/packages/edit-post/src/components/header/writing-menu/index.js
@@ -26,7 +26,7 @@ function WritingMenu( { onClose } ) {
info={ __( 'Focus on one block at a time' ) }
onToggle={ onClose } />
diff --git a/test/e2e/specs/fullscreen-mode.test.js b/test/e2e/specs/fullscreen-mode.test.js
new file mode 100644
index 0000000000000..9b697516d51d0
--- /dev/null
+++ b/test/e2e/specs/fullscreen-mode.test.js
@@ -0,0 +1,27 @@
+/**
+ * Internal dependencies
+ */
+import {
+ newPost,
+ clickOnMoreMenuItem,
+} from '../support/utils';
+
+describe( 'Fullscreen Mode', () => {
+ beforeAll( async () => {
+ await newPost();
+ } );
+
+ it( 'should open the fullscreen mode from the more menu', async () => {
+ await clickOnMoreMenuItem( 'Fullscreen Mode' );
+
+ const isFullscreenEnabled = await page.$eval( 'body', ( body ) => {
+ return body.classList.contains( 'is-fullscreen-mode' );
+ } );
+
+ expect( isFullscreenEnabled ).toBe( true );
+
+ const fullscreenToolbar = await page.$( '.edit-post-fullscreen-mode-close__toolbar' );
+
+ expect( fullscreenToolbar ).not.toBeNull();
+ } );
+} );
diff --git a/test/e2e/support/utils.js b/test/e2e/support/utils.js
index a7a7c9510ae70..3853d143635be 100644
--- a/test/e2e/support/utils.js
+++ b/test/e2e/support/utils.js
@@ -307,8 +307,7 @@ export async function pressWithModifier( modifiers, key ) {
*/
export async function clickOnMoreMenuItem( buttonLabel ) {
await expect( page ).toClick( '.edit-post-more-menu [aria-label="More"]' );
- const itemButton = ( await page.$x( `//button[contains(text(), '${ buttonLabel }')]` ) )[ 0 ];
- await itemButton.click( 'button' );
+ await page.click( `.edit-post-more-menu__content button[aria-label="${ buttonLabel }"]` );
}
/**
From 37f5398aa09cc3e81789a495c9f93767186542d5 Mon Sep 17 00:00:00 2001
From: Riad Benguella
Date: Sat, 20 Oct 2018 18:15:31 +0100
Subject: [PATCH 041/121] Update changelog for the last packages release
(#10836)
---
packages/blob/CHANGELOG.md | 2 +-
packages/block-library/CHANGELOG.md | 2 ++
packages/blocks/CHANGELOG.md | 2 ++
packages/components/CHANGELOG.md | 8 +++++++-
packages/compose/CHANGELOG.md | 2 ++
packages/core-data/CHANGELOG.md | 2 ++
packages/data/CHANGELOG.md | 2 ++
packages/dom/CHANGELOG.md | 2 ++
packages/edit-post/CHANGELOG.md | 8 ++++++++
packages/editor/CHANGELOG.md | 2 +-
packages/element/CHANGELOG.md | 2 ++
packages/escape-html/CHANGELOG.md | 2 ++
packages/list-reusable-blocks/CHANGELOG.md | 2 ++
packages/nux/CHANGELOG.md | 2 ++
packages/plugins/CHANGELOG.md | 2 ++
packages/redux-routine/CHANGELOG.md | 2 ++
packages/rich-text/CHANGELOG.md | 2 ++
packages/viewport/CHANGELOG.md | 2 ++
18 files changed, 45 insertions(+), 3 deletions(-)
diff --git a/packages/blob/CHANGELOG.md b/packages/blob/CHANGELOG.md
index b9d1a7dcbb94a..1288967c3cfdb 100644
--- a/packages/blob/CHANGELOG.md
+++ b/packages/blob/CHANGELOG.md
@@ -1,4 +1,4 @@
-## 2.1.0 (Unreleased)
+## 2.1.0 (2018-10-19)
### New Features
diff --git a/packages/block-library/CHANGELOG.md b/packages/block-library/CHANGELOG.md
index 4a92aa3faf1fa..6fe1fd0ef7ce1 100644
--- a/packages/block-library/CHANGELOG.md
+++ b/packages/block-library/CHANGELOG.md
@@ -1,3 +1,5 @@
+## 2.1.3 (2018-10-19)
+
## 2.1.2 (2018-10-18)
## 2.1.0 (2018-10-10)
diff --git a/packages/blocks/CHANGELOG.md b/packages/blocks/CHANGELOG.md
index 03bf9e20e3842..08b2e25271ecd 100644
--- a/packages/blocks/CHANGELOG.md
+++ b/packages/blocks/CHANGELOG.md
@@ -1,3 +1,5 @@
+## 4.0.4 (2018-10-19)
+
## 4.0.3 (2018-10-18)
## 4.0.0 (2018-09-30)
diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md
index 69844765bb221..bf670914795e0 100644
--- a/packages/components/CHANGELOG.md
+++ b/packages/components/CHANGELOG.md
@@ -1,4 +1,10 @@
-## 4.2.0 (Unreleased)
+## 4.2.1 (Unreleased)
+
+### Bug Fix
+
+- Fix importing `react-dates` stylesheet in production.
+
+## 4.2.0 (2018-10-19)
### New Feature
diff --git a/packages/compose/CHANGELOG.md b/packages/compose/CHANGELOG.md
index 5f9b733622058..9b4bad0bd4aa5 100644
--- a/packages/compose/CHANGELOG.md
+++ b/packages/compose/CHANGELOG.md
@@ -1,3 +1,5 @@
+## 2.0.5 (2018-10-19)
+
## 2.0.4 (2018-10-18)
## 2.0.0 (2018-09-05)
diff --git a/packages/core-data/CHANGELOG.md b/packages/core-data/CHANGELOG.md
index ca3a1f13ab58e..30bfdf415a54a 100644
--- a/packages/core-data/CHANGELOG.md
+++ b/packages/core-data/CHANGELOG.md
@@ -1,3 +1,5 @@
+## 2.0.5 (2018-10-19)
+
## 2.0.4 (2018-10-18)
## 2.0.0 (2018-09-05)
diff --git a/packages/data/CHANGELOG.md b/packages/data/CHANGELOG.md
index f2c5f3a71b507..5bb376f526d34 100644
--- a/packages/data/CHANGELOG.md
+++ b/packages/data/CHANGELOG.md
@@ -1,3 +1,5 @@
+## 2.1.4 (2018-10-19)
+
## 2.1.3 (2018-10-18)
## 2.1.0 (2018-09-30)
diff --git a/packages/dom/CHANGELOG.md b/packages/dom/CHANGELOG.md
index 79111040fa8e2..8a0d841d17433 100644
--- a/packages/dom/CHANGELOG.md
+++ b/packages/dom/CHANGELOG.md
@@ -1,3 +1,5 @@
+## 2.0.4 (2018-10-19)
+
## 2.0.3 (2018-10-18)
## 2.0.0 (2018-09-05)
diff --git a/packages/edit-post/CHANGELOG.md b/packages/edit-post/CHANGELOG.md
index c4da9c026a1b0..2db3d57d70ba1 100644
--- a/packages/edit-post/CHANGELOG.md
+++ b/packages/edit-post/CHANGELOG.md
@@ -1,3 +1,11 @@
+## 1.0.4 (Unreleased)
+
+## Bug Fixes
+
+- Fix fullscreen mode toggle.
+
+## 1.0.3 (2018-10-19)
+
## 1.0.2 (2018-10-18)
## 1.0.0 (2018-10-10)
diff --git a/packages/editor/CHANGELOG.md b/packages/editor/CHANGELOG.md
index 226d41d72bc20..691851a6bf4ef 100644
--- a/packages/editor/CHANGELOG.md
+++ b/packages/editor/CHANGELOG.md
@@ -1,4 +1,4 @@
-## 5.0.0 (Unreleased)
+## 5.0.0 (2018-10-19)
### Breaking Changes
diff --git a/packages/element/CHANGELOG.md b/packages/element/CHANGELOG.md
index 6c355b4be4fe2..7010a1b87680d 100644
--- a/packages/element/CHANGELOG.md
+++ b/packages/element/CHANGELOG.md
@@ -1,3 +1,5 @@
+## 2.1.4 (2018-10-19)
+
## 2.1.3 (2018-10-18)
## 2.1.0 (2018-09-30)
diff --git a/packages/escape-html/CHANGELOG.md b/packages/escape-html/CHANGELOG.md
index 4df1fd5bec719..e780b87752277 100644
--- a/packages/escape-html/CHANGELOG.md
+++ b/packages/escape-html/CHANGELOG.md
@@ -1,3 +1,5 @@
+## 1.0.1 (2018-10-19)
+
## 1.0.0 (2018-10-18)
- Initial release.
diff --git a/packages/list-reusable-blocks/CHANGELOG.md b/packages/list-reusable-blocks/CHANGELOG.md
index 8a9a831a1f6ca..7e54897866ced 100644
--- a/packages/list-reusable-blocks/CHANGELOG.md
+++ b/packages/list-reusable-blocks/CHANGELOG.md
@@ -1,3 +1,5 @@
+## 1.1.3 (2018-10-19)
+
## 1.1.2 (2018-10-18)
## 1.1.0 (2018-10-10)
diff --git a/packages/nux/CHANGELOG.md b/packages/nux/CHANGELOG.md
index 5f9b733622058..9b4bad0bd4aa5 100644
--- a/packages/nux/CHANGELOG.md
+++ b/packages/nux/CHANGELOG.md
@@ -1,3 +1,5 @@
+## 2.0.5 (2018-10-19)
+
## 2.0.4 (2018-10-18)
## 2.0.0 (2018-09-05)
diff --git a/packages/plugins/CHANGELOG.md b/packages/plugins/CHANGELOG.md
index 5f9b733622058..9b4bad0bd4aa5 100644
--- a/packages/plugins/CHANGELOG.md
+++ b/packages/plugins/CHANGELOG.md
@@ -1,3 +1,5 @@
+## 2.0.5 (2018-10-19)
+
## 2.0.4 (2018-10-18)
## 2.0.0 (2018-09-05)
diff --git a/packages/redux-routine/CHANGELOG.md b/packages/redux-routine/CHANGELOG.md
index 6ca66d38b1887..7e64304c2f2c3 100644
--- a/packages/redux-routine/CHANGELOG.md
+++ b/packages/redux-routine/CHANGELOG.md
@@ -1,3 +1,5 @@
+## 3.0.3 (2018-10-19)
+
## 3.0.2 (2018-10-18)
### Bug Fix
diff --git a/packages/rich-text/CHANGELOG.md b/packages/rich-text/CHANGELOG.md
index 4df1fd5bec719..e780b87752277 100644
--- a/packages/rich-text/CHANGELOG.md
+++ b/packages/rich-text/CHANGELOG.md
@@ -1,3 +1,5 @@
+## 1.0.1 (2018-10-19)
+
## 1.0.0 (2018-10-18)
- Initial release.
diff --git a/packages/viewport/CHANGELOG.md b/packages/viewport/CHANGELOG.md
index 5f9b733622058..9b4bad0bd4aa5 100644
--- a/packages/viewport/CHANGELOG.md
+++ b/packages/viewport/CHANGELOG.md
@@ -1,3 +1,5 @@
+## 2.0.5 (2018-10-19)
+
## 2.0.4 (2018-10-18)
## 2.0.0 (2018-09-05)
From 9b2fe92e1aac68d77a7ee57b0fef1a9f8f3066ad Mon Sep 17 00:00:00 2001
From: Miguel Fonseca
Date: Sat, 20 Oct 2018 18:24:02 +0100
Subject: [PATCH 042/121] Rename PHP functions to prevent conflict w/ core
(#10826)
These functions (e.g. `get_block_categories`) are declared in the plugin
before their counterparts in core, which means that guarding the
plugin's function declarations with
if ( ! function_exists( 'foo' ) ) {
doesn't make a difference, and the collisions will result in server
fatals.
---
lib/client-assets.php | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/lib/client-assets.php b/lib/client-assets.php
index e7d0a88b1efce..7f1e3b48938bb 100644
--- a/lib/client-assets.php
+++ b/lib/client-assets.php
@@ -1233,7 +1233,7 @@ function gutenberg_capture_code_editor_settings( $settings ) {
* @param WP_Post $post Post object.
* @return WP_Post|boolean The post autosave. False if none found.
*/
-function get_autosave_newer_than_post_save( $post ) {
+function gutenberg_get_autosave_newer_than_post_save( $post ) {
// Add autosave data if it is newer and changed.
$autosave = wp_get_post_autosave( $post->ID );
@@ -1262,7 +1262,7 @@ function get_autosave_newer_than_post_save( $post ) {
* @param WP_Post $post Post object.
* @return Object[] Block categories.
*/
-function get_block_categories( $post ) {
+function gutenberg_get_block_categories( $post ) {
$default_categories = array(
array(
'slug' => 'common',
@@ -1381,7 +1381,7 @@ function gutenberg_editor_scripts_and_styles( $hook ) {
wp_add_inline_script(
'wp-blocks',
- sprintf( 'wp.blocks.setCategories( %s );', wp_json_encode( get_block_categories( $post ) ) ),
+ sprintf( 'wp.blocks.setCategories( %s );', wp_json_encode( gutenberg_get_block_categories( $post ) ) ),
'after'
);
@@ -1587,7 +1587,7 @@ function gutenberg_editor_scripts_and_styles( $hook ) {
),
);
- $post_autosave = get_autosave_newer_than_post_save( $post );
+ $post_autosave = gutenberg_get_autosave_newer_than_post_save( $post );
if ( $post_autosave ) {
$editor_settings['autosave'] = array(
'editLink' => add_query_arg( 'gutenberg', true, get_edit_post_link( $post_autosave->ID ) ),
@@ -1669,11 +1669,11 @@ function gutenberg_editor_scripts_and_styles( $hook ) {
*
* @param string $hook Screen name.
*/
-function wp_load_list_reusable_blocks( $hook ) {
+function gutenberg_load_list_reusable_blocks( $hook ) {
$is_reusable_blocks_list_page = 'edit.php' === $hook && isset( $_GET['post_type'] ) && 'wp_block' === $_GET['post_type'];
if ( $is_reusable_blocks_list_page ) {
wp_enqueue_script( 'wp-list-reusable-blocks' );
wp_enqueue_style( 'wp-list-reusable-blocks' );
}
}
-add_action( 'admin_enqueue_scripts', 'wp_load_list_reusable_blocks' );
+add_action( 'admin_enqueue_scripts', 'gutenberg_load_list_reusable_blocks' );
From 0aa4ffaec61778215750dd220250ab0e3a91806f Mon Sep 17 00:00:00 2001
From: Presskopp
Date: Sun, 21 Oct 2018 15:57:07 +0200
Subject: [PATCH 043/121] Update rest-api.php (#10840)
add missing 'to' in string "not allowed _ make"
---
lib/rest-api.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/rest-api.php b/lib/rest-api.php
index c3ff18e708c16..7506f45484ee7 100644
--- a/lib/rest-api.php
+++ b/lib/rest-api.php
@@ -325,7 +325,7 @@ function gutenberg_handle_early_callback_checks( $response, $handler, $request )
}
if ( $request['per_page'] < 0 ) {
if ( ! $can_unbounded_query ) {
- return new WP_Error( 'rest_forbidden_per_page', __( 'Sorry, you are not allowed make unbounded queries.', 'gutenberg' ), array( 'status' => rest_authorization_required_code() ) );
+ return new WP_Error( 'rest_forbidden_per_page', __( 'Sorry, you are not allowed to make unbounded queries.', 'gutenberg' ), array( 'status' => rest_authorization_required_code() ) );
}
}
}
From 313a9a2803f9dd9ed0162b7c590e5852803e6b14 Mon Sep 17 00:00:00 2001
From: Jb Audras
Date: Sun, 21 Oct 2018 17:12:29 +0200
Subject: [PATCH 044/121] Remove closing tag from i18n string (#10849)
---
packages/components/src/date-time/index.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/components/src/date-time/index.js b/packages/components/src/date-time/index.js
index 9ee8eaa527f54..caf0be2778dfa 100644
--- a/packages/components/src/date-time/index.js
+++ b/packages/components/src/date-time/index.js
@@ -78,7 +78,7 @@ export class DateTimePicker extends Component {
↑/↓
{ ' ' /* JSX removes whitespace, but a space is required for screen readers. */ }
- { __( 'Move backward (up) and forward (down) by one week. ' ) }
+ { __( 'Move backward (up) and forward (down) by one week.' ) }
{ __( 'PgUp/PgDn' ) }
From 89964ea72b9f4b18a94163587f9a01197ec44159 Mon Sep 17 00:00:00 2001
From: Dominik Schilling
Date: Sun, 21 Oct 2018 17:12:58 +0200
Subject: [PATCH 045/121] Avoid PHP notices due to non-available meta boxes
(#10850)
---
lib/meta-box-partial-page.php | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/lib/meta-box-partial-page.php b/lib/meta-box-partial-page.php
index ad1a6805d327e..925450720f7f9 100644
--- a/lib/meta-box-partial-page.php
+++ b/lib/meta-box-partial-page.php
@@ -318,9 +318,13 @@ function the_gutenberg_metaboxes() {
foreach ( $locations as $location ) {
$meta_boxes_per_location[ $location ] = array();
foreach ( $priorities as $priority ) {
- $meta_boxes = (array) $wp_meta_boxes[ $current_screen->id ][ $location ][ $priority ];
- foreach ( $meta_boxes as $meta_box ) {
- if ( ! empty( $meta_box['title'] ) ) {
+ if ( isset( $wp_meta_boxes[ $current_screen->id ][ $location ][ $priority ] ) ) {
+ $meta_boxes = (array) $wp_meta_boxes[ $current_screen->id ][ $location ][ $priority ];
+ foreach ( $meta_boxes as $meta_box ) {
+ if ( false == $meta_box || ! $meta_box['title'] ) {
+ continue;
+ }
+
$meta_boxes_per_location[ $location ][] = array(
'id' => $meta_box['id'],
'title' => $meta_box['title'],
From b3edc7150b04a1b2e89a15a60bc3a5504a794f40 Mon Sep 17 00:00:00 2001
From: Andrea Fercia
Date: Sun, 21 Oct 2018 17:19:44 +0200
Subject: [PATCH 046/121] Improve the Toggle Control elements DOM order for
better accessibility. (#10870)
---
packages/components/src/toggle-control/index.js | 9 +++++++--
packages/components/src/toggle-control/style.scss | 9 +++++----
2 files changed, 12 insertions(+), 6 deletions(-)
diff --git a/packages/components/src/toggle-control/index.js b/packages/components/src/toggle-control/index.js
index 413516232fd0f..29d558d6b80c6 100644
--- a/packages/components/src/toggle-control/index.js
+++ b/packages/components/src/toggle-control/index.js
@@ -13,7 +13,7 @@ import { withInstanceId } from '@wordpress/compose';
* Internal dependencies
*/
import FormToggle from '../form-toggle';
-import BaseControl from './../base-control';
+import BaseControl from '../base-control';
class ToggleControl extends Component {
constructor() {
@@ -40,7 +40,6 @@ class ToggleControl extends Component {
return (
+
+ { label }
+
);
}
diff --git a/packages/components/src/toggle-control/style.scss b/packages/components/src/toggle-control/style.scss
index c9489553d6720..e4b3e89b3ab49 100644
--- a/packages/components/src/toggle-control/style.scss
+++ b/packages/components/src/toggle-control/style.scss
@@ -2,11 +2,12 @@
display: flex;
margin-bottom: $grid-size-small * 3;
- .components-base-control__label {
- order: 1;
- }
-
.components-form-toggle {
margin-right: $grid-size-large;
}
+
+ .components-toggle-control__label {
+ display: block;
+ margin-bottom: 4px;
+ }
}
From a8fe865af07812949e5ef97505668e5587c8aefb Mon Sep 17 00:00:00 2001
From: Andrea Fercia
Date: Sun, 21 Oct 2018 17:20:34 +0200
Subject: [PATCH 047/121] Set correct media type for video poster image and
manage focus. (#10864)
---
packages/block-library/src/video/edit.js | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)
diff --git a/packages/block-library/src/video/edit.js b/packages/block-library/src/video/edit.js
index 1d9b7b195a2eb..56a587b1c4851 100644
--- a/packages/block-library/src/video/edit.js
+++ b/packages/block-library/src/video/edit.js
@@ -25,6 +25,7 @@ import {
import { getBlobByURL, isBlobURL } from '@wordpress/blob';
const ALLOWED_MEDIA_TYPES = [ 'video' ];
+const VIDEO_POSTER_ALLOWED_MEDIA_TYPES = [ 'image' ];
class VideoEdit extends Component {
constructor() {
@@ -36,6 +37,7 @@ class VideoEdit extends Component {
};
this.videoPlayer = createRef();
+ this.posterImageButton = createRef();
this.toggleAttribute = this.toggleAttribute.bind( this );
this.onSelectURL = this.onSelectURL.bind( this );
this.onSelectPoster = this.onSelectPoster.bind( this );
@@ -96,6 +98,9 @@ class VideoEdit extends Component {
onRemovePoster() {
const { setAttributes } = this.props;
setAttributes( { poster: '' } );
+
+ // Move focus back to the Media Upload button.
+ this.posterImageButton.current.focus();
}
render() {
@@ -200,9 +205,13 @@ class VideoEdit extends Component {
(
-
+
{ ! this.props.attributes.poster ? __( 'Select Poster Image' ) : __( 'Replace image' ) }
) }
From dee3dcf49028717b4af3164e3096bfe747c41ed2 Mon Sep 17 00:00:00 2001
From: K Adam White
Date: Sun, 21 Oct 2018 13:12:16 -0400
Subject: [PATCH 048/121] Implement fetchAllMiddleware to handle per_page=-1
through pagination (#10762)
---
lib/client-assets.php | 2 +-
lib/rest-api.php | 148 ------------------
package-lock.json | 3 +-
packages/api-fetch/CHANGELOG.md | 4 +
packages/api-fetch/package.json | 3 +-
packages/api-fetch/src/index.js | 13 +-
.../src/middlewares/fetch-all-middleware.js | 93 +++++++++++
.../middlewares/test/fetch-all-middleware.js | 51 ++++++
.../hooks/components/media-upload/index.js | 2 +-
phpunit/class-gutenberg-rest-api-test.php | 38 +----
10 files changed, 166 insertions(+), 191 deletions(-)
create mode 100644 packages/api-fetch/src/middlewares/fetch-all-middleware.js
create mode 100644 packages/api-fetch/src/middlewares/test/fetch-all-middleware.js
diff --git a/lib/client-assets.php b/lib/client-assets.php
index 7f1e3b48938bb..b9b2f10037262 100644
--- a/lib/client-assets.php
+++ b/lib/client-assets.php
@@ -231,7 +231,7 @@ function gutenberg_register_scripts_and_styles() {
gutenberg_override_script(
'wp-api-fetch',
gutenberg_url( 'build/api-fetch/index.js' ),
- array( 'wp-polyfill', 'wp-hooks', 'wp-i18n' ),
+ array( 'wp-polyfill', 'wp-hooks', 'wp-i18n', 'wp-url' ),
filemtime( gutenberg_dir_path() . 'build/api-fetch/index.js' ),
true
);
diff --git a/lib/rest-api.php b/lib/rest-api.php
index 7506f45484ee7..35f4af5c52e77 100644
--- a/lib/rest-api.php
+++ b/lib/rest-api.php
@@ -290,158 +290,10 @@ function gutenberg_register_post_prepare_functions( $post_type ) {
add_filter( "rest_prepare_{$post_type}", 'gutenberg_add_permalink_template_to_posts', 10, 3 );
add_filter( "rest_prepare_{$post_type}", 'gutenberg_add_block_format_to_post_content', 10, 3 );
add_filter( "rest_prepare_{$post_type}", 'gutenberg_add_target_schema_to_links', 10, 3 );
- add_filter( "rest_{$post_type}_collection_params", 'gutenberg_filter_post_collection_parameters', 10, 2 );
- add_filter( "rest_{$post_type}_query", 'gutenberg_filter_post_query_arguments', 10, 2 );
return $post_type;
}
add_filter( 'registered_post_type', 'gutenberg_register_post_prepare_functions' );
-/**
- * Whenever a taxonomy is registered, ensure we're hooked into its WP REST API response.
- *
- * @param string $taxonomy The newly registered taxonomy.
- */
-function gutenberg_register_taxonomy_prepare_functions( $taxonomy ) {
- add_filter( "rest_{$taxonomy}_collection_params", 'gutenberg_filter_term_collection_parameters', 10, 2 );
- add_filter( "rest_{$taxonomy}_query", 'gutenberg_filter_term_query_arguments', 10, 2 );
-}
-add_filter( 'registered_taxonomy', 'gutenberg_register_taxonomy_prepare_functions' );
-
-/**
- * Handle any necessary checks early.
- *
- * @param WP_HTTP_Response $response Result to send to the client. Usually a WP_REST_Response.
- * @param WP_REST_Server $handler ResponseHandler instance (usually WP_REST_Server).
- * @param WP_REST_Request $request Request used to generate the response.
- */
-function gutenberg_handle_early_callback_checks( $response, $handler, $request ) {
- if ( 0 === strpos( $request->get_route(), '/wp/v2/' ) ) {
- $can_unbounded_query = false;
- $types = get_post_types( array( 'show_in_rest' => true ), 'objects' );
- foreach ( $types as $type ) {
- if ( current_user_can( $type->cap->edit_posts ) ) {
- $can_unbounded_query = true;
- }
- }
- if ( $request['per_page'] < 0 ) {
- if ( ! $can_unbounded_query ) {
- return new WP_Error( 'rest_forbidden_per_page', __( 'Sorry, you are not allowed to make unbounded queries.', 'gutenberg' ), array( 'status' => rest_authorization_required_code() ) );
- }
- }
- }
- return $response;
-}
-add_filter( 'rest_request_before_callbacks', 'gutenberg_handle_early_callback_checks', 10, 3 );
-
-/**
- * Include additional query parameters on the posts query endpoint.
- *
- * @see https://core.trac.wordpress.org/ticket/43998
- *
- * @param array $query_params JSON Schema-formatted collection parameters.
- * @param WP_Post_Type $post_type Post type object being accessed.
- * @return array
- */
-function gutenberg_filter_post_collection_parameters( $query_params, $post_type ) {
- if (
- isset( $query_params['per_page'] ) &&
- ( $post_type->hierarchical || 'wp_block' === $post_type->name )
- ) {
- // Change from '1' to '-1', which means unlimited.
- $query_params['per_page']['minimum'] = -1;
- // Default sanitize callback is 'absint', which won't work in our case.
- $query_params['per_page']['sanitize_callback'] = 'rest_sanitize_request_arg';
- }
- return $query_params;
-}
-
-/**
- * Filter post collection query parameters to include specific behavior.
- *
- * @see https://core.trac.wordpress.org/ticket/43998
- *
- * @param array $prepared_args Array of arguments for WP_Query.
- * @param WP_REST_Request $request The current request.
- * @return array
- */
-function gutenberg_filter_post_query_arguments( $prepared_args, $request ) {
- if (
- is_post_type_hierarchical( $prepared_args['post_type'] ) ||
- 'wp_block' === $prepared_args['post_type']
- ) {
- // Avoid triggering 'rest_post_invalid_page_number' error
- // which will need to be addressed in https://core.trac.wordpress.org/ticket/43998.
- if ( -1 === $prepared_args['posts_per_page'] ) {
- $prepared_args['posts_per_page'] = 100000;
- }
- }
- return $prepared_args;
-}
-
-/**
- * Include additional query parameters on the terms query endpoint.
- *
- * @see https://core.trac.wordpress.org/ticket/43998
- *
- * @param array $query_params JSON Schema-formatted collection parameters.
- * @param object $taxonomy Taxonomy being accessed.
- * @return array
- */
-function gutenberg_filter_term_collection_parameters( $query_params, $taxonomy ) {
- if ( $taxonomy->show_in_rest
- && ( false === $taxonomy->rest_controller_class
- || 'WP_REST_Terms_Controller' === $taxonomy->rest_controller_class )
- && isset( $query_params['per_page'] ) ) {
- // Change from '1' to '-1', which means unlimited.
- $query_params['per_page']['minimum'] = -1;
- // Default sanitize callback is 'absint', which won't work in our case.
- $query_params['per_page']['sanitize_callback'] = 'rest_sanitize_request_arg';
- }
- return $query_params;
-}
-
-/**
- * Filter term collection query parameters to include specific behavior.
- *
- * @see https://core.trac.wordpress.org/ticket/43998
- *
- * @param array $prepared_args Array of arguments for WP_Term_Query.
- * @param WP_REST_Request $request The current request.
- * @return array
- */
-function gutenberg_filter_term_query_arguments( $prepared_args, $request ) {
- // Can't check the actual taxonomy here because it's not
- // passed through in $prepared_args (or the filter generally).
- if ( 0 === strpos( $request->get_route(), '/wp/v2/' ) ) {
- if ( -1 === $prepared_args['number'] ) {
- // This should be unset( $prepared_args['number'] )
- // but WP_REST_Terms Controller needs to be updated to support
- // unbounded queries.
- // Will be addressed in https://core.trac.wordpress.org/ticket/43998.
- $prepared_args['number'] = 100000;
- }
- }
- return $prepared_args;
-}
-
-/**
- * Include additional query parameters on the user query endpoint.
- *
- * @see https://core.trac.wordpress.org/ticket/43998
- *
- * @param array $query_params JSON Schema-formatted collection parameters.
- * @return array
- */
-function gutenberg_filter_user_collection_parameters( $query_params ) {
- if ( isset( $query_params['per_page'] ) ) {
- // Change from '1' to '-1', which means unlimited.
- $query_params['per_page']['minimum'] = -1;
- // Default sanitize callback is 'absint', which won't work in our case.
- $query_params['per_page']['sanitize_callback'] = 'rest_sanitize_request_arg';
- }
- return $query_params;
-}
-add_filter( 'rest_user_collection_params', 'gutenberg_filter_user_collection_parameters' );
/**
* Silence PHP Warnings and Errors in JSON requests
diff --git a/package-lock.json b/package-lock.json
index 1867236d23dc4..179915339a81b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2007,7 +2007,8 @@
"requires": {
"@babel/runtime": "^7.0.0",
"@wordpress/hooks": "file:packages/hooks",
- "@wordpress/i18n": "file:packages/i18n"
+ "@wordpress/i18n": "file:packages/i18n",
+ "@wordpress/url": "file:packages/url"
}
},
"@wordpress/autop": {
diff --git a/packages/api-fetch/CHANGELOG.md b/packages/api-fetch/CHANGELOG.md
index a49cefcd0d75c..7d53f6aa2222f 100644
--- a/packages/api-fetch/CHANGELOG.md
+++ b/packages/api-fetch/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 2.1.0 (Unreleased)
+
+- Support `per_page=-1` paginated requests.
+
## 2.0.0 (2018-09-05)
### Breaking Change
diff --git a/packages/api-fetch/package.json b/packages/api-fetch/package.json
index ed66f99b0444e..bf5ed7b10f8f0 100644
--- a/packages/api-fetch/package.json
+++ b/packages/api-fetch/package.json
@@ -23,7 +23,8 @@
"dependencies": {
"@babel/runtime": "^7.0.0",
"@wordpress/hooks": "file:../hooks",
- "@wordpress/i18n": "file:../i18n"
+ "@wordpress/i18n": "file:../i18n",
+ "@wordpress/url": "file:../url"
},
"publishConfig": {
"access": "public"
diff --git a/packages/api-fetch/src/index.js b/packages/api-fetch/src/index.js
index a2ebeeff37a9a..b028fef611e4b 100644
--- a/packages/api-fetch/src/index.js
+++ b/packages/api-fetch/src/index.js
@@ -9,6 +9,7 @@ import { __ } from '@wordpress/i18n';
import createNonceMiddleware from './middlewares/nonce';
import createRootURLMiddleware from './middlewares/root-url';
import createPreloadingMiddleware from './middlewares/preloading';
+import fetchAllMiddleware from './middlewares/fetch-all-middleware';
import namespaceEndpointMiddleware from './middlewares/namespace-endpoint';
import httpV1Middleware from './middlewares/http-v1';
@@ -105,16 +106,19 @@ function apiFetch( options ) {
const steps = [
raw,
+ fetchAllMiddleware,
httpV1Middleware,
namespaceEndpointMiddleware,
...middlewares,
- ];
- const next = ( nextOptions ) => {
- const nextMiddleware = steps.pop();
+ ].reverse();
+
+ const runMiddleware = ( index ) => ( nextOptions ) => {
+ const nextMiddleware = steps[ index ];
+ const next = runMiddleware( index + 1 );
return nextMiddleware( nextOptions, next );
};
- return next( options );
+ return runMiddleware( 0 )( options );
}
apiFetch.use = registerMiddleware;
@@ -122,5 +126,6 @@ apiFetch.use = registerMiddleware;
apiFetch.createNonceMiddleware = createNonceMiddleware;
apiFetch.createPreloadingMiddleware = createPreloadingMiddleware;
apiFetch.createRootURLMiddleware = createRootURLMiddleware;
+apiFetch.fetchAllMiddleware = fetchAllMiddleware;
export default apiFetch;
diff --git a/packages/api-fetch/src/middlewares/fetch-all-middleware.js b/packages/api-fetch/src/middlewares/fetch-all-middleware.js
new file mode 100644
index 0000000000000..5bc17b5413d32
--- /dev/null
+++ b/packages/api-fetch/src/middlewares/fetch-all-middleware.js
@@ -0,0 +1,93 @@
+/**
+ * WordPress dependencies
+ */
+import { addQueryArgs } from '@wordpress/url';
+
+// Apply query arguments to both URL and Path, whichever is present.
+const modifyQuery = ( { path, url, ...options }, queryArgs ) => ( {
+ ...options,
+ url: url && addQueryArgs( url, queryArgs ),
+ path: path && addQueryArgs( path, queryArgs ),
+} );
+
+// Duplicates parsing functionality from apiFetch.
+const parseResponse = ( response ) => response.json ?
+ response.json() :
+ Promise.reject( response );
+
+const parseLinkHeader = ( linkHeader ) => {
+ if ( ! linkHeader ) {
+ return {};
+ }
+ const match = linkHeader.match( /<([^>]+)>; rel="next"/ );
+ return match ? {
+ next: match[ 1 ],
+ } : {};
+};
+
+const getNextPageUrl = ( response ) => {
+ const { next } = parseLinkHeader( response.headers.get( 'link' ) );
+ return next;
+};
+
+const requestContainsUnboundedQuery = ( options ) => {
+ const pathIsUnbounded = options.path && options.path.indexOf( 'per_page=-1' ) !== -1;
+ const urlIsUnbounded = options.url && options.url.indexOf( 'per_page=-1' ) !== -1;
+ return pathIsUnbounded || urlIsUnbounded;
+};
+
+// The REST API enforces an upper limit on the per_page option. To handle large
+// collections, apiFetch consumers can pass `per_page=-1`; this middleware will
+// then recursively assemble a full response array from all available pages.
+const fetchAllMiddleware = async ( options, next ) => {
+ if ( options.parse === false ) {
+ // If a consumer has opted out of parsing, do not apply middleware.
+ return next( options );
+ }
+ if ( ! requestContainsUnboundedQuery( options ) ) {
+ // If neither url nor path is requesting all items, do not apply middleware.
+ return next( options );
+ }
+
+ // Retrieve requested page of results.
+ const response = await next( {
+ ...modifyQuery( options, {
+ per_page: 100,
+ } ),
+ // Ensure headers are returned for page 1.
+ parse: false,
+ } );
+
+ const results = await parseResponse( response );
+
+ if ( ! Array.isArray( results ) ) {
+ // We have no reliable way of merging non-array results.
+ return results;
+ }
+
+ let nextPage = getNextPageUrl( response );
+
+ if ( ! nextPage ) {
+ // There are no further pages to request.
+ return results;
+ }
+
+ // Iteratively fetch all remaining pages until no "next" header is found.
+ let mergedResults = [].concat( results );
+ while ( nextPage ) {
+ const nextResponse = await next( {
+ ...options,
+ // Ensure the URL for the next page is used instead of any provided path.
+ path: undefined,
+ url: nextPage,
+ // Ensure we still get headers so we can identify the next page.
+ parse: false,
+ } );
+ const nextResults = await parseResponse( nextResponse );
+ mergedResults = mergedResults.concat( nextResults );
+ nextPage = getNextPageUrl( nextResponse );
+ }
+ return mergedResults;
+};
+
+export default fetchAllMiddleware;
diff --git a/packages/api-fetch/src/middlewares/test/fetch-all-middleware.js b/packages/api-fetch/src/middlewares/test/fetch-all-middleware.js
new file mode 100644
index 0000000000000..c002790ead45c
--- /dev/null
+++ b/packages/api-fetch/src/middlewares/test/fetch-all-middleware.js
@@ -0,0 +1,51 @@
+/**
+ * Internal dependencies
+ */
+import fetchAllMiddleware from '../fetch-all-middleware';
+
+describe( 'Fetch All Middleware', async () => {
+ it( 'should defer with the same options to the next middleware', async () => {
+ expect.hasAssertions();
+ const originalOptions = { path: '/posts' };
+ const next = ( options ) => {
+ expect( options ).toBe( originalOptions );
+ return Promise.resolve( 'ok' );
+ };
+
+ await fetchAllMiddleware( originalOptions, next );
+ } );
+
+ it( 'should paginate the request', async () => {
+ expect.hasAssertions();
+ const originalOptions = { url: '/posts?per_page=-1' };
+ let counter = 1;
+ const next = ( options ) => {
+ if ( counter === 1 ) {
+ expect( options.url ).toBe( '/posts?per_page=100' );
+ } else {
+ expect( options.url ).toBe( '/posts?per_page=100&page=2' );
+ }
+ const response = Promise.resolve( {
+ status: 200,
+ headers: {
+ get() {
+ return options.url === '/posts?per_page=100' ?
+ '; rel="next"' :
+ '';
+ },
+ },
+ json() {
+ return Promise.resolve( [ 'item' ] );
+ },
+ } );
+
+ counter++;
+
+ return response;
+ };
+
+ const result = await fetchAllMiddleware( originalOptions, next );
+
+ expect( result ).toEqual( [ 'item', 'item' ] );
+ } );
+} );
diff --git a/packages/edit-post/src/hooks/components/media-upload/index.js b/packages/edit-post/src/hooks/components/media-upload/index.js
index 72e223ce66bd5..f950d34d628ee 100644
--- a/packages/edit-post/src/hooks/components/media-upload/index.js
+++ b/packages/edit-post/src/hooks/components/media-upload/index.js
@@ -67,8 +67,8 @@ const getAttachmentsCollection = ( ids ) => {
return wp.media.query( {
order: 'ASC',
orderby: 'post__in',
- per_page: -1,
post__in: ids,
+ per_page: 100,
query: true,
type: 'image',
} );
diff --git a/phpunit/class-gutenberg-rest-api-test.php b/phpunit/class-gutenberg-rest-api-test.php
index 6dc323d6179bd..293dc7fed3526 100644
--- a/phpunit/class-gutenberg-rest-api-test.php
+++ b/phpunit/class-gutenberg-rest-api-test.php
@@ -381,17 +381,7 @@ public function test_get_items_unbounded_per_page() {
$request = new WP_REST_Request( 'GET', '/wp/v2/users' );
$request->set_param( 'per_page', '-1' );
$response = rest_get_server()->dispatch( $request );
- $this->assertEquals( 200, $response->get_status() );
- }
-
- public function test_get_items_unbounded_per_page_unauthorized() {
- wp_set_current_user( $this->subscriber );
- $request = new WP_REST_Request( 'GET', '/wp/v2/users' );
- $request->set_param( 'per_page', '-1' );
- $response = rest_get_server()->dispatch( $request );
- $this->assertEquals( 403, $response->get_status() );
- $data = $response->get_data();
- $this->assertEquals( 'rest_forbidden_per_page', $data['code'] );
+ $this->assertEquals( 400, $response->get_status() );
}
public function test_get_categories_unbounded_per_page() {
@@ -400,18 +390,7 @@ public function test_get_categories_unbounded_per_page() {
$request = new WP_REST_Request( 'GET', '/wp/v2/categories' );
$request->set_param( 'per_page', '-1' );
$response = rest_get_server()->dispatch( $request );
- $this->assertEquals( 200, $response->get_status() );
- }
-
- public function test_get_categories_unbounded_per_page_unauthorized() {
- wp_set_current_user( $this->subscriber );
- $this->factory->category->create();
- $request = new WP_REST_Request( 'GET', '/wp/v2/categories' );
- $request->set_param( 'per_page', '-1' );
- $response = rest_get_server()->dispatch( $request );
- $this->assertEquals( 403, $response->get_status() );
- $data = $response->get_data();
- $this->assertEquals( 'rest_forbidden_per_page', $data['code'] );
+ $this->assertEquals( 400, $response->get_status() );
}
public function test_get_pages_unbounded_per_page() {
@@ -420,18 +399,7 @@ public function test_get_pages_unbounded_per_page() {
$request = new WP_REST_Request( 'GET', '/wp/v2/pages' );
$request->set_param( 'per_page', '-1' );
$response = rest_get_server()->dispatch( $request );
- $this->assertEquals( 200, $response->get_status() );
- }
-
- public function test_get_pages_unbounded_per_page_unauthorized() {
- wp_set_current_user( $this->subscriber );
- $this->factory->post->create( array( 'post_type' => 'page' ) );
- $request = new WP_REST_Request( 'GET', '/wp/v2/pages' );
- $request->set_param( 'per_page', '-1' );
- $response = rest_get_server()->dispatch( $request );
- $this->assertEquals( 403, $response->get_status() );
- $data = $response->get_data();
- $this->assertEquals( 'rest_forbidden_per_page', $data['code'] );
+ $this->assertEquals( 400, $response->get_status() );
}
public function test_get_post_links_predecessor_version() {
From 3edce55557aef08d024011429237570b4ef945f5 Mon Sep 17 00:00:00 2001
From: Riad Benguella
Date: Mon, 22 Oct 2018 12:57:15 +0100
Subject: [PATCH 049/121] Update plugin version to 4.1 RC2. (#10893)
---
gutenberg.php | 2 +-
package-lock.json | 2 +-
package.json | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/gutenberg.php b/gutenberg.php
index fdc1cf2ab1767..e285a33310e81 100644
--- a/gutenberg.php
+++ b/gutenberg.php
@@ -3,7 +3,7 @@
* Plugin Name: Gutenberg
* Plugin URI: https://github.com/WordPress/gutenberg
* Description: Printing since 1440. This is the development plugin for the new block editor in core.
- * Version: 4.1.0-rc.1
+ * Version: 4.1.0-rc.2
* Author: Gutenberg Team
*
* @package gutenberg
diff --git a/package-lock.json b/package-lock.json
index 179915339a81b..31155db7a32f1 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "gutenberg",
- "version": "4.1.0-rc.1",
+ "version": "4.1.0-rc.2",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
diff --git a/package.json b/package.json
index 7368a6e70f24c..333b9e3c2b3a2 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "gutenberg",
- "version": "4.1.0-rc.1",
+ "version": "4.1.0-rc.2",
"private": true,
"description": "A new WordPress editor experience",
"repository": "git+https://github.com/WordPress/gutenberg.git",
From 08476d7fc1533357dca48ad8fb185cd76096c41d Mon Sep 17 00:00:00 2001
From: Grzegorz Ziolkowski
Date: Mon, 22 Oct 2018 14:31:52 +0200
Subject: [PATCH 050/121] chore(release): publish
- @wordpress/api-fetch@2.1.0
- @wordpress/block-library@2.1.4
- @wordpress/components@4.2.1
- @wordpress/core-data@2.0.6
- @wordpress/edit-post@1.0.4
- @wordpress/editor@5.0.1
- @wordpress/list-reusable-blocks@1.1.4
- @wordpress/nux@2.0.6
---
packages/api-fetch/package.json | 2 +-
packages/block-library/package.json | 2 +-
packages/components/package.json | 2 +-
packages/core-data/package.json | 2 +-
packages/edit-post/package.json | 2 +-
packages/editor/package.json | 2 +-
packages/list-reusable-blocks/package.json | 2 +-
packages/nux/package.json | 2 +-
8 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/packages/api-fetch/package.json b/packages/api-fetch/package.json
index bf5ed7b10f8f0..b4982c12e4f40 100644
--- a/packages/api-fetch/package.json
+++ b/packages/api-fetch/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/api-fetch",
- "version": "2.0.2",
+ "version": "2.1.0",
"description": "Utility to make WordPress REST API requests.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/block-library/package.json b/packages/block-library/package.json
index 01308b546811a..5136152d21551 100644
--- a/packages/block-library/package.json
+++ b/packages/block-library/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/block-library",
- "version": "2.1.3",
+ "version": "2.1.4",
"description": "Block library for the WordPress editor.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/components/package.json b/packages/components/package.json
index 6494611c19583..881d5b7b335e8 100644
--- a/packages/components/package.json
+++ b/packages/components/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/components",
- "version": "4.2.0",
+ "version": "4.2.1",
"description": "UI components for WordPress.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/core-data/package.json b/packages/core-data/package.json
index a829391c5bce3..2bdcb67dd005a 100644
--- a/packages/core-data/package.json
+++ b/packages/core-data/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/core-data",
- "version": "2.0.5",
+ "version": "2.0.6",
"description": "Access to and manipulation of core WordPress entities.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/edit-post/package.json b/packages/edit-post/package.json
index 24efc07ffab98..0b5bd5d6d68f8 100644
--- a/packages/edit-post/package.json
+++ b/packages/edit-post/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/edit-post",
- "version": "1.0.3",
+ "version": "1.0.4",
"description": "Edit Post module for WordPress.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/editor/package.json b/packages/editor/package.json
index db733a7e241f7..17ee77c6683f4 100644
--- a/packages/editor/package.json
+++ b/packages/editor/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/editor",
- "version": "5.0.0",
+ "version": "5.0.1",
"description": "Building blocks for WordPress editors.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/list-reusable-blocks/package.json b/packages/list-reusable-blocks/package.json
index 983d41d36e116..3249cbef1c7d7 100644
--- a/packages/list-reusable-blocks/package.json
+++ b/packages/list-reusable-blocks/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/list-reusable-blocks",
- "version": "1.1.3",
+ "version": "1.1.4",
"description": "Adding Export/Import support to the reusable blocks listing.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
diff --git a/packages/nux/package.json b/packages/nux/package.json
index 44d6b23e156cb..6077e082a7b77 100644
--- a/packages/nux/package.json
+++ b/packages/nux/package.json
@@ -1,6 +1,6 @@
{
"name": "@wordpress/nux",
- "version": "2.0.5",
+ "version": "2.0.6",
"description": "NUX (New User eXperience) module for WordPress.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
From 1cd604df7e9017e0dbe3ab64897ac3af35ca35c5 Mon Sep 17 00:00:00 2001
From: Gary Pendergast
Date: Mon, 22 Oct 2018 23:35:17 +1100
Subject: [PATCH 051/121] Update Package Changelogs (#10887)
* Update package changelogs
* Bump the changelog date, because it's now Monday for everybody.
---
packages/api-fetch/CHANGELOG.md | 2 +-
packages/block-library/CHANGELOG.md | 6 ++++++
packages/components/CHANGELOG.md | 2 +-
packages/core-data/CHANGELOG.md | 2 ++
packages/edit-post/CHANGELOG.md | 2 +-
packages/editor/CHANGELOG.md | 2 ++
packages/list-reusable-blocks/CHANGELOG.md | 2 ++
packages/nux/CHANGELOG.md | 2 ++
8 files changed, 17 insertions(+), 3 deletions(-)
diff --git a/packages/api-fetch/CHANGELOG.md b/packages/api-fetch/CHANGELOG.md
index 7d53f6aa2222f..e253b64d5bf19 100644
--- a/packages/api-fetch/CHANGELOG.md
+++ b/packages/api-fetch/CHANGELOG.md
@@ -1,4 +1,4 @@
-## 2.1.0 (Unreleased)
+## 2.1.0 (2018-10-22)
- Support `per_page=-1` paginated requests.
diff --git a/packages/block-library/CHANGELOG.md b/packages/block-library/CHANGELOG.md
index 6fe1fd0ef7ce1..a28fed6c42a21 100644
--- a/packages/block-library/CHANGELOG.md
+++ b/packages/block-library/CHANGELOG.md
@@ -1,3 +1,9 @@
+## 2.1.4 (2018-10-22)
+
+### Bug Fixes
+
+- Video Block: Set correct media types for the poster image.
+
## 2.1.3 (2018-10-19)
## 2.1.2 (2018-10-18)
diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md
index bf670914795e0..1eef1646bf3a0 100644
--- a/packages/components/CHANGELOG.md
+++ b/packages/components/CHANGELOG.md
@@ -1,4 +1,4 @@
-## 4.2.1 (Unreleased)
+## 4.2.1 (2018-10-22)
### Bug Fix
diff --git a/packages/core-data/CHANGELOG.md b/packages/core-data/CHANGELOG.md
index 30bfdf415a54a..efb7fe6c22c47 100644
--- a/packages/core-data/CHANGELOG.md
+++ b/packages/core-data/CHANGELOG.md
@@ -1,3 +1,5 @@
+## 2.0.6 (2018-10-22)
+
## 2.0.5 (2018-10-19)
## 2.0.4 (2018-10-18)
diff --git a/packages/edit-post/CHANGELOG.md b/packages/edit-post/CHANGELOG.md
index 2db3d57d70ba1..7cecf41a7060a 100644
--- a/packages/edit-post/CHANGELOG.md
+++ b/packages/edit-post/CHANGELOG.md
@@ -1,4 +1,4 @@
-## 1.0.4 (Unreleased)
+## 1.0.4 (2018-10-22)
## Bug Fixes
diff --git a/packages/editor/CHANGELOG.md b/packages/editor/CHANGELOG.md
index 691851a6bf4ef..3f7d1d3b46697 100644
--- a/packages/editor/CHANGELOG.md
+++ b/packages/editor/CHANGELOG.md
@@ -1,3 +1,5 @@
+## 5.0.1 (2018-10-22)
+
## 5.0.0 (2018-10-19)
### Breaking Changes
diff --git a/packages/list-reusable-blocks/CHANGELOG.md b/packages/list-reusable-blocks/CHANGELOG.md
index 7e54897866ced..6234aa478b9ab 100644
--- a/packages/list-reusable-blocks/CHANGELOG.md
+++ b/packages/list-reusable-blocks/CHANGELOG.md
@@ -1,3 +1,5 @@
+## 1.1.4 (2018-10-22)
+
## 1.1.3 (2018-10-19)
## 1.1.2 (2018-10-18)
diff --git a/packages/nux/CHANGELOG.md b/packages/nux/CHANGELOG.md
index 9b4bad0bd4aa5..393798e3eece1 100644
--- a/packages/nux/CHANGELOG.md
+++ b/packages/nux/CHANGELOG.md
@@ -1,3 +1,5 @@
+## 2.0.6 (2018-10-22)
+
## 2.0.5 (2018-10-19)
## 2.0.4 (2018-10-18)
From 80e782c8bfae780137f0ac4d69ddf2302aed2930 Mon Sep 17 00:00:00 2001
From: Andrew Duthie
Date: Tue, 23 Oct 2018 02:07:06 -0400
Subject: [PATCH 052/121] Components: Avoid SlotFill props destructuring
(#10921)
---
packages/components/src/slot-fill/index.js | 12 ++----------
1 file changed, 2 insertions(+), 10 deletions(-)
diff --git a/packages/components/src/slot-fill/index.js b/packages/components/src/slot-fill/index.js
index c14f7adafc879..bec51806039a6 100644
--- a/packages/components/src/slot-fill/index.js
+++ b/packages/components/src/slot-fill/index.js
@@ -10,18 +10,10 @@ export { Fill };
export { Provider };
export function createSlotFill( name ) {
- const FillComponent = ( { children, ...props } ) => (
-
- { children }
-
- );
+ const FillComponent = ( props ) => ;
FillComponent.displayName = name + 'Fill';
- const SlotComponent = ( { children, ...props } ) => (
-
- { children }
-
- );
+ const SlotComponent = ( props ) => ;
SlotComponent.displayName = name + 'Slot';
return {
From 898f2f7aa67939140b0c89c70fbb8dde6f5be1e2 Mon Sep 17 00:00:00 2001
From: Pascal Birchler
Date: Tue, 23 Oct 2018 08:12:49 +0200
Subject: [PATCH 053/121] Add some new url helper functions (#10885)
* Add some new url helper functions
See #10879.
* Document new functions in readme
* Improve docs
* Add another test for removeQueryArgs()
---
packages/url/CHANGELOG.md | 8 ++++
packages/url/README.md | 9 +++++
packages/url/src/index.js | 51 +++++++++++++++++++++--
packages/url/src/test/index.test.js | 63 +++++++++++++++++++++++++++++
4 files changed, 128 insertions(+), 3 deletions(-)
diff --git a/packages/url/CHANGELOG.md b/packages/url/CHANGELOG.md
index e0f2f033c4af9..0b1dfb761ff72 100644
--- a/packages/url/CHANGELOG.md
+++ b/packages/url/CHANGELOG.md
@@ -1,3 +1,11 @@
+## 2.2.0 (Unreleased)
+
+### Features
+
+- Added `getQueryArg`.
+- Added `hasQueryArg`.
+- Added `removeQueryArgs`.
+
## 2.1.0 (2018-10-16)
### Features
diff --git a/packages/url/README.md b/packages/url/README.md
index 6c15661232755..26f54fbaff57f 100644
--- a/packages/url/README.md
+++ b/packages/url/README.md
@@ -25,6 +25,15 @@ const newURL = addQueryArgs( 'https://google.com', { q: 'test' } ); // https://
// Prepends 'http://' to URLs that are probably mean to have them
const actualURL = prependHTTP( 'wordpress.org' ); // http://wordpress.org
+
+// Gets a single query arg from the given URL.
+const foo = getQueryArg( 'https://wordpress.org?foo=bar&bar=baz', 'foo' ); // bar
+
+// Checks whether a URL contains a given query arg.
+const hasBar = hasQueryArg( 'https://wordpress.org?foo=bar&bar=baz', 'bar' ); // true
+
+// Removes one or more query args from the given URL.
+const newUrl = removeQueryArgs( 'https://wordpress.org?foo=bar&bar=baz&baz=foobar', 'foo', 'bar' ); // https://wordpress.org?baz=foobar
```
diff --git a/packages/url/src/index.js b/packages/url/src/index.js
index 4538ee2040bbc..98cd123cfc666 100644
--- a/packages/url/src/index.js
+++ b/packages/url/src/index.js
@@ -21,10 +21,10 @@ export function isURL( url ) {
/**
* Appends arguments to the query string of the url
*
- * @param {string} url URL
- * @param {Object} args Query Args
+ * @param {string} url URL
+ * @param {Object} args Query Args
*
- * @return {string} Updated URL
+ * @return {string} Updated URL
*/
export function addQueryArgs( url, args ) {
const queryStringIndex = url.indexOf( '?' );
@@ -34,6 +34,51 @@ export function addQueryArgs( url, args ) {
return baseUrl + '?' + stringify( { ...query, ...args } );
}
+/**
+ * Returns a single query argument of the url
+ *
+ * @param {string} url URL
+ * @param {string} arg Query arg name
+ *
+ * @return {Array|string} Query arg value.
+ */
+export function getQueryArg( url, arg ) {
+ const queryStringIndex = url.indexOf( '?' );
+ const query = queryStringIndex !== -1 ? parse( url.substr( queryStringIndex + 1 ) ) : {};
+
+ return query[ arg ];
+}
+
+/**
+ * Determines whether the URL contains a given query arg.
+ *
+ * @param {string} url URL
+ * @param {string} arg Query arg name
+ *
+ * @return {boolean} Whether or not the URL contains the query aeg.
+ */
+export function hasQueryArg( url, arg ) {
+ return getQueryArg( url, arg ) !== undefined;
+}
+
+/**
+ * Removes arguments from the query string of the url
+ *
+ * @param {string} url URL
+ * @param {...string} args Query Args
+ *
+ * @return {string} Updated URL
+ */
+export function removeQueryArgs( url, ...args ) {
+ const queryStringIndex = url.indexOf( '?' );
+ const query = queryStringIndex !== -1 ? parse( url.substr( queryStringIndex + 1 ) ) : {};
+ const baseUrl = queryStringIndex !== -1 ? url.substr( 0, queryStringIndex ) : url;
+
+ args.forEach( ( arg ) => delete query[ arg ] );
+
+ return baseUrl + '?' + stringify( query );
+}
+
/**
* Prepends "http://" to a url, if it looks like something that is meant to be a TLD.
*
diff --git a/packages/url/src/test/index.test.js b/packages/url/src/test/index.test.js
index 7815a1cae68ed..46548528f3bc1 100644
--- a/packages/url/src/test/index.test.js
+++ b/packages/url/src/test/index.test.js
@@ -9,6 +9,9 @@ import { every } from 'lodash';
import {
isURL,
addQueryArgs,
+ getQueryArg,
+ hasQueryArg,
+ removeQueryArgs,
prependHTTP,
safeDecodeURI,
} from '../';
@@ -69,6 +72,66 @@ describe( 'addQueryArgs', () => {
} );
} );
+describe( 'getQueryArg', () => {
+ it( 'should get the value of an existing query arg', () => {
+ const url = 'https://andalouses.example/beach?foo=bar&bar=baz';
+
+ expect( getQueryArg( url, 'foo' ) ).toBe( 'bar' );
+ } );
+
+ it( 'should not return a value of an unknown query arg', () => {
+ const url = 'https://andalouses.example/beach?foo=bar&bar=baz';
+
+ expect( getQueryArg( url, 'baz' ) ).toBeUndefined();
+ } );
+
+ it( 'should get the value of an arry query arg', () => {
+ const url = 'https://andalouses.example/beach?foo[]=bar&foo[]=baz';
+
+ expect( getQueryArg( url, 'foo' ) ).toEqual( [ 'bar', 'baz' ] );
+ } );
+} );
+
+describe( 'hasQueryArg', () => {
+ it( 'should return true for an existing query arg', () => {
+ const url = 'https://andalouses.example/beach?foo=bar&bar=baz';
+
+ expect( hasQueryArg( url, 'foo' ) ).toBeTruthy();
+ } );
+
+ it( 'should return false for an unknown query arg', () => {
+ const url = 'https://andalouses.example/beach?foo=bar&bar=baz';
+
+ expect( hasQueryArg( url, 'baz' ) ).toBeFalsy();
+ } );
+
+ it( 'should return true for an arry query arg', () => {
+ const url = 'https://andalouses.example/beach?foo[]=bar&foo[]=baz';
+
+ expect( hasQueryArg( url, 'foo' ) ).toBeTruthy();
+ } );
+} );
+
+describe( 'removeQueryArgs', () => {
+ it( 'should not change URL not containing query args', () => {
+ const url = 'https://andalouses.example/beach?foo=bar&bar=baz';
+
+ expect( removeQueryArgs( url, 'baz', 'test' ) ).toEqual( url );
+ } );
+
+ it( 'should remove existing query args', () => {
+ const url = 'https://andalouses.example/beach?foo=bar&baz=foo&bar=baz';
+
+ expect( removeQueryArgs( url, 'foo', 'bar' ) ).toEqual( 'https://andalouses.example/beach?baz=foo' );
+ } );
+
+ it( 'should remove array query arg', () => {
+ const url = 'https://andalouses.example/beach?foo[]=bar&foo[]=baz&bar=foobar';
+
+ expect( removeQueryArgs( url, 'foo' ) ).toEqual( 'https://andalouses.example/beach?bar=foobar' );
+ } );
+} );
+
describe( 'prependHTTP', () => {
it( 'should prepend http to a domain', () => {
const url = 'wordpress.org';
From 2da4f0ce224ca6193f40be5939e4d129d00cc447 Mon Sep 17 00:00:00 2001
From: Danilo Ercoli
Date: Tue, 23 Oct 2018 08:52:39 +0200
Subject: [PATCH 054/121] Remove the root tags returned by Aztec by using the
correct tag reference (#10797)
---
.../src/components/rich-text/index.native.js | 23 ++++++++++++-------
1 file changed, 15 insertions(+), 8 deletions(-)
diff --git a/packages/editor/src/components/rich-text/index.native.js b/packages/editor/src/components/rich-text/index.native.js
index 48a0ec86b1634..7ec958136f8f5 100644
--- a/packages/editor/src/components/rich-text/index.native.js
+++ b/packages/editor/src/components/rich-text/index.native.js
@@ -114,9 +114,8 @@ export class RichText extends Component {
valueToFormat( { formats, text } ) {
const value = toHTMLString( { formats, text }, this.multilineTag );
- // remove the outer p tags
- const returningContentWithoutParaTag = value.replace( /|<\/p>/gi, '' );
- return returningContentWithoutParaTag;
+ // remove the outer root tags
+ return this.removeRootTagsProduceByAztec( value );
}
onActiveFormatsChange( formats ) {
@@ -133,6 +132,18 @@ export class RichText extends Component {
selectedNodeId: this.state.selectedNodeId + 1,
} );
}
+
+ /*
+ * Cleans up any root tags produced by aztec.
+ * TODO: This should be removed on a later version when aztec doesn't return the top tag of the text being edited
+ */
+
+ removeRootTagsProduceByAztec( html ) {
+ const openingTagRegexp = RegExp( '^<' + this.props.tagName + '>', 'gim' );
+ const closingTagRegexp = RegExp( '' + this.props.tagName + '>$', 'gim' );
+ return html.replace( openingTagRegexp, '' ).replace( closingTagRegexp, '' );
+ }
+
/**
* Handles any case where the content of the AztecRN instance has changed.
*/
@@ -143,11 +154,7 @@ export class RichText extends Component {
clearTimeout( this.currentTimer );
}
this.lastEventCount = event.nativeEvent.eventCount;
- // The following method just cleans up any root tags produced by aztec and replaces them with a br tag
- // This should be removed on a later version when aztec doesn't return the top tag of the text being edited
- const openingTagRegexp = RegExp( '^<' + this.props.tagName + '>', 'gim' );
- const closingTagRegexp = RegExp( '' + this.props.tagName + '>$', 'gim' );
- const contentWithoutRootTag = event.nativeEvent.text.replace( openingTagRegexp, '' ).replace( closingTagRegexp, '' );
+ const contentWithoutRootTag = this.removeRootTagsProduceByAztec( event.nativeEvent.text );
this.lastContent = contentWithoutRootTag;
// Set a time to call the onChange prop if nothing changes in the next second
this.currentTimer = setTimeout( function() {
From 61798e98fb37dfd7ee1ef5edab3c29f0fe0d8e1a Mon Sep 17 00:00:00 2001
From: Ben Lowery
Date: Tue, 23 Oct 2018 03:11:05 -0400
Subject: [PATCH 055/121] Update react-click-outside to 3.0 (#10748)
* Update react-click-outside to 3.0
* update package-lock
---
package-lock.json | 22 ++++++++++++----------
packages/components/package.json | 2 +-
2 files changed, 13 insertions(+), 11 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index 31155db7a32f1..8a6d80fac0540 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2142,7 +2142,7 @@
"moment": "^2.22.1",
"mousetrap": "^1.6.2",
"re-resizable": "^4.7.1",
- "react-click-outside": "^2.3.1",
+ "react-click-outside": "^3.0.0",
"react-dates": "^17.1.1",
"rememo": "^3.0.0",
"tinycolor2": "^1.4.1",
@@ -9820,11 +9820,6 @@
"minimalistic-crypto-utils": "^1.0.1"
}
},
- "hoist-non-react-statics": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz",
- "integrity": "sha1-qkSM8JhtVcxAdzsXF0t90GbLfPs="
- },
"home-or-tmp": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz",
@@ -17102,11 +17097,18 @@
}
},
"react-click-outside": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/react-click-outside/-/react-click-outside-2.3.1.tgz",
- "integrity": "sha1-MYc3698IGko7zUaCVmNnTL6YNus=",
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/react-click-outside/-/react-click-outside-3.0.1.tgz",
+ "integrity": "sha512-d0KWFvBt+esoZUF15rL2UBB7jkeAqLU8L/Ny35oLK6fW6mIbOv/ChD+ExF4sR9PD26kVx+9hNfD0FTIqRZEyRQ==",
"requires": {
- "hoist-non-react-statics": "^1.2.0"
+ "hoist-non-react-statics": "^2.1.1"
+ },
+ "dependencies": {
+ "hoist-non-react-statics": {
+ "version": "2.5.5",
+ "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz",
+ "integrity": "sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw=="
+ }
}
},
"react-dates": {
diff --git a/packages/components/package.json b/packages/components/package.json
index 881d5b7b335e8..0ad6b90441d9e 100644
--- a/packages/components/package.json
+++ b/packages/components/package.json
@@ -41,7 +41,7 @@
"moment": "^2.22.1",
"mousetrap": "^1.6.2",
"re-resizable": "^4.7.1",
- "react-click-outside": "^2.3.1",
+ "react-click-outside": "^3.0.0",
"react-dates": "^17.1.1",
"rememo": "^3.0.0",
"tinycolor2": "^1.4.1",
From 74214c0757f645e287ad6d48016697383733e7eb Mon Sep 17 00:00:00 2001
From: Jorge
Date: Tue, 23 Oct 2018 10:17:30 +0100
Subject: [PATCH 056/121] Fix: Show resizer on "Media & Text" block on unified
toolbar mode (#10913)
## Description
Fixes: https://github.com/WordPress/gutenberg/issues/10895
On unified toolbar mode, it was not possible to resize the media on Media & Text block.
During the elaboration of https://github.com/WordPress/gutenberg/pull/9416 this bug was addressed but I think during subsequent rebases and changes more concretely the usage of our abstracted Resizable box we had a regression.
This PR applies exactly the same fix that was applied in spacer block to the Media & Text block.
## How has this been tested?
I checked it is possible to resize the media in Media & Text block in all modes including the unified toolbar.
---
packages/block-library/src/media-text/edit.js | 2 ++
packages/block-library/src/media-text/editor.scss | 3 +--
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/packages/block-library/src/media-text/edit.js b/packages/block-library/src/media-text/edit.js
index 7570b2cc7b053..8187d51c7e4b8 100644
--- a/packages/block-library/src/media-text/edit.js
+++ b/packages/block-library/src/media-text/edit.js
@@ -104,6 +104,7 @@ class MediaTextEdit extends Component {
attributes,
className,
backgroundColor,
+ isSelected,
setAttributes,
setBackgroundColor,
} = this.props;
@@ -111,6 +112,7 @@ class MediaTextEdit extends Component {
const temporaryMediaWidth = this.state.mediaWidth;
const classNames = classnames( className, {
'has-media-on-the-right': 'right' === mediaPosition,
+ 'is-selected': isSelected,
[ backgroundColor.class ]: backgroundColor.class,
} );
const widthString = `${ temporaryMediaWidth || mediaWidth }%`;
diff --git a/packages/block-library/src/media-text/editor.scss b/packages/block-library/src/media-text/editor.scss
index b0eccc5aace15..41c38c97758da 100644
--- a/packages/block-library/src/media-text/editor.scss
+++ b/packages/block-library/src/media-text/editor.scss
@@ -51,8 +51,7 @@ figure.block-library-media-text__media-container {
display: none;
}
-.editor-block-list__block.is-selected,
-.editor-block-list__block.is-focused {
+.wp-block-media-text.is-selected {
.editor-media-container__resizer .components-resizable-box__handle {
display: block;
}
From 68b454c43ad73da04d01173a4b64fe25c09d68fd Mon Sep 17 00:00:00 2001
From: Joen Asmussen
Date: Tue, 23 Oct 2018 11:19:58 +0200
Subject: [PATCH 057/121] Try WordPress logo animation for preview (#10896)
* Try WordPress logo animation for preview
This adds a WordPress logo that paints itself when you click preview.
Labelled "Try" because it still needs a few thoughts. We might need to speed up the animation because most previews are super fast and you barely see the animation.
* Clean up CSS, add vendor prefixes
* Tweak the changelog
---
packages/editor/CHANGELOG.md | 6 ++-
.../components/post-preview-button/index.js | 42 ++++++++++++++++++-
2 files changed, 45 insertions(+), 3 deletions(-)
diff --git a/packages/editor/CHANGELOG.md b/packages/editor/CHANGELOG.md
index 3f7d1d3b46697..a57ac48e8f0f9 100644
--- a/packages/editor/CHANGELOG.md
+++ b/packages/editor/CHANGELOG.md
@@ -1,5 +1,9 @@
## 5.0.1 (2018-10-22)
+### Polish
+
+- Add animated logo to preview interstitial screen.
+
## 5.0.0 (2018-10-19)
### Breaking Changes
@@ -52,7 +56,7 @@
- `isFetchingSharedBlock` selector has been removed. Use `isFetchingReusableBlock` instead.
- `getSharedBlocks` selector has been removed. Use `getReusableBlocks` instead.
- `editorMediaUpload` has been removed. Use `mediaUpload` instead.
-- Change how required built-ins are polyfilled with Babel 7 ([#9171](https://github.com/WordPress/gutenberg/pull/9171)). If you're using an environment that has limited or no support for ES2015+ such as lower versions of IE then using [core-js](https://github.com/zloirock/core-js) or [@babel/polyfill](https://babeljs.io/docs/en/next/babel-polyfill) will add support for these methods.
+- Change how required built-ins are polyfilled with Babel 7 ([#9171](https://github.com/WordPress/gutenberg/pull/9171)). If you're using an environment that has limited or no support for ES2015+ such as lower versions of IE then using [core-js](https://github.com/zloirock/core-js) or [@babel/polyfill](https://babeljs.io/docs/en/next/babel-polyfill) will add support for these methods.
- `wp.editor.DocumentTitle` component has been removed.
- `getDocumentTitle` selector (`core/editor`) has been removed.
diff --git a/packages/editor/src/components/post-preview-button/index.js b/packages/editor/src/components/post-preview-button/index.js
index 1a2494dca98f9..6d4f594195114 100644
--- a/packages/editor/src/components/post-preview-button/index.js
+++ b/packages/editor/src/components/post-preview-button/index.js
@@ -90,8 +90,11 @@ export class PostPreviewButton extends Component {
const markup = `
-
Please wait…
-
Generating preview.
+
+
+
+
+
Generating preview...